Support for updateing CPUs and RAM.
This commit is contained in:
parent
fa759df107
commit
eac113c640
13 changed files with 682 additions and 58 deletions
|
|
@ -0,0 +1,8 @@
|
||||||
|
add_header=true
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
header_text=/*\n * VM-Operator\n * Copyright (C) 2023 Michael N. Lipp\n * \n * This program is free software\: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https\://www.gnu.org/licenses/>.\n */
|
||||||
|
project_specific_settings=true
|
||||||
|
replacements=<?xml version\="1.0" standalone\="yes"?>\n\n<replacements>\n<replacement key\="get" scope\="1" mode\="0">Returns the</replacement>\n<replacement key\="set" scope\="1" mode\="0">Sets the</replacement>\n<replacement key\="add" scope\="1" mode\="0">Adds the</replacement>\n<replacement key\="edit" scope\="1" mode\="0">Edits the</replacement>\n<replacement key\="remove" scope\="1" mode\="0">Removes the</replacement>\n<replacement key\="init" scope\="1" mode\="0">Inits the</replacement>\n<replacement key\="parse" scope\="1" mode\="0">Parses the</replacement>\n<replacement key\="create" scope\="1" mode\="0">Creates the</replacement>\n<replacement key\="build" scope\="1" mode\="0">Builds the</replacement>\n<replacement key\="is" scope\="1" mode\="0">Checks if is</replacement>\n<replacement key\="print" scope\="1" mode\="0">Prints the</replacement>\n<replacement key\="has" scope\="1" mode\="0">Checks for</replacement>\n</replacements>\n\n
|
||||||
|
visibility_package=false
|
||||||
|
visibility_private=false
|
||||||
|
visibility_protected=false
|
||||||
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* 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.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class CpuController.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
|
||||||
|
public class CpuController extends Component {
|
||||||
|
|
||||||
|
private static ObjectMapper mapper;
|
||||||
|
private static JsonNode queryHotpluggableCpus;
|
||||||
|
|
||||||
|
private final QemuMonitor monitor;
|
||||||
|
private Integer desiredCpus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new CPU controller.
|
||||||
|
*
|
||||||
|
* @param componentChannel the component channel
|
||||||
|
* @param qemuMonitor
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("PMD.AssignmentToNonFinalStatic")
|
||||||
|
public CpuController(Channel componentChannel, QemuMonitor monitor) {
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On monitor result.
|
||||||
|
*
|
||||||
|
* @param result the result
|
||||||
|
* @param channel the channel
|
||||||
|
*/
|
||||||
|
@Handler
|
||||||
|
public void onMonitorResult(MonitorResult result) {
|
||||||
|
if (!result.executed()
|
||||||
|
.equals(queryHotpluggableCpus.get("execute").asText())
|
||||||
|
|| desiredCpus == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort
|
||||||
|
List<ObjectNode> used = new ArrayList<>();
|
||||||
|
List<ObjectNode> unused = new ArrayList<>();
|
||||||
|
for (var itr = result.returned().iterator(); itr.hasNext();) {
|
||||||
|
ObjectNode cpu = (ObjectNode) itr.next();
|
||||||
|
if (cpu.has("qom-path")) {
|
||||||
|
used.add(cpu);
|
||||||
|
} else {
|
||||||
|
unused.add(cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int addCpus(List<ObjectNode> used, List<ObjectNode> unused,
|
||||||
|
int diff) {
|
||||||
|
Set<String> usedIds = new HashSet<>();
|
||||||
|
for (var cpu : used) {
|
||||||
|
String qomPath = cpu.get("qom-path").asText();
|
||||||
|
if (qomPath.startsWith("/machine/peripheral/cpu-")) {
|
||||||
|
usedIds
|
||||||
|
.add(qomPath.substring(qomPath.lastIndexOf('/') + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
unused.remove(0);
|
||||||
|
diff += 1;
|
||||||
|
}
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int deleteCpus(List<ObjectNode> used, int diff) {
|
||||||
|
while (diff > 0 && !used.isEmpty()) {
|
||||||
|
ObjectNode cpu = used.remove(0);
|
||||||
|
String qomPath = cpu.get("qom-path").asText();
|
||||||
|
if (!qomPath.startsWith("/machine/peripheral/cpu-")) {
|
||||||
|
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);
|
||||||
|
diff -= 1;
|
||||||
|
}
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,7 +19,10 @@
|
||||||
package org.jdrupes.vmoperator.runner.qemu;
|
package org.jdrupes.vmoperator.runner.qemu;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
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.io.IOException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.lang.reflect.UndeclaredThrowableException;
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
|
|
@ -27,10 +30,14 @@ import java.net.UnixDomainSocketAddress;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.HashMap;
|
import java.util.LinkedList;
|
||||||
import java.util.Map;
|
import java.util.Queue;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
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.MonitorReady;
|
||||||
|
import org.jdrupes.vmoperator.runner.qemu.events.MonitorResult;
|
||||||
import org.jgrapes.core.Channel;
|
import org.jgrapes.core.Channel;
|
||||||
import org.jgrapes.core.Component;
|
import org.jgrapes.core.Component;
|
||||||
import org.jgrapes.core.Components;
|
import org.jgrapes.core.Components;
|
||||||
|
|
@ -58,34 +65,49 @@ import org.jgrapes.util.events.WatchFile;
|
||||||
* If the log level for this class is set to fine, the messages
|
* If the log level for this class is set to fine, the messages
|
||||||
* exchanged on the monitor socket are logged.
|
* exchanged on the monitor socket are logged.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
|
||||||
public class QemuMonitor extends Component {
|
public class QemuMonitor extends Component {
|
||||||
|
|
||||||
@SuppressWarnings({ "PMD.FieldNamingConventions",
|
private static ObjectMapper mapper;
|
||||||
"PMD.VariableNamingConventions" })
|
private static JsonNode connect;
|
||||||
private static final Logger logger
|
private static JsonNode cont;
|
||||||
= Logger.getLogger(QemuMonitor.class.getName());
|
private static JsonNode powerdown;
|
||||||
|
|
||||||
@SuppressWarnings("PMD.UseConcurrentHashMap")
|
@SuppressWarnings("PMD.UseConcurrentHashMap")
|
||||||
private final Map<String, String> monitorMessages = new HashMap<>(Map.of(
|
|
||||||
"connect", "{ \"execute\": \"qmp_capabilities\" }",
|
|
||||||
"powerdown", "{ \"execute\": \"system_powerdown\" }",
|
|
||||||
"setBalloon", "{ \"execute\": \"balloon\", \"arguments\": "
|
|
||||||
+ "{ \"value\": %d } }"));
|
|
||||||
|
|
||||||
private Path socketPath;
|
private Path socketPath;
|
||||||
private int powerdownTimeout;
|
private int powerdownTimeout;
|
||||||
private SocketIOChannel monitorChannel;
|
private SocketIOChannel monitorChannel;
|
||||||
|
private final Queue<String> executing = new LinkedList<>();
|
||||||
private Stop suspendedStop;
|
private Stop suspendedStop;
|
||||||
|
|
||||||
private Timer powerdownTimer;
|
private Timer powerdownTimer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new qemu monitor.
|
* Instantiates a new qemu monitor.
|
||||||
*
|
*
|
||||||
* @param componentChannel the component channel
|
* @param componentChannel the component channel
|
||||||
|
* @param mapper
|
||||||
|
* @throws JsonProcessingException
|
||||||
|
* @throws JsonMappingException
|
||||||
*/
|
*/
|
||||||
public QemuMonitor(Channel componentChannel) {
|
@SuppressWarnings("PMD.AssignmentToNonFinalStatic")
|
||||||
|
public QemuMonitor(Channel componentChannel) throws IOException {
|
||||||
super(componentChannel);
|
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\": "
|
||||||
|
+ "\"query-cpus-fast\",\"arguments\":{}}", 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -158,7 +180,7 @@ public class QemuMonitor extends Component {
|
||||||
throw new UndeclaredThrowableException(e);
|
throw new UndeclaredThrowableException(e);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
writeToMonitor(monitorMessages.get("connect"));
|
sendToMonitor(connect);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -175,26 +197,30 @@ public class QemuMonitor extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeToMonitor(String message) {
|
/* default */ void sendToMonitor(JsonNode message) {
|
||||||
logger.fine(() -> "monitor(out): " + message);
|
String asText;
|
||||||
monitorChannel.associated(Writer.class).ifPresent(writer -> {
|
try {
|
||||||
try {
|
asText = mapper.writeValueAsString(message);
|
||||||
writer.append(message).append('\n').flush();
|
} catch (JsonProcessingException e) {
|
||||||
} catch (IOException e) {
|
logger.log(Level.SEVERE, e,
|
||||||
// Cannot happen, but...
|
() -> "Cannot serialize Json: " + e.getMessage());
|
||||||
logger.log(Level.WARNING, e, () -> 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());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
}
|
||||||
* Sets the current ram.
|
|
||||||
*
|
|
||||||
* @param amount the new current ram
|
|
||||||
*/
|
|
||||||
public void setCurrentRam(Number amount) {
|
|
||||||
writeToMonitor(
|
|
||||||
String.format(monitorMessages.get("setBalloon"), amount));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -217,10 +243,18 @@ public class QemuMonitor extends Component {
|
||||||
throws IOException {
|
throws IOException {
|
||||||
logger.fine(() -> "monitor(in): " + line);
|
logger.fine(() -> "monitor(in): " + line);
|
||||||
try {
|
try {
|
||||||
var response
|
var response = mapper.readValue(line, ObjectNode.class);
|
||||||
= ((Runner) channel()).mapper().readValue(line, JsonNode.class);
|
|
||||||
if (response.has("QMP")) {
|
if (response.has("QMP")) {
|
||||||
fire(new QemuMonitorAvailable());
|
fire(new MonitorReady());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (response.has("return")) {
|
||||||
|
String executed = executing.poll();
|
||||||
|
logger.fine(
|
||||||
|
() -> String.format("(Previous \"monitor(in)\" is result "
|
||||||
|
+ "from executing %s)", executed));
|
||||||
|
fire(new MonitorResult(executed, response));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
|
|
@ -248,6 +282,20 @@ public class QemuMonitor extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On monitor command.
|
||||||
|
*
|
||||||
|
* @param event the event
|
||||||
|
*/
|
||||||
|
@Handler
|
||||||
|
public void onMonitorCommand(MonitorCommand event) {
|
||||||
|
if (event.command() != CONTINUE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendToMonitor(cont);
|
||||||
|
fire(new MonitorCommandCompleted(event.command(), null));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shutdown the VM.
|
* Shutdown the VM.
|
||||||
*
|
*
|
||||||
|
|
@ -259,7 +307,7 @@ public class QemuMonitor extends Component {
|
||||||
// We have a connection to Qemu, attempt ACPI shutdown.
|
// We have a connection to Qemu, attempt ACPI shutdown.
|
||||||
event.suspendHandling();
|
event.suspendHandling();
|
||||||
suspendedStop = event;
|
suspendedStop = event;
|
||||||
writeToMonitor(monitorMessages.get("powerdown"));
|
sendToMonitor(powerdown);
|
||||||
|
|
||||||
// Schedule timer as fallback
|
// Schedule timer as fallback
|
||||||
powerdownTimer = Components.schedule(t -> {
|
powerdownTimer = Components.schedule(t -> {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* 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.math.BigInteger;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
public class RamController extends Component {
|
||||||
|
|
||||||
|
private static ObjectMapper mapper;
|
||||||
|
private static JsonNode setBalloon;
|
||||||
|
private final QemuMonitor monitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new CPU controller.
|
||||||
|
*
|
||||||
|
* @param componentChannel the component channel
|
||||||
|
* @param qemuMonitor
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("PMD.AssignmentToNonFinalStatic")
|
||||||
|
public RamController(Channel componentChannel, QemuMonitor monitor) {
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -51,6 +51,12 @@ import org.apache.commons.cli.DefaultParser;
|
||||||
import org.apache.commons.cli.Option;
|
import org.apache.commons.cli.Option;
|
||||||
import org.apache.commons.cli.Options;
|
import org.apache.commons.cli.Options;
|
||||||
import org.jdrupes.vmoperator.runner.qemu.StateController.State;
|
import org.jdrupes.vmoperator.runner.qemu.StateController.State;
|
||||||
|
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.util.ExtendedObjectWrapper;
|
import org.jdrupes.vmoperator.util.ExtendedObjectWrapper;
|
||||||
import org.jdrupes.vmoperator.util.FsdUtils;
|
import org.jdrupes.vmoperator.util.FsdUtils;
|
||||||
import org.jgrapes.core.Component;
|
import org.jgrapes.core.Component;
|
||||||
|
|
@ -95,6 +101,7 @@ import org.jgrapes.util.events.WatchFile;
|
||||||
* state "Start swtpm" as swtpm
|
* state "Start swtpm" as swtpm
|
||||||
* state "Start qemu" as qemu
|
* state "Start qemu" as qemu
|
||||||
* state "Open monitor" as monitor
|
* state "Open monitor" as monitor
|
||||||
|
* state "Configure" as configure
|
||||||
* state success <<exitPoint>>
|
* state success <<exitPoint>>
|
||||||
* state error <<exitPoint>>
|
* state error <<exitPoint>>
|
||||||
*
|
*
|
||||||
|
|
@ -108,8 +115,11 @@ import org.jgrapes.util.events.WatchFile;
|
||||||
* qemu --> monitor : FileChanged[monitor socket created]
|
* qemu --> monitor : FileChanged[monitor socket created]
|
||||||
*
|
*
|
||||||
* monitor: entry/fire OpenSocketConnection
|
* monitor: entry/fire OpenSocketConnection
|
||||||
* monitor --> success: ClientConnected[for monitor]/set balloon value
|
* monitor --> configure: ClientConnected[for monitor]
|
||||||
* monitor -> error: ConnectError[for monitor]
|
* monitor -> error: ConnectError[for monitor]
|
||||||
|
*
|
||||||
|
* configure: entry/fire configuration commands
|
||||||
|
* configure --> success: last completed/fire cont command
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* Initializing --> which: Started
|
* Initializing --> which: Started
|
||||||
|
|
@ -156,7 +166,7 @@ public class Runner extends Component {
|
||||||
private static final String SAVED_TEMPLATE = "VM.ftl.yaml";
|
private static final String SAVED_TEMPLATE = "VM.ftl.yaml";
|
||||||
private static final String FW_VARS = "fw-vars.fd";
|
private static final String FW_VARS = "fw-vars.fd";
|
||||||
|
|
||||||
private final ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
|
private final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
|
||||||
private final JsonNode defaults;
|
private final JsonNode defaults;
|
||||||
@SuppressWarnings("PMD.UseConcurrentHashMap")
|
@SuppressWarnings("PMD.UseConcurrentHashMap")
|
||||||
private Configuration config = new Configuration();
|
private Configuration config = new Configuration();
|
||||||
|
|
@ -175,11 +185,11 @@ public class Runner extends Component {
|
||||||
@SuppressWarnings("PMD.SystemPrintln")
|
@SuppressWarnings("PMD.SystemPrintln")
|
||||||
public Runner(CommandLine cmdLine) throws IOException {
|
public Runner(CommandLine cmdLine) throws IOException {
|
||||||
state = new StateController(this);
|
state = new StateController(this);
|
||||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
|
yamlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
// Get defaults
|
// Get defaults
|
||||||
defaults = mapper.readValue(
|
defaults = yamlMapper.readValue(
|
||||||
Runner.class.getResourceAsStream("defaults.yaml"), JsonNode.class);
|
Runner.class.getResourceAsStream("defaults.yaml"), JsonNode.class);
|
||||||
|
|
||||||
// Configure freemarker library
|
// Configure freemarker library
|
||||||
|
|
@ -212,10 +222,6 @@ public class Runner extends Component {
|
||||||
fire(new WatchFile(config.toPath()));
|
fire(new WatchFile(config.toPath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* default */ ObjectMapper mapper() {
|
|
||||||
return mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On configuration update.
|
* On configuration update.
|
||||||
*
|
*
|
||||||
|
|
@ -235,7 +241,7 @@ public class Runner extends Component {
|
||||||
private void processInitialConfiguration(
|
private void processInitialConfiguration(
|
||||||
Map<String, Object> runnerConfiguration) {
|
Map<String, Object> runnerConfiguration) {
|
||||||
try {
|
try {
|
||||||
config = mapper.convertValue(runnerConfiguration,
|
config = yamlMapper.convertValue(runnerConfiguration,
|
||||||
Configuration.class);
|
Configuration.class);
|
||||||
if (!config.check()) {
|
if (!config.check()) {
|
||||||
// Invalid configuration, not used, problems already logged.
|
// Invalid configuration, not used, problems already logged.
|
||||||
|
|
@ -322,7 +328,7 @@ public class Runner extends Component {
|
||||||
var fmTemplate = fmConfig.getTemplate(templatePath.toString());
|
var fmTemplate = fmConfig.getTemplate(templatePath.toString());
|
||||||
StringWriter out = new StringWriter();
|
StringWriter out = new StringWriter();
|
||||||
fmTemplate.process(model, out);
|
fmTemplate.process(model, out);
|
||||||
return mapper.readValue(out.toString(), JsonNode.class);
|
return yamlMapper.readValue(out.toString(), JsonNode.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|
@ -337,7 +343,21 @@ public class Runner extends Component {
|
||||||
synchronized (state) {
|
synchronized (state) {
|
||||||
config.vm.currentRam = cr;
|
config.vm.currentRam = cr;
|
||||||
if (state.get() == State.RUNNING) {
|
if (state.get() == State.RUNNING) {
|
||||||
qemuMonitor.setCurrentRam(cr);
|
fire(new MonitorCommand(SET_CURRENT_RAM, cr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Optional.ofNullable((Map<String, Object>) conf.get("vm"))
|
||||||
|
.map(vm -> vm.get("currentCpus"))
|
||||||
|
.map(v -> v instanceof Number number ? number.intValue() : null)
|
||||||
|
.ifPresent(cpus -> {
|
||||||
|
if (config.vm.currentCpus == cpus) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
synchronized (state) {
|
||||||
|
config.vm.currentCpus = cpus;
|
||||||
|
if (state.get() == State.RUNNING) {
|
||||||
|
fire(new MonitorCommand(SET_CURRENT_CPUS, cpus));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -476,10 +496,27 @@ public class Runner extends Component {
|
||||||
* @param event the event
|
* @param event the event
|
||||||
*/
|
*/
|
||||||
@Handler
|
@Handler
|
||||||
public void onQemuMonitorAvailable(QemuMonitorAvailable event) {
|
public void onMonitorReady(MonitorReady event) {
|
||||||
synchronized (state) {
|
synchronized (state) {
|
||||||
Optional.ofNullable(config.vm.currentRam)
|
Optional.ofNullable(config.vm.currentRam).ifPresent(ram -> {
|
||||||
.ifPresent(qemuMonitor::setCurrentRam);
|
fire(new MonitorCommand(SET_CURRENT_RAM, ram));
|
||||||
|
});
|
||||||
|
Optional.ofNullable(config.vm.currentCpus).ifPresent(cpus -> {
|
||||||
|
fire(new MonitorCommand(SET_CURRENT_CPUS, cpus));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On monitor command completed.
|
||||||
|
*
|
||||||
|
* @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);
|
state.set(State.RUNNING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* 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 java.util.Arrays;
|
||||||
|
import org.jgrapes.core.Channel;
|
||||||
|
import org.jgrapes.core.Components;
|
||||||
|
import org.jgrapes.core.Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A command to be executed by the monitor.
|
||||||
|
*/
|
||||||
|
public class MonitorCommand extends Event<Void> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The available commands.
|
||||||
|
*/
|
||||||
|
public enum Command {
|
||||||
|
CONTINUE, SET_CURRENT_CPUS, SET_CURRENT_RAM
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Command command;
|
||||||
|
private final Object[] arguments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new monitor command.
|
||||||
|
*
|
||||||
|
* @param command the command
|
||||||
|
* @param arguments the arguments
|
||||||
|
*/
|
||||||
|
public MonitorCommand(Command command, Object... arguments) {
|
||||||
|
super();
|
||||||
|
this.command = command;
|
||||||
|
this.arguments = Arrays.copyOf(arguments, arguments.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the command.
|
||||||
|
*
|
||||||
|
* @return the command
|
||||||
|
*/
|
||||||
|
public Command 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();
|
||||||
|
builder.append(Components.objectName(this))
|
||||||
|
.append(" [").append(command);
|
||||||
|
if (channels() != null) {
|
||||||
|
builder.append(", channels=");
|
||||||
|
builder.append(Channel.toString(channels()));
|
||||||
|
}
|
||||||
|
builder.append(']');
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* 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 arguments the arguments
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* 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.jgrapes.core.Event;
|
||||||
|
|
||||||
|
// TODO: Auto-generated Javadoc
|
||||||
|
/**
|
||||||
|
* Signals the reception of an event from the monitor.
|
||||||
|
*/
|
||||||
|
public class MonitorEvent extends Event<Void> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The kind of monitor event.
|
||||||
|
*/
|
||||||
|
public enum Kind {
|
||||||
|
READY
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Kind kind;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new monitor event.
|
||||||
|
*
|
||||||
|
* @param kind the kind
|
||||||
|
*/
|
||||||
|
public MonitorEvent(Kind kind) {
|
||||||
|
this.kind = kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the kind of event.
|
||||||
|
*
|
||||||
|
* @return the kind
|
||||||
|
*/
|
||||||
|
public Kind kind() {
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,14 +16,19 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.Event;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signals that the connection to the Qemu monitor socket has been
|
* Signals that the connection to the Qemu monitor socket has been
|
||||||
* established successfully.
|
* established successfully.
|
||||||
*/
|
*/
|
||||||
public class QemuMonitorAvailable extends Event<Void> {
|
public class MonitorReady extends MonitorEvent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new monitor ready.
|
||||||
|
*/
|
||||||
|
public MonitorReady() {
|
||||||
|
super(Kind.READY);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -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.jgrapes.core.Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals the reception of a result from the monitor.
|
||||||
|
*/
|
||||||
|
public class MonitorResult extends Event<Void> {
|
||||||
|
|
||||||
|
private final String executed;
|
||||||
|
private final JsonNode returned;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new monitor result.
|
||||||
|
*
|
||||||
|
* @param executed the command executed
|
||||||
|
* @param returned the values returned
|
||||||
|
*/
|
||||||
|
public MonitorResult(String executed, JsonNode response) {
|
||||||
|
this.executed = executed;
|
||||||
|
this.returned = response.get("return");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the executed command.
|
||||||
|
*
|
||||||
|
* @return the string
|
||||||
|
*/
|
||||||
|
public String executed() {
|
||||||
|
return executed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the values returned.
|
||||||
|
*/
|
||||||
|
public JsonNode returned() {
|
||||||
|
return returned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
package org.jdrupes.vmoperator.runner.qemu.events;
|
||||||
|
|
@ -23,6 +23,8 @@
|
||||||
# Useful links:
|
# Useful links:
|
||||||
# - https://joonas.fi/2021/02/uefi-pc-boot-process-and-uefi-with-qemu/
|
# - https://joonas.fi/2021/02/uefi-pc-boot-process-and-uefi-with-qemu/
|
||||||
"arguments":
|
"arguments":
|
||||||
|
# Mandatory
|
||||||
|
- "-S"
|
||||||
# Qemu configuration
|
# Qemu configuration
|
||||||
- "-no-user-config"
|
- "-no-user-config"
|
||||||
# * https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt
|
# * https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt
|
||||||
|
|
@ -82,7 +84,7 @@
|
||||||
</#if>
|
</#if>
|
||||||
- [ "-cpu", "${ vm.cpuModel }" ]
|
- [ "-cpu", "${ vm.cpuModel }" ]
|
||||||
<#if vm.maximumCpus gt 1>
|
<#if vm.maximumCpus gt 1>
|
||||||
- [ "-smp", "${ vm.currentCpus },maxcpus=${ vm.maximumCpus }\
|
- [ "-smp", "1,maxcpus=${ vm.maximumCpus }\
|
||||||
<#if vm.cpuSockets gt 0>,sockets=${ vm.cpuSockets }</#if>\
|
<#if vm.cpuSockets gt 0>,sockets=${ vm.cpuSockets }</#if>\
|
||||||
<#if vm.diesPerSocket gt 0>,cores=${ vm.diesPerSocket }</#if>\
|
<#if vm.diesPerSocket gt 0>,cores=${ vm.diesPerSocket }</#if>\
|
||||||
<#if vm.coresPerDie gt 0>,cores=${ vm.coresPerDie }</#if>\
|
<#if vm.coresPerDie gt 0>,cores=${ vm.coresPerDie }</#if>\
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#
|
#
|
||||||
#Thu Jul 27 18:33:49 CEST 2023
|
#Sun Jul 30 18:47:26 CEST 2023
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue