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; + } +}