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