Support for display secrets (#21)
Some checks failed
Java CI with Gradle / build (push) Has been cancelled
Some checks failed
Java CI with Gradle / build (push) Has been cancelled
This commit is contained in:
parent
85b0a160f3
commit
3103452170
38 changed files with 2081 additions and 658 deletions
1
org.jdrupes.vmoperator.runner.qemu/display-password
Normal file
1
org.jdrupes.vmoperator.runner.qemu/display-password
Normal file
|
|
@ -0,0 +1 @@
|
|||
test-vm
|
||||
|
|
@ -25,8 +25,8 @@ 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.RunnerConfigurationUpdate;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.TrayMovedEvent;
|
||||
import org.jgrapes.core.Channel;
|
||||
|
|
@ -68,7 +68,7 @@ public class CdMediaController extends Component {
|
|||
@Handler
|
||||
@SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition",
|
||||
"PMD.AvoidInstantiatingObjectsInLoops" })
|
||||
public void onConfigureQemu(RunnerConfigurationUpdate event) {
|
||||
public void onConfigureQemu(ConfigureQemu event) {
|
||||
if (event.state() == State.TERMINATING) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class Configuration implements Dto {
|
|||
@SuppressWarnings("PMD.FieldNamingConventions")
|
||||
protected final Logger logger = Logger.getLogger(getClass().getName());
|
||||
|
||||
/** Configuration timestamp */
|
||||
/** Configuration timestamp. */
|
||||
public Instant asOf;
|
||||
|
||||
/** The data dir. */
|
||||
|
|
@ -73,6 +73,9 @@ public class Configuration implements Dto {
|
|||
/** The firmware vars. */
|
||||
public Path firmwareVars;
|
||||
|
||||
/** The display password. */
|
||||
public boolean hasDisplayPassword;
|
||||
|
||||
/** Optional cloud-init data. */
|
||||
public CloudInit cloudInit;
|
||||
|
||||
|
|
@ -87,10 +90,16 @@ public class Configuration implements Dto {
|
|||
* Subsection "cloud-init".
|
||||
*/
|
||||
public static class CloudInit implements Dto {
|
||||
|
||||
/** The meta data. */
|
||||
@SuppressWarnings("PMD.UseConcurrentHashMap")
|
||||
public Map<String, Object> metaData;
|
||||
|
||||
/** The user data. */
|
||||
@SuppressWarnings("PMD.UseConcurrentHashMap")
|
||||
public Map<String, Object> userData;
|
||||
|
||||
/** The network config. */
|
||||
@SuppressWarnings("PMD.UseConcurrentHashMap")
|
||||
public Map<String, Object> networkConfig;
|
||||
}
|
||||
|
|
@ -230,6 +239,8 @@ public class Configuration implements Dto {
|
|||
* The Class Display.
|
||||
*/
|
||||
public static class Display implements Dto {
|
||||
|
||||
/** The spice. */
|
||||
public Spice spice;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ import java.util.Set;
|
|||
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.HotpluggableCpuStatus;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Component;
|
||||
|
|
@ -45,7 +45,7 @@ public class CpuController extends Component {
|
|||
|
||||
private Integer currentCpus;
|
||||
private Integer desiredCpus;
|
||||
private RunnerConfigurationUpdate suspendedConfigure;
|
||||
private ConfigureQemu suspendedConfigure;
|
||||
|
||||
/**
|
||||
* Instantiates a new CPU controller.
|
||||
|
|
@ -62,7 +62,7 @@ public class CpuController extends Component {
|
|||
* @param event the event
|
||||
*/
|
||||
@Handler
|
||||
public void onConfigureQemu(RunnerConfigurationUpdate event) {
|
||||
public void onConfigureQemu(ConfigureQemu event) {
|
||||
if (event.state() == State.TERMINATING) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
import org.jdrupes.vmoperator.runner.qemu.commands.QmpSetDisplayPassword;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.ConfigureQemu;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Component;
|
||||
import org.jgrapes.core.annotation.Handler;
|
||||
import org.jgrapes.util.events.FileChanged;
|
||||
import org.jgrapes.util.events.WatchFile;
|
||||
|
||||
/**
|
||||
* The Class DisplayController.
|
||||
*/
|
||||
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
|
||||
public class DisplayController extends Component {
|
||||
|
||||
public static final String DISPLAY_PASSWORD_FILE = "display-password";
|
||||
private String currentPassword;
|
||||
private String protocol;
|
||||
private final Path configDir;
|
||||
|
||||
/**
|
||||
* Instantiates a new Display controller.
|
||||
*
|
||||
* @param componentChannel the component channel
|
||||
* @param configDir
|
||||
*/
|
||||
@SuppressWarnings("PMD.AssignmentToNonFinalStatic")
|
||||
public DisplayController(Channel componentChannel, Path configDir) {
|
||||
super(componentChannel);
|
||||
this.configDir = configDir;
|
||||
fire(new WatchFile(configDir.resolve(DISPLAY_PASSWORD_FILE)));
|
||||
}
|
||||
|
||||
/**
|
||||
* On configure qemu.
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
@Handler
|
||||
public void onConfigureQemu(ConfigureQemu event) {
|
||||
if (event.state() == State.TERMINATING) {
|
||||
return;
|
||||
}
|
||||
protocol
|
||||
= event.configuration().vm.display.spice != null ? "spice" : null;
|
||||
updatePassword();
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch for changes of the password file.
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
@Handler
|
||||
@SuppressWarnings("PMD.EmptyCatchBlock")
|
||||
public void onFileChanged(FileChanged event) {
|
||||
if (event.path().equals(configDir.resolve(DISPLAY_PASSWORD_FILE))) {
|
||||
updatePassword();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
|
||||
private void updatePassword() {
|
||||
if (protocol == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String password;
|
||||
Path dpPath = configDir.resolve(DISPLAY_PASSWORD_FILE);
|
||||
if (dpPath.toFile().canRead()) {
|
||||
logger.finer(() -> "Found display password");
|
||||
try {
|
||||
password = Files.readString(dpPath);
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.WARNING, e, () -> "Cannot read display"
|
||||
+ " password: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
logger.finer(() -> "No display password");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Objects.equals(this.currentPassword, password)) {
|
||||
return;
|
||||
}
|
||||
logger.fine(() -> "Updating display password");
|
||||
fire(new MonitorCommand(new QmpSetDisplayPassword(protocol, password)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -35,12 +35,12 @@ 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.ConfigureQemu;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand;
|
||||
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.jdrupes.vmoperator.runner.qemu.events.PowerdownEvent;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Component;
|
||||
import org.jgrapes.core.Components;
|
||||
|
|
@ -87,13 +87,16 @@ public class QemuMonitor extends Component {
|
|||
* Instantiates a new qemu monitor.
|
||||
*
|
||||
* @param componentChannel the component channel
|
||||
* @param configDir the config dir
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
@SuppressWarnings("PMD.AssignmentToNonFinalStatic")
|
||||
public QemuMonitor(Channel componentChannel) throws IOException {
|
||||
public QemuMonitor(Channel componentChannel, Path configDir)
|
||||
throws IOException {
|
||||
super(componentChannel);
|
||||
attach(new RamController(channel()));
|
||||
attach(new CpuController(channel()));
|
||||
attach(new DisplayController(channel(), configDir));
|
||||
attach(new CdMediaController(channel()));
|
||||
}
|
||||
|
||||
|
|
@ -254,17 +257,18 @@ public class QemuMonitor extends Component {
|
|||
* @param event the event
|
||||
*/
|
||||
@Handler
|
||||
@SuppressWarnings("PMD.AvoidLiteralsInIfCondition")
|
||||
public void onExecQmpCommand(MonitorCommand event) {
|
||||
var command = event.command();
|
||||
logger.fine(() -> "monitor(out): " + command.toString());
|
||||
String asText;
|
||||
try {
|
||||
asText = mapper.writeValueAsString(command.toJson());
|
||||
asText = command.asText();
|
||||
} catch (JsonProcessingException e) {
|
||||
logger.log(Level.SEVERE, e,
|
||||
() -> "Cannot serialize Json: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
logger.fine(() -> "monitor(out): " + asText);
|
||||
synchronized (executing) {
|
||||
monitorChannel.associated(Writer.class).ifPresent(writer -> {
|
||||
try {
|
||||
|
|
@ -343,7 +347,7 @@ public class QemuMonitor extends Component {
|
|||
* @param event the event
|
||||
*/
|
||||
@Handler
|
||||
public void onConfigureQemu(RunnerConfigurationUpdate event) {
|
||||
public void onConfigureQemu(ConfigureQemu event) {
|
||||
int newTimeout = event.configuration().vm.powerdownTimeout;
|
||||
if (powerdownTimeout != newTimeout) {
|
||||
powerdownTimeout = newTimeout;
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ package org.jdrupes.vmoperator.runner.qemu;
|
|||
import java.math.BigInteger;
|
||||
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 org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Component;
|
||||
import org.jgrapes.core.annotation.Handler;
|
||||
|
|
@ -50,7 +50,7 @@ public class RamController extends Component {
|
|||
* @param event the event
|
||||
*/
|
||||
@Handler
|
||||
public void onConfigureQemu(RunnerConfigurationUpdate event) {
|
||||
public void onConfigureQemu(ConfigureQemu event) {
|
||||
Optional.ofNullable(event.configuration().vm.currentRam)
|
||||
.ifPresent(cr -> {
|
||||
if (currentRam != null && currentRam.equals(cr)) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
* Copyright (C) 2023,2024 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
|
||||
|
|
@ -55,10 +55,10 @@ import org.apache.commons.cli.Option;
|
|||
import org.apache.commons.cli.Options;
|
||||
import static org.jdrupes.vmoperator.common.Constants.APP_NAME;
|
||||
import org.jdrupes.vmoperator.runner.qemu.commands.QmpCont;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.ConfigureQemu;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.Exit;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.QmpConfigured;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State;
|
||||
import org.jdrupes.vmoperator.util.ExtendedObjectWrapper;
|
||||
|
|
@ -143,8 +143,8 @@ import org.jgrapes.util.events.WatchFile;
|
|||
* waitForConfigured: entry/fire QmpCapabilities
|
||||
* waitForConfigured --> configure: QmpConfigured
|
||||
*
|
||||
* configure: entry/fire RunnerConfigurationUpdate
|
||||
* configure --> success: RunnerConfigurationUpdate (last handler)/fire cont command
|
||||
* configure: entry/fire ConfigureQemu
|
||||
* configure --> success: ConfigureQemu (last handler)/fire cont command
|
||||
* }
|
||||
*
|
||||
* Initializing --> prepFork: Started
|
||||
|
|
@ -207,6 +207,7 @@ public class Runner extends Component {
|
|||
private final JsonNode defaults;
|
||||
@SuppressWarnings("PMD.UseConcurrentHashMap")
|
||||
private final File configFile;
|
||||
private final Path configDir;
|
||||
private Configuration config = new Configuration();
|
||||
private final freemarker.template.Configuration fmConfig;
|
||||
private CommandDefinition swtpmDefinition;
|
||||
|
|
@ -240,6 +241,17 @@ public class Runner extends Component {
|
|||
defaults = yamlMapper.readValue(
|
||||
Runner.class.getResourceAsStream("defaults.yaml"), JsonNode.class);
|
||||
|
||||
// Get the config
|
||||
configFile = new File(cmdLine.getOptionValue('c',
|
||||
"/etc/opt/" + APP_NAME.replace("-", "") + "/config.yaml"));
|
||||
// Don't rely on night config to produce a good exception
|
||||
// for this simple case
|
||||
if (!Files.isReadable(configFile.toPath())) {
|
||||
throw new IOException(
|
||||
"Cannot read configuration file " + configFile);
|
||||
}
|
||||
configDir = configFile.getParentFile().toPath().toRealPath();
|
||||
|
||||
// Configure freemarker library
|
||||
fmConfig = new freemarker.template.Configuration(
|
||||
freemarker.template.Configuration.VERSION_2_3_32);
|
||||
|
|
@ -256,17 +268,8 @@ public class Runner extends Component {
|
|||
attach(new FileSystemWatcher(channel()));
|
||||
attach(new ProcessManager(channel()));
|
||||
attach(new SocketConnector(channel()));
|
||||
attach(qemuMonitor = new QemuMonitor(channel()));
|
||||
attach(qemuMonitor = new QemuMonitor(channel(), configDir));
|
||||
attach(new StatusUpdater(channel()));
|
||||
|
||||
configFile = new File(cmdLine.getOptionValue('c',
|
||||
"/etc/opt/" + APP_NAME.replace("-", "") + "/config.yaml"));
|
||||
// Don't rely on night config to produce a good exception
|
||||
// for this simple case
|
||||
if (!Files.isReadable(configFile.toPath())) {
|
||||
throw new IOException(
|
||||
"Cannot read configuration file " + configFile);
|
||||
}
|
||||
attach(new YamlConfigurationStore(channel(), configFile, false));
|
||||
fire(new WatchFile(configFile.toPath()));
|
||||
}
|
||||
|
|
@ -294,13 +297,20 @@ public class Runner extends Component {
|
|||
public void onConfigurationUpdate(ConfigurationUpdate event) {
|
||||
event.structured(componentPath()).ifPresent(c -> {
|
||||
var newConf = yamlMapper.convertValue(c, Configuration.class);
|
||||
|
||||
// Add some values from other sources to configuration
|
||||
newConf.asOf = Instant.ofEpochSecond(configFile.lastModified());
|
||||
Path dsPath
|
||||
= configDir.resolve(DisplayController.DISPLAY_PASSWORD_FILE);
|
||||
newConf.hasDisplayPassword = dsPath.toFile().canRead();
|
||||
|
||||
// Special actions for initial configuration (startup)
|
||||
if (event instanceof InitialConfiguration) {
|
||||
processInitialConfiguration(newConf);
|
||||
return;
|
||||
}
|
||||
logger.fine(() -> "Updating configuration");
|
||||
rep.fire(new RunnerConfigurationUpdate(newConf, state));
|
||||
rep.fire(new ConfigureQemu(newConf, state));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -388,12 +398,9 @@ public class Runner extends Component {
|
|||
.map(Object::toString).orElse(null));
|
||||
model.put("firmwareVars", Optional.ofNullable(config.firmwareVars)
|
||||
.map(Object::toString).orElse(null));
|
||||
model.put("hasDisplayPassword", config.hasDisplayPassword);
|
||||
model.put("cloudInit", config.cloudInit);
|
||||
model.put("vm", config.vm);
|
||||
if (Optional.ofNullable(config.vm.display)
|
||||
.map(d -> d.spice).map(s -> s.ticket).isPresent()) {
|
||||
model.put("ticketPath", config.runtimeDir.resolve("ticket.txt"));
|
||||
}
|
||||
|
||||
// Combine template and data and parse result
|
||||
// (tempting, but no need to use a pipe here)
|
||||
|
|
@ -598,7 +605,7 @@ public class Runner extends Component {
|
|||
*/
|
||||
@Handler
|
||||
public void onQmpConfigured(QmpConfigured event) {
|
||||
rep.fire(new RunnerConfigurationUpdate(config, state));
|
||||
rep.fire(new ConfigureQemu(config, state));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -607,7 +614,7 @@ public class Runner extends Component {
|
|||
* @param event the event
|
||||
*/
|
||||
@Handler(priority = -1000)
|
||||
public void onConfigureQemu(RunnerConfigurationUpdate event) {
|
||||
public void onConfigureQemu(ConfigureQemu event) {
|
||||
if (state == State.STARTING) {
|
||||
fire(new MonitorCommand(new QmpCont()));
|
||||
state = State.RUNNING;
|
||||
|
|
|
|||
|
|
@ -42,9 +42,9 @@ import org.jdrupes.vmoperator.common.K8sClient;
|
|||
import org.jdrupes.vmoperator.common.K8sDynamicModel;
|
||||
import org.jdrupes.vmoperator.common.K8sDynamicStub;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.BalloonChangeEvent;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.ConfigureQemu;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.Exit;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.HotpluggableCpuStatus;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.ShutdownEvent;
|
||||
|
|
@ -178,7 +178,7 @@ public class StatusUpdater extends Component {
|
|||
* @throws ApiException
|
||||
*/
|
||||
@Handler
|
||||
public void onRunnerConfigurationUpdate(RunnerConfigurationUpdate event)
|
||||
public void onConfigureQemu(ConfigureQemu event)
|
||||
throws ApiException {
|
||||
guestShutdownStops = event.configuration().guestShutdownStops;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.jdrupes.vmoperator.runner.qemu.commands;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.io.IOException;
|
||||
|
|
@ -55,4 +56,30 @@ public abstract class QmpCommand {
|
|||
* @return the json node
|
||||
*/
|
||||
public abstract JsonNode toJson();
|
||||
|
||||
/**
|
||||
* Returns the string representation.
|
||||
*
|
||||
* @return the string
|
||||
* @throws JsonProcessingException the JSON processing exception
|
||||
*/
|
||||
public String asText() throws JsonProcessingException {
|
||||
return mapper.writeValueAsString(toJson());
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #asText()} but suppresses the
|
||||
* {@link JsonProcessingException}.
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return asText();
|
||||
} catch (JsonProcessingException e) {
|
||||
return "(no string representation)";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.fasterxml.jackson.databind.node.TextNode;
|
||||
|
||||
/**
|
||||
* A {@link QmpCommand} that sets the display password.
|
||||
*/
|
||||
public class QmpSetDisplayPassword extends QmpCommand {
|
||||
|
||||
private final String password;
|
||||
private final String protocol;
|
||||
|
||||
/**
|
||||
* Instantiates a new command.
|
||||
*
|
||||
* @param protocol the protocol
|
||||
* @param password the password
|
||||
*/
|
||||
public QmpSetDisplayPassword(String protocol, String password) {
|
||||
this.protocol = protocol;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode toJson() {
|
||||
ObjectNode cmd = mapper.createObjectNode();
|
||||
cmd.put("execute", "set_password");
|
||||
ObjectNode args = mapper.createObjectNode();
|
||||
cmd.set("arguments", args);
|
||||
args.set("protocol", new TextNode(protocol));
|
||||
args.set("password", new TextNode(password));
|
||||
return cmd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
var json = toJson();
|
||||
((ObjectNode) json.get("arguments")).set("password",
|
||||
new TextNode("********"));
|
||||
return mapper.writeValueAsString(json);
|
||||
} catch (JsonProcessingException e) {
|
||||
return "(no string representation)";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ import org.jgrapes.core.Event;
|
|||
* on the event and only {@link Event#resumeHandling() resume handling}
|
||||
* when the adaption has completed.
|
||||
*/
|
||||
public class RunnerConfigurationUpdate extends Event<Void> {
|
||||
public class ConfigureQemu extends Event<Void> {
|
||||
|
||||
private final Configuration configuration;
|
||||
private final State state;
|
||||
|
|
@ -41,7 +41,7 @@ public class RunnerConfigurationUpdate extends Event<Void> {
|
|||
*
|
||||
* @param channels the channels
|
||||
*/
|
||||
public RunnerConfigurationUpdate(Configuration configuration, State state,
|
||||
public ConfigureQemu(Configuration configuration, State state,
|
||||
Channel... channels) {
|
||||
super(channels);
|
||||
this.state = state;
|
||||
|
|
@ -215,12 +215,8 @@
|
|||
<#assign spice = vm.display.spice/>
|
||||
# SPICE (display, channels ...)
|
||||
# https://www.linux-kvm.org/page/SPICE
|
||||
<#if ticketPath??>
|
||||
- [ "-object", "secret,id=spiceTicket,file=${ ticketPath }" ]
|
||||
</#if>
|
||||
- [ "-spice", "port=${ spice.port?c }\
|
||||
<#if spice.ticket??>,password-secret=spiceTicket\
|
||||
<#else>,disable-ticketing=on</#if>\
|
||||
,disable-ticketing=<#if hasDisplayPassword!false>off<#else>on</#if>\
|
||||
<#if spice.streamingVideo??>,streaming-video=${ spice.streamingVideo }</#if>\
|
||||
,seamless-migration=on" ]
|
||||
- [ "-chardev", "spicevmc,id=vdagentdev,name=vdagent" ]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue