Skip to content

Commit

Permalink
Add default value to AMPL variables
Browse files Browse the repository at this point in the history
Implements #7
  • Loading branch information
rudi committed Jul 10, 2024
1 parent dbc1581 commit cdc5034
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,36 @@ private static long getCpuRequirement(JsonNode c, String componentName) {
}
}

/**
* Return the long value of the given JSON node. If the meaning is
* "memory", also handle "Mi" and "Gi" suffixes.
*/
public static long kubevelaNumberToLong(JsonNode number, String meaning) throws NumberFormatException {
if ("memory".equals(meaning)) {
String numericString = number.asText();
if (numericString.endsWith("Mi")) {
return Long.parseLong(numericString.substring(0, numericString.length() - 2));
} else if (numericString.endsWith("Gi")) {
return Long.parseLong(numericString.substring(0, numericString.length() - 2)) * 1024;
} else {
log.warn("Unsupported memory specification in component: '" + numericString + "' (wanted 'Mi' or 'Gi') ");
if (number.canConvertToLong()) {
// we got no suffix at all; optimistically continue
return number.asLong();
} else {
// continue even more optimistically (this throws NumberFormatException)
return Long.parseLong(numericString);
}
}
} else {
if (number.canConvertToLong()) {
return number.asLong();
} else {
throw new NumberFormatException("Unable to parse " + meaning + " value '" + number + "' as integer (long) value");
}
}
}

/**
* Get memory requirement, taken from "memory" resource requirement in KubeVela
* and converted to Megabytes. We currently handle the "Mi" and "Gi"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import eu.nebulouscloud.optimiser.kubevela.KubevelaAnalyzer;
import lombok.extern.slf4j.Slf4j;

/**
Expand All @@ -34,17 +35,19 @@ public static List<String> getMetricList(NebulousApp app) {
}

/**
* Generate AMPL code for the app, based on the parameter definition(s).
* Public for testability, not because we'll be calling it outside of its
* class.
* Generate AMPL code.
*
* @param app the application object.
* @param kubevela the kubevela file, used to obtain default variable values.
* @return AMPL code for the solver.
*/
public static String generateAMPL(NebulousApp app) {
public static String generateAMPL(NebulousApp app, ObjectNode kubevela) {
final StringWriter result = new StringWriter();
final PrintWriter out = new PrintWriter(result);
out.format("# AMPL file for application '%s' with id %s%n", app.getName(), app.getUUID());
out.println();

generateVariablesSection(app, out);
generateVariablesSection(app, kubevela, out);
generateMetricsSection(app, out);
generateConstants(app, out);
generatePerformanceIndicatorsSection(app, out);
Expand Down Expand Up @@ -228,7 +231,7 @@ private static Set<String> usedMetrics(NebulousApp app) {
return result;
}

private static void generateVariablesSection(NebulousApp app, PrintWriter out) {
private static void generateVariablesSection(NebulousApp app, ObjectNode kubevela, PrintWriter out) {
out.println("# Variables");
for (final JsonNode p : app.getKubevelaVariables().values()) {
ObjectNode param = (ObjectNode) p;
Expand All @@ -237,6 +240,7 @@ private static void generateVariablesSection(NebulousApp app, PrintWriter out) {
String paramPath = param.get("path").textValue();
String paramType = param.get("type").textValue();
String paramMeaning = param.get("meaning").textValue();
JsonNode defaultValue = kubevela.at(paramPath);
// Even if these variables are sent over as "float", we know they
// have to be treated as integers for kubevela (replicas, memory)
// or SAL (cpu). I.e., paramMeaning overrides paramType.
Expand All @@ -256,6 +260,24 @@ private static void generateVariablesSection(NebulousApp app, PrintWriter out) {
if (upper.isNumber()) {
out.format("%s <= %s", separator, upper.numberValue());
}
if (!defaultValue.isMissingNode()) {
if (shouldBeInt){
try {
long number = KubevelaAnalyzer.kubevelaNumberToLong(defaultValue, paramMeaning);
out.format(" := %s", number);
} catch (NumberFormatException e) {
log.error("Unable to parse value '" + defaultValue
+ "' as integer (long) value of " + paramName);
}
} else {
if (defaultValue.isFloatingPointNumber()) {
out.format(" := %s", defaultValue.asDouble());
} else {
log.warn("Expected floating point default value at " + paramPath
+ " but got value " + defaultValue);
}
}
}
}
out.println(";");
} else if (paramType.equals("string")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public class LocalExecution implements Callable<Integer> {
}
} else {
log.info("No deploy requested, printing AMPL and performance metric list");
String ampl = AMPLGenerator.generateAMPL(app);
String ampl = AMPLGenerator.generateAMPL(app, app.getOriginalKubevela());
System.out.println("--------------------");
System.out.println("AMPL");
System.out.println("--------------------");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,15 +193,9 @@ public enum State {
@Getter
private Map<String, Integer> componentReplicaCounts = Map.of();

/** When an app gets deployed, this is where we send the AMPL file */
private Publisher ampl_message_channel;
// /** Have we ever been deployed? I.e., when we rewrite KubeVela, are there
// * already nodes running for us? */
// private boolean deployed = false;

/** The KubeVela as it was most recently sent to the app's controller. */
@Getter
private JsonNode deployedKubevela;
private ObjectNode deployedKubevela = null;

/**
* The EXN connector for this class. At the moment all apps share the
Expand Down Expand Up @@ -385,7 +379,7 @@ public boolean setStateDeploying() {
/** Set state from DEPLOYING to RUNNING and update app cluster information.
* @return false if not in state deploying, otherwise true. */
@Synchronized
public boolean setStateDeploymentFinished(Map<String, List<Requirement>> componentRequirements, Map<String, Integer> nodeCounts, Map<String, Set<String>> componentNodeNames, Map<String, NodeCandidate> nodeEdgeCandidates, JsonNode deployedKubevela) {
public boolean setStateDeploymentFinished(Map<String, List<Requirement>> componentRequirements, Map<String, Integer> nodeCounts, Map<String, Set<String>> componentNodeNames, Map<String, NodeCandidate> nodeEdgeCandidates, ObjectNode deployedKubevela) {
if (state != State.DEPLOYING) {
return false;
} else {
Expand Down Expand Up @@ -571,14 +565,10 @@ public ObjectNode rewriteKubevelaWithSolution(ObjectNode variableValues) {

/**
* Calculate AMPL file and send it off to the solver.
*
* <p> TODO: this should be done once from a message handler that listens
* for an incoming "solver ready" message
*
* <p> TODO: also send performance indicators to solver here
*/
public void sendAMPL() {
String ampl_model = AMPLGenerator.generateAMPL(this);
String ampl_model = AMPLGenerator.generateAMPL(this,
this.deployedKubevela == null ? this.originalKubevela : this.deployedKubevela);
String ampl_data = relevantPerformanceIndicators.at("/initialDataFile").textValue();
ObjectNode msg = jsonMapper.createObjectNode();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ public static List<Requirement> getControllerRequirements(String jobID, Set<Stri
* not modified.
* @return a fresh KubeVela specification with added nodeAffinity traits.
*/
public static JsonNode createDeploymentKubevela(JsonNode kubevela) {
JsonNode result = kubevela.deepCopy();
public static ObjectNode createDeploymentKubevela(JsonNode kubevela) {
ObjectNode result = kubevela.deepCopy();
for (final JsonNode c : result.withArray("/spec/components")) {
// Do not add trait to components that define a volume
if (c.at("/type").asText().equals("raw")) continue;
Expand Down Expand Up @@ -268,7 +268,7 @@ public static void deployApplication(NebulousApp app, JsonNode kubevela) {

// ------------------------------------------------------------
// Rewrite KubeVela
JsonNode rewritten = createDeploymentKubevela(kubevela);
ObjectNode rewritten = createDeploymentKubevela(kubevela);
String rewritten_kubevela = "---\n# Did not manage to create rewritten KubeVela";
try {
rewritten_kubevela = yamlMapper.writeValueAsString(rewritten);
Expand Down

0 comments on commit cdc5034

Please sign in to comment.