aboutsummaryrefslogtreecommitdiffstats
path: root/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java
blob: 9e6e2c3bb678e18d8e8805f14f69eeed5723217c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.provision;

import com.yahoo.config.provision.host.FlavorOverrides;
import com.yahoo.config.provisioning.FlavorsConfig;

import java.util.Objects;
import java.util.Optional;

/**
 * A host or node flavor.
 * *Host* flavors come from a configured set which corresponds to the actual flavors available in a zone.
 * *Node* flavors are simply a wrapper of a NodeResources object.
 *
 * @author bratseth
 */
public class Flavor {

    private final boolean configured;
    private final String name;
    private final int cost;
    private final Type type;

    /** The hardware resources of this flavor */
    private final NodeResources resources;

    private final Optional<FlavorOverrides> flavorOverrides;

    private static final double coreSpeedupRolloutFactor = 1.0/3.0; // TODO: Increase to 2/3 (then 1) on later releases

    /** Creates a *host* flavor from configuration */
    public Flavor(FlavorsConfig.Flavor flavorConfig) {
        this(flavorConfig.name(),
             new NodeResources(flavorConfig.minCpuCores() *
                                      (1 + (flavorConfig.cpuCoreSpeedup() - 1) * coreSpeedupRolloutFactor),
                               flavorConfig.minMainMemoryAvailableGb(),
                               flavorConfig.minDiskAvailableGb(),
                               flavorConfig.bandwidth() / 1000,
                               flavorConfig.fastDisk() ? NodeResources.DiskSpeed.fast : NodeResources.DiskSpeed.slow),
             Optional.empty(),
             Type.valueOf(flavorConfig.environment()),
             true,
             flavorConfig.cost());
    }

    /** Creates a *node* flavor from a node resources spec */
    public Flavor(NodeResources resources) {
        this(resources.toString(), resources, Optional.empty(), Type.DOCKER_CONTAINER, false, 0);
    }

    private Flavor(String name, NodeResources resources, Optional<FlavorOverrides> flavorOverrides, Type type, boolean configured, int cost) {
        this.name = Objects.requireNonNull(name, "Name cannot be null");
        this.resources = Objects.requireNonNull(resources, "Resources cannot be null");
        this.flavorOverrides = Objects.requireNonNull(flavorOverrides, "Flavor overrides cannot be null");
        this.type = Objects.requireNonNull(type, "Type cannot be null");
        this.configured = configured;
        this.cost = cost;
    }

    public Flavor with(FlavorOverrides flavorOverrides) {
        if (!configured)
            throw new IllegalArgumentException("Cannot override non-configured flavor");

        NodeResources newResources = new NodeResources(
                resources.vcpu(),
                resources.memoryGb(),
                flavorOverrides.diskGb().orElseGet(resources::diskGb),
                resources.bandwidthGbps(),
                resources.diskSpeed());
        return new Flavor(name, newResources, Optional.of(flavorOverrides), type, true, cost);
    }

    public Flavor with(NodeResources resources) {
        if (type == Type.DOCKER_CONTAINER && !configured)
            return new Flavor(resources);

        if (!resources.equals(this.resources.withDiskGb(resources.diskGb())))
            throw new IllegalArgumentException("Can only override disk GB for configured flavor");

        return with(FlavorOverrides.ofDisk(resources.diskGb()));
    }

    /** Returns the unique identity of this flavor if it is configured, or the resource spec string otherwise */
    public String name() { return name; }

    /**
     * Get the monthly cost (total cost of ownership) in USD for this flavor, typically total cost
     * divided by 36 months.
     * 
     * @return monthly cost in USD
     */
    public int cost() { return cost; }
    
    /**
     * True if this is a configured flavor used for hosts,
     * false if it is a virtual flavor created on the fly from node resources
     */
    public boolean isConfigured() { return configured; }

    public NodeResources resources() { return resources; }

    public Optional<FlavorOverrides> flavorOverrides() {
        return flavorOverrides;
    }

    public double getMinMainMemoryAvailableGb() { return resources.memoryGb(); }

    public double getMinDiskAvailableGb() { return resources.diskGb(); }

    public boolean hasFastDisk() { return resources.diskSpeed() == NodeResources.DiskSpeed.fast; }

    public double getBandwidthGbps() { return resources.bandwidthGbps(); }

    public double getMinCpuCores() { return resources.vcpu(); }

    public Type getType() { return type; }
    
    /** Convenience, returns getType() == Type.DOCKER_CONTAINER */
    public boolean isDocker() { return type == Type.DOCKER_CONTAINER; }

    @Override
    public int hashCode() { return Objects.hash(name, flavorOverrides); }

    @Override
    public boolean equals(Object o) {
        if (o == this) return true;
        if ( ! (o instanceof Flavor)) return false;
        Flavor other = (Flavor)o;
        if (configured)
            return Objects.equals(this.name, other.name) &&
                    Objects.equals(this.flavorOverrides, other.flavorOverrides);
        else
            return this.resources.equals(other.resources);
    }

    @Override
    public String toString() {
        if (isConfigured())
            return "flavor '" + name + "'" + flavorOverrides.map(o -> " with overrides: " + o).orElse("");
        else
            return name;
    }

    public enum Type {
        undefined, // Default value in config (flavors.def)
        BARE_METAL,
        VIRTUAL_MACHINE,
        DOCKER_CONTAINER
    }

}