Add booted state.
This commit is contained in:
parent
e4bba582a0
commit
ec8152bd51
4 changed files with 70 additions and 33 deletions
|
|
@ -33,7 +33,6 @@ import java.util.logging.Level;
|
||||||
import org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand;
|
import org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand;
|
||||||
import org.jdrupes.vmoperator.runner.qemu.commands.QmpGuestGetOsinfo;
|
import org.jdrupes.vmoperator.runner.qemu.commands.QmpGuestGetOsinfo;
|
||||||
import org.jdrupes.vmoperator.runner.qemu.events.GuestAgentCommand;
|
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.OsinfoEvent;
|
||||||
import org.jdrupes.vmoperator.runner.qemu.events.VserportChangeEvent;
|
import org.jdrupes.vmoperator.runner.qemu.events.VserportChangeEvent;
|
||||||
import org.jgrapes.core.Channel;
|
import org.jgrapes.core.Channel;
|
||||||
|
|
@ -188,10 +187,6 @@ public class GuestAgentClient extends Component {
|
||||||
logger.fine(() -> "guest agent(in): " + line);
|
logger.fine(() -> "guest agent(in): " + line);
|
||||||
try {
|
try {
|
||||||
var response = mapper.readValue(line, ObjectNode.class);
|
var response = mapper.readValue(line, ObjectNode.class);
|
||||||
if (response.has("QMP")) {
|
|
||||||
rep.fire(new MonitorReady());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (response.has("return") || response.has("error")) {
|
if (response.has("return") || response.has("error")) {
|
||||||
QmpCommand executed = executing.poll();
|
QmpCommand executed = executing.poll();
|
||||||
logger.fine(
|
logger.fine(
|
||||||
|
|
|
||||||
|
|
@ -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.ConfigureQemu;
|
||||||
import org.jdrupes.vmoperator.runner.qemu.events.Exit;
|
import org.jdrupes.vmoperator.runner.qemu.events.Exit;
|
||||||
import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand;
|
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.QmpConfigured;
|
||||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange;
|
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange;
|
||||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.RunState;
|
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.RunState;
|
||||||
|
|
@ -619,8 +620,8 @@ public class Runner extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On monitor ready.
|
* When the monitor is ready, send QEMU its initial configuration.
|
||||||
*
|
*
|
||||||
* @param event the event
|
* @param event the event
|
||||||
*/
|
*/
|
||||||
@Handler
|
@Handler
|
||||||
|
|
@ -629,28 +630,14 @@ public class Runner extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On configure qemu.
|
* Whenever a new QEMU configuration is available, check if it
|
||||||
*
|
* is supposed to trigger a reset.
|
||||||
* @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.
|
|
||||||
*
|
*
|
||||||
* @param event the event
|
* @param event the event
|
||||||
*/
|
*/
|
||||||
@Handler
|
@Handler
|
||||||
public void onConfigureQemu(ConfigureQemu event) {
|
public void onConfigureQemu(ConfigureQemu event) {
|
||||||
if (state == RunState.RUNNING) {
|
if (state.vmActive()) {
|
||||||
if (resetCounter != null
|
if (resetCounter != null
|
||||||
&& event.configuration().resetCounter != null
|
&& event.configuration().resetCounter != null
|
||||||
&& event.configuration().resetCounter > resetCounter) {
|
&& 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.
|
* On process exited.
|
||||||
*
|
*
|
||||||
|
|
@ -675,6 +692,7 @@ public class Runner extends Component {
|
||||||
mayBeStartQemu(QemuPreps.CloudInit);
|
mayBeStartQemu(QemuPreps.CloudInit);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No other process(es) may exit during startup
|
// No other process(es) may exit during startup
|
||||||
if (state == RunState.STARTING) {
|
if (state == RunState.STARTING) {
|
||||||
logger.severe(() -> "Process " + procDef.name
|
logger.severe(() -> "Process " + procDef.name
|
||||||
|
|
@ -683,7 +701,9 @@ public class Runner extends Component {
|
||||||
rep.fire(new Stop());
|
rep.fire(new Stop());
|
||||||
return;
|
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()));
|
rep.fire(new Exit(event.exitValue()));
|
||||||
}
|
}
|
||||||
logger.info(() -> "Process " + procDef.name
|
logger.info(() -> "Process " + procDef.name
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* VM-Operator
|
* 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
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as
|
* 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 io.kubernetes.client.openapi.models.EventsV1Event;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import static org.jdrupes.vmoperator.common.Constants.APP_NAME;
|
import static org.jdrupes.vmoperator.common.Constants.APP_NAME;
|
||||||
import static org.jdrupes.vmoperator.common.Constants.VM_OP_GROUP;
|
import static org.jdrupes.vmoperator.common.Constants.VM_OP_GROUP;
|
||||||
|
|
@ -66,9 +65,6 @@ public class StatusUpdater extends VmDefUpdater {
|
||||||
private static final ObjectMapper objectMapper
|
private static final ObjectMapper objectMapper
|
||||||
= new ObjectMapper().registerModule(new JavaTimeModule());
|
= new ObjectMapper().registerModule(new JavaTimeModule());
|
||||||
|
|
||||||
private static final Set<RunState> RUNNING_STATES
|
|
||||||
= Set.of(RunState.RUNNING, RunState.TERMINATING);
|
|
||||||
|
|
||||||
private long observedGeneration;
|
private long observedGeneration;
|
||||||
private boolean guestShutdownStops;
|
private boolean guestShutdownStops;
|
||||||
private boolean shutdownByGuest;
|
private boolean shutdownByGuest;
|
||||||
|
|
@ -186,16 +182,23 @@ public class StatusUpdater extends VmDefUpdater {
|
||||||
}
|
}
|
||||||
vmStub.updateStatus(vmDef, from -> {
|
vmStub.updateStatus(vmDef, from -> {
|
||||||
JsonObject status = from.statusJson();
|
JsonObject status = from.statusJson();
|
||||||
boolean running = RUNNING_STATES.contains(event.runState());
|
boolean running = event.runState().vmRunning();
|
||||||
updateCondition(vmDef, vmDef.statusJson(), "Running", running,
|
updateCondition(vmDef, vmDef.statusJson(), "Running", running,
|
||||||
event.reason(), event.message());
|
event.reason(), event.message());
|
||||||
|
updateCondition(vmDef, vmDef.statusJson(), "Booted",
|
||||||
|
event.runState() == RunState.BOOTED, event.reason(),
|
||||||
|
event.message());
|
||||||
if (event.runState() == RunState.STARTING) {
|
if (event.runState() == RunState.STARTING) {
|
||||||
status.addProperty("ram", GsonPtr.to(from.data())
|
status.addProperty("ram", GsonPtr.to(from.data())
|
||||||
.getAsString("spec", "vm", "maximumRam").orElse("0"));
|
.getAsString("spec", "vm", "maximumRam").orElse("0"));
|
||||||
status.addProperty("cpus", 1);
|
status.addProperty("cpus", 1);
|
||||||
|
|
||||||
|
// In case we had an irregular shutdown
|
||||||
|
status.remove("osinfo");
|
||||||
} else if (event.runState() == RunState.STOPPED) {
|
} else if (event.runState() == RunState.STOPPED) {
|
||||||
status.addProperty("ram", "0");
|
status.addProperty("ram", "0");
|
||||||
status.addProperty("cpus", 0);
|
status.addProperty("cpus", 0);
|
||||||
|
status.remove("osinfo");
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case console connection was still present
|
// In case console connection was still present
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.runner.qemu.events;
|
package org.jdrupes.vmoperator.runner.qemu.events;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
import org.jgrapes.core.Channel;
|
import org.jgrapes.core.Channel;
|
||||||
import org.jgrapes.core.Components;
|
import org.jgrapes.core.Components;
|
||||||
import org.jgrapes.core.Event;
|
import org.jgrapes.core.Event;
|
||||||
|
|
@ -29,10 +30,28 @@ import org.jgrapes.core.Event;
|
||||||
public class RunnerStateChange extends Event<Void> {
|
public class RunnerStateChange extends Event<Void> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The state.
|
* The states.
|
||||||
*/
|
*/
|
||||||
public enum RunState {
|
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;
|
private final RunState state;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue