From 8a7d9d6621992b8f755b37e3e05fb216f842b48c Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Fri, 15 Sep 2023 11:51:19 +0200 Subject: [PATCH] Report current CPUs in status. --- .../vmoperator/runner/qemu/CpuController.java | 50 +++++++++---------- .../vmoperator/runner/qemu/StatusUpdater.java | 23 +++++++++ .../qemu/events/HotpluggableCpuStatus.java | 40 +++++++++++++++ 3 files changed, 86 insertions(+), 27 deletions(-) diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CpuController.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CpuController.java index 6d8ccc1..f0face4 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CpuController.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CpuController.java @@ -19,8 +19,8 @@ package org.jdrupes.vmoperator.runner.qemu; import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.ArrayList; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.Set; @@ -81,34 +81,28 @@ public class CpuController extends Component { /** * On monitor result. * - * @param result the result + * @param event the result */ @Handler - public void onHotpluggableCpuStatus(HotpluggableCpuStatus result) { - if (!result.successful()) { + public void onHotpluggableCpuStatus(HotpluggableCpuStatus event) { + if (!event.successful()) { logger.warning(() -> "Failed to get hotpluggable CPU status " - + "(won't adjust number of CPUs.): " + result.errorMessage()); + + "(won't adjust number of CPUs.): " + event.errorMessage()); } - - // Sort - List used = new ArrayList<>(); - List unused = new ArrayList<>(); - for (var itr = result.values().iterator(); itr.hasNext();) { - ObjectNode cpu = (ObjectNode) itr.next(); - if (cpu.has("qom-path")) { - used.add(cpu); - } else { - unused.add(cpu); - } - } - currentCpus = used.size(); if (desiredCpus == null) { return; } // Process - int diff = used.size() - desiredCpus; - diff = addCpus(used, unused, diff); - deleteCpus(used, diff); + currentCpus = event.usedCpus().size(); + int diff = currentCpus - desiredCpus; + if (diff == 0) { + return; + } + diff = addCpus(event.usedCpus(), event.unusedCpus(), diff); + removeCpus(event.usedCpus(), diff); + + // Report result + fire(new MonitorCommand(new QmpQueryHotpluggableCpus())); } @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") @@ -123,22 +117,24 @@ public class CpuController extends Component { } } int nextId = 1; - while (diff < 0 && !unused.isEmpty()) { + List remaining = new LinkedList<>(unused); + while (diff < 0 && !remaining.isEmpty()) { String id; do { id = "cpu-" + nextId++; } while (usedIds.contains(id)); - fire(new MonitorCommand(new QmpAddCpu(unused.get(0), id))); - unused.remove(0); + fire(new MonitorCommand(new QmpAddCpu(remaining.get(0), id))); + remaining.remove(0); diff += 1; } return diff; } @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") - private int deleteCpus(List used, int diff) { - while (diff > 0 && !used.isEmpty()) { - ObjectNode cpu = used.remove(0); + private int removeCpus(List used, int diff) { + List removable = new LinkedList<>(used); + while (diff > 0 && !removable.isEmpty()) { + ObjectNode cpu = removable.remove(0); String qomPath = cpu.get("qom-path").asText(); if (!qomPath.startsWith("/machine/peripheral/cpu-")) { continue; diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java index b72a478..030075d 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java @@ -37,6 +37,7 @@ import java.util.Optional; import java.util.Set; import java.util.logging.Level; import org.jdrupes.vmoperator.runner.qemu.events.BalloonChangeEvent; +import org.jdrupes.vmoperator.runner.qemu.events.HotpluggableCpuStatus; import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate; import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange; import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State; @@ -223,8 +224,10 @@ public class StatusUpdater extends Component { if (event.state() == State.STARTING) { status.addProperty("ram", GsonPtr.to(from.getRaw()) .getAsString("spec", "vm", "maximumRam").orElse("0")); + status.addProperty("cpus", 1); } else if (event.state() == State.STOPPED) { status.addProperty("ram", "0"); + status.addProperty("cpus", 0); } return status; }); @@ -273,4 +276,24 @@ public class StatusUpdater extends Component { return status; }); } + + /** + * On ballon change. + * + * @param event the event + * @throws ApiException + */ + @Handler + public void onCpuChange(HotpluggableCpuStatus event) throws ApiException { + if (vmCrApi == null) { + return; + } + var vmCr + = vmCrApi.get(namespace, vmName).throwsApiException().getObject(); + vmCrApi.updateStatus(vmCr, from -> { + JsonObject status = currentStatus(from); + status.addProperty("cpus", event.usedCpus().size()); + return status; + }); + } } diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/HotpluggableCpuStatus.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/HotpluggableCpuStatus.java index 3ed2e50..68641c9 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/HotpluggableCpuStatus.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/HotpluggableCpuStatus.java @@ -19,6 +19,10 @@ package org.jdrupes.vmoperator.runner.qemu.events; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand; /** @@ -26,6 +30,9 @@ import org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand; */ public class HotpluggableCpuStatus extends MonitorResult { + private List usedCpus = new ArrayList<>(); + private List unusedCpus = new ArrayList<>(); + /** * Instantiates a new hotpluggable cpu result. * @@ -34,6 +41,39 @@ public class HotpluggableCpuStatus extends MonitorResult { */ public HotpluggableCpuStatus(QmpCommand command, JsonNode response) { super(command, response); + if (!successful()) { + return; + } + + // Sort + for (var itr = values().iterator(); itr.hasNext();) { + ObjectNode cpu = (ObjectNode) itr.next(); + if (cpu.has("qom-path")) { + usedCpus.add(cpu); + } else { + unusedCpus.add(cpu); + } + } + usedCpus = Collections.unmodifiableList(usedCpus); + unusedCpus = Collections.unmodifiableList(unusedCpus); + } + + /** + * Gets the used cpus. + * + * @return the usedCpus + */ + public List usedCpus() { + return usedCpus; + } + + /** + * Gets the unused cpus. + * + * @return the unusedCpus + */ + public List unusedCpus() { + return unusedCpus; } }