Add booted state.

This commit is contained in:
Michael Lipp 2025-02-17 20:47:00 +01:00
parent e4bba582a0
commit ec8152bd51
4 changed files with 70 additions and 33 deletions

View file

@ -33,7 +33,6 @@ import java.util.logging.Level;
import org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand;
import org.jdrupes.vmoperator.runner.qemu.commands.QmpGuestGetOsinfo;
import org.jdrupes.vmoperator.runner.qemu.events.GuestAgentCommand;
import org.jdrupes.vmoperator.runner.qemu.events.MonitorReady;
import org.jdrupes.vmoperator.runner.qemu.events.OsinfoEvent;
import org.jdrupes.vmoperator.runner.qemu.events.VserportChangeEvent;
import org.jgrapes.core.Channel;
@ -188,10 +187,6 @@ public class GuestAgentClient extends Component {
logger.fine(() -> "guest agent(in): " + line);
try {
var response = mapper.readValue(line, ObjectNode.class);
if (response.has("QMP")) {
rep.fire(new MonitorReady());
return;
}
if (response.has("return") || response.has("error")) {
QmpCommand executed = executing.poll();
logger.fine(

View file

@ -61,6 +61,7 @@ import org.jdrupes.vmoperator.runner.qemu.commands.QmpReset;
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.OsinfoEvent;
import org.jdrupes.vmoperator.runner.qemu.events.QmpConfigured;
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange;
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.RunState;
@ -619,7 +620,7 @@ public class Runner extends Component {
}
/**
* On monitor ready.
* When the monitor is ready, send QEMU its initial configuration.
*
* @param event the event
*/
@ -629,28 +630,14 @@ public class Runner extends Component {
}
/**
* On configure qemu.
*
* @param event the event
*/
@Handler(priority = -1000)
public void onConfigureQemuFinal(ConfigureQemu event) {
if (state == RunState.STARTING) {
fire(new MonitorCommand(new QmpCont()));
state = RunState.RUNNING;
rep.fire(new RunnerStateChange(state, "VmStarted",
"Qemu has been configured and is continuing"));
}
}
/**
* On configure qemu.
* Whenever a new QEMU configuration is available, check if it
* is supposed to trigger a reset.
*
* @param event the event
*/
@Handler
public void onConfigureQemu(ConfigureQemu event) {
if (state == RunState.RUNNING) {
if (state.vmActive()) {
if (resetCounter != null
&& event.configuration().resetCounter != null
&& event.configuration().resetCounter > resetCounter) {
@ -660,6 +647,36 @@ public class Runner extends Component {
}
}
/**
* As last step when handling a new configuration, check if
* QEMU is suspended after startup and should be continued.
*
* @param event the event
*/
@Handler(priority = -1000)
public void onConfigureQemuFinal(ConfigureQemu event) {
if (state == RunState.STARTING) {
state = RunState.BOOTING;
fire(new MonitorCommand(new QmpCont()));
rep.fire(new RunnerStateChange(state, "VmStarted",
"Qemu has been configured and is continuing"));
}
}
/**
* Receiving the OSinfo means that the OS has been booted.
*
* @param event the event
*/
@Handler
public void onOsinfo(OsinfoEvent event) {
if (state == RunState.BOOTING) {
state = RunState.BOOTED;
rep.fire(new RunnerStateChange(state, "VmBooted",
"The VM has started the guest agent."));
}
}
/**
* On process exited.
*
@ -675,6 +692,7 @@ public class Runner extends Component {
mayBeStartQemu(QemuPreps.CloudInit);
return;
}
// No other process(es) may exit during startup
if (state == RunState.STARTING) {
logger.severe(() -> "Process " + procDef.name
@ -683,7 +701,9 @@ public class Runner extends Component {
rep.fire(new Stop());
return;
}
if (procDef.equals(qemuDefinition) && state == RunState.RUNNING) {
// No processes may exit while the VM is running normally
if (procDef.equals(qemuDefinition) && state.vmActive()) {
rep.fire(new Exit(event.exitValue()));
}
logger.info(() -> "Process " + procDef.name

View file

@ -1,6 +1,6 @@
/*
* VM-Operator
* Copyright (C) 2023,2024 Michael N. Lipp
* Copyright (C) 2023,2025 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
@ -31,7 +31,6 @@ import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.models.EventsV1Event;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Set;
import java.util.logging.Level;
import static org.jdrupes.vmoperator.common.Constants.APP_NAME;
import static org.jdrupes.vmoperator.common.Constants.VM_OP_GROUP;
@ -66,9 +65,6 @@ public class StatusUpdater extends VmDefUpdater {
private static final ObjectMapper objectMapper
= new ObjectMapper().registerModule(new JavaTimeModule());
private static final Set<RunState> RUNNING_STATES
= Set.of(RunState.RUNNING, RunState.TERMINATING);
private long observedGeneration;
private boolean guestShutdownStops;
private boolean shutdownByGuest;
@ -186,16 +182,23 @@ public class StatusUpdater extends VmDefUpdater {
}
vmStub.updateStatus(vmDef, from -> {
JsonObject status = from.statusJson();
boolean running = RUNNING_STATES.contains(event.runState());
boolean running = event.runState().vmRunning();
updateCondition(vmDef, vmDef.statusJson(), "Running", running,
event.reason(), event.message());
updateCondition(vmDef, vmDef.statusJson(), "Booted",
event.runState() == RunState.BOOTED, event.reason(),
event.message());
if (event.runState() == RunState.STARTING) {
status.addProperty("ram", GsonPtr.to(from.data())
.getAsString("spec", "vm", "maximumRam").orElse("0"));
status.addProperty("cpus", 1);
// In case we had an irregular shutdown
status.remove("osinfo");
} else if (event.runState() == RunState.STOPPED) {
status.addProperty("ram", "0");
status.addProperty("cpus", 0);
status.remove("osinfo");
}
// In case console connection was still present

View file

@ -18,6 +18,7 @@
package org.jdrupes.vmoperator.runner.qemu.events;
import java.util.EnumSet;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Components;
import org.jgrapes.core.Event;
@ -29,10 +30,28 @@ import org.jgrapes.core.Event;
public class RunnerStateChange extends Event<Void> {
/**
* The state.
* The states.
*/
public enum RunState {
INITIALIZING, STARTING, RUNNING, TERMINATING, STOPPED
INITIALIZING, STARTING, BOOTING, BOOTED, TERMINATING, STOPPED;
/**
* Checks if the state is one of the states in which the VM is running.
*
* @return true, if is running
*/
public boolean vmRunning() {
return EnumSet.of(BOOTING, BOOTED, TERMINATING).contains(this);
}
/**
* Checks if the state is one of the states in which the VM is active.
*
* @return true, if is active
*/
public boolean vmActive() {
return EnumSet.of(BOOTING, BOOTED).contains(this);
}
}
private final RunState state;