Add run_service(..., autostart=False) argument
This allows to run a service but do not cause a qube to be started it isn't already running. This is especially useful for background / internal calls designed to service a running target VM - if VM is not running, those do not make sense to be called in the first place. Specifically, this will allow qvm-start-gui to avoid re-starting a domain while calling qubes.NotifyMonitorLayout, when a VM is shutdown shortly after its startup.
This commit is contained in:
		
							parent
							
								
									1fcb031192
								
							
						
					
					
						commit
						98260ff148
					
				| @ -487,7 +487,7 @@ class QubesBase(qubesadmin.base.PropertyHolder): | |||||||
|             'class: qubesadmin.Qubes()') |             'class: qubesadmin.Qubes()') | ||||||
| 
 | 
 | ||||||
|     def run_service(self, dest, service, filter_esc=False, user=None, |     def run_service(self, dest, service, filter_esc=False, user=None, | ||||||
|                     localcmd=None, wait=True, **kwargs): |                     localcmd=None, wait=True, autostart=True, **kwargs): | ||||||
|         """Run qrexec service in a given destination |         """Run qrexec service in a given destination | ||||||
| 
 | 
 | ||||||
|         *kwargs* are passed verbatim to :py:meth:`subprocess.Popen`. |         *kwargs* are passed verbatim to :py:meth:`subprocess.Popen`. | ||||||
| @ -500,6 +500,7 @@ class QubesBase(qubesadmin.base.PropertyHolder): | |||||||
|         :param str user: username to run service as |         :param str user: username to run service as | ||||||
|         :param str localcmd: Command to connect stdin/stdout to |         :param str localcmd: Command to connect stdin/stdout to | ||||||
|         :param bool wait: Wait service run |         :param bool wait: Wait service run | ||||||
|  |         :param bool autostart: Automatically start the target VM | ||||||
|         :rtype: subprocess.Popen |         :rtype: subprocess.Popen | ||||||
|         """ |         """ | ||||||
|         raise NotImplementedError( |         raise NotImplementedError( | ||||||
| @ -576,7 +577,7 @@ class QubesLocal(QubesBase): | |||||||
|         return self._parse_qubesd_response(return_data) |         return self._parse_qubesd_response(return_data) | ||||||
| 
 | 
 | ||||||
|     def run_service(self, dest, service, filter_esc=False, user=None, |     def run_service(self, dest, service, filter_esc=False, user=None, | ||||||
|                     localcmd=None, wait=True, **kwargs): |                     localcmd=None, wait=True, autostart=True, **kwargs): | ||||||
|         """Run qrexec service in a given destination |         """Run qrexec service in a given destination | ||||||
| 
 | 
 | ||||||
|         :param str dest: Destination - may be a VM name or empty |         :param str dest: Destination - may be a VM name or empty | ||||||
| @ -594,10 +595,14 @@ class QubesLocal(QubesBase): | |||||||
|             raise ValueError('Empty destination name allowed only from a VM') |             raise ValueError('Empty destination name allowed only from a VM') | ||||||
|         if not wait and localcmd: |         if not wait and localcmd: | ||||||
|             raise ValueError('wait=False incompatible with localcmd') |             raise ValueError('wait=False incompatible with localcmd') | ||||||
|         try: |         if autostart: | ||||||
|             self.qubesd_call(dest, 'admin.vm.Start') |             try: | ||||||
|         except qubesadmin.exc.QubesVMNotHaltedError: |                 self.qubesd_call(dest, 'admin.vm.Start') | ||||||
|             pass |             except qubesadmin.exc.QubesVMNotHaltedError: | ||||||
|  |                 pass | ||||||
|  |         elif not self.domains.get_blind(dest).is_running(): | ||||||
|  |             raise qubesadmin.exc.QubesVMNotRunningError( | ||||||
|  |                 '%s is not running', dest) | ||||||
|         qrexec_opts = ['-d', dest] |         qrexec_opts = ['-d', dest] | ||||||
|         if filter_esc: |         if filter_esc: | ||||||
|             qrexec_opts.extend(['-t']) |             qrexec_opts.extend(['-t']) | ||||||
| @ -665,7 +670,7 @@ class QubesRemote(QubesBase): | |||||||
|         return self._parse_qubesd_response(stdout) |         return self._parse_qubesd_response(stdout) | ||||||
| 
 | 
 | ||||||
|     def run_service(self, dest, service, filter_esc=False, user=None, |     def run_service(self, dest, service, filter_esc=False, user=None, | ||||||
|                     localcmd=None, wait=True, **kwargs): |                     localcmd=None, wait=True, autostart=True, **kwargs): | ||||||
|         """Run qrexec service in a given destination |         """Run qrexec service in a given destination | ||||||
| 
 | 
 | ||||||
|         :param str dest: Destination - may be a VM name or empty |         :param str dest: Destination - may be a VM name or empty | ||||||
| @ -678,6 +683,9 @@ class QubesRemote(QubesBase): | |||||||
|         :param bool wait: wait for process to finish |         :param bool wait: wait for process to finish | ||||||
|         :rtype: subprocess.Popen |         :rtype: subprocess.Popen | ||||||
|         """ |         """ | ||||||
|  |         if not autostart and not dest: | ||||||
|  |             raise ValueError( | ||||||
|  |                 'autostart=False makes sense only with a defined target') | ||||||
|         if user: |         if user: | ||||||
|             raise ValueError( |             raise ValueError( | ||||||
|                 'non-default user not possible for calls from VM') |                 'non-default user not possible for calls from VM') | ||||||
| @ -686,8 +694,12 @@ class QubesRemote(QubesBase): | |||||||
|         qrexec_opts = [] |         qrexec_opts = [] | ||||||
|         if filter_esc: |         if filter_esc: | ||||||
|             qrexec_opts.extend(['-t']) |             qrexec_opts.extend(['-t']) | ||||||
|         if filter_esc or os.isatty(sys.stderr.fileno()): |         if filter_esc or ( | ||||||
|  |                 os.isatty(sys.stderr.fileno()) and 'stderr' not in kwargs): | ||||||
|             qrexec_opts.extend(['-T']) |             qrexec_opts.extend(['-T']) | ||||||
|  |         if not autostart and not self.domains.get_blind(dest).is_running(): | ||||||
|  |             raise qubesadmin.exc.QubesVMNotRunningError( | ||||||
|  |                 '%s is not running', dest) | ||||||
|         if not wait: |         if not wait: | ||||||
|             # qrexec-client-vm can only request service calls, which are |             # qrexec-client-vm can only request service calls, which are | ||||||
|             # started using MSG_EXEC_CMDLINE qrexec protocol message; this |             # started using MSG_EXEC_CMDLINE qrexec protocol message; this | ||||||
|  | |||||||
| @ -29,6 +29,7 @@ try: | |||||||
|     import unittest.mock as mock |     import unittest.mock as mock | ||||||
| except ImportError: | except ImportError: | ||||||
|     import mock |     import mock | ||||||
|  | from mock import call | ||||||
| 
 | 
 | ||||||
| import tempfile | import tempfile | ||||||
| 
 | 
 | ||||||
| @ -911,3 +912,30 @@ class TC_30_QubesRemote(unittest.TestCase): | |||||||
|             '-T', '', 'service.name'], |             '-T', '', 'service.name'], | ||||||
|             stdin=subprocess.PIPE, stdout=subprocess.PIPE, |             stdin=subprocess.PIPE, stdout=subprocess.PIPE, | ||||||
|             stderr=subprocess.PIPE) |             stderr=subprocess.PIPE) | ||||||
|  | 
 | ||||||
|  |     @mock.patch('os.isatty', lambda fd: fd == 2) | ||||||
|  |     def test_014_run_service_no_autostart1(self): | ||||||
|  |         self.set_proc_stdout( b'0\x00some-vm class=AppVM state=Running\n') | ||||||
|  |         self.app.run_service('some-vm', 'service.name', autostart=False) | ||||||
|  |         self.proc_mock.assert_has_calls([ | ||||||
|  |             call([qubesadmin.config.QREXEC_CLIENT_VM, | ||||||
|  |                   'some-vm', 'admin.vm.List'], | ||||||
|  |                  stdin=subprocess.PIPE, stdout=subprocess.PIPE, | ||||||
|  |                  stderr=subprocess.PIPE), | ||||||
|  |             call().communicate(None), | ||||||
|  |             call([qubesadmin.config.QREXEC_CLIENT_VM, | ||||||
|  |                   '-T', 'some-vm', 'service.name'], | ||||||
|  |                  stdin=subprocess.PIPE, stdout=subprocess.PIPE, | ||||||
|  |                  stderr=subprocess.PIPE), | ||||||
|  |         ]) | ||||||
|  | 
 | ||||||
|  |     @mock.patch('os.isatty', lambda fd: fd == 2) | ||||||
|  |     def test_015_run_service_no_autostart2(self): | ||||||
|  |         self.set_proc_stdout( b'0\x00some-vm class=AppVM state=Halted\n') | ||||||
|  |         with self.assertRaises(qubesadmin.exc.QubesVMNotRunningError): | ||||||
|  |             self.app.run_service('some-vm', 'service.name', autostart=False) | ||||||
|  |         self.proc_mock.assert_called_once_with([ | ||||||
|  |             qubesadmin.config.QREXEC_CLIENT_VM, | ||||||
|  |             'some-vm', 'admin.vm.List'], | ||||||
|  |             stdin=subprocess.PIPE, stdout=subprocess.PIPE, | ||||||
|  |             stderr=subprocess.PIPE) | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Marek Marczykowski-Górecki
						Marek Marczykowski-Górecki