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 e45b5e6..3523af9 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
@@ -19,6 +19,8 @@
package org.jdrupes.vmoperator.runner.qemu;
import com.google.gson.JsonObject;
+import io.kubernetes.client.custom.Quantity;
+import io.kubernetes.client.custom.Quantity.Format;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.ApiextensionsV1Api;
import io.kubernetes.client.openapi.models.V1CustomResourceDefinitionVersion;
@@ -26,6 +28,7 @@ import io.kubernetes.client.util.Config;
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi;
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
import java.io.IOException;
+import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
@@ -33,11 +36,13 @@ import java.util.Map;
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.RunnerConfigurationUpdate;
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange;
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State;
import static org.jdrupes.vmoperator.util.Constants.VM_OP_CRD_NAME;
import static org.jdrupes.vmoperator.util.Constants.VM_OP_GROUP;
+import org.jdrupes.vmoperator.util.GsonPtr;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Component;
import org.jgrapes.core.annotation.Handler;
@@ -161,55 +166,6 @@ public class StatusUpdater extends Component {
return vmCr.getRaw().getAsJsonObject("status").deepCopy();
}
- /**
- * On runner state changed.
- *
- * @param event the event
- 8 * @throws ApiException the api exception
- */
- @Handler
- public void onRunnerStateChanged(RunnerStateChange event)
- throws ApiException {
- if (vmCrApi == null) {
- return;
- }
- var vmCr = vmCrApi.get(namespace, vmName)
- .throwsApiException().getObject();
- vmCrApi.updateStatus(vmCr, from -> {
- JsonObject status = currentStatus(from);
- status.getAsJsonArray("conditions").asList().stream()
- .map(cond -> (JsonObject) cond)
- .forEach(cond -> {
- if ("Running".equals(cond.get("type").getAsString())) {
- updateRunningCondition(event, from, cond);
- }
- });
- return status;
- });
- }
-
- private void updateRunningCondition(RunnerStateChange event,
- DynamicKubernetesObject from, JsonObject cond) {
- boolean reportedRunning
- = "True".equals(cond.get("status").getAsString());
- if (RUNNING_STATES.contains(event.state())
- && !reportedRunning) {
- cond.addProperty("status", "True");
- cond.addProperty("lastTransitionTime",
- Instant.now().toString());
- }
- if (!RUNNING_STATES.contains(event.state())
- && reportedRunning) {
- cond.addProperty("status", "False");
- cond.addProperty("lastTransitionTime",
- Instant.now().toString());
- }
- cond.addProperty("reason", event.reason());
- cond.addProperty("message", event.message());
- cond.addProperty("observedGeneration",
- from.getMetadata().getGeneration());
- }
-
/**
* On runner configuration update.
*
@@ -241,4 +197,78 @@ public class StatusUpdater extends Component {
});
}
+ /**
+ * On runner state changed.
+ *
+ * @param event the event
+ * @throws ApiException the api exception
+ */
+ @Handler
+ public void onRunnerStateChanged(RunnerStateChange event)
+ throws ApiException {
+ if (vmCrApi == null) {
+ return;
+ }
+ var vmCr = vmCrApi.get(namespace, vmName)
+ .throwsApiException().getObject();
+ vmCrApi.updateStatus(vmCr, from -> {
+ JsonObject status = currentStatus(from);
+ status.getAsJsonArray("conditions").asList().stream()
+ .map(cond -> (JsonObject) cond)
+ .forEach(cond -> {
+ if ("Running".equals(cond.get("type").getAsString())) {
+ updateRunningCondition(event, from, cond);
+ }
+ });
+ if (event.state() == State.STARTING) {
+ status.addProperty("ram", GsonPtr.to(from.getRaw())
+ .getAsString("spec", "vm", "maximumRam").orElse("0"));
+ }
+ return status;
+ });
+ }
+
+ private void updateRunningCondition(RunnerStateChange event,
+ DynamicKubernetesObject from, JsonObject cond) {
+ boolean reportedRunning
+ = "True".equals(cond.get("status").getAsString());
+ if (RUNNING_STATES.contains(event.state())
+ && !reportedRunning) {
+ cond.addProperty("status", "True");
+ cond.addProperty("lastTransitionTime",
+ Instant.now().toString());
+ }
+ if (!RUNNING_STATES.contains(event.state())
+ && reportedRunning) {
+ cond.addProperty("status", "False");
+ cond.addProperty("lastTransitionTime",
+ Instant.now().toString());
+ }
+ cond.addProperty("reason", event.reason());
+ cond.addProperty("message", event.message());
+ cond.addProperty("observedGeneration",
+ from.getMetadata().getGeneration());
+ }
+
+ /**
+ * On ballon change.
+ *
+ * @param event the event
+ * @throws ApiException
+ */
+ @Handler
+ public void onBallonChange(BalloonChangeEvent 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("ram",
+ new Quantity(new BigDecimal(event.size()), Format.BINARY_SI)
+ .toSuffixedString());
+ return status;
+ });
+ }
}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/BalloonChangeEvent.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/BalloonChangeEvent.java
new file mode 100644
index 0000000..9cc67c8
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/BalloonChangeEvent.java
@@ -0,0 +1,47 @@
+/*
+ * VM-Operator
+ * Copyright (C) 2023 Michael N. Lipp
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.jdrupes.vmoperator.runner.qemu.events;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import java.math.BigInteger;
+
+/**
+ * Signals a change of the balloon.
+ */
+public class BalloonChangeEvent extends MonitorEvent {
+
+ /**
+ * Instantiates a new tray moved.
+ *
+ * @param kind the kind
+ * @param data the data
+ */
+ public BalloonChangeEvent(Kind kind, JsonNode data) {
+ super(kind, data);
+ }
+
+ /**
+ * Returns the actual value.
+ *
+ * @return the actual value
+ */
+ public BigInteger size() {
+ return new BigInteger(data().get("actual").asText());
+ }
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorEvent.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorEvent.java
index 28d2e4c..72647a1 100644
--- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorEvent.java
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorEvent.java
@@ -32,7 +32,7 @@ public class MonitorEvent extends Event {
* The kind of monitor event.
*/
public enum Kind {
- READY, POWERDOWN, DEVICE_TRAY_MOVED
+ READY, POWERDOWN, DEVICE_TRAY_MOVED, BALLOON_CHANGE
}
private final Kind kind;
@@ -55,6 +55,9 @@ public class MonitorEvent extends Event {
case DEVICE_TRAY_MOVED:
return Optional
.of(new TrayMovedEvent(kind, response.get("data")));
+ case BALLOON_CHANGE:
+ return Optional
+ .of(new BalloonChangeEvent(kind, response.get("data")));
default:
return Optional
.of(new MonitorEvent(kind, response.get("data")));