diff --git a/org.jdrupes.vmoperator.runner.qemu/logging.properties b/org.jdrupes.vmoperator.runner.qemu/logging.properties
index 6b0542d..1cf84fe 100644
--- a/org.jdrupes.vmoperator.runner.qemu/logging.properties
+++ b/org.jdrupes.vmoperator.runner.qemu/logging.properties
@@ -19,8 +19,8 @@
handlers=java.util.logging.ConsoleHandler
-#org.jgrapes.level=FINE
-#org.jgrapes.core.handlerTracking.level=FINER
+org.jgrapes.level=FINE
+org.jgrapes.core.handlerTracking.level=FINER
org.jdrupes.vmoperator.runner.qemu.level=FINE
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CdMediaController.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CdMediaController.java
new file mode 100644
index 0000000..cc50b56
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CdMediaController.java
@@ -0,0 +1,148 @@
+/*
+ * 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;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpChangeMedium;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpOpenTray;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpRemoveMedium;
+import org.jdrupes.vmoperator.runner.qemu.events.ConfigureQemu;
+import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand;
+import org.jdrupes.vmoperator.runner.qemu.events.MonitorResult;
+import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State;
+import org.jdrupes.vmoperator.runner.qemu.events.TrayMovedEvent;
+import org.jgrapes.core.Channel;
+import org.jgrapes.core.Component;
+import org.jgrapes.core.annotation.Handler;
+
+/**
+ * The Class CdMediaController.
+ */
+@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
+public class CdMediaController extends Component {
+
+ /**
+ * The Enum TrayState.
+ */
+ public enum TrayState {
+ OPEN, CLOSED
+ }
+
+ private final Map trayState = new ConcurrentHashMap<>();
+ private final Map current = new ConcurrentHashMap<>();
+ private final Map pending = new ConcurrentHashMap<>();
+
+ /**
+ * Instantiates a new cdrom controller.
+ *
+ * @param componentChannel the component channel
+ * @param monitor the monitor
+ */
+ @SuppressWarnings("PMD.AssignmentToNonFinalStatic")
+ public CdMediaController(Channel componentChannel) {
+ super(componentChannel);
+ }
+
+ /**
+ * On configure qemu.
+ *
+ * @param event the event
+ */
+ @Handler
+ @SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition",
+ "PMD.AvoidInstantiatingObjectsInLoops" })
+ public void onConfigureQemu(ConfigureQemu event) {
+
+ int cdCounter = 0;
+ var drives = event.configuration().vm.drives;
+ for (int i = 0; i < drives.length; i++) {
+ if (!"ide-cd".equals(drives[i].type)) {
+ continue;
+ }
+ var driveId = "cd" + cdCounter++;
+ var newFile = Optional.ofNullable(drives[i].file).orElse("");
+ if (event.state() == State.STARTING) {
+ current.put(driveId, newFile);
+ continue;
+ }
+ if (!Objects.equals(current.get(driveId), newFile)) {
+ pending.put(driveId, newFile);
+ if (trayState.computeIfAbsent(driveId,
+ k -> TrayState.CLOSED) == TrayState.CLOSED) {
+ fire(new MonitorCommand(new QmpOpenTray(driveId)));
+ continue;
+ }
+ changeMedium(driveId);
+ }
+ }
+
+ }
+
+ private void changeMedium(String driveId) {
+ current.put(driveId, pending.get(driveId));
+ if (pending.get(driveId).isEmpty()) {
+ fire(new MonitorCommand(new QmpRemoveMedium(driveId)));
+ } else {
+ fire(new MonitorCommand(
+ new QmpChangeMedium(driveId, pending.get(driveId))));
+ }
+ }
+
+ /**
+ * On monitor event.
+ *
+ * @param event the event
+ */
+ @Handler
+ public void onTrayMovedEvent(TrayMovedEvent event) {
+ trayState.put(event.driveId(), event.state());
+ if (event.state() == TrayState.OPEN
+ && pending.containsKey(event.driveId())) {
+ changeMedium(event.driveId());
+ }
+ }
+
+ /**
+ * On monitor result.
+ *
+ * @param result the result
+ */
+ @Handler
+ public void onMonitorResult(MonitorResult result) {
+ if (result.executed() instanceof QmpOpenTray) {
+// if (!result.executed().equals(changeMedium.get("execute").asText())
+// && !result.executed()
+// .equals(removeMedium.get("execute").asText())) {
+// return;
+// }
+// String drive = result.arguments().get("id").asText();
+// String newFile = pending.get(drive);
+// if (newFile == null) {
+// return;
+// }
+// if (result.successful()) {
+// fire(new MonitorCommandCompleted(CHANGE_MEDIUM, drive, newFile));
+// pending.remove(drive);
+// }
+ }
+ }
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CdromController.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CdromController.java
deleted file mode 100644
index 1ab44da..0000000
--- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CdromController.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import java.io.IOException;
-import java.util.logging.Level;
-import org.jdrupes.vmoperator.runner.qemu.events.ChangeMediumCommand;
-import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand.Command;
-import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommandCompleted;
-import org.jgrapes.core.Channel;
-import org.jgrapes.core.Component;
-import org.jgrapes.core.annotation.Handler;
-
-// TODO: Auto-generated Javadoc
-/**
- * The Class CdromController.
- */
-public class CdromController extends Component {
-
- private static ObjectMapper mapper;
- private static JsonNode openTray;
- private static JsonNode removeMedium;
- private static JsonNode changeMedium;
- private final QemuMonitor monitor;
-
- /**
- * Instantiates a new cdrom controller.
- *
- * @param componentChannel the component channel
- * @param monitor the monitor
- */
- @SuppressWarnings("PMD.AssignmentToNonFinalStatic")
- public CdromController(Channel componentChannel, QemuMonitor monitor) {
- super(componentChannel);
- if (mapper == null) {
- mapper = new ObjectMapper();
- try {
- openTray = mapper.readValue("{ \"execute\": "
- + "\"blockdev-open-tray\",\"arguments\": {"
- + "\"id\": \"\" } }", JsonNode.class);
- removeMedium = mapper.readValue("{ \"execute\": "
- + "\"blockdev-remove-medium\",\"arguments\": {"
- + "\"id\": \"\" } }", JsonNode.class);
- changeMedium = mapper.readValue("{ \"execute\": "
- + "\"blockdev-change-medium\",\"arguments\": {"
- + "\"id\": \"\",\"filename\": \"\","
- + "\"format\": \"raw\",\"read-only-mode\": "
- + "\"read-only\" } }", JsonNode.class);
- } catch (IOException e) {
- logger.log(Level.SEVERE, e,
- () -> "Cannot initialize class: " + e.getMessage());
- }
- }
- this.monitor = monitor;
- }
-
- /**
- * On monitor command.
- *
- * @param event the event
- */
- @Handler
- @SuppressWarnings("PMD.AvoidDuplicateLiterals")
- public void onChangeMediumCommand(ChangeMediumCommand event) {
- if (event.command() != Command.CHANGE_MEDIUM) {
- return;
- }
- if (event.file() == null || event.file().isEmpty()) {
- var msg = openTray.deepCopy();
- ((ObjectNode) msg.get("arguments")).put("id", event.id());
- monitor.sendToMonitor(msg);
- msg = removeMedium.deepCopy();
- ((ObjectNode) msg.get("arguments")).put("id", event.id());
- monitor.sendToMonitor(msg);
- fire(new MonitorCommandCompleted(event.command(), null));
- return;
- }
- var msg = changeMedium.deepCopy();
- ((ObjectNode) msg.get("arguments")).put("id", event.id());
- ((ObjectNode) msg.get("arguments")).put("filename", event.file());
- monitor.sendToMonitor(msg);
- fire(new MonitorCommandCompleted(event.command(), null));
- }
-
-}
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 9442c2a..7916409 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
@@ -40,7 +40,7 @@ import org.jdrupes.vmoperator.util.FsdUtils;
* The configuration information from the configuration file.
*/
@SuppressWarnings("PMD.ExcessivePublicCount")
-class Configuration implements Dto {
+public class Configuration implements Dto {
@SuppressWarnings("PMD.FieldNamingConventions")
protected final Logger logger = Logger.getLogger(getClass().getName());
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 cd38fcc..0220bef 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
@@ -18,19 +18,20 @@
package org.jdrupes.vmoperator.runner.qemu;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
-import java.util.logging.Level;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpAddCpu;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpDelCpu;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpQueryHotpluggableCpus;
+import org.jdrupes.vmoperator.runner.qemu.events.ConfigureQemu;
+import org.jdrupes.vmoperator.runner.qemu.events.CpuAdded;
+import org.jdrupes.vmoperator.runner.qemu.events.CpuDeleted;
+import org.jdrupes.vmoperator.runner.qemu.events.HotpluggableCpuResult;
import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand;
-import static org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand.Command.SET_CURRENT_CPUS;
-import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommandCompleted;
-import org.jdrupes.vmoperator.runner.qemu.events.MonitorResult;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Component;
import org.jgrapes.core.annotation.Handler;
@@ -41,11 +42,9 @@ import org.jgrapes.core.annotation.Handler;
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class CpuController extends Component {
- private static ObjectMapper mapper;
- private static JsonNode queryHotpluggableCpus;
-
- private final QemuMonitor monitor;
+ private Integer currentCpus;
private Integer desiredCpus;
+ private ConfigureQemu suspendedConfigure;
/**
* Instantiates a new CPU controller.
@@ -53,35 +52,27 @@ public class CpuController extends Component {
* @param componentChannel the component channel
* @param monitor the monitor
*/
- @SuppressWarnings("PMD.AssignmentToNonFinalStatic")
- public CpuController(Channel componentChannel, QemuMonitor monitor) {
+ public CpuController(Channel componentChannel) {
super(componentChannel);
- if (mapper == null) {
- mapper = new ObjectMapper();
- try {
- queryHotpluggableCpus = mapper.readValue(
- "{\"execute\":\"query-hotpluggable-cpus\",\"arguments\":{}}",
- JsonNode.class);
- } catch (IOException e) {
- logger.log(Level.SEVERE, e,
- () -> "Cannot initialize class: " + e.getMessage());
- }
- }
- this.monitor = monitor;
}
/**
- * On monitor command.
+ * On configure qemu.
*
* @param event the event
*/
@Handler
- public void onMonitorCommand(MonitorCommand event) {
- if (event.command() != SET_CURRENT_CPUS) {
- return;
- }
- desiredCpus = (Integer) event.arguments()[0];
- monitor.sendToMonitor(queryHotpluggableCpus);
+ public void onConfigureQemu(ConfigureQemu event) {
+ Optional.ofNullable(event.configuration().vm.currentCpus)
+ .ifPresent(cpus -> {
+ if (desiredCpus != null && desiredCpus.equals(cpus)) {
+ return;
+ }
+ event.suspendHandling();
+ suspendedConfigure = event;
+ desiredCpus = cpus;
+ fire(new MonitorCommand(new QmpQueryHotpluggableCpus()));
+ });
}
/**
@@ -90,17 +81,11 @@ public class CpuController extends Component {
* @param result the result
*/
@Handler
- public void onMonitorResult(MonitorResult result) {
- if (!result.executed()
- .equals(queryHotpluggableCpus.get("execute").asText())
- || desiredCpus == null) {
- return;
- }
-
+ public void onHotpluggableCpuResult(HotpluggableCpuResult result) {
// Sort
List used = new ArrayList<>();
List unused = new ArrayList<>();
- for (var itr = result.returned().iterator(); itr.hasNext();) {
+ for (var itr = result.values().iterator(); itr.hasNext();) {
ObjectNode cpu = (ObjectNode) itr.next();
if (cpu.has("qom-path")) {
used.add(cpu);
@@ -108,15 +93,47 @@ public class CpuController extends Component {
unused.add(cpu);
}
}
-
+ currentCpus = used.size();
+ if (desiredCpus == null) {
+ return;
+ }
// Process
int diff = used.size() - desiredCpus;
diff = addCpus(used, unused, diff);
- diff = deleteCpus(used, diff);
- fire(new MonitorCommandCompleted(SET_CURRENT_CPUS, desiredCpus + diff));
- desiredCpus = null;
+ deleteCpus(used, diff);
}
+ /**
+ * On cpu added.
+ *
+ * @param event the event
+ */
+ @Handler
+ public void onCpuAdded(CpuAdded event) {
+ currentCpus += 1;
+ checkCpus();
+ }
+
+ /**
+ * On cpu deleted.
+ *
+ * @param event the event
+ */
+ @Handler
+ public void onCpuDeleted(CpuDeleted event) {
+ currentCpus -= 1;
+ checkCpus();
+ }
+
+ private void checkCpus() {
+ if (suspendedConfigure != null && desiredCpus != null
+ && currentCpus == desiredCpus.intValue()) {
+ suspendedConfigure.resumeHandling();
+ suspendedConfigure = null;
+ }
+ }
+
+ @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
private int addCpus(List used, List unused,
int diff) {
Set usedIds = new HashSet<>();
@@ -129,24 +146,18 @@ public class CpuController extends Component {
}
int nextId = 1;
while (diff < 0 && !unused.isEmpty()) {
- ObjectNode cmd = mapper.createObjectNode();
- cmd.put("execute", "device_add");
- ObjectNode args = mapper.createObjectNode();
- cmd.set("arguments", args);
- args.setAll((ObjectNode) (unused.get(0).get("props").deepCopy()));
- args.set("driver", unused.get(0).get("type"));
String id;
do {
id = "cpu-" + nextId++;
} while (usedIds.contains(id));
- args.put("id", id);
- monitor.sendToMonitor(cmd);
+ fire(new MonitorCommand(new QmpAddCpu(unused.get(0), id)));
unused.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);
@@ -155,12 +166,7 @@ public class CpuController extends Component {
continue;
}
String id = qomPath.substring(qomPath.lastIndexOf('/') + 1);
- ObjectNode cmd = mapper.createObjectNode();
- cmd.put("execute", "device_del");
- ObjectNode args = mapper.createObjectNode();
- cmd.set("arguments", args);
- args.put("id", id);
- monitor.sendToMonitor(cmd);
+ fire(new MonitorCommand(new QmpDelCpu(id)));
diff -= 1;
}
return diff;
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/QemuMonitor.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/QemuMonitor.java
index 69df213..605b5bc 100644
--- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/QemuMonitor.java
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/QemuMonitor.java
@@ -19,7 +19,6 @@
package org.jdrupes.vmoperator.runner.qemu;
import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
@@ -32,9 +31,11 @@ import java.time.Duration;
import java.util.LinkedList;
import java.util.Queue;
import java.util.logging.Level;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpCapabilities;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpPowerdown;
import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand;
-import static org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand.Command.CONTINUE;
-import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommandCompleted;
+import org.jdrupes.vmoperator.runner.qemu.events.MonitorEvent;
import org.jdrupes.vmoperator.runner.qemu.events.MonitorReady;
import org.jdrupes.vmoperator.runner.qemu.events.MonitorResult;
import org.jgrapes.core.Channel;
@@ -54,7 +55,6 @@ import org.jgrapes.net.SocketIOChannel;
import org.jgrapes.net.events.ClientConnected;
import org.jgrapes.util.events.ConfigurationUpdate;
import org.jgrapes.util.events.FileChanged;
-import org.jgrapes.util.events.FileChanged.Kind;
import org.jgrapes.util.events.WatchFile;
/**
@@ -67,16 +67,13 @@ import org.jgrapes.util.events.WatchFile;
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class QemuMonitor extends Component {
- private static ObjectMapper mapper;
- private static JsonNode connect;
- private static JsonNode cont;
- private static JsonNode powerdown;
+ private static ObjectMapper mapper = new ObjectMapper();
@SuppressWarnings("PMD.UseConcurrentHashMap")
private Path socketPath;
private int powerdownTimeout;
private SocketIOChannel monitorChannel;
- private final Queue executing = new LinkedList<>();
+ private final Queue executing = new LinkedList<>();
private Stop suspendedStop;
private Timer powerdownTimer;
@@ -89,23 +86,9 @@ public class QemuMonitor extends Component {
@SuppressWarnings("PMD.AssignmentToNonFinalStatic")
public QemuMonitor(Channel componentChannel) throws IOException {
super(componentChannel);
- if (mapper == null) {
- mapper = new ObjectMapper();
- try {
- connect = mapper.readValue("{ \"execute\": "
- + "\"qmp_capabilities\" }", JsonNode.class);
- cont = mapper.readValue("{ \"execute\": "
- + "\"cont\" }", JsonNode.class);
- powerdown = mapper.readValue("{ \"execute\": "
- + "\"system_powerdown\" }", JsonNode.class);
- } catch (IOException e) {
- logger.log(Level.SEVERE, e,
- () -> "Cannot initialize class: " + e.getMessage());
- }
- }
- attach(new RamController(channel(), this));
- attach(new CpuController(channel(), this));
- attach(new CdromController(channel(), this));
+ attach(new RamController(channel()));
+ attach(new CpuController(channel()));
+ attach(new CdMediaController(channel()));
}
/**
@@ -145,7 +128,8 @@ public class QemuMonitor extends Component {
*/
@Handler
public void onFileChanged(FileChanged event) {
- if (event.change() == Kind.CREATED && event.path().equals(socketPath)) {
+ if (event.change() == FileChanged.Kind.CREATED
+ && event.path().equals(socketPath)) {
// qemu running, open socket
fire(new OpenSocketConnection(
UnixDomainSocketAddress.of(socketPath))
@@ -178,7 +162,7 @@ public class QemuMonitor extends Component {
throw new UndeclaredThrowableException(e);
}
}));
- sendToMonitor(connect);
+ fire(new MonitorCommand(new QmpCapabilities()));
});
}
@@ -195,32 +179,6 @@ public class QemuMonitor extends Component {
});
}
- /* default */ void sendToMonitor(JsonNode message) {
- String asText;
- try {
- asText = mapper.writeValueAsString(message);
- } catch (JsonProcessingException e) {
- logger.log(Level.SEVERE, e,
- () -> "Cannot serialize Json: " + e.getMessage());
- return;
- }
- logger.fine(() -> "monitor(out): " + asText);
- synchronized (executing) {
- if (message.has("execute")) {
- executing.add(message.get("execute").asText());
- }
- monitorChannel.associated(Writer.class).ifPresent(writer -> {
- try {
- writer.append(asText).append('\n').flush();
- } catch (IOException e) {
- // Cannot happen, but...
- logger.log(Level.WARNING, e, () -> e.getMessage());
- }
- });
-
- }
- }
-
/**
* Handle data from qemu monitor connection.
*
@@ -247,13 +205,16 @@ public class QemuMonitor extends Component {
return;
}
if (response.has("return") || response.has("error")) {
- String executed = executing.poll();
+ QmpCommand executed = executing.poll();
logger.fine(
() -> String.format("(Previous \"monitor(in)\" is result "
+ "from executing %s)", executed));
- fire(new MonitorResult(executed, response));
+ fire(MonitorResult.from(executed, response));
return;
}
+ if (response.has("event")) {
+ MonitorEvent.from(response).ifPresent(this::fire);
+ }
} catch (JsonProcessingException e) {
throw new IOException(e);
}
@@ -286,12 +247,28 @@ public class QemuMonitor extends Component {
* @param event the event
*/
@Handler
- public void onMonitorCommand(MonitorCommand event) {
- if (event.command() != CONTINUE) {
+ public void onExecQmpCommand(MonitorCommand event) {
+ var command = event.command();
+ String asText;
+ try {
+ asText = mapper.writeValueAsString(command.toJson());
+ } catch (JsonProcessingException e) {
+ logger.log(Level.SEVERE, e,
+ () -> "Cannot serialize Json: " + e.getMessage());
return;
}
- sendToMonitor(cont);
- fire(new MonitorCommandCompleted(event.command(), null));
+ logger.fine(() -> "monitor(out): " + asText);
+ synchronized (executing) {
+ monitorChannel.associated(Writer.class).ifPresent(writer -> {
+ try {
+ executing.add(command);
+ writer.append(asText).append('\n').flush();
+ } catch (IOException e) {
+ // Cannot happen, but...
+ logger.log(Level.WARNING, e, () -> e.getMessage());
+ }
+ });
+ }
}
/**
@@ -305,7 +282,7 @@ public class QemuMonitor extends Component {
// We have a connection to Qemu, attempt ACPI shutdown.
event.suspendHandling();
suspendedStop = event;
- sendToMonitor(powerdown);
+ fire(new MonitorCommand(new QmpPowerdown()));
// Schedule timer as fallback
powerdownTimer = Components.schedule(t -> {
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/RamController.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/RamController.java
index 76b3c5a..cb0a4bf 100644
--- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/RamController.java
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/RamController.java
@@ -18,27 +18,21 @@
package org.jdrupes.vmoperator.runner.qemu;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import java.io.IOException;
import java.math.BigInteger;
-import java.util.logging.Level;
+import java.util.Optional;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpSetBalloon;
+import org.jdrupes.vmoperator.runner.qemu.events.ConfigureQemu;
import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand;
-import static org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand.Command.SET_CURRENT_RAM;
-import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommandCompleted;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Component;
import org.jgrapes.core.annotation.Handler;
/**
- * The Class CpuController.
+ * The Class RamController.
*/
public class RamController extends Component {
- private static ObjectMapper mapper;
- private static JsonNode setBalloon;
- private final QemuMonitor monitor;
+ private BigInteger currentRam;
/**
* Instantiates a new CPU controller.
@@ -47,36 +41,25 @@ public class RamController extends Component {
* @param monitor the monitor
*/
@SuppressWarnings("PMD.AssignmentToNonFinalStatic")
- public RamController(Channel componentChannel, QemuMonitor monitor) {
+ public RamController(Channel componentChannel) {
super(componentChannel);
- if (mapper == null) {
- mapper = new ObjectMapper();
- try {
- setBalloon = mapper.readValue("{ \"execute\": \"balloon\", "
- + "\"arguments\": " + "{ \"value\": 0 } }", JsonNode.class);
- } catch (IOException e) {
- logger.log(Level.SEVERE, e,
- () -> "Cannot initialize class: " + e.getMessage());
- }
- }
- this.monitor = monitor;
}
/**
- * On monitor command.
+ * On configure qemu.
*
* @param event the event
*/
@Handler
- public void onMonitorCommand(MonitorCommand event) {
- if (event.command() != SET_CURRENT_RAM) {
- return;
- }
- var msg = setBalloon.deepCopy();
- ((ObjectNode) msg.get("arguments")).put("value",
- (BigInteger) event.arguments()[0]);
- monitor.sendToMonitor(msg);
- fire(new MonitorCommandCompleted(event.command(), null));
+ public void onConfigureQemu(ConfigureQemu event) {
+ Optional.ofNullable(event.configuration().vm.currentRam)
+ .ifPresent(cr -> {
+ if (currentRam != null && currentRam.equals(cr)) {
+ return;
+ }
+ currentRam = cr;
+ fire(new MonitorCommand(new QmpSetBalloon(cr)));
+ });
}
}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java
index e672fd5..84094f2 100644
--- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java
@@ -41,7 +41,6 @@ import java.nio.file.Paths;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
-import java.util.Objects;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.LogManager;
@@ -51,14 +50,12 @@ import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
-import org.jdrupes.vmoperator.runner.qemu.StateController.State;
-import org.jdrupes.vmoperator.runner.qemu.events.ChangeMediumCommand;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpCont;
+import org.jdrupes.vmoperator.runner.qemu.events.ConfigureQemu;
import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand;
-import static org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand.Command.CONTINUE;
-import static org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand.Command.SET_CURRENT_CPUS;
-import static org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand.Command.SET_CURRENT_RAM;
-import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommandCompleted;
import org.jdrupes.vmoperator.runner.qemu.events.MonitorReady;
+import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange;
+import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State;
import org.jdrupes.vmoperator.util.ExtendedObjectWrapper;
import org.jdrupes.vmoperator.util.FsdUtils;
import org.jgrapes.core.Component;
@@ -120,8 +117,8 @@ import org.jgrapes.util.events.WatchFile;
* monitor --> configure: ClientConnected[for monitor]
* monitor -> error: ConnectError[for monitor]
*
- * configure: entry/fire configuration commands
- * configure --> success: last completed/fire cont command
+ * configure: entry/fire ConfigureQemu
+ * configure --> success: ConfigureQemu (last handler)/fire cont command
* }
*
* Initializing --> which: Started
@@ -161,6 +158,7 @@ import org.jgrapes.util.events.WatchFile;
"PMD.DataflowAnomalyAnalysis" })
public class Runner extends Component {
+ /** The Constant APP_NAME. */
public static final String APP_NAME = "vmrunner";
private static final String TEMPLATE_DIR
= "/opt/" + APP_NAME + "/templates";
@@ -174,20 +172,19 @@ public class Runner extends Component {
@SuppressWarnings("PMD.UseConcurrentHashMap")
private Configuration config = new Configuration();
private final freemarker.template.Configuration fmConfig;
- private final StateController state;
private CommandDefinition swtpmDefinition;
private CommandDefinition qemuDefinition;
private final QemuMonitor qemuMonitor;
+ private State state = State.INITIALIZING;
/**
* Instantiates a new runner.
- * @param cmdLine
*
+ * @param cmdLine the cmd line
* @throws IOException Signals that an I/O exception has occurred.
*/
@SuppressWarnings("PMD.SystemPrintln")
public Runner(CommandLine cmdLine) throws IOException {
- state = new StateController(this);
yamlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
@@ -237,7 +234,9 @@ public class Runner extends Component {
processInitialConfiguration(c);
return;
}
- updateConfiguration(c);
+ logger.fine(() -> "Updating configuration");
+ var newConf = yamlMapper.convertValue(c, Configuration.class);
+ fire(new ConfigureQemu(newConf, state));
});
}
@@ -334,51 +333,6 @@ public class Runner extends Component {
return yamlMapper.readValue(out.toString(), JsonNode.class);
}
- @SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition",
- "PMD.AvoidInstantiatingObjectsInLoops" })
- private void updateConfiguration(Map conf) {
- logger.fine(() -> "Updating configuration");
- var newConf = yamlMapper.convertValue(conf, Configuration.class);
- Optional.ofNullable(newConf.vm.currentRam).ifPresent(cr -> {
- if (config.vm.currentRam != null
- && config.vm.currentRam.equals(cr)) {
- return;
- }
- synchronized (state) {
- config.vm.currentRam = cr;
- if (state.get() == State.RUNNING) {
- fire(new MonitorCommand(SET_CURRENT_RAM, cr));
- }
- }
- });
- if (config.vm.currentCpus != newConf.vm.currentCpus) {
- synchronized (state) {
- config.vm.currentCpus = newConf.vm.currentCpus;
- if (state.get() == State.RUNNING) {
- fire(new MonitorCommand(SET_CURRENT_CPUS,
- newConf.vm.currentCpus));
- }
- }
- }
-
- int cdCounter = 0;
- for (int i = 0; i < Math.min(config.vm.drives.length,
- newConf.vm.drives.length); i++) {
- if (!"ide-cd".equals(config.vm.drives[i].type)) {
- continue;
- }
- String curFile = config.vm.drives[i].file;
- String newFile = newConf.vm.drives[i].file;
- if (!Objects.equals(curFile, newFile)) {
- config.vm.drives[i].file = newConf.vm.drives[i].file;
- synchronized (state) {
- fire(new ChangeMediumCommand("cd" + cdCounter, newFile));
- }
- }
- cdCounter += 1;
- }
- }
-
/**
* Handle the start event.
*
@@ -424,7 +378,8 @@ public class Runner extends Component {
*/
@Handler
public void onStarted(Started event) {
- state.set(State.STARTING);
+ state = State.STARTING;
+ fire(new RunnerStateChange(state));
// Start first process
if (config.vm.useTpm && swtpmDefinition != null) {
startProcess(swtpmDefinition);
@@ -507,33 +462,26 @@ public class Runner extends Component {
}
/**
- * On qemu monitor started.
+ * On monitor ready.
*
* @param event the event
*/
@Handler
public void onMonitorReady(MonitorReady event) {
- synchronized (state) {
- Optional.ofNullable(config.vm.currentRam).ifPresent(ram -> {
- fire(new MonitorCommand(SET_CURRENT_RAM, ram));
- });
- Optional.ofNullable(config.vm.currentCpus).ifPresent(cpus -> {
- fire(new MonitorCommand(SET_CURRENT_CPUS, cpus));
- });
- }
+ fire(new ConfigureQemu(config, state));
}
/**
- * On monitor command completed.
+ * On configure qemu.
*
* @param event the event
*/
- @Handler
- public void onMonitorCommandCompleted(MonitorCommandCompleted event) {
- if (state.get() != State.RUNNING
- && event.command() == SET_CURRENT_CPUS) {
- fire(new MonitorCommand(CONTINUE));
- state.set(State.RUNNING);
+ @Handler(priority = -1000)
+ public void onConfigureQemu(ConfigureQemu event) {
+ if (state == State.STARTING) {
+ fire(new MonitorCommand(new QmpCont()));
+ state = State.RUNNING;
+ fire(new RunnerStateChange(state));
}
}
@@ -547,15 +495,14 @@ public class Runner extends Component {
public void onProcessExited(ProcessExited event, ProcessChannel channel) {
channel.associated(CommandDefinition.class).ifPresent(procDef -> {
// No process(es) may exit during startup
- if (state.get() == State.STARTING) {
+ if (state == State.STARTING) {
logger.severe(() -> "Process " + procDef.name
+ " has exited with value " + event.exitValue()
+ " during startup.");
fire(new Stop());
return;
}
- if (procDef.equals(qemuDefinition)
- && state.get() == State.RUNNING) {
+ if (procDef.equals(qemuDefinition) && state == State.RUNNING) {
fire(new Stop());
}
logger.info(() -> "Process " + procDef.name
@@ -570,11 +517,12 @@ public class Runner extends Component {
*/
@Handler(priority = 10_000)
public void onStop(Stop event) {
- state.set(State.TERMINATING);
+ state = State.TERMINATING;
+ fire(new RunnerStateChange(state));
}
private void shutdown() {
- if (state.get() != State.TERMINATING) {
+ if (state != State.TERMINATING) {
fire(new Stop());
}
try {
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpAddCpu.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpAddCpu.java
new file mode 100644
index 0000000..0fd8908
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpAddCpu.java
@@ -0,0 +1,71 @@
+/*
+ * 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.commands;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * The Class QmpAddCpu.
+ */
+public class QmpAddCpu extends QmpCommand {
+
+ private final JsonNode unused;
+ private final String cpuId;
+
+ /**
+ * Instantiates a new command.
+ *
+ * @param unused description of an unused cpu slot
+ * @param cpuId the cpu id
+ */
+ public QmpAddCpu(JsonNode unused, String cpuId) {
+ super();
+ this.unused = unused.deepCopy();
+ this.cpuId = cpuId;
+ }
+
+ /**
+ * To Json.
+ *
+ * @return the json node
+ */
+ @Override
+ public JsonNode toJson() {
+ ObjectNode cmd = mapper.createObjectNode();
+ cmd.put("execute", "device_add");
+ ObjectNode args = mapper.createObjectNode();
+ cmd.set("arguments", args);
+ args.setAll((ObjectNode) (unused.get("props")));
+ args.set("driver", unused.get("type"));
+ args.put("id", cpuId);
+ return cmd;
+ }
+
+ /**
+ * To string.
+ *
+ * @return the string
+ */
+ @Override
+ public String toString() {
+ return "QmpAddCpu(" + unused.get("type") + ", " + cpuId + ")";
+ }
+
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpCapabilities.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpCapabilities.java
new file mode 100644
index 0000000..54c6bb9
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpCapabilities.java
@@ -0,0 +1,53 @@
+/*
+ * 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.commands;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * The Class QmpCapabilities.
+ */
+public class QmpCapabilities extends QmpCommand {
+
+ @SuppressWarnings({ "PMD.FieldNamingConventions",
+ "PMD.VariableNamingConventions" })
+ private static final JsonNode jsonTemplate
+ = parseJson("{ \"execute\": \"qmp_capabilities\" }");
+
+ /**
+ * To Json.
+ *
+ * @return the json node
+ */
+ @Override
+ public JsonNode toJson() {
+ return jsonTemplate.deepCopy();
+ }
+
+ /**
+ * To string.
+ *
+ * @return the string
+ */
+ @Override
+ public String toString() {
+ return "QmpCapabilities()";
+ }
+
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpChangeMedium.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpChangeMedium.java
new file mode 100644
index 0000000..13a5878
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpChangeMedium.java
@@ -0,0 +1,71 @@
+/*
+ * 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.commands;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * The Class QmpChangeMedium.
+ */
+public class QmpChangeMedium extends QmpCommand {
+
+ @SuppressWarnings({ "PMD.FieldNamingConventions",
+ "PMD.VariableNamingConventions" })
+ private static final JsonNode jsonTemplate
+ = parseJson("{ \"execute\": \"blockdev-change-medium\",\"arguments\": {"
+ + "\"id\": \"\",\"filename\": \"\",\"format\": \"raw\","
+ + "\"read-only-mode\": \"read-only\" } }");
+ private final String driveId;
+ private final String file;
+
+ /**
+ * Instantiates a new sets the current ram.
+ *
+ * @param driveId the drive id
+ */
+ public QmpChangeMedium(String driveId, String file) {
+ this.driveId = driveId;
+ this.file = file;
+ }
+
+ /**
+ * To Json.
+ *
+ * @return the json node
+ */
+ @Override
+ public JsonNode toJson() {
+ var cmd = jsonTemplate.deepCopy();
+ ((ObjectNode) cmd.get("arguments")).put("id", driveId);
+ ((ObjectNode) cmd.get("arguments")).put("filename", file);
+ return cmd;
+ }
+
+ /**
+ * To string.
+ *
+ * @return the string
+ */
+ @Override
+ public String toString() {
+ return "QmpRemoveMedium(" + driveId + ")";
+ }
+
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpCommand.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpCommand.java
new file mode 100644
index 0000000..1ba80da
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpCommand.java
@@ -0,0 +1,58 @@
+/*
+ * 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.commands;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The Class QmpCommand.
+ */
+public abstract class QmpCommand {
+
+ @SuppressWarnings({ "PMD.FieldNamingConventions",
+ "PMD.VariableNamingConventions" })
+ protected static final ObjectMapper mapper = new ObjectMapper();
+
+ /**
+ * Parses the json.
+ *
+ * @param json the json
+ * @return the json node
+ */
+ protected static JsonNode parseJson(String json) {
+ try {
+ return mapper.readValue(json, JsonNode.class);
+ } catch (IOException e) {
+ Logger.getLogger(QmpCommand.class.getName()).log(Level.SEVERE, e,
+ () -> "Cannot initialize class: " + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * To json.
+ *
+ * @return the json node
+ */
+ public abstract JsonNode toJson();
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpCont.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpCont.java
new file mode 100644
index 0000000..a78ad58
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpCont.java
@@ -0,0 +1,53 @@
+/*
+ * 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.commands;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * The Class QmpCont.
+ */
+public class QmpCont extends QmpCommand {
+
+ @SuppressWarnings({ "PMD.FieldNamingConventions",
+ "PMD.VariableNamingConventions" })
+ private static final JsonNode jsonTemplate
+ = parseJson("{ \"execute\": \"cont\" }");
+
+ /**
+ * To Json.
+ *
+ * @return the json node
+ */
+ @Override
+ public JsonNode toJson() {
+ return jsonTemplate.deepCopy();
+ }
+
+ /**
+ * To string.
+ *
+ * @return the string
+ */
+ @Override
+ public String toString() {
+ return "QmpCont()";
+ }
+
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpDelCpu.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpDelCpu.java
new file mode 100644
index 0000000..2b4486d
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpDelCpu.java
@@ -0,0 +1,67 @@
+/*
+ * 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.commands;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * The Class QmpDelCpu.
+ */
+public class QmpDelCpu extends QmpCommand {
+
+ @SuppressWarnings({ "PMD.FieldNamingConventions",
+ "PMD.VariableNamingConventions" })
+ private static final JsonNode jsonTemplate
+ = parseJson("{ \"execute\": \"device_del\", "
+ + "\"arguments\": " + "{ \"id\": 0 } }");
+ private final String cpuId;
+
+ /**
+ * Instantiates a new sets the current ram.
+ *
+ * @param size the size
+ */
+ public QmpDelCpu(String cpuId) {
+ this.cpuId = cpuId;
+ }
+
+ /**
+ * To Json.
+ *
+ * @return the json node
+ */
+ @Override
+ public JsonNode toJson() {
+ var cmd = jsonTemplate.deepCopy();
+ ((ObjectNode) cmd.get("arguments")).put("id", cpuId);
+ return cmd;
+ }
+
+ /**
+ * To string.
+ *
+ * @return the string
+ */
+ @Override
+ public String toString() {
+ return "QmpDelCpu(" + cpuId + ")";
+ }
+
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpOpenTray.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpOpenTray.java
new file mode 100644
index 0000000..3c04135
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpOpenTray.java
@@ -0,0 +1,67 @@
+/*
+ * 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.commands;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * The Class QmpOpenTray.
+ */
+public class QmpOpenTray extends QmpCommand {
+
+ @SuppressWarnings({ "PMD.FieldNamingConventions",
+ "PMD.VariableNamingConventions" })
+ private static final JsonNode jsonTemplate
+ = parseJson("{ \"execute\": \"blockdev-open-tray\",\"arguments\": {"
+ + "\"id\": \"\" } }");
+ private final String driveId;
+
+ /**
+ * Instantiates a new sets the current ram.
+ *
+ * @param driveId the drive id
+ */
+ public QmpOpenTray(String driveId) {
+ this.driveId = driveId;
+ }
+
+ /**
+ * To Json.
+ *
+ * @return the json node
+ */
+ @Override
+ public JsonNode toJson() {
+ var cmd = jsonTemplate.deepCopy();
+ ((ObjectNode) cmd.get("arguments")).put("id", driveId);
+ return cmd;
+ }
+
+ /**
+ * To string.
+ *
+ * @return the string
+ */
+ @Override
+ public String toString() {
+ return "QmpOpenTray(" + driveId + ")";
+ }
+
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpPowerdown.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpPowerdown.java
new file mode 100644
index 0000000..bc11ff3
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpPowerdown.java
@@ -0,0 +1,48 @@
+/*
+ * 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.commands;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * The Class QmpSetBalloon.
+ */
+public class QmpPowerdown extends QmpCommand {
+
+ @SuppressWarnings({ "PMD.FieldNamingConventions",
+ "PMD.VariableNamingConventions" })
+ private static final JsonNode jsonTemplate
+ = parseJson("{ \"execute\": \"system_powerdown\" }");
+
+ /**
+ * To Json.
+ *
+ * @return the json node
+ */
+ @Override
+ public JsonNode toJson() {
+ return jsonTemplate.deepCopy();
+ }
+
+ @Override
+ public String toString() {
+ return "QmpPowerdown()";
+ }
+
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpQueryHotpluggableCpus.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpQueryHotpluggableCpus.java
new file mode 100644
index 0000000..2cc9b1b
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpQueryHotpluggableCpus.java
@@ -0,0 +1,54 @@
+/*
+ * 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.commands;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+// TODO: Auto-generated Javadoc
+/**
+ * The Class QmpQueryHotpluggableCpus.
+ */
+public class QmpQueryHotpluggableCpus extends QmpCommand {
+
+ @SuppressWarnings({ "PMD.FieldNamingConventions",
+ "PMD.VariableNamingConventions" })
+ private static final JsonNode jsonTemplate = parseJson(
+ "{\"execute\":\"query-hotpluggable-cpus\",\"arguments\":{}}");
+
+ /**
+ * To Json.
+ *
+ * @return the json node
+ */
+ @Override
+ public JsonNode toJson() {
+ return jsonTemplate.deepCopy();
+ }
+
+ /**
+ * To string.
+ *
+ * @return the string
+ */
+ @Override
+ public String toString() {
+ return "QmpQueryHotpluggableCpus()";
+ }
+
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpRemoveMedium.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpRemoveMedium.java
new file mode 100644
index 0000000..b3e46d2
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpRemoveMedium.java
@@ -0,0 +1,67 @@
+/*
+ * 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.commands;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * The Class QmpRemoveMedium.
+ */
+public class QmpRemoveMedium extends QmpCommand {
+
+ @SuppressWarnings({ "PMD.FieldNamingConventions",
+ "PMD.VariableNamingConventions" })
+ private static final JsonNode jsonTemplate
+ = parseJson("{ \"execute\": \"blockdev-remove-medium\",\"arguments\": {"
+ + "\"id\": \"\" } }");
+ private final String driveId;
+
+ /**
+ * Instantiates a new sets the current ram.
+ *
+ * @param driveId the drive id
+ */
+ public QmpRemoveMedium(String driveId) {
+ this.driveId = driveId;
+ }
+
+ /**
+ * To Json.
+ *
+ * @return the json node
+ */
+ @Override
+ public JsonNode toJson() {
+ var cmd = jsonTemplate.deepCopy();
+ ((ObjectNode) cmd.get("arguments")).put("id", driveId);
+ return cmd;
+ }
+
+ /**
+ * To string.
+ *
+ * @return the string
+ */
+ @Override
+ public String toString() {
+ return "QmpRemoveMedium(" + driveId + ")";
+ }
+
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpSetBalloon.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpSetBalloon.java
new file mode 100644
index 0000000..203095d
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpSetBalloon.java
@@ -0,0 +1,61 @@
+/*
+ * 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.commands;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.math.BigInteger;
+
+/**
+ * The Class QmpSetBalloon.
+ */
+public class QmpSetBalloon extends QmpCommand {
+
+ @SuppressWarnings({ "PMD.FieldNamingConventions",
+ "PMD.VariableNamingConventions" })
+ private static final JsonNode jsonTemplate
+ = parseJson("{ \"execute\": \"balloon\", "
+ + "\"arguments\": " + "{ \"value\": 0 } }");
+ private final BigInteger size;
+
+ /**
+ * Instantiates a new sets the current ram.
+ */
+ public QmpSetBalloon(BigInteger size) {
+ this.size = size;
+ }
+
+ /**
+ * To Json.
+ *
+ * @return the json node
+ */
+ @Override
+ public JsonNode toJson() {
+ var cmd = jsonTemplate.deepCopy();
+ ((ObjectNode) cmd.get("arguments")).put("value", size);
+ return cmd;
+ }
+
+ @Override
+ public String toString() {
+ return "QmpSetBalloon(" + size + ")";
+ }
+
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpSetCurrentCpus.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpSetCurrentCpus.java
new file mode 100644
index 0000000..8430c78
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpSetCurrentCpus.java
@@ -0,0 +1,59 @@
+/*
+ * 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.commands;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * The Class QmpSetBalloon.
+ */
+public class QmpSetCurrentCpus extends QmpCommand {
+
+ @SuppressWarnings({ "PMD.FieldNamingConventions",
+ "PMD.VariableNamingConventions" })
+ private static final JsonNode jsonTemplate = parseJson(
+ "{\"execute\":\"query-hotpluggable-cpus\",\"arguments\":{}}");
+ private final int cpus;
+
+ /**
+ * Instantiates a new sets the current ram.
+ */
+ public QmpSetCurrentCpus(int cpus) {
+ this.cpus = cpus;
+ }
+
+ /**
+ * To Json.
+ *
+ * @return the json node
+ */
+ @Override
+ public JsonNode toJson() {
+ var cmd = jsonTemplate.deepCopy();
+ ((ObjectNode) cmd.get("arguments")).put("value", cpus);
+ return cmd;
+ }
+
+ @Override
+ public String toString() {
+ return "QmpSetCurrentCpus(" + cpus + ")";
+ }
+
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/package-info.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/package-info.java
new file mode 100644
index 0000000..f7881b9
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/package-info.java
@@ -0,0 +1 @@
+package org.jdrupes.vmoperator.runner.qemu.commands;
\ No newline at end of file
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/ConfigureQemu.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/ConfigureQemu.java
new file mode 100644
index 0000000..5de5cf6
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/ConfigureQemu.java
@@ -0,0 +1,63 @@
+/*
+ * 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 org.jdrupes.vmoperator.runner.qemu.Configuration;
+import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State;
+import org.jgrapes.core.Channel;
+import org.jgrapes.core.Event;
+
+/**
+ * The Class ConfigureQemu.
+ */
+public class ConfigureQemu extends Event {
+
+ private final Configuration configuration;
+ private final State state;
+
+ /**
+ * Instantiates a new configure qemu.
+ *
+ * @param channels the channels
+ */
+ public ConfigureQemu(Configuration configuration, State state,
+ Channel... channels) {
+ super(channels);
+ this.state = state;
+ this.configuration = configuration;
+ }
+
+ /**
+ * Returns the configuration.
+ *
+ * @return the configuration
+ */
+ public Configuration configuration() {
+ return configuration;
+ }
+
+ /**
+ * Returns the runner's state.
+ *
+ * @return the state
+ */
+ public State state() {
+ return state;
+ }
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/ChangeMediumCommand.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/CpuAdded.java
similarity index 56%
rename from org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/ChangeMediumCommand.java
rename to org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/CpuAdded.java
index f3f1d73..1c1b6f9 100644
--- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/ChangeMediumCommand.java
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/CpuAdded.java
@@ -18,37 +18,22 @@
package org.jdrupes.vmoperator.runner.qemu.events;
+import com.fasterxml.jackson.databind.JsonNode;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand;
+
/**
- * The Class ChangeMediumCommand.
+ * The Class CpuAdded.
*/
-public class ChangeMediumCommand extends MonitorCommand {
+public class CpuAdded extends MonitorResult {
/**
- * Instantiates a new change medium command.
+ * Instantiates a new cpu added.
*
- * @param id the id
- * @param file the file path
+ * @param command the command
+ * @param response the response
*/
- public ChangeMediumCommand(String id, String file) {
- super(Command.CHANGE_MEDIUM, id, file);
+ public CpuAdded(QmpCommand command, JsonNode response) {
+ super(command, response);
}
- /**
- * Gets the id.
- *
- * @return the id
- */
- @SuppressWarnings("PMD.ShortMethodName")
- public String id() {
- return (String) arguments()[0];
- }
-
- /**
- * Gets the file.
- *
- * @return the file
- */
- public String file() {
- return (String) arguments()[1];
- }
}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/CpuDeleted.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/CpuDeleted.java
new file mode 100644
index 0000000..8831795
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/CpuDeleted.java
@@ -0,0 +1,39 @@
+/*
+ * 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 org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand;
+
+/**
+ * The Class CpuDeleted.
+ */
+public class CpuDeleted extends MonitorResult {
+
+ /**
+ * Instantiates a new cpu deleted.
+ *
+ * @param command the command
+ * @param response the response
+ */
+ public CpuDeleted(QmpCommand command, JsonNode response) {
+ super(command, response);
+ }
+
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/HotpluggableCpuResult.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/HotpluggableCpuResult.java
new file mode 100644
index 0000000..0df3263
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/HotpluggableCpuResult.java
@@ -0,0 +1,39 @@
+/*
+ * 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 org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand;
+
+/**
+ * The Class HotpluggableCpuResult.
+ */
+public class HotpluggableCpuResult extends MonitorResult {
+
+ /**
+ * Instantiates a new hotpluggable cpu result.
+ *
+ * @param command the command
+ * @param response the response
+ */
+ public HotpluggableCpuResult(QmpCommand command, JsonNode response) {
+ super(command, response);
+ }
+
+}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorCommand.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorCommand.java
index a5a9011..d93cbea 100644
--- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorCommand.java
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorCommand.java
@@ -18,7 +18,7 @@
package org.jdrupes.vmoperator.runner.qemu.events;
-import java.util.Arrays;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Components;
import org.jgrapes.core.Event;
@@ -28,26 +28,15 @@ import org.jgrapes.core.Event;
*/
public class MonitorCommand extends Event {
- /**
- * The available commands.
- */
- public enum Command {
- CONTINUE, SET_CURRENT_CPUS, SET_CURRENT_RAM, CHANGE_MEDIUM
- }
-
- private final Command command;
- private final Object[] arguments;
+ private final QmpCommand command;
/**
- * Instantiates a new monitor command.
+ * Instantiates a new exec qmp command.
*
* @param command the command
- * @param arguments the arguments
*/
- public MonitorCommand(Command command, Object... arguments) {
- super();
+ public MonitorCommand(QmpCommand command) {
this.command = command;
- this.arguments = Arrays.copyOf(arguments, arguments.length);
}
/**
@@ -55,19 +44,10 @@ public class MonitorCommand extends Event {
*
* @return the command
*/
- public Command command() {
+ public QmpCommand command() {
return command;
}
- /**
- * Gets the arguments.
- *
- * @return the arguments
- */
- public Object[] arguments() {
- return Arrays.copyOf(arguments, arguments.length);
- }
-
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorCommandCompleted.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorCommandCompleted.java
deleted file mode 100644
index ed590c4..0000000
--- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorCommandCompleted.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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 org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand.Command;
-import org.jgrapes.core.Channel;
-import org.jgrapes.core.Components;
-import org.jgrapes.core.Event;
-
-/**
- * Signals the completion of a monitor command.
- */
-public class MonitorCommandCompleted extends Event {
-
- private final Command command;
- private final Object result;
-
- /**
- * Instantiates a new monitor command.
- *
- * @param command the command
- * @param result the result
- */
- public MonitorCommandCompleted(Command command, Object result) {
- super();
- this.command = command;
- this.result = result;
- }
-
- /**
- * Gets the command.
- *
- * @return the command
- */
- public Command command() {
- return command;
- }
-
- /**
- * Gets the result.
- *
- * @return the arguments
- */
- public Object result() {
- return result;
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append(Components.objectName(this))
- .append(" [").append(command);
- if (channels() != null) {
- builder.append(", channels=");
- builder.append(Channel.toString(channels()));
- }
- builder.append(']');
- return builder.toString();
- }
-}
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 492df65..dd47423 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
@@ -18,9 +18,10 @@
package org.jdrupes.vmoperator.runner.qemu.events;
+import com.fasterxml.jackson.databind.JsonNode;
+import java.util.Optional;
import org.jgrapes.core.Event;
-// TODO: Auto-generated Javadoc
/**
* Signals the reception of an event from the monitor.
*/
@@ -30,18 +31,45 @@ public class MonitorEvent extends Event {
* The kind of monitor event.
*/
public enum Kind {
- READY
+ READY, DEVICE_TRAY_MOVED
}
private final Kind kind;
+ private final JsonNode data;
+
+ /**
+ * Create event from response.
+ *
+ * @param response the response
+ * @return the optional
+ */
+ @SuppressWarnings("PMD.TooFewBranchesForASwitchStatement")
+ public static Optional from(JsonNode response) {
+ try {
+ var kind
+ = MonitorEvent.Kind.valueOf(response.get("event").asText());
+ switch (kind) {
+ case DEVICE_TRAY_MOVED:
+ return Optional
+ .of(new TrayMovedEvent(kind, response.get("data")));
+ default:
+ return Optional
+ .of(new MonitorEvent(kind, response.get("data")));
+ }
+ } catch (IllegalArgumentException e) {
+ return Optional.empty();
+ }
+ }
/**
* Instantiates a new monitor event.
*
* @param kind the kind
+ * @param data the data
*/
- public MonitorEvent(Kind kind) {
+ protected MonitorEvent(Kind kind, JsonNode data) {
this.kind = kind;
+ this.data = data;
}
/**
@@ -52,4 +80,13 @@ public class MonitorEvent extends Event {
public Kind kind() {
return kind;
}
+
+ /**
+ * Returns the data associated with the event.
+ *
+ * @return the object[]
+ */
+ public JsonNode data() {
+ return data;
+ }
}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorReady.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorReady.java
index 8ff1f25..a19dea9 100644
--- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorReady.java
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorReady.java
@@ -28,7 +28,7 @@ public class MonitorReady extends MonitorEvent {
* Instantiates a new monitor ready.
*/
public MonitorReady() {
- super(Kind.READY);
+ super(Kind.READY, null);
}
}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorResult.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorResult.java
index cf25b13..f8ad4cf 100644
--- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorResult.java
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorResult.java
@@ -19,6 +19,12 @@
package org.jdrupes.vmoperator.runner.qemu.events;
import com.fasterxml.jackson.databind.JsonNode;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpAddCpu;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpDelCpu;
+import org.jdrupes.vmoperator.runner.qemu.commands.QmpQueryHotpluggableCpus;
+import org.jgrapes.core.Channel;
+import org.jgrapes.core.Components;
import org.jgrapes.core.Event;
/**
@@ -26,33 +32,83 @@ import org.jgrapes.core.Event;
*/
public class MonitorResult extends Event {
- private final String executed;
- private final JsonNode returned;
+ private final QmpCommand executed;
+ private final JsonNode response;
+
+ /**
+ * Create event from data.
+ *
+ * @param command the command
+ * @param response the response
+ * @return the monitor result
+ */
+ public static MonitorResult from(QmpCommand command, JsonNode response) {
+ if (command instanceof QmpQueryHotpluggableCpus) {
+ return new HotpluggableCpuResult(command, response);
+ }
+ if (command instanceof QmpAddCpu) {
+ return new CpuAdded(command, response);
+ }
+ if (command instanceof QmpDelCpu) {
+ return new CpuDeleted(command, response);
+ }
+ return new MonitorResult(command, response);
+ }
/**
* Instantiates a new monitor result.
*
- * @param executed the command executed
+ * @param executed the executed
* @param response the response
*/
- public MonitorResult(String executed, JsonNode response) {
- this.executed = executed;
- this.returned = response.get("return");
+ protected MonitorResult(QmpCommand command, JsonNode response) {
+ this.executed = command;
+ this.response = response;
}
/**
- * Return the executed command.
+ * Returns the executed executed.
*
- * @return the string
+ * @return the executed
*/
- public String executed() {
+ public QmpCommand executed() {
return executed;
}
/**
- * Return the values returned.
+ * Returns true if executed has been executed successfully.
+ *
+ * @return true, if successful
*/
- public JsonNode returned() {
- return returned;
+ public boolean successful() {
+ return response.has("return");
+ }
+
+ /**
+ * Returns the values that come with the response.
+ *
+ * @return the json node
+ */
+ public JsonNode values() {
+ if (response.has("return")) {
+ return response.get("return");
+ }
+ if (response.has("error")) {
+ return response.get("error");
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(Components.objectName(this))
+ .append(" [").append(executed).append(", ").append(successful());
+ if (channels() != null) {
+ builder.append(", channels=");
+ builder.append(Channel.toString(channels()));
+ }
+ builder.append(']');
+ return builder.toString();
}
}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StateController.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/RunnerStateChange.java
similarity index 60%
rename from org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StateController.java
rename to org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/RunnerStateChange.java
index 8561b88..a827fa4 100644
--- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StateController.java
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/RunnerStateChange.java
@@ -16,53 +16,41 @@
* along with this program. If not, see .
*/
-package org.jdrupes.vmoperator.runner.qemu;
+package org.jdrupes.vmoperator.runner.qemu.events;
+
+import org.jgrapes.core.Channel;
+import org.jgrapes.core.Event;
/**
- * The context.
+ * The Class RunnerStateChange.
*/
-/* default */ class StateController {
-
- private final Runner runner;
+public class RunnerStateChange extends Event {
/**
* The state.
*/
- enum State {
+ public enum State {
INITIALIZING, STARTING, RUNNING, TERMINATING
}
- private State state = State.INITIALIZING;
+ private final State state;
/**
- * Instantiates a new state controller.
+ * Instantiates a new runner state change.
*
- * @param runner the runner
+ * @param channels the channels
*/
- public StateController(Runner runner) {
- this.runner = runner;
- }
-
- /**
- * Sets the state.
- *
- * @param state the new state
- */
- public void set(State state) {
+ public RunnerStateChange(State state, Channel... channels) {
+ super(channels);
this.state = state;
}
/**
- * Returns the state.
+ * Returns the new state.
*
* @return the state
*/
- public State get() {
+ public State state() {
return state;
}
-
- @Override
- public String toString() {
- return "StateController [state=" + state + "]";
- }
}
diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/TrayMovedEvent.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/TrayMovedEvent.java
new file mode 100644
index 0000000..c848605
--- /dev/null
+++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/TrayMovedEvent.java
@@ -0,0 +1,58 @@
+/*
+ * 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 org.jdrupes.vmoperator.runner.qemu.CdMediaController.TrayState;
+
+/**
+ * The Class TrayMovedEvent.
+ */
+public class TrayMovedEvent extends MonitorEvent {
+
+ /**
+ * Instantiates a new tray moved.
+ *
+ * @param kind the kind
+ * @param data the data
+ */
+ public TrayMovedEvent(Kind kind, JsonNode data) {
+ super(kind, data);
+ }
+
+ /**
+ * returns the drive id.
+ *
+ * @return the string
+ */
+ public String driveId() {
+ return data().get("id").asText();
+ }
+
+ /**
+ * Returns the tray state.
+ *
+ * @return the tray state
+ */
+ public TrayState state() {
+ return data().get("tray-open").asBoolean()
+ ? TrayState.OPEN
+ : TrayState.CLOSED;
+ }
+}