6767)
6868
6969
70- async def shutdown (args , domains : list [qubesadmin .vm .QubesVM ]):
71- """
72- Asynchronously shutdown qubes and return qubes that failed to shutdown
73- because and the client can't handle, as well as qubes that were in use
74- while --force was not provided, as well as timed out.
75- """
76- # pylint: disable=missing-docstring
77- unhandled , used , timedout = [], [], []
78- tasks = [
79- asyncio .to_thread (qube .shutdown , force = args .force , wait = args .wait )
80- for qube in domains
81- ]
82- results = await asyncio .gather (* tasks , return_exceptions = True )
83- for qube , res in zip (domains , results ):
84- if not isinstance (res , BaseException ):
85- qube .log .info ("Shutdown succeeded" )
86- continue
87- try :
88- raise res
89- except qubesadmin .exc .QubesVMNotStartedError :
90- pass
91- except qubesadmin .exc .QubesVMInUseError as e :
92- if args .wait :
93- qube .log .error ("Shutdown error: {}" .format (e ))
94- else :
95- qube .log .error ("Shutdown error: (try --force): {}" .format (e ))
96- used .append (qube )
97- except qubesadmin .exc .QubesVMShutdownTimeoutError as e :
98- if args .wait :
99- qube .log .error ("Shutdown error: {}" .format (e ))
100- else :
101- qube .log .error ("Shutdown error: (try qvm-kill): {}" .format (e ))
102- timedout .append (qube )
103- except qubesadmin .exc .QubesException as e :
104- qube .log .error ("Shutdown error: {}" .format (e ))
105- unhandled .append (qube )
70+ async def shutdown (domains , ** shutdown_kwargs ):
71+ # pylint: disable=missing-function-docstring
72+ failed = await qubesadmin .utils .shutdown (domains = domains , ** shutdown_kwargs )
73+ used = {
74+ qube : exc
75+ for qube , exc in failed .items ()
76+ if isinstance (exc , qubesadmin .exc .QubesVMInUseError )
77+ }
78+ timedout = {
79+ qube : exc
80+ for qube , exc in failed .items ()
81+ if isinstance (exc , qubesadmin .exc .QubesVMShutdownTimeoutError )
82+ }
83+ unhandled = {
84+ qube : exc
85+ for qube , exc in failed .items ()
86+ if qube not in used and qube not in timedout
87+ }
10688 return unhandled , used , timedout
10789
10890
109- async def kill (domains : list [qubesadmin .vm .QubesVM ]):
110- """
111- Asynchronously kill qubes and return qubes that failed to shutdown.
112- """
113- # pylint: disable=missing-docstring
114- unhandled = domains .copy ()
115- tasks = [asyncio .to_thread (qube .kill ) for qube in domains ]
116- results = await asyncio .gather (* tasks , return_exceptions = True )
117- for qube , res in zip (domains , results ):
118- if not isinstance (res , BaseException ):
119- qube .log .info ("Killing succeeded" )
120- unhandled .remove (qube )
121- continue
122- try :
123- raise res
124- except qubesadmin .exc .QubesVMNotStartedError :
125- unhandled .remove (qube )
126- except qubesadmin .exc .QubesException as e :
127- qube .log .error ("Kill error: {}" .format (e ))
128- return unhandled
129-
130-
13191async def run_async (args = None , app = None ):
13292 # pylint: disable=missing-docstring
13393 args = parser .parse_args (args , app = app )
@@ -140,10 +100,16 @@ async def run_async(args=None, app=None):
140100 )
141101 args .wait = True
142102 args .force = args .force or (args .all_domains and not args .exclude )
143-
144- unhandled , used , timedout = await shutdown (args = args , domains = args .domains )
145- unhandled_retry = []
146- timedout_retry = []
103+ shutdown_kwargs = {
104+ "force" : args .force ,
105+ "wait" : args .wait ,
106+ }
107+
108+ unhandled , used , timedout = await shutdown (
109+ domains = args .domains , ** shutdown_kwargs
110+ )
111+ unhandled_retry = {}
112+ timedout_retry = {}
147113 if used :
148114 old_failed = unhandled , used , timedout
149115 parser .print_error (
@@ -157,7 +123,7 @@ async def run_async(args=None, app=None):
157123 ", " .join (qube .name for qube in used )
158124 )
159125 )
160- failed = await shutdown (args = args , domains = used )
126+ failed = await shutdown (domains = used , ** shutdown_kwargs )
161127 unhandled_retry , used , timedout_retry = failed
162128 if not failed :
163129 break
@@ -168,8 +134,8 @@ async def run_async(args=None, app=None):
168134 break
169135 old_failed = failed
170136
171- unhandled .extend ( qube for qube in unhandled_retry if qube not in unhandled )
172- timedout .extend ( qube for qube in timedout_retry if qube not in timedout )
137+ unhandled .update ( unhandled_retry )
138+ timedout .update ( timedout_retry )
173139
174140 # Retry timed out only once, as it can take a long time, 60s by default.
175141 if timedout :
@@ -178,38 +144,28 @@ async def run_async(args=None, app=None):
178144 ", " .join (qube .name for qube in timedout )
179145 )
180146 )
181- unhandled , used , timedout = await shutdown (args = args , domains = timedout )
147+ unhandled , used , timedout = await shutdown (
148+ domains = timedout , ** shutdown_kwargs
149+ )
182150
183151 if timedout :
184152 parser .print_error (
185153 "Killing timed out qubes: {}" .format (
186154 ", " .join (qube .name for qube in timedout )
187155 )
188156 )
189- unhandled = await kill (domains = timedout )
157+ timedout = await qubesadmin . utils . kill (domains = timedout )
190158
191- if not unhandled and not used and not timedout :
192- return
193-
194- if unhandled :
195- parser .print_error (
196- "Failed to shut down for unknown reason: {}" .format (
197- ", " .join (qube .name for qube in unhandled )
198- )
199- )
200- if used :
201- parser .print_error (
202- "Failed to shut down because it's in use: {}" .format (
203- ", " .join (qube .name for qube in used )
159+ for item in [unhandled , used , timedout ]:
160+ for qube , exc in item .items ():
161+ parser .print_error (
162+ "Failed to shut down: {}: {}" .format (qube .name , str (exc ))
204163 )
205- )
206- if timedout :
207- parser .print_error (
208- "Failed to shut down because of time out: {}" .format (
209- ", " .join (qube .name for qube in timedout )
210- )
211- )
212- raise SystemExit (len (unhandled + used + timedout ))
164+
165+ exit_code = len (unhandled ) + len (used ) + len (timedout )
166+ if exit_code == 0 :
167+ return
168+ raise SystemExit (exit_code )
213169
214170
215171def main (args = None , app = None ):
0 commit comments