Refactor.
This commit is contained in:
parent
e8b10b32b0
commit
fbc25d4db8
32 changed files with 1364 additions and 507 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<String, TrayState> trayState = new ConcurrentHashMap<>();
|
||||
private final Map<String, String> current = new ConcurrentHashMap<>();
|
||||
private final Map<String, String> 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);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
public void onConfigureQemu(ConfigureQemu event) {
|
||||
Optional.ofNullable(event.configuration().vm.currentCpus)
|
||||
.ifPresent(cpus -> {
|
||||
if (desiredCpus != null && desiredCpus.equals(cpus)) {
|
||||
return;
|
||||
}
|
||||
desiredCpus = (Integer) event.arguments()[0];
|
||||
monitor.sendToMonitor(queryHotpluggableCpus);
|
||||
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<ObjectNode> used = new ArrayList<>();
|
||||
List<ObjectNode> 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<ObjectNode> used, List<ObjectNode> unused,
|
||||
int diff) {
|
||||
Set<String> 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<ObjectNode> 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;
|
||||
|
|
|
|||
|
|
@ -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<String> executing = new LinkedList<>();
|
||||
private final Queue<QmpCommand> 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 -> {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
public void onConfigureQemu(ConfigureQemu event) {
|
||||
Optional.ofNullable(event.configuration().vm.currentRam)
|
||||
.ifPresent(cr -> {
|
||||
if (currentRam != null && currentRam.equals(cr)) {
|
||||
return;
|
||||
}
|
||||
var msg = setBalloon.deepCopy();
|
||||
((ObjectNode) msg.get("arguments")).put("value",
|
||||
(BigInteger) event.arguments()[0]);
|
||||
monitor.sendToMonitor(msg);
|
||||
fire(new MonitorCommandCompleted(event.command(), null));
|
||||
currentRam = cr;
|
||||
fire(new MonitorCommand(new QmpSetBalloon(cr)));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String, Object> 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 {
|
||||
|
|
|
|||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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()";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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()";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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()";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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()";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
package org.jdrupes.vmoperator.runner.qemu.commands;
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Void> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<Void> {
|
||||
|
||||
/**
|
||||
* 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<Void> {
|
|||
*
|
||||
* @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();
|
||||
|
|
|
|||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Void> {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Void> {
|
|||
* 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<MonitorEvent> 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<Void> {
|
|||
public Kind kind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data associated with the event.
|
||||
*
|
||||
* @return the object[]
|
||||
*/
|
||||
public JsonNode data() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public class MonitorReady extends MonitorEvent {
|
|||
* Instantiates a new monitor ready.
|
||||
*/
|
||||
public MonitorReady() {
|
||||
super(Kind.READY);
|
||||
super(Kind.READY, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Void> {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,53 +16,41 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Void> {
|
||||
|
||||
/**
|
||||
* 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 + "]";
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue