qvm-shutdown: report errors, don't crash on DispVMs
qvm-shutdown with the --wait option checks if the machine state is 'Halted', but a disposable VM is usually deleted by the time of the final check, resulting in a non-zero exit code. This change handles properly disposable VMs, and makes sure we always output an error message when finishing with a non-zero exit code. Fixes QubesOS/qubes-issues#5245.
This commit is contained in:
		
							parent
							
								
									de1969150d
								
							
						
					
					
						commit
						405464a67e
					
				| @ -293,6 +293,7 @@ class TC_00_qvm_shutdown(qubesadmin.tests.QubesTestCase): | |||||||
|         self.app.expected_calls[ |         self.app.expected_calls[ | ||||||
|             ('sys-net', 'admin.vm.List', None, None)] = \ |             ('sys-net', 'admin.vm.List', None, None)] = \ | ||||||
|             b'0\x00sys-net class=AppVM state=Halted\n' |             b'0\x00sys-net class=AppVM state=Halted\n' | ||||||
|  |         with self.assertRaisesRegexp(SystemExit, '2'): | ||||||
|             qubesadmin.tools.qvm_shutdown.main( |             qubesadmin.tools.qvm_shutdown.main( | ||||||
|                 ['--wait', '--all', '--timeout=1'], app=self.app) |                 ['--wait', '--all', '--timeout=1'], app=self.app) | ||||||
|         self.assertAllCalled() |         self.assertAllCalled() | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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()) | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Pawel Marczewski
						Pawel Marczewski