diff --git a/doc/manpages/qvm-service.rst b/doc/manpages/qvm-service.rst index e376497..ea1bb62 100644 --- a/doc/manpages/qvm-service.rst +++ b/doc/manpages/qvm-service.rst @@ -8,6 +8,7 @@ Synopsis ======== | :command:`qvm-service` [-l] <*vmname*> | :command:`qvm-service` [-e|-d|-D] <*vmname*> <*service*> +| :command:`qvm-service` <*vmname*> <*service*> [on|off] Options ======= @@ -27,11 +28,19 @@ Options Disable service -.. option:: --default, -D +.. option:: --default, -D, --delete, --unset Reset service to its default state (remove from the list). Default state means "lets VM choose" and can depend on VM type (NetVM, AppVM etc). +.. option:: --verbose, -v + + increase verbosity + +.. option:: --quiet, -q + + decrease verbosity + Supported services ================== @@ -50,11 +59,6 @@ meminfo-writer remove it (reset to default state), will be recreated with the rule: enabled if VM have no PCI devices assigned, otherwise disabled. -qubes-dvm - Default: disabled - - Used internally when creating DispVM savefile. - qubes-firewall Default: enabled only in ProxyVM @@ -72,14 +76,6 @@ qubes-network Expose network for other VMs. This includes enabling network forwarding, MASQUERADE, DNS redirection and basic firewall. -qubes-netwatcher - Default: enabled only in ProxyVM - - Monitor IP change notification from NetVM. When received, reload - qubes-firewall service (to force DNS resolution). - - This service makes sense only with qubes-firewall enabled. - qubes-update-check Default: enabled @@ -103,12 +99,12 @@ network-manager Enable NetworkManager. Only VM with direct access to network device needs this service, but can be useful in ProxyVM to ease VPN setup. -ntpd +clocksync Default: disabled - Enable NTPD service. By default Qubes calls ntpdate every 6 minutes in - selected VM (aka ClockVM), then propagate the result using qrexec calls. - Enabling ntpd *do not* disable this behaviour. + Enable NTPD (or equivalent) service. If disabled, VM will sync clock with + selected VM (aka ClockVM) instead. ClockVM for particular VM can be set in + policy of qubes.GetDate service, using target= parameter. qubes-yum-proxy Deprecated name for qubes-updates-proxy. @@ -130,9 +126,8 @@ updates-proxy-setup .. note:: - this service is automatically enabled when you allow VM to access yum - proxy (in firewall settings) and disabled when you deny access to yum - proxy. + this service is automatically enabled when you allow VM to access updates + proxy and disabled when you deny access to updates proxy. disable-default-route Default: disabled diff --git a/doc/manpages/qvm-shutdown.rst b/doc/manpages/qvm-shutdown.rst index 8ac86e0..da62e5d 100644 --- a/doc/manpages/qvm-shutdown.rst +++ b/doc/manpages/qvm-shutdown.rst @@ -15,7 +15,7 @@ Synopsis -------- -:command:`qvm-shutdown` [-h] [--verbose] [--quiet] [--all] [--exclude *EXCLUDE*] [--force] [--wait] [--timeout *TIMEOUT*] [*VMNAME*] +:command:`qvm-shutdown` [-h] [--verbose] [--quiet] [--all] [--exclude *EXCLUDE*] [--wait] [--timeout *TIMEOUT*] [*VMNAME*] Options ------- @@ -40,11 +40,6 @@ Options exclude the qube from :option:`--all` -.. option:: --force - - force operation, even if may damage other VMs (eg. shutdown of network - provider) - .. option:: --wait wait for the VMs to shut down diff --git a/doc/manpages/qvm-start-gui.rst b/doc/manpages/qvm-start-gui.rst index 8a966bc..eb78794 100644 --- a/doc/manpages/qvm-start-gui.rst +++ b/doc/manpages/qvm-start-gui.rst @@ -15,7 +15,7 @@ Synopsis -------- -:command:`qvm-start-gui` [-h] [--verbose] [--quiet] [--all] [--exclude *EXCLUDE*] [--watch] [--pidfile *PIDFILE*] [--notify-monitory-layout] [*VMNAME* [*VMNAME* ...]] +:command:`qvm-start-gui` [-h] [--verbose] [--quiet] [--all] [--exclude *EXCLUDE*] [--watch] [--force-stubdomain] [--pidfile *PIDFILE*] [--notify-monitory-layout] [*VMNAME* [*VMNAME* ...]] Options ------- @@ -44,6 +44,10 @@ Options Keep watching for further domains startups, must be used with --all +.. option:: --force-stubdomain + + Start GUI to stubdomain-emulated VGA, even if gui-agent is running in the VM + .. option:: --pidfile Pidfile path to create in --watch mode diff --git a/qubesadmin/features.py b/qubesadmin/features.py index 4ed8ef8..e7a9cee 100644 --- a/qubesadmin/features.py +++ b/qubesadmin/features.py @@ -42,9 +42,10 @@ class Features(object): self.vm.qubesd_call(self.vm.name, 'admin.vm.feature.Remove', key) def __setitem__(self, key, value): - if value is False: + if isinstance(value, bool): # False value needs to be serialized as empty string - self.vm.qubesd_call(self.vm.name, 'admin.vm.feature.Set', key, b'') + self.vm.qubesd_call(self.vm.name, 'admin.vm.feature.Set', key, + b'1' if value else b'') else: self.vm.qubesd_call(self.vm.name, 'admin.vm.feature.Set', key, str(value).encode()) diff --git a/qubesadmin/tests/backup/backupcompatibility.py b/qubesadmin/tests/backup/backupcompatibility.py index e583529..4a3db1a 100644 --- a/qubesadmin/tests/backup/backupcompatibility.py +++ b/qubesadmin/tests/backup/backupcompatibility.py @@ -1350,6 +1350,8 @@ class TC_10_BackupCompatibility(qubesadmin.tests.backup.BackupTestCase): for feature, value in vm['features'].items(): if value is False: value = '' + elif value is True: + value = '1' self.app.expected_calls[ (name, 'admin.vm.feature.Set', feature, str(value).encode())] = b'0\0' diff --git a/qubesadmin/tests/features.py b/qubesadmin/tests/features.py index 0a1d30c..d66cdce 100644 --- a/qubesadmin/tests/features.py +++ b/qubesadmin/tests/features.py @@ -78,7 +78,7 @@ class TC_00_Features(qubesadmin.tests.QubesTestCase): def test_021_set_bool(self): self.app.expected_calls[ - ('test-vm', 'admin.vm.feature.Set', 'feature1', b'True')] = \ + ('test-vm', 'admin.vm.feature.Set', 'feature1', b'1')] = \ b'0\0' self.vm.features['feature1'] = True self.assertAllCalled() diff --git a/qubesadmin/tests/tools/qvm_features.py b/qubesadmin/tests/tools/qvm_features.py index ea85e51..5174916 100644 --- a/qubesadmin/tests/tools/qvm_features.py +++ b/qubesadmin/tests/tools/qvm_features.py @@ -91,162 +91,3 @@ class TC_00_qvm_features(qubesadmin.tests.QubesTestCase): self.assertEqual(stdout.getvalue(), '') self.assertAllCalled() - - def test_004_running_verbose(self): - self.app.expected_calls[ - ('dom0', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm class=AppVM state=Running\n' \ - b'some-vm2 class=AppVM state=Running\n' \ - b'some-vm3 class=AppVM state=Halted\n' - self.app.expected_calls[ - ('some-vm', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm class=AppVM state=Running\n' - with qubesadmin.tests.tools.StdoutBuffer() as stdout: - self.assertEqual( - qubesadmin.tools.qvm_check.main(['--running', - 'some-vm'], app=self.app), - 0) - self.assertEqual(stdout.getvalue(), - 'VM some-vm is running\n') - self.assertAllCalled() - - def test_005_running_multi_verbose(self): - self.app.expected_calls[ - ('dom0', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm class=AppVM state=Running\n' \ - b'some-vm2 class=AppVM state=Running\n' \ - b'some-vm3 class=AppVM state=Halted\n' - self.app.expected_calls[ - ('some-vm', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm class=AppVM state=Running\n' - self.app.expected_calls[ - ('some-vm2', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm2 class=AppVM state=Running\n' - with qubesadmin.tests.tools.StdoutBuffer() as stdout: - self.assertEqual( - qubesadmin.tools.qvm_check.main(['--running', - 'some-vm', 'some-vm2'], - app=self.app), - 0) - self.assertEqual(stdout.getvalue(), - 'VMs some-vm, some-vm2 are running\n') - self.assertAllCalled() - - def test_006_running_multi_verbose2(self): - self.app.expected_calls[ - ('dom0', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm class=AppVM state=Running\n' \ - b'some-vm2 class=AppVM state=Running\n' \ - b'some-vm3 class=AppVM state=Halted\n' - self.app.expected_calls[ - ('some-vm', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm class=AppVM state=Running\n' - self.app.expected_calls[ - ('some-vm2', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm2 class=AppVM state=Running\n' - self.app.expected_calls[ - ('some-vm3', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm3 class=AppVM state=Halted\n' - with qubesadmin.tests.tools.StdoutBuffer() as stdout: - self.assertEqual( - qubesadmin.tools.qvm_check.main(['--running', - '--all'], - app=self.app), - 0) - self.assertEqual(stdout.getvalue(), - 'VMs some-vm, some-vm2 are running\n') - self.assertAllCalled() - - def test_007_not_running_verbose(self): - self.app.expected_calls[ - ('dom0', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm class=AppVM state=Running\n' \ - b'some-vm2 class=AppVM state=Running\n' \ - b'some-vm3 class=AppVM state=Halted\n' - self.app.expected_calls[ - ('some-vm3', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm3 class=AppVM state=Halted\n' - with qubesadmin.tests.tools.StdoutBuffer() as stdout: - self.assertEqual( - qubesadmin.tools.qvm_check.main(['--running', - 'some-vm3'], - app=self.app), - 1) - self.assertEqual(stdout.getvalue(), - 'None of given VM is running\n') - self.assertAllCalled() - - def test_008_paused(self): - self.app.expected_calls[ - ('dom0', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm class=AppVM state=Running\n' \ - b'some-vm2 class=AppVM state=Paused\n' \ - b'some-vm3 class=AppVM state=Halted\n' - self.app.expected_calls[ - ('some-vm2', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm2 class=AppVM state=Paused\n' - with qubesadmin.tests.tools.StdoutBuffer() as stdout: - self.assertEqual( - qubesadmin.tools.qvm_check.main(['--paused', - 'some-vm2'], - app=self.app), - 0) - self.assertEqual(stdout.getvalue(), - 'VM some-vm2 is paused\n') - self.assertAllCalled() - - def test_009_paused_multi(self): - self.app.expected_calls[ - ('dom0', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm class=AppVM state=Running\n' \ - b'some-vm2 class=AppVM state=Paused\n' \ - b'some-vm3 class=AppVM state=Halted\n' - self.app.expected_calls[ - ('some-vm2', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm2 class=AppVM state=Paused\n' - self.app.expected_calls[ - ('some-vm', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm class=AppVM state=Running\n' - with qubesadmin.tests.tools.StdoutBuffer() as stdout: - self.assertEqual( - qubesadmin.tools.qvm_check.main(['--paused', - 'some-vm2', 'some-vm'], - app=self.app), - 0) - self.assertEqual(stdout.getvalue(), - 'VM some-vm2 is paused\n') - self.assertAllCalled() - - - def test_010_template(self): - self.app.expected_calls[ - ('dom0', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm class=AppVM state=Running\n' \ - b'some-vm2 class=AppVM state=Paused\n' \ - b'some-vm3 class=TemplateVM state=Halted\n' - with qubesadmin.tests.tools.StdoutBuffer() as stdout: - self.assertEqual( - qubesadmin.tools.qvm_check.main(['--template', - 'some-vm3'], - app=self.app), - 0) - self.assertEqual(stdout.getvalue(), - 'VM some-vm3 is a template\n') - self.assertAllCalled() - - def test_011_template_multi(self): - self.app.expected_calls[ - ('dom0', 'admin.vm.List', None, None)] = \ - b'0\x00some-vm class=AppVM state=Running\n' \ - b'some-vm2 class=AppVM state=Paused\n' \ - b'some-vm3 class=TemplateVM state=Halted\n' - with qubesadmin.tests.tools.StdoutBuffer() as stdout: - self.assertEqual( - qubesadmin.tools.qvm_check.main(['--template', - 'some-vm2', 'some-vm3'], - app=self.app), - 0) - self.assertEqual(stdout.getvalue(), - 'VM some-vm3 is a template\n') - self.assertAllCalled() - diff --git a/qubesadmin/tests/tools/qvm_service.py b/qubesadmin/tests/tools/qvm_service.py new file mode 100644 index 0000000..0352219 --- /dev/null +++ b/qubesadmin/tests/tools/qvm_service.py @@ -0,0 +1,203 @@ +# -*- encoding: utf8 -*- +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2017 Marek Marczykowski-Górecki +# +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with this program; if not, see . +import unittest + +import qubesadmin.tests +import qubesadmin.tools.qvm_service + + +class TC_00_qvm_service(qubesadmin.tests.QubesTestCase): + def test_000_list(self): + self.app.expected_calls[ + ('dom0', 'admin.vm.List', None, None)] = \ + b'0\x00some-vm class=AppVM state=Running\n' + self.app.expected_calls[ + ('some-vm', 'admin.vm.feature.List', None, None)] = \ + b'0\x00feature1\nservice.service1\nservice.service2\n' + self.app.expected_calls[ + ('some-vm', 'admin.vm.feature.Get', 'service.service1', None)] = \ + b'0\x00value1' + self.app.expected_calls[ + ('some-vm', 'admin.vm.feature.Get', 'service.service2', None)] = \ + b'0\x00' + with qubesadmin.tests.tools.StdoutBuffer() as stdout: + self.assertEqual( + qubesadmin.tools.qvm_service.main(['some-vm'], app=self.app), + 0) + self.assertEqual(stdout.getvalue(), + 'service1 on\n' + 'service2 off\n') + self.assertAllCalled() + + def test_001_list_l(self): + self.app.expected_calls[ + ('dom0', 'admin.vm.List', None, None)] = \ + b'0\x00some-vm class=AppVM state=Running\n' + self.app.expected_calls[ + ('some-vm', 'admin.vm.feature.List', None, None)] = \ + b'0\x00feature1\nservice.service1\nservice.service2\n' + self.app.expected_calls[ + ('some-vm', 'admin.vm.feature.Get', 'service.service1', None)] = \ + b'0\x00value1' + self.app.expected_calls[ + ('some-vm', 'admin.vm.feature.Get', 'service.service2', None)] = \ + b'0\x00' + with qubesadmin.tests.tools.StdoutBuffer() as stdout: + self.assertEqual( + qubesadmin.tools.qvm_service.main(['-l', 'some-vm'], + app=self.app), + 0) + self.assertEqual(stdout.getvalue(), + 'service1 on\n' + 'service2 off\n') + self.assertAllCalled() + + def test_002_enable(self): + self.app.expected_calls[ + ('dom0', 'admin.vm.List', None, None)] = \ + b'0\x00some-vm class=AppVM state=Running\n' + self.app.expected_calls[ + ('some-vm', 'admin.vm.feature.Set', + 'service.service1', b'1')] = b'0\x00' + self.assertEqual( + qubesadmin.tools.qvm_service.main(['some-vm', 'service1', + 'on'], + app=self.app), + 0) + self.assertAllCalled() + + def test_003_enable_opt(self): + self.app.expected_calls[ + ('dom0', 'admin.vm.List', None, None)] = \ + b'0\x00some-vm class=AppVM state=Running\n' + self.app.expected_calls[ + ('some-vm', 'admin.vm.feature.Set', + 'service.service1', b'1')] = b'0\x00' + self.assertEqual( + qubesadmin.tools.qvm_service.main(['--enable', 'some-vm', + 'service1'], + app=self.app), + 0) + self.assertAllCalled() + + @unittest.expectedFailure + def test_004_enable_opt_mixed(self): + self.app.expected_calls[ + ('dom0', 'admin.vm.List', None, None)] = \ + b'0\x00some-vm class=AppVM state=Running\n' + self.app.expected_calls[ + ('some-vm', 'admin.vm.feature.Set', + 'service.service1', b'1')] = b'0\x00' + with self.assertNotRaises(SystemExit): + self.assertEqual( + qubesadmin.tools.qvm_service.main( + ['some-vm', '--enable', 'service1'], + app=self.app), + 0) + self.assertAllCalled() + + def test_005_disable(self): + self.app.expected_calls[ + ('dom0', 'admin.vm.List', None, None)] = \ + b'0\x00some-vm class=AppVM state=Running\n' + self.app.expected_calls[ + ('some-vm', 'admin.vm.feature.Set', + 'service.service1', b'')] = b'0\x00' + self.assertEqual( + qubesadmin.tools.qvm_service.main( + ['some-vm', 'service1', 'off'], + app=self.app), + 0) + self.assertAllCalled() + + def test_006_disable_opt(self): + self.app.expected_calls[ + ('dom0', 'admin.vm.List', None, None)] = \ + b'0\x00some-vm class=AppVM state=Running\n' + self.app.expected_calls[ + ('some-vm', 'admin.vm.feature.Set', + 'service.service1', b'')] = b'0\x00' + with self.assertNotRaises(SystemExit): + self.assertEqual( + qubesadmin.tools.qvm_service.main( + ['--disable', 'some-vm', 'service1'], + app=self.app), + 0) + self.assertAllCalled() + + def test_007_get(self): + self.app.expected_calls[ + ('dom0', 'admin.vm.List', None, None)] = \ + b'0\x00some-vm class=AppVM state=Running\n' + self.app.expected_calls[ + ('some-vm', 'admin.vm.feature.Get', 'service.service3', None)] = \ + b'0\x00value2 with spaces' + with qubesadmin.tests.tools.StdoutBuffer() as stdout: + self.assertEqual( + qubesadmin.tools.qvm_service.main(['some-vm', 'service3'], + app=self.app), + 0) + self.assertEqual(stdout.getvalue(), + 'on\n') + self.assertAllCalled() + + def test_008_del(self): + self.app.expected_calls[ + ('dom0', 'admin.vm.List', None, None)] = \ + b'0\x00some-vm class=AppVM state=Running\n' + self.app.expected_calls[ + ('some-vm', 'admin.vm.feature.Remove', 'service.srv4', None)] = \ + b'0\x00' + with qubesadmin.tests.tools.StdoutBuffer() as stdout: + self.assertEqual( + qubesadmin.tools.qvm_service.main( + ['--unset', 'some-vm', 'srv4'], + app=self.app), + 0) + self.assertEqual(stdout.getvalue(), + '') + self.assertAllCalled() + + def test_009_del_legacy(self): + self.app.expected_calls[ + ('dom0', 'admin.vm.List', None, None)] = \ + b'0\x00some-vm class=AppVM state=Running\n' + self.app.expected_calls[ + ('some-vm', 'admin.vm.feature.Remove', 'service.srv4', None)] = \ + b'0\x00' + with qubesadmin.tests.tools.StdoutBuffer() as stdout: + self.assertEqual( + qubesadmin.tools.qvm_service.main( + ['--default', 'some-vm', 'srv4'], + app=self.app), + 0) + self.assertEqual(stdout.getvalue(), + '') + self.assertAllCalled() + + def test_010_set_invalid(self): + self.app.expected_calls[ + ('dom0', 'admin.vm.List', None, None)] = \ + b'0\x00some-vm class=AppVM state=Running\n' + with self.assertRaises(SystemExit): + qubesadmin.tools.qvm_service.main( + ['some-vm', 'service1', 'invalid-value'], + app=self.app), + self.assertAllCalled() diff --git a/qubesadmin/tests/tools/qvm_template_postprocess.py b/qubesadmin/tests/tools/qvm_template_postprocess.py index 78d8009..e2d74c9 100644 --- a/qubesadmin/tests/tools/qvm_template_postprocess.py +++ b/qubesadmin/tests/tools/qvm_template_postprocess.py @@ -243,7 +243,7 @@ class TC_00_qvm_template_postprocess(qubesadmin.tests.QubesTestCase): self.app.expected_calls[ ('test-vm', 'admin.vm.property.Reset', 'netvm', None)] = b'0\0' self.app.expected_calls[ - ('test-vm', 'admin.vm.feature.Set', 'qrexec', b'True')] = b'0\0' + ('test-vm', 'admin.vm.feature.Set', 'qrexec', b'1')] = b'0\0' self.app.expected_calls[ ('test-vm', 'admin.vm.Start', None, None)] = b'0\0' self.app.expected_calls[ @@ -293,7 +293,7 @@ class TC_00_qvm_template_postprocess(qubesadmin.tests.QubesTestCase): self.app.expected_calls[ ('test-vm', 'admin.vm.property.Reset', 'netvm', None)] = b'0\0' self.app.expected_calls[ - ('test-vm', 'admin.vm.feature.Set', 'qrexec', b'True')] = b'0\0' + ('test-vm', 'admin.vm.feature.Set', 'qrexec', b'1')] = b'0\0' self.app.expected_calls[ ('test-vm', 'admin.vm.Start', None, None)] = b'0\0' self.app.expected_calls[ diff --git a/qubesadmin/tools/qvm_ls.py b/qubesadmin/tools/qvm_ls.py index 10c7570..860b15c 100644 --- a/qubesadmin/tools/qvm_ls.py +++ b/qubesadmin/tools/qvm_ls.py @@ -351,7 +351,7 @@ Column('MEMORY', doc='Memory currently used by VM') Column('DISK', - attr=(lambda vm: vm.storage.get_disk_utilization() / 1024 / 1024), + attr=(lambda vm: vm.get_disk_utilization() // 1024 // 1024), doc='Total disk utilisation.') diff --git a/qubesadmin/tools/qvm_service.py b/qubesadmin/tools/qvm_service.py new file mode 100644 index 0000000..f30b5bb --- /dev/null +++ b/qubesadmin/tools/qvm_service.py @@ -0,0 +1,136 @@ +# coding=utf-8 +# +# The Qubes OS Project, https://www.qubes-os.org/ +# +# Copyright (C) 2010-2016 Joanna Rutkowska +# Copyright (C) 2016 Wojtek Porczyk +# Copyright (C) 2017 Marek Marczykowski-Górecki +# +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +'''qvm-service - Manage domain's services''' + +from __future__ import print_function + +import argparse +import sys + +import qubesadmin +import qubesadmin.exc +import qubesadmin.tools + +parser = qubesadmin.tools.QubesArgumentParser( + vmname_nargs=1, + argument_default=argparse.SUPPRESS, + description='manage domain\'s services') + +parser.add_argument('service', metavar='FEATURE', + action='store', nargs='?', + help='name of the feature') + +parser.add_argument('value', metavar='VALUE', + action='store', nargs='?', + help='new value of the service') + +parser.add_argument('--unset', '--default', '--delete', '-D', + dest='delete', default=False, + action='store_true', + help='unset service (default to VM preference)') + +parser.add_argument('--list', '-l', + dest='list', + action='store_true', + help='list services (default action)') + +parser.add_argument('--enable', '-e', + dest='value', + action='store_const', const='1', + help='enable service') + +parser.add_argument('--disable', '-d', + dest='value', + action='store_const', const='0', + help='disable service') + +def parse_bool(value): + '''Convert string value to bool according to well known representations + + It accepts (case-insensitive) ``'0'``, ``'no'`` and ``false`` as + :py:obj:`False` and ``'1'``, ``'yes'`` and ``'true'`` as + :py:obj:`True`. + ''' + if isinstance(value, str): + lcvalue = value.lower() + if lcvalue in ('0', 'no', 'false', 'off'): + return False + if lcvalue in ('1', 'yes', 'true', 'on'): + return True + raise qubesadmin.exc.QubesValueError( + 'Invalid literal for boolean value: {!r}'.format(value)) + + return bool(value) + + +def main(args=None, app=None): + '''Main routine of :program:`qvm-features`. + + :param list args: Optional arguments to override those delivered from \ + command line. + ''' + + args = parser.parse_args(args, app=app) + vm = args.domains[0] + + if not hasattr(args, 'service'): + if args.delete: + parser.error('--unset requires a feature') + + services = [(feat[len('service.'):], + 'on' if vm.features[feat] else 'off') for feat in + vm.features if feat.startswith('service.')] + qubesadmin.tools.print_table(services) + + elif args.delete: + if hasattr(args, 'value'): + parser.error('cannot both set and unset a value') + try: + del vm.features['service.' + args.service] + except KeyError: + pass + except qubesadmin.exc.QubesException as err: + parser.error_runtime(str(err)) + + elif hasattr(args, 'value'): + try: + vm.features['service.' + args.service] = parse_bool(args.value) + except qubesadmin.exc.QubesException as err: + parser.error_runtime(str(err)) + + else: + try: + print('on' if vm.features['service.' + args.service] else 'off') + return 0 + except KeyError: + return 1 + except qubesadmin.exc.QubesException as err: + parser.error_runtime(str(err)) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/qubesadmin/tools/qvm_shutdown.py b/qubesadmin/tools/qvm_shutdown.py index 5d75574..b6d24da 100644 --- a/qubesadmin/tools/qvm_shutdown.py +++ b/qubesadmin/tools/qvm_shutdown.py @@ -40,11 +40,6 @@ import qubesadmin.exc parser = qubesadmin.tools.QubesArgumentParser( description=__doc__, vmname_nargs='+') -parser.add_argument('--force', - action='store_true', default=False, - help='force operation, even if may damage other VMs (eg. shutdown of' - ' network provider)') - parser.add_argument('--wait', action='store_true', default=False, help='wait for the VMs to shut down') @@ -53,7 +48,7 @@ parser.add_argument('--timeout', action='store', type=float, default=60, help='timeout after which domains are killed when using --wait' - ' (default: %d)') + ' (default: %(default)d)') def main(args=None, app=None): # pylint: disable=missing-docstring diff --git a/qubesadmin/tools/qvm_start_gui.py b/qubesadmin/tools/qvm_start_gui.py index 411227f..cf2651b 100644 --- a/qubesadmin/tools/qvm_start_gui.py +++ b/qubesadmin/tools/qvm_start_gui.py @@ -347,6 +347,9 @@ parser = qubesadmin.tools.QubesArgumentParser( description='start GUI for qube(s)', vmname_nargs='*') parser.add_argument('--watch', action='store_true', help='Keep watching for further domains startups, must be used with --all') +parser.add_argument('--force-stubdomain', action='store_true', + help='Start GUI to stubdomain-emulated VGA, even if gui-agent is running ' + 'in the VM') parser.add_argument('--pidfile', action='store', default=pidfile_path, help='Pidfile path to create in --watch mode') parser.add_argument('--notify-monitor-layout', action='store_true', @@ -400,7 +403,8 @@ def main(args=None): tasks = [] for vm in args.domains: if vm.is_running(): - tasks.append(asyncio.ensure_future(launcher.start_gui(vm))) + tasks.append(asyncio.ensure_future(launcher.start_gui( + vm, force_stubdom=args.force_stubdomain))) if tasks: loop.run_until_complete(asyncio.wait(tasks)) loop.stop() diff --git a/qubesadmin/vm/__init__.py b/qubesadmin/vm/__init__.py index 11abc2a..e131dba 100644 --- a/qubesadmin/vm/__init__.py +++ b/qubesadmin/vm/__init__.py @@ -257,6 +257,10 @@ class QubesVM(qubesadmin.base.PropertyHolder): vm=self.name, vm_name=volname) return self._volumes + def get_disk_utilization(self): + '''Get total disk usage of the VM''' + return sum(vol.usage for vol in self.volumes.values()) + def run_service(self, service, **kwargs): '''Run service on this VM