diff --git a/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml b/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml index b4abbd5..94abfa8 100644 --- a/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml +++ b/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml @@ -42,6 +42,11 @@ # * secure # "bootMode": "uefi" + # When terminating, a graceful powerdown is attempted. If it + # doesn't succeed within the given timeout (seconds) SIGTERM + # is sent to Qemu. + # "powerdownTimeout": 60 + # RAM settings # "maximumRam": "1G" # "currentRam": "1G" diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java index 31b3133..b2c0d10 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java @@ -69,6 +69,7 @@ class Configuration implements Dto { public String accelerator = "kvm"; public String rtcBase = "utc"; public String rtcClock = "rt"; + public int powerdownTimeout = 60; public Drive[] drives; public Spice spice; } diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/QemuMonitor.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/QemuMonitor.java index f187685..ea5a35b 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/QemuMonitor.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/QemuMonitor.java @@ -67,9 +67,8 @@ public class QemuMonitor extends Component { "powerdown", "{ \"execute\": \"system_powerdown\" }")); private Path socketPath; - + private int powerdownTimeout; private SocketIOChannel monitorChannel; - private Stop suspendedStop; /** @@ -88,9 +87,11 @@ public class QemuMonitor extends Component { * {@link Runner} instead. * * @param socketPath the socket path + * @param powerdownTimeout */ - /* default */ void configure(Path socketPath) { + /* default */ void configure(Path socketPath, int powerdownTimeout) { this.socketPath = socketPath; + this.powerdownTimeout = powerdownTimeout; } /** @@ -245,7 +246,7 @@ public class QemuMonitor extends Component { suspendedStop = null; } } - }, Duration.ofMillis(5000)); + }, Duration.ofSeconds(powerdownTimeout)); } } } diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java index 21a98cd..877dbbc 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java @@ -200,35 +200,6 @@ public class Runner extends Component { // Invalid configuration, not used, problems already logged. config = null; } - // Forward some values to child components - qemuMonitor.configure(config.monitorSocket); - } catch (IllegalArgumentException e) { - logger.log(Level.SEVERE, e, () -> "Invalid configuration: " - + e.getMessage()); - // Don't use default configuration - config = null; - } - } - - /** - * Handle the start event. - * - * @param event the event - */ - @Handler - public void onStart(Start event) { - try { - if (config == null) { - // Missing configuration, fail - fire(new Stop()); - return; - } - - // Store process id - try (var pidFile = Files.newBufferedWriter( - Path.of(config.runtimeDir, "runner.pid"))) { - pidFile.write(ProcessHandle.current().pid() + "\n"); - } // Prepare firmware files and add to config setFirmwarePaths(); @@ -240,13 +211,14 @@ public class Runner extends Component { qemuDefinition = Optional.ofNullable(tplData.get("qemu")) .map(d -> new CommandDefinition("qemu", d)).orElse(null); - // Files to watch for - Files.deleteIfExists(config.swtpmSocket); - fire(new WatchFile(config.swtpmSocket)); - } catch (IOException | TemplateException e) { - logger.log(Level.SEVERE, e, - () -> "Cannot configure runner: " + e.getMessage()); - fire(new Stop()); + // Forward some values to child components + qemuMonitor.configure(config.monitorSocket, + config.vm.powerdownTimeout); + } catch (IllegalArgumentException | IOException | TemplateException e) { + logger.log(Level.SEVERE, e, () -> "Invalid configuration: " + + e.getMessage()); + // Don't use default configuration + config = null; } } @@ -314,6 +286,36 @@ public class Runner extends Component { return mapper.readValue(out.toString(), JsonNode.class); } + /** + * Handle the start event. + * + * @param event the event + */ + @Handler + public void onStart(Start event) { + try { + if (config == null) { + // Missing configuration, fail + fire(new Stop()); + return; + } + + // Store process id + try (var pidFile = Files.newBufferedWriter( + Path.of(config.runtimeDir, "runner.pid"))) { + pidFile.write(ProcessHandle.current().pid() + "\n"); + } + + // Files to watch for + Files.deleteIfExists(config.swtpmSocket); + fire(new WatchFile(config.swtpmSocket)); + } catch (IOException e) { + logger.log(Level.SEVERE, e, + () -> "Cannot start runner: " + e.getMessage()); + fire(new Stop()); + } + } + /** * Handle the started event. *