diff --git a/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/runnerConfig.ftl.yaml b/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/runnerConfig.ftl.yaml index 7aaae51..dce5148 100644 --- a/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/runnerConfig.ftl.yaml +++ b/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/runnerConfig.ftl.yaml @@ -85,7 +85,7 @@ data: # cause the corresponding property to be omitted from the "-smp" option. # If currentCpus is greater than maximumCpus, the latter is adjusted. <#if cr.spec.vm.maximumCpus?? > - maximumCpus: ${ cr.spec.vm.maximumCpus.asInt?c } + maximumCpus: ${ parseQuantity(cr.spec.vm.maximumCpus.asString)?c } <#if cr.spec.vm.cpuTopology?? > cpuSockets: ${ cr.spec.vm.cpuTopology.cpuSockets.asInt?c } @@ -94,14 +94,14 @@ data: threadsPerCore: ${ cr.spec.vm.cpuTopology.threadsPerCore.asInt?c } <#if cr.spec.vm.currentCpus?? > - currentCpus: ${ cr.spec.vm.currentCpus.asInt?c } + currentCpus: ${ parseQuantity(cr.spec.vm.currentCpus.asString)?c } # RAM settings # Maximum defaults to 1G - maximumRam: "${ cr.spec.vm.maximumRam.asString }" + maximumRam: "${ formatMemory(parseQuantity(cr.spec.vm.maximumRam.asString)) }" <#if cr.spec.vm.currentRam?? > - currentRam: "${ cr.spec.vm.currentRam.asString }" + currentRam: "${ formatMemory(parseQuantity(cr.spec.vm.currentRam.asString)) }" # RTC settings. diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java index f135a7e..7815864 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java @@ -24,6 +24,7 @@ import freemarker.core.ParseException; import freemarker.template.Configuration; import freemarker.template.DefaultObjectWrapperBuilder; import freemarker.template.MalformedTemplateNameException; +import freemarker.template.SimpleNumber; import freemarker.template.TemplateException; import freemarker.template.TemplateExceptionHandler; import freemarker.template.TemplateHashModel; @@ -34,6 +35,8 @@ import io.kubernetes.client.custom.Quantity; import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi; import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; @@ -42,6 +45,7 @@ import java.util.Map; import java.util.Optional; import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP; import org.jdrupes.vmoperator.manager.VmDefChanged.Type; +import org.jdrupes.vmoperator.util.Convertions; import org.jdrupes.vmoperator.util.ExtendedObjectWrapper; import org.jgrapes.core.Channel; import org.jgrapes.core.Component; @@ -203,6 +207,7 @@ public class Reconciler extends Component { lbReconciler.reconcile(event, model, channel); } + @SuppressWarnings("PMD.CognitiveComplexity") private Map prepareModel(JsonObject vmDef) throws TemplateModelException { @SuppressWarnings("PMD.UseConcurrentHashMap") @@ -236,6 +241,34 @@ public class Reconciler extends Component { } } }); + model.put("formatMemory", new TemplateMethodModelEx() { + @Override + @SuppressWarnings("PMD.PreserveStackTrace") + public Object exec(@SuppressWarnings("rawtypes") List arguments) + throws TemplateModelException { + var arg = arguments.get(0); + if (arg instanceof SimpleNumber number) { + arg = number.getAsNumber(); + } + BigInteger bigInt; + if (arg instanceof BigInteger value) { + bigInt = value; + } else if (arg instanceof BigDecimal dec) { + try { + bigInt = dec.toBigIntegerExact(); + } catch (ArithmeticException e) { + return arg; + } + } else if (arg instanceof Integer value) { + bigInt = BigInteger.valueOf(value); + } else if (arg instanceof Long value) { + bigInt = BigInteger.valueOf(value); + } else { + return arg; + } + return Convertions.formatMemory(bigInt); + } + }); return model; } diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java index 0ecde1f..c83b021 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java @@ -30,7 +30,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.jdrupes.vmoperator.util.Dto; import org.jdrupes.vmoperator.util.FsdUtils; -import org.jdrupes.vmoperator.util.ParseUtils; +import org.jdrupes.vmoperator.util.Convertions; /** * The configuration information from the configuration file. @@ -144,7 +144,7 @@ public class Configuration implements Dto { * @param value the new maximum ram */ public void setMaximumRam(String value) { - maximumRam = ParseUtils.parseMemory(value); + maximumRam = Convertions.parseMemory(value); } /** @@ -153,7 +153,7 @@ public class Configuration implements Dto { * @param value the new current ram */ public void setCurrentRam(String value) { - currentRam = ParseUtils.parseMemory(value); + currentRam = Convertions.parseMemory(value); } } diff --git a/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/ParseUtils.java b/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/Convertions.java similarity index 79% rename from org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/ParseUtils.java rename to org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/Convertions.java index 8226c18..d2975b4 100644 --- a/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/ParseUtils.java +++ b/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/Convertions.java @@ -29,11 +29,14 @@ import java.util.regex.Pattern; * Provides methods for parsing "official" memory sizes.. */ @SuppressWarnings("PMD.UseUtilityClass") -public class ParseUtils { +public class Convertions { @SuppressWarnings({ "PMD.UseConcurrentHashMap", "PMD.FieldNamingConventions", "PMD.VariableNamingConventions" }) private static final Map unitMap = new HashMap<>(); + @SuppressWarnings({ "PMD.FieldNamingConventions", + "PMD.VariableNamingConventions" }) + private static final List> unitMappings; @SuppressWarnings({ "PMD.FieldNamingConventions", "PMD.VariableNamingConventions" }) private static final Pattern memorySize @@ -55,6 +58,9 @@ public class ParseUtils { unitMap.put(unit, factor); factor = factor.multiply(scale); } + unitMappings = unitMap.entrySet().stream() + .sorted((a, b) -> -1 * a.getValue().compareTo(b.getValue())) + .toList(); } /** @@ -91,4 +97,20 @@ public class ParseUtils { .toBigInteger(); } + /** + * Format memory size for humans. + * + * @param size the size + * @return the string + */ + public static String formatMemory(BigInteger size) { + for (var mapping : unitMappings) { + if (size.compareTo(mapping.getValue()) >= 0 + && size.mod(mapping.getValue()).equals(BigInteger.ZERO)) { + return size.divide(mapping.getValue()).toString() + + " " + mapping.getKey(); + } + } + return size.toString(); + } }