aboutsummaryrefslogtreecommitdiffstats
path: root/config-provisioning
diff options
context:
space:
mode:
authorValerij Fredriksen <valerijf@verizonmedia.com>2019-02-26 16:54:22 +0100
committerValerij Fredriksen <valerijf@verizonmedia.com>2019-03-08 09:35:34 +0100
commitb85e50a93d0af8537898526f93f1278eda3dfcca (patch)
treeeb58606427e9e5b66a351b12a3f91d4c02a3ea58 /config-provisioning
parent85e978f6c57edafb6d24c4a9f47ae9760dc65c53 (diff)
Make Flavor interface
Diffstat (limited to 'config-provisioning')
-rw-r--r--config-provisioning/abi-spec.json111
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java174
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java72
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/internal/ConfigFlavor.java172
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/internal/ConfigNodeFlavors.java83
-rw-r--r--config-provisioning/src/main/resources/configdefinitions/flavors.def21
6 files changed, 404 insertions, 229 deletions
diff --git a/config-provisioning/abi-spec.json b/config-provisioning/abi-spec.json
index af61fb46e50..32027a9e48c 100644
--- a/config-provisioning/abi-spec.json
+++ b/config-provisioning/abi-spec.json
@@ -343,7 +343,48 @@
"public static final enum com.yahoo.config.provision.Environment perf"
]
},
- "com.yahoo.config.provision.Flavor$Type": {
+ "com.yahoo.config.provision.Flavor$Bandwidth": {
+ "superClass": "java.lang.Object",
+ "interfaces": [],
+ "attributes": [
+ "public",
+ "interface",
+ "abstract"
+ ],
+ "methods": [
+ "public abstract double mbits()"
+ ],
+ "fields": []
+ },
+ "com.yahoo.config.provision.Flavor$Cpu": {
+ "superClass": "java.lang.Object",
+ "interfaces": [],
+ "attributes": [
+ "public",
+ "interface",
+ "abstract"
+ ],
+ "methods": [
+ "public abstract double cores()"
+ ],
+ "fields": []
+ },
+ "com.yahoo.config.provision.Flavor$Disk": {
+ "superClass": "java.lang.Object",
+ "interfaces": [],
+ "attributes": [
+ "public",
+ "interface",
+ "abstract"
+ ],
+ "methods": [
+ "public abstract double sizeInBase10Gb()",
+ "public double sizeInBase2Gb()",
+ "public abstract boolean isFast()"
+ ],
+ "fields": []
+ },
+ "com.yahoo.config.provision.Flavor$Environment": {
"superClass": "java.lang.Enum",
"interfaces": [],
"attributes": [
@@ -352,46 +393,49 @@
"enum"
],
"methods": [
- "public static com.yahoo.config.provision.Flavor$Type[] values()",
- "public static com.yahoo.config.provision.Flavor$Type valueOf(java.lang.String)"
+ "public static com.yahoo.config.provision.Flavor$Environment[] values()",
+ "public static com.yahoo.config.provision.Flavor$Environment valueOf(java.lang.String)"
],
"fields": [
- "public static final enum com.yahoo.config.provision.Flavor$Type undefined",
- "public static final enum com.yahoo.config.provision.Flavor$Type BARE_METAL",
- "public static final enum com.yahoo.config.provision.Flavor$Type VIRTUAL_MACHINE",
- "public static final enum com.yahoo.config.provision.Flavor$Type DOCKER_CONTAINER"
+ "public static final enum com.yahoo.config.provision.Flavor$Environment BARE_METAL",
+ "public static final enum com.yahoo.config.provision.Flavor$Environment VIRTUAL_MACHINE",
+ "public static final enum com.yahoo.config.provision.Flavor$Environment DOCKER_CONTAINER"
]
},
+ "com.yahoo.config.provision.Flavor$Memory": {
+ "superClass": "java.lang.Object",
+ "interfaces": [],
+ "attributes": [
+ "public",
+ "interface",
+ "abstract"
+ ],
+ "methods": [
+ "public abstract double sizeInGb()"
+ ],
+ "fields": []
+ },
"com.yahoo.config.provision.Flavor": {
"superClass": "java.lang.Object",
"interfaces": [],
"attributes": [
- "public"
+ "public",
+ "interface",
+ "abstract"
],
"methods": [
- "public void <init>(com.yahoo.config.provisioning.FlavorsConfig$Flavor)",
- "public java.lang.String name()",
- "public int cost()",
- "public boolean isStock()",
- "public double getMinMainMemoryAvailableGb()",
- "public double getMinDiskAvailableGb()",
- "public boolean hasFastDisk()",
- "public double getBandwidth()",
- "public double getMinCpuCores()",
- "public java.lang.String getDescription()",
- "public boolean isRetired()",
- "public com.yahoo.config.provision.Flavor$Type getType()",
- "public boolean isDocker()",
- "public int getIdealHeadroom()",
+ "public abstract java.lang.String flavorName()",
"public java.lang.String canonicalName()",
- "public boolean isCanonical()",
- "public java.util.List replaces()",
+ "public abstract int cost()",
+ "public abstract boolean isStock()",
+ "public abstract boolean isRetired()",
"public boolean satisfies(com.yahoo.config.provision.Flavor)",
- "public void freeze()",
- "public boolean isLargerThan(com.yahoo.config.provision.Flavor)",
- "public int hashCode()",
- "public boolean equals(java.lang.Object)",
- "public java.lang.String toString()"
+ "public abstract com.yahoo.config.provision.Flavor$Cpu cpu()",
+ "public abstract com.yahoo.config.provision.Flavor$Memory memory()",
+ "public abstract com.yahoo.config.provision.Flavor$Disk disk()",
+ "public abstract com.yahoo.config.provision.Flavor$Bandwidth bandwidth()",
+ "public abstract com.yahoo.config.provision.Flavor$Environment environment()",
+ "public abstract java.util.List replaces()"
],
"fields": []
},
@@ -547,12 +591,13 @@
"superClass": "java.lang.Object",
"interfaces": [],
"attributes": [
- "public"
+ "public",
+ "interface",
+ "abstract"
],
"methods": [
- "public void <init>(com.yahoo.config.provisioning.FlavorsConfig)",
- "public java.util.List getFlavors()",
- "public java.util.Optional getFlavor(java.lang.String)",
+ "public abstract java.util.List getFlavors()",
+ "public abstract java.util.Optional getFlavor(java.lang.String)",
"public com.yahoo.config.provision.Flavor getFlavorOrThrow(java.lang.String)"
],
"fields": []
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java b/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java
index 79a17c23dd7..e2c39d6efda 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java
@@ -1,93 +1,17 @@
// 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.google.common.collect.ImmutableList;
-import com.yahoo.config.provisioning.FlavorsConfig;
-
-import java.util.ArrayList;
import java.util.List;
/**
- * A host flavor (type). This is a value object where the identity is the name.
- * Use {@link NodeFlavors} to create a flavor.
+ * A host flavor (type).
*
* @author bratseth
*/
-public class Flavor {
-
- private final String name;
- private final int cost;
- private final boolean isStock;
- private final Type type;
- private final double minCpuCores;
- private final double minMainMemoryAvailableGb;
- private final double minDiskAvailableGb;
- private final boolean fastDisk;
- private final double bandwidth;
- private final String description;
- private final boolean retired;
- private List<Flavor> replacesFlavors;
- private int idealHeadroom; // Note: Not used after Vespa 6.282
-
- /**
- * Creates a Flavor, but does not set the replacesFlavors.
- * @param flavorConfig config to be used for Flavor.
- */
- public Flavor(FlavorsConfig.Flavor flavorConfig) {
- this.name = flavorConfig.name();
- this.replacesFlavors = new ArrayList<>();
- this.cost = flavorConfig.cost();
- this.isStock = flavorConfig.stock();
- this.type = Type.valueOf(flavorConfig.environment());
- this.minCpuCores = flavorConfig.minCpuCores();
- this.minMainMemoryAvailableGb = flavorConfig.minMainMemoryAvailableGb();
- this.minDiskAvailableGb = flavorConfig.minDiskAvailableGb();
- this.fastDisk = flavorConfig.fastDisk();
- this.bandwidth = flavorConfig.bandwidth();
- this.description = flavorConfig.description();
- this.retired = flavorConfig.retired();
- this.idealHeadroom = flavorConfig.idealHeadroom();
- }
-
- /** Returns the unique identity of this flavor */
- 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; }
-
- public boolean isStock() { return isStock; }
-
- public double getMinMainMemoryAvailableGb() { return minMainMemoryAvailableGb; }
+public interface Flavor {
- public double getMinDiskAvailableGb() { return minDiskAvailableGb; }
-
- public boolean hasFastDisk() { return fastDisk; }
-
- public double getBandwidth() { return bandwidth; }
-
- public double getMinCpuCores() { return minCpuCores; }
-
- public String getDescription() { return description; }
-
- /** Returns whether the flavor is retired */
- public boolean isRetired() {
- return retired;
- }
-
- public Type getType() { return type; }
-
- /** Convenience, returns getType() == Type.DOCKER_CONTAINER */
- public boolean isDocker() { return type == Type.DOCKER_CONTAINER; }
-
- /** The free capacity we would like to preserve for this flavor */
- public int getIdealHeadroom() {
- return idealHeadroom;
- }
+ /** @return the unique identity of this flavor */
+ String flavorName();
/**
* Returns the canonical name of this flavor - which is the name which should be used as an interface to users.
@@ -102,69 +26,85 @@ public class Flavor {
* replace the canonical name we want. However, if a node replaces multiple names, we have no basis for choosing one
* of them as the canonical, so we return the current as canonical.
*/
- public String canonicalName() {
- return isCanonical() ? name : replacesFlavors.get(0).canonicalName();
- }
-
- /** Returns whether this is a canonical flavor */
- public boolean isCanonical() {
- return replacesFlavors.size() != 1;
+ default String canonicalName() {
+ return replaces().size() != 1 ? flavorName() : replaces().get(0).canonicalName();
}
+ /** @return the cost associated with usage of this flavor */
+ int cost();
+
/**
- * The flavors this (directly) replaces.
- * This is immutable if this is frozen, and a mutable list otherwise.
+ * A stock flavor is any flavor we expect more of in the future.
+ * Stock flavors are assigned to applications by cost priority.
+ *
+ * Non-stock flavors are used for nodes for which a fixed amount has already been added
+ * to the system for some historical reason. These nodes are assigned to applications
+ * when available by exact match and ignoring cost.
*/
- public List<Flavor> replaces() { return replacesFlavors; }
+ boolean isStock();
+
+ /** Returns whether the flavor is retired (should no longer be allocated) */
+ boolean isRetired();
/**
* Returns whether this flavor satisfies the requested flavor, either directly
* (by being the same), or by directly or indirectly replacing it
*/
- public boolean satisfies(Flavor flavor) {
- if (this.equals(flavor)) {
+ default boolean satisfies(Flavor flavor) {
+ if (equals(flavor)) {
return true;
}
- if (this.retired) {
+ if (isRetired()) {
return false;
}
- for (Flavor replaces : replacesFlavors)
+ for (Flavor replaces : replaces())
if (replaces.satisfies(flavor))
return true;
return false;
}
- /** Irreversibly freezes the content of this */
- public void freeze() {
- replacesFlavors = ImmutableList.copyOf(replacesFlavors);
- }
-
- /** Returns whether this flavor has at least as much as each hardware resource as the given flavor */
- public boolean isLargerThan(Flavor other) {
- return this.minCpuCores >= other.minCpuCores &&
- this.minDiskAvailableGb >= other.minDiskAvailableGb &&
- this.minMainMemoryAvailableGb >= other.minMainMemoryAvailableGb &&
- this.fastDisk || ! other.fastDisk;
+ Cpu cpu();
+
+ Memory memory();
+
+ Disk disk();
+
+ Bandwidth bandwidth();
+
+ Environment environment();
+
+ /** The flavors this (directly) replaces. */
+ List<Flavor> replaces();
+
+
+ interface Disk {
+
+ /** @return Disk size in GB in base 10 (1GB = 10^9 bytes) */
+ double sizeInBase10Gb();
+
+ /** @return Disk size in GB in base 2, also known as GiB (1GiB = 2^30 bytes), rounded to nearest integer value */
+ default double sizeInBase2Gb() {
+ return Math.round(sizeInBase10Gb() / Math.pow(1.024, 3));
+ }
+
+ boolean isFast();
}
- @Override
- public int hashCode() { return name.hashCode(); }
+ interface Memory {
+ double sizeInGb();
+ }
- @Override
- public boolean equals(Object other) {
- if (other == this) return true;
- if ( ! (other instanceof Flavor)) return false;
- return ((Flavor)other).name.equals(this.name);
+ interface Cpu {
+ double cores();
}
- @Override
- public String toString() { return "flavor '" + name + "'"; }
+ interface Bandwidth {
+ double mbits();
+ }
- public enum Type {
- undefined, // Default value in config (flavors.def)
+ enum Environment {
BARE_METAL,
VIRTUAL_MACHINE,
DOCKER_CONTAINER
}
-
}
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java
index e64028e216f..43ef5602d8a 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java
@@ -1,88 +1,30 @@
// 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.google.common.collect.ImmutableMap;
-import com.google.inject.Inject;
-import com.yahoo.config.provisioning.FlavorsConfig;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* All the available node flavors.
*
- * @author bratseth
+ * @author freva
*/
-public class NodeFlavors {
-
- /** Flavors <b>which are configured</b> in this zone */
- private final ImmutableMap<String, Flavor> flavors;
-
- @Inject
- public NodeFlavors(FlavorsConfig config) {
- ImmutableMap.Builder<String, Flavor> b = new ImmutableMap.Builder<>();
- for (Flavor flavor : toFlavors(config))
- b.put(flavor.name(), flavor);
- this.flavors = b.build();
- }
+public interface NodeFlavors {
- public List<Flavor> getFlavors() {
- return new ArrayList<>(flavors.values());
- }
+ /** Returns list of all available flavors in the system */
+ List<Flavor> getFlavors();
/** Returns a flavor by name, or empty if there is no flavor with this name. */
- public Optional<Flavor> getFlavor(String name) {
- return Optional.ofNullable(flavors.get(name));
- }
+ Optional<Flavor> getFlavor(String name);
/** Returns the flavor with the given name or throws an IllegalArgumentException if it does not exist */
- public Flavor getFlavorOrThrow(String flavorName) {
+ default Flavor getFlavorOrThrow(String flavorName) {
return getFlavor(flavorName).orElseThrow(() -> new IllegalArgumentException("Unknown flavor '" + flavorName +
"'. Flavors are " + canonicalFlavorNames()));
}
private List<String> canonicalFlavorNames() {
- return flavors.values().stream().map(Flavor::canonicalName).distinct().sorted().collect(Collectors.toList());
+ return getFlavors().stream().map(Flavor::canonicalName).distinct().sorted().collect(Collectors.toList());
}
-
- private static Collection<Flavor> toFlavors(FlavorsConfig config) {
- Map<String, Flavor> flavors = new HashMap<>();
- // First pass, create all flavors, but do not include flavorReplacesConfig.
- for (FlavorsConfig.Flavor flavorConfig : config.flavor()) {
- flavors.put(flavorConfig.name(), new Flavor(flavorConfig));
- }
- // Second pass, set flavorReplacesConfig to point to correct flavor.
- for (FlavorsConfig.Flavor flavorConfig : config.flavor()) {
- Flavor flavor = flavors.get(flavorConfig.name());
- for (FlavorsConfig.Flavor.Replaces flavorReplacesConfig : flavorConfig.replaces()) {
- if (! flavors.containsKey(flavorReplacesConfig.name())) {
- throw new IllegalStateException("Replaces for " + flavor.name() +
- " pointing to a non existing flavor: " + flavorReplacesConfig.name());
- }
- flavor.replaces().add(flavors.get(flavorReplacesConfig.name()));
- }
- flavor.freeze();
- }
- // Third pass, ensure that retired flavors have a replacement
- for (Flavor flavor : flavors.values()) {
- if (flavor.isRetired() && !hasReplacement(flavors.values(), flavor)) {
- throw new IllegalStateException(
- String.format("Flavor '%s' is retired, but has no replacement", flavor.name())
- );
- }
- }
- return flavors.values();
- }
-
- private static boolean hasReplacement(Collection<Flavor> flavors, Flavor flavor) {
- return flavors.stream()
- .filter(f -> !f.equals(flavor))
- .anyMatch(f -> f.satisfies(flavor));
- }
-
}
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/internal/ConfigFlavor.java b/config-provisioning/src/main/java/com/yahoo/config/provision/internal/ConfigFlavor.java
new file mode 100644
index 00000000000..e70476455cd
--- /dev/null
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/internal/ConfigFlavor.java
@@ -0,0 +1,172 @@
+// 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.internal;
+
+import com.google.common.collect.ImmutableList;
+import com.yahoo.config.provision.Flavor;
+import com.yahoo.config.provisioning.FlavorsConfig;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A {@link Flavor} generated from config.
+ *
+ * @author freva
+ */
+public class ConfigFlavor implements Flavor {
+
+ private final String name;
+ private final int cost;
+ private final boolean isStock;
+ private final Environment environment;
+ private final Cpu cpu;
+ private final Memory memory;
+ private final Disk disk;
+ private final Bandwidth bandwidth;
+ private final boolean retired;
+ private List<Flavor> replacesFlavors;
+
+ /**
+ * Creates a Flavor, but does not set the replacesFlavors.
+ * @param flavorConfig config to be used for Flavor.
+ */
+ public ConfigFlavor(FlavorsConfig.Flavor flavorConfig) {
+ this.name = flavorConfig.name();
+ this.replacesFlavors = new ArrayList<>();
+ this.cost = flavorConfig.cost();
+ this.isStock = flavorConfig.stock();
+ this.environment = Environment.valueOf(flavorConfig.environment());
+ this.cpu = new ConfigCpu(flavorConfig.cpu());
+ this.memory = new ConfigMemory(flavorConfig.memory());
+ this.disk = new ConfigDisk(flavorConfig.disk());
+ this.bandwidth = new ConfigBandwidth(flavorConfig.bandwidth());
+ this.retired = flavorConfig.retired();
+ }
+
+ @Override
+ public String flavorName() {
+ return name;
+ }
+
+ @Override
+ public int cost() {
+ return cost;
+ }
+
+ @Override
+ public boolean isStock() {
+ return isStock;
+ }
+
+ @Override
+ public boolean isRetired() {
+ return retired;
+ }
+
+ @Override
+ public Cpu cpu() {
+ return cpu;
+ }
+
+ @Override
+ public Memory memory() {
+ return memory;
+ }
+
+ @Override
+ public Disk disk() {
+ return disk;
+ }
+
+ @Override
+ public Bandwidth bandwidth() {
+ return bandwidth;
+ }
+
+ @Override
+ public Environment environment() {
+ return environment;
+ }
+
+ @Override
+ public List<Flavor> replaces() {
+ return replacesFlavors;
+ }
+
+ /** Irreversibly freezes the content of this */
+ public void freeze() {
+ replacesFlavors = ImmutableList.copyOf(replacesFlavors);
+ }
+
+ @Override
+ public int hashCode() { return name.hashCode(); }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) return true;
+ if ( ! (other instanceof ConfigFlavor)) return false;
+ return ((ConfigFlavor)other).name.equals(this.name);
+ }
+
+ @Override
+ public String toString() { return "flavor '" + name + "'"; }
+
+ private class ConfigCpu implements Flavor.Cpu {
+ private final double cores;
+
+ private ConfigCpu(FlavorsConfig.Flavor.Cpu cpu) {
+ this.cores = cpu.cores();
+ }
+
+ @Override
+ public double cores() {
+ return cores;
+ }
+ }
+
+ private class ConfigMemory implements Flavor.Memory {
+ private final double sizeInGb;
+
+ private ConfigMemory(FlavorsConfig.Flavor.Memory memory) {
+ this.sizeInGb = memory.sizeInGb();
+ }
+
+ @Override
+ public double sizeInGb() {
+ return sizeInGb;
+ }
+ }
+
+ private class ConfigDisk implements Flavor.Disk {
+ private final double sizeInBase10;
+ private final boolean isFast;
+
+ private ConfigDisk(FlavorsConfig.Flavor.Disk disk) {
+ this.sizeInBase10 = disk.sizeInGb();
+ this.isFast = disk.fast();
+ }
+
+ @Override
+ public double sizeInBase10Gb() {
+ return sizeInBase10;
+ }
+
+ @Override
+ public boolean isFast() {
+ return isFast;
+ }
+ }
+
+ private class ConfigBandwidth implements Flavor.Bandwidth {
+ private final double mbits;
+
+ private ConfigBandwidth(FlavorsConfig.Flavor.Bandwidth bandwidth) {
+ this.mbits = bandwidth.mbits();
+ }
+
+ @Override
+ public double mbits() {
+ return mbits;
+ }
+ }
+}
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/internal/ConfigNodeFlavors.java b/config-provisioning/src/main/java/com/yahoo/config/provision/internal/ConfigNodeFlavors.java
new file mode 100644
index 00000000000..c67a3faf36f
--- /dev/null
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/internal/ConfigNodeFlavors.java
@@ -0,0 +1,83 @@
+// 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.internal;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Inject;
+import com.yahoo.config.provision.Flavor;
+import com.yahoo.config.provision.NodeFlavors;
+import com.yahoo.config.provisioning.FlavorsConfig;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * {@link NodeFlavors} generated from config
+ *
+ * @author bratseth
+ */
+public class ConfigNodeFlavors implements NodeFlavors {
+
+ /** Flavors <b>which are configured</b> in this zone */
+ private final Map<String, Flavor> flavors;
+
+ @Inject
+ public ConfigNodeFlavors(FlavorsConfig config) {
+ this(toFlavors(config));
+ }
+
+ public ConfigNodeFlavors(Collection<Flavor> flavors) {
+ ImmutableMap.Builder<String, Flavor> b = new ImmutableMap.Builder<>();
+ for (Flavor flavor : flavors)
+ b.put(flavor.flavorName(), flavor);
+ this.flavors = b.build();
+ }
+
+ public List<Flavor> getFlavors() {
+ return new ArrayList<>(flavors.values());
+ }
+
+ /** Returns a flavor by name, or empty if there is no flavor with this name. */
+ public Optional<Flavor> getFlavor(String name) {
+ return Optional.ofNullable(flavors.get(name));
+ }
+
+ private static Collection<Flavor> toFlavors(FlavorsConfig config) {
+ Map<String, Flavor> flavors = new HashMap<>();
+ // First pass, create all flavors, but do not include flavorReplacesConfig.
+ for (FlavorsConfig.Flavor flavorConfig : config.flavor()) {
+ flavors.put(flavorConfig.name(), new ConfigFlavor(flavorConfig));
+ }
+ // Second pass, set flavorReplacesConfig to point to correct flavor.
+ for (FlavorsConfig.Flavor flavorConfig : config.flavor()) {
+ Flavor flavor = flavors.get(flavorConfig.name());
+ for (FlavorsConfig.Flavor.Replaces flavorReplacesConfig : flavorConfig.replaces()) {
+ if (! flavors.containsKey(flavorReplacesConfig.name())) {
+ throw new IllegalStateException("Replaces for " + flavor.flavorName() +
+ " pointing to a non existing flavor: " + flavorReplacesConfig.name());
+ }
+ flavor.replaces().add(flavors.get(flavorReplacesConfig.name()));
+ }
+ ((ConfigFlavor) flavor).freeze();
+ }
+ // Third pass, ensure that retired flavors have a replacement
+ for (Flavor flavor : flavors.values()) {
+ if (flavor.isRetired() && !hasReplacement(flavors.values(), flavor)) {
+ throw new IllegalStateException(
+ String.format("Flavor '%s' is retired, but has no replacement", flavor.flavorName())
+ );
+ }
+ }
+ return flavors.values();
+ }
+
+ private static boolean hasReplacement(Collection<Flavor> flavors, Flavor flavor) {
+ return flavors.stream()
+ .filter(f -> !f.equals(flavor))
+ .anyMatch(f -> f.satisfies(flavor));
+ }
+
+}
diff --git a/config-provisioning/src/main/resources/configdefinitions/flavors.def b/config-provisioning/src/main/resources/configdefinitions/flavors.def
index 1e40f6f8f36..d46f49b728d 100644
--- a/config-provisioning/src/main/resources/configdefinitions/flavors.def
+++ b/config-provisioning/src/main/resources/configdefinitions/flavors.def
@@ -26,29 +26,22 @@ flavor[].cost int default=0
flavor[].stock bool default=true
# The type of node: BARE_METAL, VIRTUAL_MACHINE or DOCKER_CONTAINER
-flavor[].environment string default="undefined"
+flavor[].environment string default="BARE_METAL"
# The minimum number of CPU cores available.
-flavor[].minCpuCores double default=0.0
+flavor[].cpu.cores double default=0.0
# The minimum amount of main memory available.
-flavor[].minMainMemoryAvailableGb double default=0.0
+flavor[].memory.sizeInGb double default=0.0
-# The minimum amount of disk available.
-flavor[].minDiskAvailableGb double default=0.0
+# The minimum amount of disk available in GB in base 10 (1GB = 10^9 bytes)
+flavor[].disk.sizeInGb double default=0.0
# Whether the disk is fast (typically SSD) or slow (typically spinning HDD).
-flavor[].fastDisk bool default=true
+flavor[].disk.fast bool default=true
# Expected network interface bandwidth available for this flavor, in Mbit/s.
-flavor[].bandwidth double default=0.0
-
-# Human readable free text for description of node.
-flavor[].description string default=""
+flavor[].bandwidth.mbits double default=0.0
# The flavor is retired and should no longer be used.
flavor[].retired bool default=false
-
-# The free capacity we would like to preserve for this flavor
-# Note: Not used after Vespa 6.282
-flavor[].idealHeadroom int default=0