From 173752a608dc2017427e18cdd8c2143e9e7f964d Mon Sep 17 00:00:00 2001 From: Rudi Schlatte Date: Mon, 13 May 2024 14:30:07 +0200 Subject: [PATCH] Add filtering by cloud IDs. Change-Id: I99535d261c84a91700fe2c95c70e507cd84f6e42 --- .../optimiser/kubevela/KubevelaAnalyzer.java | 45 +++++++++++++------ .../optimiser/controller/ExnConnector.java | 4 +- .../optimiser/controller/NebulousApp.java | 21 ++++++++- .../controller/NebulousAppDeployer.java | 4 +- .../controller/NebulousAppTests.java | 4 +- 5 files changed, 58 insertions(+), 20 deletions(-) diff --git a/nebulous-requirements-extractor/src/main/java/eu/nebulouscloud/optimiser/kubevela/KubevelaAnalyzer.java b/nebulous-requirements-extractor/src/main/java/eu/nebulouscloud/optimiser/kubevela/KubevelaAnalyzer.java index f142c50..8ede88a 100644 --- a/nebulous-requirements-extractor/src/main/java/eu/nebulouscloud/optimiser/kubevela/KubevelaAnalyzer.java +++ b/nebulous-requirements-extractor/src/main/java/eu/nebulouscloud/optimiser/kubevela/KubevelaAnalyzer.java @@ -17,6 +17,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; /** * A collection of methods to extract node requirements from KubeVela files. @@ -76,17 +77,27 @@ public class KubevelaAnalyzer { } /** - * Add frequirements for Ubuntu version 22.04. Also add requirement for - * 2GB of RAM for now until we know more about the size / cpu requirements - * of the nebulous runtime. + * Add the following requirements: + * * * @param reqs The list of requirements to add to. + * @param cloudIDs the Cloud IDs to filter for. */ - private static void addNebulousRequirements(List reqs) { + private static void addNebulousRequirements(List reqs, Set cloudIDs) { reqs.add(new AttributeRequirement("image", "operatingSystem.family", RequirementOperator.IN, OperatingSystemFamily.UBUNTU.toString())); reqs.add(new AttributeRequirement("image", "name", RequirementOperator.INC, "22")); reqs.add(new AttributeRequirement("hardware", "ram", RequirementOperator.GEQ, "2048")); + if (cloudIDs != null && !cloudIDs.isEmpty()) { + reqs.add(new AttributeRequirement("cloud", "id", + RequirementOperator.IN, String.join(" ", cloudIDs))); + } + } /** @@ -188,18 +199,21 @@ public class KubevelaAnalyzer { * @param includeNebulousRequirements if true, include requirements for * minimum memory size, Ubuntu OS. These requirements ensure that the * node candidate can run the Nebulous software. + * @param cloudIDs The IDs of the clouds that the node candidates should + * come from. Will only be handled if non-null and + * includeNebulousRequirements is true. * @return a map of component name to (potentially empty, except for OS * family) list of requirements for that component. No requirements mean * any node will suffice. */ - public static Map> getBoundedRequirements(JsonNode kubevela, boolean includeNebulousRequirements) { + public static Map> getBoundedRequirements(JsonNode kubevela, boolean includeNebulousRequirements, Set cloudIDs) { Map> result = new HashMap<>(); ArrayNode components = kubevela.withArray("/spec/components"); for (final JsonNode c : components) { String componentName = c.get("name").asText(); ArrayList reqs = new ArrayList<>(); if (includeNebulousRequirements) { - addNebulousRequirements(reqs); + addNebulousRequirements(reqs, cloudIDs); } long cores = getCpuRequirement(c, componentName); if (cores > 0) { @@ -228,8 +242,8 @@ public class KubevelaAnalyzer { * * @see #getBoundedRequirements(JsonNode, boolean) */ - public static Map> getBoundedRequirements(JsonNode kubevela) { - return getBoundedRequirements(kubevela, true); + public static Map> getBoundedRequirements(JsonNode kubevela, Set cloudIDs) { + return getBoundedRequirements(kubevela, true, cloudIDs); } /** @@ -239,13 +253,13 @@ public class KubevelaAnalyzer { * cpu >= 2, cpu <= 4. Take care to not ask for less than 2048Mb of * memory since that's the minimum Nebulous requirement for now. */ - public static Map> getClampedRequirements(JsonNode kubevela) { + public static Map> getClampedRequirements(JsonNode kubevela, Set cloudIDs) { Map> result = new HashMap<>(); ArrayNode components = kubevela.withArray("/spec/components"); for (final JsonNode c : components) { String componentName = c.get("name").asText(); ArrayList reqs = new ArrayList<>(); - addNebulousRequirements(reqs); + addNebulousRequirements(reqs, cloudIDs); long cores = getCpuRequirement(c, componentName); if (cores > 0) { reqs.add(new AttributeRequirement("hardware", "cores", @@ -279,13 +293,13 @@ public class KubevelaAnalyzer { * asking for >= or <=. Note that we still ask for >= 2048 Mb since * that's the nebulous lower bound for now. */ - public static Map> getPreciseRequirements(JsonNode kubevela) { + public static Map> getPreciseRequirements(JsonNode kubevela, Set cloudIDs) { Map> result = new HashMap<>(); ArrayNode components = kubevela.withArray("/spec/components"); for (final JsonNode c : components) { String componentName = c.get("name").asText(); ArrayList reqs = new ArrayList<>(); - addNebulousRequirements(reqs); + addNebulousRequirements(reqs, cloudIDs); long cores = getCpuRequirement(c, componentName); if (cores > 0) { reqs.add(new AttributeRequirement("hardware", "cores", @@ -313,13 +327,16 @@ public class KubevelaAnalyzer { * * @see #getBoundedRequirements(JsonNode) * @param kubevela The KubeVela file, as a YAML string. + * @param cloudIDs The IDs of the clouds that the node candidates should + * come from. Will only be handled if non-null and + * includeNebulousRequirements is true. * @return a map of component name to (potentially empty, except for OS * family) list of requirements for that component. No requirements mean * any node will suffice. * @throws JsonProcessingException if kubevela does not contain valid YAML. */ - public static Map> getBoundedRequirements(String kubevela) throws JsonProcessingException { - return getBoundedRequirements(parseKubevela(kubevela)); + public static Map> getBoundedRequirements(String kubevela, Set cloudIDs) throws JsonProcessingException { + return getBoundedRequirements(parseKubevela(kubevela), cloudIDs); } /** diff --git a/optimiser-controller/src/main/java/eu/nebulouscloud/optimiser/controller/ExnConnector.java b/optimiser-controller/src/main/java/eu/nebulouscloud/optimiser/controller/ExnConnector.java index a4b64e1..18c56b9 100644 --- a/optimiser-controller/src/main/java/eu/nebulouscloud/optimiser/controller/ExnConnector.java +++ b/optimiser-controller/src/main/java/eu/nebulouscloud/optimiser/controller/ExnConnector.java @@ -229,7 +229,9 @@ public class ExnConnector { try { log.info("App creation message received"); JsonNode appMessage = mapper.valueToTree(body); - Main.logFile("app-message-" + appMessage.at("/uuid").asText() + ".json", appMessage.toPrettyString()); + String appID = appMessage.at("/uuid").asText(); + MDC.put("appId", appID); + Main.logFile("app-message-" + appID + ".json", appMessage.toPrettyString()); app = NebulousApp.newFromAppMessage(mapper.valueToTree(body), ExnConnector.this); String appIdFromMessage = app.getUUID(); MDC.put("appId", appIdFromMessage); diff --git a/optimiser-controller/src/main/java/eu/nebulouscloud/optimiser/controller/NebulousApp.java b/optimiser-controller/src/main/java/eu/nebulouscloud/optimiser/controller/NebulousApp.java index 56a99d3..bd680bf 100644 --- a/optimiser-controller/src/main/java/eu/nebulouscloud/optimiser/controller/NebulousApp.java +++ b/optimiser-controller/src/main/java/eu/nebulouscloud/optimiser/controller/NebulousApp.java @@ -139,6 +139,13 @@ public class NebulousApp { @Getter private JsonNode originalAppMessage; private ObjectNode originalKubevela; + /** + * The cloud IDs to be used. These are given in the `resources` section + * of the app message; we collect all cloud IDs where `enabled:true` + * holds. + */ + @Getter private Set cloudIDs; + /** * The current "generation" of deployment. Initial deployment sets this * to 1, each subsequent redeployment increases by 1. This value is used @@ -237,7 +244,9 @@ public class NebulousApp { // are performance indicators in disguise. boolean done = false; Set metrics = StreamSupport.stream( - Spliterators.spliteratorUnknownSize(app_message.withArray("/metrics").elements(), Spliterator.ORDERED), false) + Spliterators.spliteratorUnknownSize( + app_message.withArray("/metrics").elements(), + Spliterator.ORDERED), false) .collect(Collectors.toSet()); while (!done) { // Pick out all raw metrics. Then pick out all composite metrics @@ -284,6 +293,16 @@ public class NebulousApp { break; } } + cloudIDs = StreamSupport.stream( + Spliterators.spliteratorUnknownSize( + app_message.withArray("/resources").elements(), + Spliterator.ORDERED), false) + .filter((c) -> c.get("enabled").asBoolean()) + .map((c) -> c.get("uuid").asText()) + .collect(Collectors.toSet()); + if (cloudIDs.isEmpty()) { + log.warn("No clouds enabled or specified in app creation message, will try to deploy only on edge nodes."); + } log.debug("New App instantiated."); } diff --git a/optimiser-controller/src/main/java/eu/nebulouscloud/optimiser/controller/NebulousAppDeployer.java b/optimiser-controller/src/main/java/eu/nebulouscloud/optimiser/controller/NebulousAppDeployer.java index 55ff696..0994ef0 100644 --- a/optimiser-controller/src/main/java/eu/nebulouscloud/optimiser/controller/NebulousAppDeployer.java +++ b/optimiser-controller/src/main/java/eu/nebulouscloud/optimiser/controller/NebulousAppDeployer.java @@ -254,7 +254,7 @@ public class NebulousAppDeployer { // ------------------------------------------------------------ // Extract node requirements - Map> componentRequirements = KubevelaAnalyzer.getBoundedRequirements(kubevela); + Map> componentRequirements = KubevelaAnalyzer.getBoundedRequirements(kubevela, app.getCloudIDs()); Map nodeCounts = KubevelaAnalyzer.getNodeCount(kubevela); List controllerRequirements = getControllerRequirements(appUUID); // // HACK: do this only when cloud id = nrec @@ -520,7 +520,7 @@ public class NebulousAppDeployer { // ------------------------------------------------------------ // 1. Extract node requirements - Map> componentRequirements = KubevelaAnalyzer.getBoundedRequirements(updatedKubevela); + Map> componentRequirements = KubevelaAnalyzer.getBoundedRequirements(updatedKubevela, app.getCloudIDs()); Map componentReplicaCounts = KubevelaAnalyzer.getNodeCount(updatedKubevela); Map> oldComponentRequirements = app.getComponentRequirements(); diff --git a/optimiser-controller/src/test/java/eu/nebulouscloud/optimiser/controller/NebulousAppTests.java b/optimiser-controller/src/test/java/eu/nebulouscloud/optimiser/controller/NebulousAppTests.java index f5479aa..7663d1b 100644 --- a/optimiser-controller/src/test/java/eu/nebulouscloud/optimiser/controller/NebulousAppTests.java +++ b/optimiser-controller/src/test/java/eu/nebulouscloud/optimiser/controller/NebulousAppTests.java @@ -91,7 +91,7 @@ public class NebulousAppTests { String kubevela_str = Files.readString(getResourcePath("vela-deployment-v2.yml"), StandardCharsets.UTF_8); JsonNode kubevela = yaml_mapper.readTree(kubevela_str); - Map> requirements = KubevelaAnalyzer.getBoundedRequirements(kubevela); + Map> requirements = KubevelaAnalyzer.getBoundedRequirements(kubevela, null); // We could compare the requirements with what is contained in // KubeVela, or compare keys with component names, but this would // essentially duplicate the method code--so we just make sure the @@ -111,7 +111,7 @@ public class NebulousAppTests { ObjectNode replacements = solutions.withObject("VariableValues"); ObjectNode kubevela1 = app.rewriteKubevelaWithSolution(replacements); - Map> requirements = KubevelaAnalyzer.getBoundedRequirements(kubevela1); + Map> requirements = KubevelaAnalyzer.getBoundedRequirements(kubevela1, null); // We could compare the requirements with what is contained in // KubeVela, or compare keys with component names, but this would // essentially duplicate the method code--so we just make sure the