Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions qubesadmin/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,23 @@ def qubesd_call(self, dest, method, arg=None, payload=None,
:param payload_stream: file-like object to read payload from
:return: Data returned by qubesd (string)
'''
if not self.app:
raise NotImplementedError
if dest is None:
dest = self._method_dest
if (
getattr(self, "_redirect_dispvm_calls", False)
and dest.startswith("@dispvm")
):
if dest.startswith("@dispvm:"):
dest = dest[len("@dispvm:") :]
else:
dest = getattr(self.app, "default_dispvm", None)
if dest:
dest = dest.name
# have the actual implementation at Qubes() instance
if self.app:
return self.app.qubesd_call(dest, method, arg, payload,
payload_stream)
raise NotImplementedError
return self.app.qubesd_call(dest, method, arg, payload,
payload_stream)

@staticmethod
def _parse_qubesd_response(response_data):
Expand Down
24 changes: 12 additions & 12 deletions qubesadmin/tests/tools/qvm_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -682,9 +682,6 @@ def test_008_dispvm_remote(self):
self.assertAllCalled()

def test_009_dispvm_remote_specific(self):
self.app.expected_calls[("dom0", "admin.vm.List", None, None)] = (
b"0\x00test-vm class=AppVM state=Running\n"
)
self.app.expected_calls[
("test-vm", "admin.vm.property.Get", "guivm", None)
] = b"0\x00default=True type=vm dom0"
Expand Down Expand Up @@ -756,9 +753,6 @@ def test_010_dispvm_local(self):

def test_011_dispvm_local_specific(self):
self.app.qubesd_connection_type = "socket"
self.app.expected_calls[("dom0", "admin.vm.List", None, None)] = (
b"0\x00test-vm class=AppVM state=Running\n"
)
self.app.expected_calls[
("test-vm", "admin.vm.property.Get", "guivm", None)
] = b"0\x00default=False type=vm "
Expand Down Expand Up @@ -1150,7 +1144,7 @@ def test_022_no_shell(self):
def test_023_dispvm_no_shell(self):
self.app.expected_calls[
(
"@dispvm:test-vm",
"test-vm",
"admin.vm.feature.CheckWithTemplate",
"vmexec",
None,
Expand Down Expand Up @@ -1277,7 +1271,7 @@ def test_026_no_shell_double_dashdash(self):
def test_027_no_shell_dispvm(self):
self.app.expected_calls[
(
"@dispvm:test-vm",
"test-vm",
"admin.vm.feature.CheckWithTemplate",
"vmexec",
None,
Expand Down Expand Up @@ -1308,7 +1302,7 @@ def test_027_no_shell_dispvm(self):
def test_028_argparse_bug_workaround(self):
self.app.expected_calls[
(
"@dispvm:test-vm",
"test-vm",
"admin.vm.feature.CheckWithTemplate",
"vmexec",
None,
Expand Down Expand Up @@ -1339,7 +1333,7 @@ def test_028_argparse_bug_workaround(self):
def test_029_command_is_dashdash(self):
self.app.expected_calls[
(
"@dispvm:test-vm",
"test-vm",
"admin.vm.feature.CheckWithTemplate",
"vmexec",
None,
Expand Down Expand Up @@ -1369,7 +1363,10 @@ def test_029_command_is_dashdash(self):

def test_030_no_shell_dispvm(self):
self.app.expected_calls[
("@dispvm", "admin.vm.feature.CheckWithTemplate", "vmexec", None)
("dom0", "admin.property.Get", "default_dispvm", None)
] = b"0\x00default=True type=vm test-vm"
self.app.expected_calls[
("test-vm", "admin.vm.feature.CheckWithTemplate", "vmexec", None)
] = b"0\x001"
ret = qubesadmin.tools.qvm_run.main(
["--no-gui", "--dispvm", "--", "test-vm", "command", "arg"],
Expand All @@ -1395,7 +1392,10 @@ def test_030_no_shell_dispvm(self):

def test_031_argparse_bug_workaround(self):
self.app.expected_calls[
("@dispvm", "admin.vm.feature.CheckWithTemplate", "vmexec", None)
("dom0", "admin.property.Get", "default_dispvm", None)
] = b"0\x00default=True type=vm test-vm"
self.app.expected_calls[
("test-vm", "admin.vm.feature.CheckWithTemplate", "vmexec", None)
] = b"0\x001"
ret = qubesadmin.tools.qvm_run.main(
["--no-gui", "--dispvm", "--", "test-vm", "command", "--"],
Expand Down
18 changes: 8 additions & 10 deletions qubesadmin/tools/qvm_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@
action="store",
nargs="?",
const=True,
metavar="BASE_APPVM",
help="start a service in new Disposable VM; "
"optionally specify base AppVM for DispVM",
metavar="DISPOSABLE_TEMPLATE",
help="start a service in new disposable qube; optionally specify a "
"disposable template, else the system default will be used",
)
parser.add_argument("VMNAME", nargs="?", action=qubesadmin.tools.VmNameAction)

Expand Down Expand Up @@ -258,6 +258,9 @@ def run_command_single(args, vm):
args.cmd = args.VMNAME
args.VMNAME = None

if args.dispvm and args.gui is None:
args.gui = has_gui(vm)

use_exec = len(args.cmd_args) > 0 or args.no_shell

copy_proc = None
Expand Down Expand Up @@ -358,14 +361,9 @@ def main(args=None, app=None):
if args.dispvm:
if args.exclude:
parser.error("Cannot use --exclude with --dispvm")
if args.gui is None:
args.gui = has_gui(
args.app.default_dispvm
if args.dispvm is True
else args.app.domains[args.dispvm]
)
dispvm = qubesadmin.vm.DispVM.from_appvm(
args.app, None if args.dispvm is True else args.dispvm
args.app, None if args.dispvm is True else args.dispvm,
redirect_dispvm_calls=True
)
domains = [dispvm]
elif args.all_domains:
Expand Down
17 changes: 14 additions & 3 deletions qubesadmin/vm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,10 @@ class DispVMWrapper(QubesVM):
after service call ends.
"""

def __init__(self, *args, **kwargs):
self._redirect_dispvm_calls = kwargs.pop("redirect_dispvm_calls", False)
super().__init__(*args, **kwargs)

def run_service(self, service, **kwargs):
"""Create disposable if absent and run service."""
if (
Expand Down Expand Up @@ -513,14 +517,21 @@ class DispVM(QubesVM):
"""Disposable VM"""

@classmethod
def from_appvm(cls, app, appvm):
def from_appvm(cls, app, appvm, redirect_dispvm_calls=False):
"""Returns a wrapper for calling service in a new DispVM based on given
AppVM. If *appvm* is none, use default DispVM template"""
AppVM. If *appvm* is none, use default DispVM template

If **redirect_dispvm_calls** is used, calls made before the disposable
is created will instead guess the disposable template to query for
features, properties etc.
"""

if appvm:
method_dest = "@dispvm:" + str(appvm)
else:
method_dest = "@dispvm"

wrapper = DispVMWrapper(app, method_dest)
wrapper = DispVMWrapper(
app, method_dest, redirect_dispvm_calls=redirect_dispvm_calls
)
return wrapper