Merge remote-tracking branch 'origin/pr/127'

* origin/pr/127:
  qvm-shutdown: report errors, don't crash on DispVMs
This commit is contained in:
Marek Marczykowski-Górecki 2020-01-17 05:06:18 +01:00
commit 204c33afd1
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
3 changed files with 25 additions and 7 deletions

View File

@ -293,6 +293,7 @@ class TC_00_qvm_shutdown(qubesadmin.tests.QubesTestCase):
self.app.expected_calls[ self.app.expected_calls[
('sys-net', 'admin.vm.CurrentState', None, None)] = \ ('sys-net', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Halted' b'0\x00power_state=Halted'
qubesadmin.tools.qvm_shutdown.main( with self.assertRaisesRegexp(SystemExit, '2'):
['--wait', '--all', '--timeout=1'], app=self.app) qubesadmin.tools.qvm_shutdown.main(
['--wait', '--all', '--timeout=1'], app=self.app)
self.assertAllCalled() self.assertAllCalled()

View File

@ -408,12 +408,12 @@ class QubesArgumentParser(argparse.ArgumentParser):
return namespace return namespace
def error_runtime(self, message): def error_runtime(self, message, exit_code=1):
'''Runtime error, without showing usage. '''Runtime error, without showing usage.
:param str message: message to show :param str message: message to show
''' '''
self.exit(1, '{}: error: {}\n'.format(self.prog, message)) self.exit(exit_code, '{}: error: {}\n'.format(self.prog, message))
@staticmethod @staticmethod

View File

@ -73,7 +73,12 @@ def main(args=None, app=None): # pylint: disable=missing-docstring
else: else:
remaining_domains.add(vm) remaining_domains.add(vm)
if not args.wait: if not args.wait:
return len(remaining_domains) if remaining_domains:
parser.error_runtime(
'Failed to shut down: ' +
', '.join(vm.name for vm in remaining_domains),
len(remaining_domains))
return
this_round_domains.difference_update(remaining_domains) this_round_domains.difference_update(remaining_domains)
if not this_round_domains: if not this_round_domains:
# no VM shutdown request succeed, no sense to try again # no VM shutdown request succeed, no sense to try again
@ -122,8 +127,20 @@ def main(args=None, app=None): # pylint: disable=missing-docstring
if args.wait: if args.wait:
if have_events: if have_events:
loop.close() loop.close()
return len([vm for vm in args.domains failed = []
if vm.get_power_state() != 'Halted']) for vm in args.domains:
power_state = vm.get_power_state()
# DispVM might have been deleted before we check them,
# so NA is acceptable.
if not (power_state == 'Halted' or
(vm.klass == 'DispVM' and power_state == 'NA')):
failed.append(vm)
if failed:
parser.error_runtime(
'Failed to shut down: ' +
', '.join(vm.name for vm in failed),
len(failed))
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) sys.exit(main())