@@ -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