From 5f7003da9967679158568679df2c856be3100b2b Mon Sep 17 00:00:00 2001 From: Piotr Bartman Date: Thu, 4 Jan 2024 11:25:42 +0100 Subject: [PATCH] q-dev: persistent -> required --- doc/manpages/qvm-device.rst | 10 ++- qubesadmin/app.py | 4 +- qubesadmin/backup/restore.py | 2 +- qubesadmin/devices.py | 65 +++++++++---------- qubesadmin/tests/app.py | 8 +-- .../tests/backup/backupcompatibility.py | 3 +- qubesadmin/tests/devices.py | 53 +++++++-------- qubesadmin/tests/tools/qvm_device.py | 12 ++-- qubesadmin/tests/tools/qvm_start.py | 14 ++-- qubesadmin/tools/qvm_device.py | 20 ++++-- qubesadmin/tools/qvm_start.py | 4 +- 11 files changed, 107 insertions(+), 88 deletions(-) diff --git a/doc/manpages/qvm-device.rst b/doc/manpages/qvm-device.rst index 2a6bab7ea..fc522701f 100644 --- a/doc/manpages/qvm-device.rst +++ b/doc/manpages/qvm-device.rst @@ -72,9 +72,17 @@ Attach the device with *DEVICE_ID* from *BACKEND_DOMAIN* to the domain *VMNAME* Alias for the `read-only=yes` option. If you specify both `--ro` and `--option read-only=no`, `--ro` takes precedence. +.. option:: --required, -r + + Assign device persistently which means it will be required to the qube's startup and then automatically attached. + .. option:: --persistent, -p - Attach device persistently, which means have it attached also after qube restart. + Alias for `--required` for backward compatibility. + +.. option:: --auto-attach, -a + + Assign the device to a qube. It will be automatically attached upon the qube's startup or connection. The device will not be automatically attached if it has been manually detached or is already attached to another qube. aliases: a, at diff --git a/qubesadmin/app.py b/qubesadmin/app.py index 987dbc5cd..0efb98871 100644 --- a/qubesadmin/app.py +++ b/qubesadmin/app.py @@ -543,12 +543,12 @@ def clone_vm(self, src_vm, new_name, new_cls=None, pool=None, pools=None, try: for devclass in src_vm.devices: for assignment in src_vm.devices[devclass].assignments( - persistent=True): + required=True): new_assignment = qubesadmin.devices.DeviceAssignment( backend_domain=assignment.backend_domain, ident=assignment.ident, options=assignment.options, - persistent=assignment.persistent) + required=assignment.required) dst_vm.devices[devclass].attach(new_assignment) except qubesadmin.exc.QubesException: if not ignore_errors: diff --git a/qubesadmin/backup/restore.py b/qubesadmin/backup/restore.py index 8eb4dd243..37686a097 100644 --- a/qubesadmin/backup/restore.py +++ b/qubesadmin/backup/restore.py @@ -2091,7 +2091,7 @@ def _restore_vms_metadata(self, restore_info): backend_domain=backend_domain, ident=ident, options=options, - persistent=True) + required=True) try: if not self.options.verify_only: new_vm.devices[bus].attach(assignment) diff --git a/qubesadmin/devices.py b/qubesadmin/devices.py index 5d3060457..d6b55a557 100644 --- a/qubesadmin/devices.py +++ b/qubesadmin/devices.py @@ -552,12 +552,11 @@ def __init__(self, backend_domain, devclass, ident, **kwargs): class DeviceAssignment(Device): """ Maps a device to a frontend_domain. """ - def __init__(self, backend_domain, ident, options=None, persistent=False, + def __init__(self, backend_domain, ident, options=None, frontend_domain=None, devclass=None, required=False, attach_automatically=False): super().__init__(backend_domain, ident, devclass) self.__options = options or {} - self.persistent = persistent self.__required = required self.__attach_automatically = attach_automatically self.__frontend_domain = frontend_domain @@ -565,12 +564,13 @@ def __init__(self, backend_domain, ident, options=None, persistent=False, def clone(self): """Clone object instance""" return self.__class__( - self.backend_domain, - self.ident, - self.options, - self.persistent, - self.frontend_domain, - self.devclass, + backend_domain=self.backend_domain, + ident=self.ident, + options=self.options, + required=self.required, + attach_automatically=self.attach_automatically, + frontend_domain=self.frontend_domain, + devclass=self.devclass, ) @property @@ -595,28 +595,24 @@ def required(self) -> bool: """ Is the presence of this device required for the domain to start? If yes, it will be attached automatically. - TODO: this possibly should not be available for usb device? or always False? - TODO: this is a reworking of the previously existing "persistent" attachment, split in two option """ - return self.persistent # TODO + return self.__required @required.setter def required(self, required: bool): - self.persistent = required # TODO + self.__required = required @property def attach_automatically(self) -> bool: """ Should this device automatically connect to the frontend domain when available and not connected to other qubes? - TODO: this possibly should not be available for usb device? or always False? - TODO: this is a reworking of the previously existing "persistent" attachment, split in two option """ - return self.persistent # TODO + return self.__attach_automatically @attach_automatically.setter def attach_automatically(self, attach_automatically: bool): - self.persistent = attach_automatically # TODO + self.__attach_automatically = attach_automatically @property def options(self) -> Dict[str, Any]: @@ -663,8 +659,10 @@ def attach(self, device_assignment): f"{device_assignment.devclass=}!={self._class=}") options = device_assignment.options.copy() - if device_assignment.persistent: - options['persistent'] = 'True' + if device_assignment.required: + options['required'] = 'True' + # if device_assignment.attach_automatically: + # options['attach_automatically'] = 'True' options_str = ' '.join('{}={}'.format(opt, val) for opt, val in sorted(options.items())) self._vm.qubesd_call(None, @@ -698,16 +696,17 @@ def detach(self, device_assignment): device_assignment.backend_domain, device_assignment.ident)) - def assignments(self, persistent=None): + def assignments(self, required=None): """List assignments for devices which are (or may be) attached to the vm. + # TODO: handle auto-attach Devices may be attached persistently (so they are included in :file:`qubes.xml`) or not. Device can also be in :file:`qubes.xml`, but be temporarily detached. - :param bool persistent: only include devices which are or are not - attached persistently. + :param bool required: only include devices which are or are not + required to start qube. """ assignments_str = self._vm.qubesd_call(None, @@ -719,28 +718,28 @@ def assignments(self, persistent=None): options = dict(opt_single.split('=', 1) for opt_single in options_all.split(' ') if opt_single) - dev_persistent = (options.pop('persistent', False) in + dev_required = (options.pop('required', False) in ['True', 'yes', True]) - if persistent is not None and dev_persistent != persistent: + dev_auto_attach = (options.pop('attach_automatically', False) in + ['True', 'yes', True]) # TODO + if required is not None and dev_required != required: continue backend_domain = self._vm.app.domains.get_blind(backend_domain) yield DeviceAssignment(backend_domain, ident, options, - persistent=dev_persistent, + required=dev_required, frontend_domain=self._vm, devclass=self._class) def attached(self): """List devices which are (or may be) attached to this vm """ - for assignment in self.assignments(): yield assignment.device - def persistent(self): + def required(self): """ Devices persistently attached and safe to access before libvirt bootstrap. """ - - for assignment in self.assignments(True): + for assignment in self.assignments(required=True): yield assignment.device def available(self): @@ -754,11 +753,11 @@ def available(self): expected_devclass=self._class, ) - def update_persistent(self, device, persistent): - """Update `persistent` flag of already attached device. + def update_required(self, device: DeviceInfo, required: bool): # TODO: update auto-attach + """Update `required` flag of already attached device. - :param DeviceInfo device: device for which change persistent flag - :param bool persistent: new persistent flag + :param DeviceInfo device: device for which change required flag + :param bool required: new required flag """ self._vm.qubesd_call(None, @@ -766,7 +765,7 @@ def update_persistent(self, device, persistent): self._class), '{!s}+{!s}'.format(device.backend_domain, device.ident), - str(persistent).encode('utf-8')) + str(required).encode('utf-8')) __iter__ = available diff --git a/qubesadmin/tests/app.py b/qubesadmin/tests/app.py index b99197220..b331f0aac 100644 --- a/qubesadmin/tests/app.py +++ b/qubesadmin/tests/app.py @@ -736,11 +736,11 @@ def test_043_clone_devices(self): self.app.expected_calls[ ('test-vm', 'admin.vm.device.pci.List', None, None)] = \ b'0\0test-vm2+dev1 ro=True\n' \ - b'test-vm3+dev2 persistent=True\n' + b'test-vm3+dev2 required=True\n' self.app.expected_calls[ ('new-name', 'admin.vm.device.pci.Attach', 'test-vm3+dev2', - b'persistent=True')] = b'0\0' + b'required=True')] = b'0\0' new_vm = self.app.clone_vm('test-vm', 'new-name') self.assertEqual(new_vm.name, 'new-name') @@ -772,11 +772,11 @@ def test_044_clone_devices_fail(self): self.app.expected_calls[ ('test-vm', 'admin.vm.device.pci.List', None, None)] = \ b'0\0test-vm2+dev1 ro=True\n' \ - b'test-vm3+dev2 persistent=True\n' + b'test-vm3+dev2 required=True\n' self.app.expected_calls[ ('new-name', 'admin.vm.device.pci.Attach', 'test-vm3+dev2', - b'persistent=True')] = \ + b'required=True')] = \ b'2\0QubesException\0\0something happened\0' self.app.expected_calls[ diff --git a/qubesadmin/tests/backup/backupcompatibility.py b/qubesadmin/tests/backup/backupcompatibility.py index 3be5f9257..c170ed086 100644 --- a/qubesadmin/tests/backup/backupcompatibility.py +++ b/qubesadmin/tests/backup/backupcompatibility.py @@ -1435,7 +1435,8 @@ def setup_expected_calls(self, parsed_qubes_xml, templates_map=None): for bus, devices in vm['devices'].items(): for (backend_domain, ident), options in devices.items(): all_options = options.copy() - all_options['persistent'] = True + all_options['required'] = True + # all_options['attach_automatically'] = True # TODO encoded_options = ' '.join('{}={}'.format(key, value) for key, value in all_options.items()).encode() self.app.expected_calls[ diff --git a/qubesadmin/tests/devices.py b/qubesadmin/tests/devices.py index 164da6f20..2deeb5332 100644 --- a/qubesadmin/tests/devices.py +++ b/qubesadmin/tests/devices.py @@ -41,7 +41,8 @@ def test_000_available(self): self.assertIsInstance(dev, qubesadmin.devices.DeviceInfo) self.assertEqual(dev.backend_domain, self.vm) self.assertEqual(dev.ident, 'dev1') - self.assertEqual(dev.description, '') + self.assertEqual( + dev.description, 'unknown test device (unknown vendor)') self.assertEqual(dev.data, {}) self.assertEqual(str(dev), 'test-vm:dev1') self.assertAllCalled() @@ -122,23 +123,23 @@ def test_021_attach_options(self): self.vm.devices['test'].attach(assign) self.assertAllCalled() - def test_022_attach_persistent(self): + def test_022_attach_required(self): self.app.expected_calls[ ('test-vm', 'admin.vm.device.test.Attach', 'test-vm2+dev1', - b'persistent=True')] = b'0\0' + b'required=True')] = b'0\0' assign = qubesadmin.devices.DeviceAssignment( self.app.domains['test-vm2'], 'dev1') - assign.persistent = True + assign.required = True self.vm.devices['test'].attach(assign) self.assertAllCalled() - def test_023_attach_persistent_options(self): + def test_023_attach_required_options(self): self.app.expected_calls[ ('test-vm', 'admin.vm.device.test.Attach', 'test-vm2+dev1', - b'persistent=True ro=True')] = b'0\0' + b'required=True ro=True')] = b'0\0' assign = qubesadmin.devices.DeviceAssignment( self.app.domains['test-vm2'], 'dev1') - assign.persistent = True + assign.required = True assign.options['ro'] = True self.vm.devices['test'].attach(assign) self.assertAllCalled() @@ -193,7 +194,7 @@ def test_041_assignments_options(self): self.app.expected_calls[ ('test-vm', 'admin.vm.device.test.List', None, None)] = \ b'0\0test-vm2+dev1 ro=True\n' \ - b'test-vm3+dev2 ro=False persistent=True\n' + b'test-vm3+dev2 ro=False required=True\n' assigns = list(self.vm.devices['test'].assignments()) self.assertEqual(len(assigns), 2) self.assertIsInstance(assigns[0], qubesadmin.devices.DeviceAssignment) @@ -203,7 +204,7 @@ def test_041_assignments_options(self): self.assertEqual(assigns[0].frontend_domain, self.app.domains['test-vm']) self.assertEqual(assigns[0].options, {'ro': 'True'}) - self.assertEqual(assigns[0].persistent, False) + self.assertEqual(assigns[0].required, False) self.assertEqual(assigns[0].devclass, 'test') self.assertIsInstance(assigns[1], qubesadmin.devices.DeviceAssignment) @@ -213,16 +214,16 @@ def test_041_assignments_options(self): self.assertEqual(assigns[1].frontend_domain, self.app.domains['test-vm']) self.assertEqual(assigns[1].options, {'ro': 'False'}) - self.assertEqual(assigns[1].persistent, True) + self.assertEqual(assigns[1].required, True) self.assertEqual(assigns[1].devclass, 'test') self.assertAllCalled() - def test_041_assignments_persistent(self): + def test_041_assignments_required(self): self.app.expected_calls[ ('test-vm', 'admin.vm.device.test.List', None, None)] = \ b'0\0test-vm2+dev1\n' \ - b'test-vm3+dev2 persistent=True\n' + b'test-vm3+dev2 required=True\n' assigns = list(self.vm.devices['test'].assignments(True)) self.assertEqual(len(assigns), 1) self.assertIsInstance(assigns[0], qubesadmin.devices.DeviceAssignment) @@ -232,15 +233,15 @@ def test_041_assignments_persistent(self): self.assertEqual(assigns[0].frontend_domain, self.app.domains['test-vm']) self.assertEqual(assigns[0].options, {}) - self.assertEqual(assigns[0].persistent, True) + self.assertEqual(assigns[0].required, True) self.assertEqual(assigns[0].devclass, 'test') self.assertAllCalled() - def test_042_assignments_non_persistent(self): + def test_042_assignments_non_required(self): self.app.expected_calls[ ('test-vm', 'admin.vm.device.test.List', None, None)] = \ b'0\0test-vm2+dev1\n' \ - b'test-vm3+dev2 persistent=True\n' + b'test-vm3+dev2 required=True\n' assigns = list(self.vm.devices['test'].assignments(False)) self.assertEqual(len(assigns), 1) self.assertIsInstance(assigns[0], qubesadmin.devices.DeviceAssignment) @@ -250,18 +251,18 @@ def test_042_assignments_non_persistent(self): self.assertEqual(assigns[0].frontend_domain, self.app.domains['test-vm']) self.assertEqual(assigns[0].options, {}) - self.assertEqual(assigns[0].persistent, False) + self.assertEqual(assigns[0].required, False) self.assertAllCalled() - def test_050_persistent(self): + def test_050_required(self): self.app.expected_calls[ ('test-vm', 'admin.vm.device.test.List', None, None)] = \ b'0\0test-vm2+dev1\n' \ - b'test-vm3+dev2 persistent=True\n' + b'test-vm3+dev2 required=True\n' self.app.expected_calls[ ('test-vm3', 'admin.vm.device.test.Available', None, None)] = \ b'0\0dev2\n' - devs = list(self.vm.devices['test'].persistent()) + devs = list(self.vm.devices['test'].required()) self.assertEqual(len(devs), 1) self.assertIsInstance(devs[0], qubesadmin.devices.DeviceInfo) self.assertEqual(devs[0].backend_domain, self.app.domains['test-vm3']) @@ -272,7 +273,7 @@ def test_060_attached(self): self.app.expected_calls[ ('test-vm', 'admin.vm.device.test.List', None, None)] = \ b'0\0test-vm2+dev1\n' \ - b'test-vm3+dev2 persistent=True\n' + b'test-vm3+dev2 required=True\n' self.app.expected_calls[ ('test-vm2', 'admin.vm.device.test.Available', None, None)] = \ b'0\0dev1\n' @@ -289,22 +290,22 @@ def test_060_attached(self): self.assertEqual(devs[1].ident, 'dev2') self.assertAllCalled() - def test_070_update_persistent(self): + def test_070_update_required(self): self.app.expected_calls[ ('test-vm', 'admin.vm.device.test.Set.persistent', 'test-vm2+dev1', b'True')] = b'0\0' dev = qubesadmin.devices.DeviceInfo( - self.app.domains['test-vm2'], 'test', 'dev1') - self.vm.devices['test'].update_persistent(dev, True) + self.app.domains['test-vm2'], devclass='test', ident='dev1') + self.vm.devices['test'].update_required(dev, True) self.assertAllCalled() - def test_071_update_persistent_false(self): + def test_071_update_required_false(self): self.app.expected_calls[ ('test-vm', 'admin.vm.device.test.Set.persistent', 'test-vm2+dev1', b'False')] = b'0\0' dev = qubesadmin.devices.DeviceInfo( - self.app.domains['test-vm2'], 'test', 'dev1') - self.vm.devices['test'].update_persistent(dev, False) + self.app.domains['test-vm2'], devclass='test', ident='dev1') + self.vm.devices['test'].update_required(dev, False) self.assertAllCalled() def test_072_list(self): diff --git a/qubesadmin/tests/tools/qvm_device.py b/qubesadmin/tests/tools/qvm_device.py index 0bf4f77bb..2b0964653 100644 --- a/qubesadmin/tests/tools/qvm_device.py +++ b/qubesadmin/tests/tools/qvm_device.py @@ -72,9 +72,9 @@ def test_000_list_all(self): 'test-vm2:dev2 Description here2'] ) - def test_001_list_persistent_attach(self): - ''' Attach the device exposed by the `vm1` to the `vm3` persistently. - ''' + def test_001_list_required_attach(self): # TODO + """ Attach the device exposed by the `vm1` to the `vm3` persistently. + """ self.app.expected_calls[('test-vm2', 'admin.vm.device.test.Available', None, None)] = \ b'0\0dev2 description=Description here2\n' @@ -87,7 +87,7 @@ def test_001_list_persistent_attach(self): None, None)] = b'0\0' self.app.expected_calls[('test-vm3', 'admin.vm.device.test.List', None, None)] = \ - b'0\0test-vm1+dev1 persistent=True\n' + b'0\0test-vm1+dev1 required=True\n' with qubesadmin.tests.tools.StdoutBuffer() as buf: qubesadmin.tools.qvm_device.main( @@ -141,10 +141,10 @@ def test_011_attach_options(self): app=self.app) self.assertAllCalled() - def test_011_attach_persistent(self): + def test_011_attach_required(self): ''' Test attach action ''' self.app.expected_calls[('test-vm2', 'admin.vm.device.test.Attach', - 'test-vm1+dev1', b'persistent=True')] = b'0\0' + 'test-vm1+dev1', b'required=True')] = b'0\0' qubesadmin.tools.qvm_device.main( ['test', 'attach', '-p', 'test-vm2', 'test-vm1:dev1'], app=self.app) diff --git a/qubesadmin/tests/tools/qvm_start.py b/qubesadmin/tests/tools/qvm_start.py index 93e790517..8bcdaa248 100644 --- a/qubesadmin/tests/tools/qvm_start.py +++ b/qubesadmin/tests/tools/qvm_start.py @@ -85,7 +85,7 @@ def test_010_drive_cdrom(self): ('some-vm', 'admin.vm.Start', None, None)] = b'0\x00' self.app.expected_calls[ ('some-vm', 'admin.vm.device.block.Attach', 'dom0+sr0', - b'devtype=cdrom persistent=True read-only=True')] = b'0\x00' + b'devtype=cdrom required=True read-only=True')] = b'0\x00' self.app.expected_calls[ ('some-vm', 'admin.vm.device.block.Set.persistent', 'dom0+sr0', b'False')] = b'0\x00' @@ -108,7 +108,7 @@ def test_011_drive_disk(self): ('some-vm', 'admin.vm.Start', None, None)] = b'0\x00' self.app.expected_calls[ ('some-vm', 'admin.vm.device.block.Attach', 'dom0+sdb1', - b'devtype=disk persistent=True read-only=False')] = b'0\x00' + b'devtype=disk required=True read-only=False')] = b'0\x00' self.app.expected_calls[ ('some-vm', 'admin.vm.device.block.Set.persistent', 'dom0+sdb1', b'False')] = b'0\x00' @@ -131,7 +131,7 @@ def test_012_drive_disk(self): ('some-vm', 'admin.vm.Start', None, None)] = b'0\x00' self.app.expected_calls[ ('some-vm', 'admin.vm.device.block.Attach', 'dom0+sdb1', - b'devtype=disk persistent=True read-only=False')] = b'0\x00' + b'devtype=disk required=True read-only=False')] = b'0\x00' self.app.expected_calls[ ('some-vm', 'admin.vm.device.block.Set.persistent', 'dom0+sdb1', b'False')] = b'0\x00' @@ -155,7 +155,7 @@ def test_013_drive_loop_local(self, mock_subprocess): ('some-vm', 'admin.vm.Start', None, None)] = b'0\x00' self.app.expected_calls[ ('some-vm', 'admin.vm.device.block.Attach', 'dom0+loop12', - b'devtype=cdrom persistent=True read-only=True')] = b'0\x00' + b'devtype=cdrom required=True read-only=True')] = b'0\x00' self.app.expected_calls[ ('some-vm', 'admin.vm.device.block.Set.persistent', 'dom0+loop12', b'False')] = b'0\x00' @@ -184,7 +184,7 @@ def test_014_drive_loop_remote(self): ('some-vm', 'admin.vm.Start', None, None)] = b'0\x00' self.app.expected_calls[ ('some-vm', 'admin.vm.device.block.Attach', 'other-vm+loop7', - b'devtype=cdrom persistent=True read-only=True')] = b'0\x00' + b'devtype=cdrom required=True read-only=True')] = b'0\x00' self.app.expected_calls[ ('some-vm', 'admin.vm.device.block.Set.persistent', 'other-vm+loop7', @@ -216,7 +216,7 @@ def test_015_drive_failed_start(self): b'0\x00power_state=Halted' self.app.expected_calls[ ('some-vm', 'admin.vm.device.block.Attach', 'other-vm+loop7', - b'devtype=cdrom persistent=True read-only=True')] = b'0\x00' + b'devtype=cdrom required=True read-only=True')] = b'0\x00' self.app.expected_calls[ ('some-vm', 'admin.vm.Start', None, None)] = \ b'2\x00QubesException\x00\x00An error occurred\x00' @@ -240,7 +240,7 @@ def test_016_drive_failed_attach(self): b'0\x00power_state=Halted' self.app.expected_calls[ ('some-vm', 'admin.vm.device.block.Attach', 'other-vm+loop7', - b'devtype=cdrom persistent=True read-only=True')] = \ + b'devtype=cdrom required=True read-only=True')] = \ b'2\x00QubesException\x00\x00An error occurred\x00' retcode = qubesadmin.tools.qvm_start.main([ '--cdrom=other-vm:loop7', diff --git a/qubesadmin/tools/qvm_device.py b/qubesadmin/tools/qvm_device.py index fb88c1497..ab1e0daf8 100644 --- a/qubesadmin/tools/qvm_device.py +++ b/qubesadmin/tools/qvm_device.py @@ -134,7 +134,7 @@ def attach_device(args): options = dict(opt.split('=', 1) for opt in args.option or []) if args.ro: options['read-only'] = 'yes' - device_assignment.persistent = args.persistent + device_assignment.required = args.required device_assignment.options = options vm.devices[args.devclass].attach(device_assignment) @@ -286,11 +286,21 @@ def get_parser(device_class=None): help="Attach device read-only (alias for " "read-only=yes option, " "takes precedence)") - attach_parser.add_argument('--persistent', '-p', action='store_true', + attach_parser.add_argument('--persistent', '--required', '-p', '-r', + dest='required', + action='store_true', default=False, - help="Attach device persistently (so it will " - "be automatically " - "attached at qube startup)") + help="Assign device persistently (so it will " + "be required to the qube's startup and then" + " automatically attached)") + attach_parser.add_argument('--auto-attach', '-a', action='store_true', + default=False, + help="Assign the device to a qube. It will be " + "automatically attached upon the qube's " + "startup or connection. The device will not" + " be automatically attached if it has been " + "manually detached or is already attached " + "to another qube.") attach_parser.set_defaults(func=attach_device) detach_parser.set_defaults(func=detach_device) diff --git a/qubesadmin/tools/qvm_start.py b/qubesadmin/tools/qvm_start.py index 877b9d0ab..f32fa6c2a 100644 --- a/qubesadmin/tools/qvm_start.py +++ b/qubesadmin/tools/qvm_start.py @@ -159,7 +159,7 @@ def get_drive_assignment(app, drive_str): backend_domain, ident, options=options, - persistent=True) + required=True) return assignment @@ -196,7 +196,7 @@ def main(args=None, app=None): if drive_assignment: # don't reconnect this device after VM reboot - domain.devices['block'].update_persistent( + domain.devices['block'].update_required( drive_assignment.device, False) except (IOError, OSError, qubesadmin.exc.QubesException, ValueError) as e: