Skip to content

Commit fd3bc89

Browse files
committed
Transient state means Starting state
"Transient" state was used by clients to indicate an impermanent state, while on the server, to indicate that Qrexec should be running but is not. One of the issues is that this is not a libvirt state, The state aren't always as transient (short duration) as we think it is, clients were never never notified of state changes from "Running/Paused/Suspended" to "Transient", which can happen on "qrexec" feature change and on qube's qrexec agent stopping. A replacement for "Transient" state that clients will be satisfied is "Starting" and "Halting". I judged introducing these new states, although not libvirt states, as a better replacement because they can be dealt with events already received by the client "domain-(pre-)?(start|shutdown)(-failed)?", and don't need to learn a new argument for "domain-(resumed|unpaused)" to set the state from "Paused/Suspended" to "Transient". In case the old "Transient" meaning of qrexec not being ready, is needed in the future, add it somewhere else, with another more insightful name, and not as a power state. Note that "Transient" is a temporary name and it will be replaced by "Starting" on R5.0. For: QubesOS/qubes-issues#10966
1 parent 258b025 commit fd3bc89

1 file changed

Lines changed: 174 additions & 105 deletions

File tree

‎qubes/vm/qubesvm.py‎

Lines changed: 174 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,6 +1454,12 @@ async def _ensure_shutdown_handled(self):
14541454
self._lifecycle_waiter = None
14551455
raise
14561456

1457+
async def notify_failed_startup(self, exc: Exception):
1458+
self.log.error("Start failed: %s", str(exc))
1459+
self._power_state = "Halted"
1460+
# let anyone receiving domain-pre-start know that startup failed
1461+
await self.fire_event_async("domain-start-failed", reason=str(exc))
1462+
14571463
async def start(
14581464
self, start_guid=True, notify_function=None, mem_required=None
14591465
):
@@ -1487,6 +1493,7 @@ async def start(
14871493
)
14881494

14891495
self.log.info("Starting qube {}".format(self.name))
1496+
self._power_state = "Transient"
14901497

14911498
try:
14921499
await self.fire_event_async(
@@ -1496,10 +1503,7 @@ async def start(
14961503
mem_required=mem_required,
14971504
)
14981505
except Exception as exc:
1499-
self.log.error("Start failed: %s", str(exc))
1500-
await self.fire_event_async(
1501-
"domain-start-failed", reason=str(exc)
1502-
)
1506+
await self.notify_failed_startup(exc=exc)
15031507
raise
15041508

15051509
qmemman_client = None
@@ -1537,11 +1541,7 @@ async def start(
15371541
await self.storage.start()
15381542

15391543
except Exception as exc:
1540-
self.log.error("Start failed: %s", str(exc))
1541-
# let anyone receiving domain-pre-start know that startup failed
1542-
await self.fire_event_async(
1543-
"domain-start-failed", reason=str(exc)
1544-
)
1544+
await self.notify_failed_startup(exc=exc)
15451545
if qmemman_client:
15461546
qmemman_client.close()
15471547
raise
@@ -1583,18 +1583,11 @@ async def start(
15831583
"Failed to start an HVM qube with PCI devices assigned "
15841584
"- hardware does not support IOMMU/VT-d/AMD-Vi"
15851585
)
1586-
self.log.error("Start failed: %s", str(exc))
1587-
await self.fire_event_async(
1588-
"domain-start-failed", reason=str(exc)
1589-
)
1586+
await self.notify_failed_startup(exc=exc)
15901587
await self.storage.stop()
15911588
raise exc
15921589
except Exception as exc:
1593-
self.log.error("Start failed: %s", str(exc))
1594-
# let anyone receiving domain-pre-start know that startup failed
1595-
await self.fire_event_async(
1596-
"domain-start-failed", reason=str(exc)
1597-
)
1590+
await self.notify_failed_startup(exc=exc)
15981591
await self.storage.stop()
15991592
raise
16001593

@@ -1633,19 +1626,14 @@ async def start(
16331626
):
16341627
await self.start_qrexec_daemon(stubdom=True)
16351628
await self.start_qrexec_daemon()
1636-
if self._power_state == "Transient" and self.is_fully_usable():
1637-
self._power_state = "Running"
16381629

1630+
self._power_state = "Running"
16391631
await self.fire_event_async(
16401632
"domain-start", start_guid=start_guid
16411633
)
16421634

16431635
except Exception as exc: # pylint: disable=bare-except
1644-
self.log.error("Start failed: %s", str(exc))
1645-
# let anyone receiving domain-pre-start know that startup failed
1646-
await self.fire_event_async(
1647-
"domain-start-failed", reason=str(exc)
1648-
)
1636+
await self.notify_failed_startup(exc=exc)
16491637
# This avoids losing the exception if an exception is
16501638
# raised in self.kill(), because the vm is not
16511639
# running or paused
@@ -1718,18 +1706,22 @@ def on_libvirt_domain_defined(self):
17181706
17191707
This is not a Qubes event handler.
17201708
"""
1721-
self._is_running = False
1722-
self._power_state = "Halted"
1709+
if self._is_running is None:
1710+
self._is_running = False
1711+
if self._power_state is None:
1712+
self._power_state = "Halted"
17231713
self.end_lifecycle_waiter()
17241714

1725-
def on_libvirt_domain_started(self, detail: int):
1715+
def on_libvirt_domain_started(self, detail):
17261716
"""Handle VIR_DOMAIN_EVENT_STARTED event from libvirt when booted.
17271717
17281718
This is not a Qubes event handler.
17291719
"""
17301720
self._is_running = True
17311721
if detail == libvirt.VIR_DOMAIN_EVENT_STARTED_BOOTED:
1732-
self._power_state = "Paused"
1722+
# Let power state be "Transient/Starting" if set.
1723+
if self._power_state is None:
1724+
self._power_state = "Running"
17331725
elif detail == libvirt.VIR_DOMAIN_EVENT_STARTED_WAKEUP:
17341726
self._power_state = "Running"
17351727
self.end_lifecycle_waiter()
@@ -2588,87 +2580,167 @@ def set_power_state(self):
25882580
=============== ========================================================
25892581
return value meaning
25902582
=============== ========================================================
2591-
``'Halted'`` Machine is not active.
2592-
``'Transient'`` Machine is running, but does not have :program:`guid`
2593-
or :program:`qrexec` available.
2594-
``'Running'`` Machine is ready and running.
2595-
``'Paused'`` Machine is paused.
2596-
``'Suspended'`` Machine is S3-suspended.
2597-
``'Halting'`` Machine is in process of shutting down.
2598-
``'Crashed'`` Machine crashed and is unusable, probably because of
2583+
``"Halted"`` Machine is not active.
2584+
``"Transient"`` Machine is in process of starting. This state will be
2585+
deprecated in the future in favor of ``"Starting"``.
2586+
``"Running"`` Machine is ready and running.
2587+
``"Paused"`` Machine is paused.
2588+
``"Suspended"`` Machine is S3-suspended.
2589+
``"Halting"`` Machine is in process of shutting down.
2590+
``"Crashed"`` Machine crashed and is unusable, probably because of
25992591
bug in dom0.
2600-
``'NA'`` Machine is in unknown state (most likely libvirt domain
2592+
``"NA"`` Machine is in unknown state (most likely libvirt domain
26012593
is undefined).
26022594
=============== ========================================================
26032595
2604-
FIXME: graph below may be incomplete and wrong. Click on method name to
2605-
see its documentation.
2596+
Click on method name to see its documentation.
26062597
26072598
.. graphviz::
26082599
26092600
digraph {
2610-
node [fontname="sans-serif"];
2611-
edge [fontname="mono"];
2612-
2613-
2614-
Halted;
2615-
NA;
2616-
Dying;
2617-
Crashed;
2618-
Transient;
2619-
Halting;
2620-
Running;
2621-
Paused [color=gray75 fontcolor=gray75];
2622-
Suspended;
2623-
2601+
rankdir=LR;
2602+
2603+
node [
2604+
fontname="sans-serif",
2605+
shape=box,
2606+
style="rounded,filled",
2607+
color="darkslategray",
2608+
fillcolor="aliceblue",
2609+
];
2610+
2611+
edge [
2612+
fontname="mono",
2613+
color="lightslategray",
2614+
];
2615+
2616+
NA [fillcolor="lightgray", color="dimgray"];
2617+
Halted [fillcolor="lightgray", color="black"];
2618+
Transient [fillcolor="lightblue", color="blue"];
2619+
Running [fillcolor="palegreen", color="forestgreen"];
2620+
Paused [fillcolor="lightyellow", color="yellow"];
2621+
Suspended [fillcolor="lavenderblush", color="purple"];
2622+
Crashed [fillcolor="mistyrose", color="red"];
2623+
Halting [fillcolor="lightcoral", color="red"];
2624+
2625+
// From NA/Halted/Transient
26242626
NA -> Halted;
26252627
Halted -> NA [constraint=false];
2626-
2627-
Halted -> Transient
2628-
[xlabel="start()" URL="#qubes.vm.qubesvm.QubesVM.start"];
2629-
Transient -> Running;
2630-
2631-
Running -> Halting
2632-
[xlabel="shutdown()"
2633-
URL="#qubes.vm.qubesvm.QubesVM.shutdown"
2634-
constraint=false];
2635-
Halting -> Dying -> Halted [constraint=false];
2636-
2637-
/* cosmetic, invisible edges to put rank constraint */
2638-
Dying -> Halting [style="invis"];
2639-
Halting -> Transient [style="invis"];
2640-
2641-
Running -> Halted
2642-
[label="kill()"
2643-
URL="#qubes.vm.qubesvm.QubesVM.kill"
2644-
constraint=false];
2645-
2646-
Running -> Crashed [constraint=false];
2647-
Crashed -> Halted [constraint=false];
2648-
2649-
Running -> Paused
2650-
[label="pause()" URL="#qubes.vm.qubesvm.QubesVM.pause"
2651-
color=gray75 fontcolor=gray75];
2652-
Running -> Suspended
2653-
[label="suspend()" URL="#qubes.vm.qubesvm.QubesVM.suspend"
2654-
color=gray50 fontcolor=gray50];
2655-
Paused -> Running
2656-
[label="unpause()" URL="#qubes.vm.qubesvm.QubesVM.unpause"
2657-
color=gray75 fontcolor=gray75];
2658-
Suspended -> Running
2659-
[label="resume()" URL="#qubes.vm.qubesvm.QubesVM.resume"
2660-
color=gray50 fontcolor=gray50];
2661-
2662-
Running -> Suspended
2663-
[label="suspend()" URL="#qubes.vm.qubesvm.QubesVM.suspend"];
2664-
Suspended -> Running
2665-
[label="resume()" URL="#qubes.vm.qubesvm.QubesVM.resume"];
2666-
2667-
2668-
{ rank=source; Halted NA };
2669-
{ rank=same; Transient Halting };
2670-
{ rank=same; Crashed Dying };
2671-
{ rank=sink; Paused Suspended };
2628+
Halted -> Transient -> Running [
2629+
label="start()",
2630+
URL="#qubes.vm.qubesvm.QubesVM.start",
2631+
color="blue",
2632+
constraint=true,
2633+
];
2634+
Transient -> Halted [
2635+
label="domain-start-failed",
2636+
URL="#event-domain-start-failed",
2637+
color="black",
2638+
constraint=true,
2639+
];
2640+
2641+
// From Running
2642+
Running -> Halting [
2643+
label="shutdown()",
2644+
URL="#qubes.vm.qubesvm.QubesVM.shutdown",
2645+
constraint=false,
2646+
color="red",
2647+
];
2648+
Running -> Halting [
2649+
label="kill()",
2650+
URL="#qubes.vm.qubesvm.QubesVM.kill",
2651+
color="red",
2652+
constraint=false,
2653+
];
2654+
Running -> Paused [
2655+
label="pause()",
2656+
URL="#qubes.vm.qubesvm.QubesVM.pause",
2657+
color="yellow",
2658+
constraint=true,
2659+
];
2660+
Running -> Suspended [
2661+
label="suspend()",
2662+
URL="#qubes.vm.qubesvm.QubesVM.suspend",
2663+
color="purple",
2664+
constraint=true,
2665+
];
2666+
Running -> Crashed [
2667+
color="black",
2668+
constraint=false,
2669+
];
2670+
Running -> Halted [
2671+
label="shutdown without API",
2672+
color="black",
2673+
constraint=false,
2674+
];
2675+
2676+
// From Paused/Suspended
2677+
Paused -> Running [
2678+
label="unpause()",
2679+
URL="#qubes.vm.qubesvm.QubesVM.unpause",
2680+
color="yellow",
2681+
constraint=false,
2682+
];
2683+
Paused -> Halting [
2684+
label="kill()",
2685+
URL="#qubes.vm.qubesvm.QubesVM.kill",
2686+
color="red4",
2687+
constraint=true,
2688+
];
2689+
Suspended -> Running [
2690+
label="resume()",
2691+
URL="#qubes.vm.qubesvm.QubesVM.resume",
2692+
color="purple",
2693+
constraint=false,
2694+
];
2695+
Suspended -> Halting [
2696+
label="kill()",
2697+
URL="#qubes.vm.qubesvm.QubesVM.kill",
2698+
color="red4",
2699+
constraint=true,
2700+
];
2701+
2702+
// From Halting
2703+
Halting -> Halted [
2704+
color="black",
2705+
constraint=true,
2706+
];
2707+
Halting -> Running [
2708+
label="domain-shutdown-failed",
2709+
URL="#event-domain-shutdown-failed",
2710+
color="blue",
2711+
constraint=true,
2712+
];
2713+
2714+
// From Crashed
2715+
Crashed -> Halted [
2716+
color="black",
2717+
constraint=false,
2718+
];
2719+
2720+
2721+
subgraph cluster_inactive {
2722+
label="Inactive (no ID)"
2723+
color="gray"
2724+
{ rank=""; Halted; NA; }
2725+
}
2726+
2727+
subgraph cluster_active {
2728+
label="Active (has ID)"
2729+
color="dodgerblue"
2730+
{ rank="same"; Running; }
2731+
subgraph cluster_transition {
2732+
label="Transition"
2733+
style="dashed"
2734+
color="lightskyblue"
2735+
{ rank="sink"; Transient; Halting; Crashed; }
2736+
}
2737+
subgraph cluster_suspended {
2738+
label="Suspended"
2739+
style="rounded"
2740+
color="plum"
2741+
{ rank="sink"; Paused; Suspended; }
2742+
}
2743+
}
26722744
}
26732745
26742746
.. seealso::
@@ -2706,10 +2778,6 @@ def set_power_state(self):
27062778
}
27072779
if state in state_dict:
27082780
self._power_state = state_dict[state]
2709-
elif not self.is_fully_usable():
2710-
# libvirt.VIR_DOMAIN_RUNNING # 0x1
2711-
# libvirt.VIR_DOMAIN_BLOCKED # 0x2
2712-
self._power_state = "Transient"
27132781
except libvirt.libvirtError as e:
27142782
if e.get_error_code() != libvirt.VIR_ERR_NO_DOMAIN:
27152783
raise
@@ -2724,7 +2792,8 @@ def is_halted(self):
27242792
return self.get_power_state() == "Halted"
27252793

27262794
def is_running(self):
2727-
"""Check whether this domain is running.
2795+
"""Check whether this domain is active. This is not the same as
2796+
``Running`` power state, it only means the qube has a libvirt ID.
27282797
27292798
:returns: :py:obj:`True` if this domain is started, \
27302799
:py:obj:`False` otherwise.

0 commit comments

Comments
 (0)