From 1ec3549b241747583269a89932f22e06e3454fa4 Mon Sep 17 00:00:00 2001 From: Piotr Bartman Date: Tue, 23 Apr 2024 02:07:55 +0200 Subject: [PATCH] q-dev: update and fix tests for backup --- qubesadmin/backup/core2.py | 9 ++++---- qubesadmin/backup/core3.py | 4 ++++ qubesadmin/backup/restore.py | 16 +++++++++---- qubesadmin/device_protocol.py | 14 +++++++---- .../tests/backup/backupcompatibility.py | 23 +++++++++---------- 5 files changed, 40 insertions(+), 26 deletions(-) diff --git a/qubesadmin/backup/core2.py b/qubesadmin/backup/core2.py index 84850b21d..eed3d1ca9 100644 --- a/qubesadmin/backup/core2.py +++ b/qubesadmin/backup/core2.py @@ -336,11 +336,10 @@ def import_core2_vm(self, element): if pcidevs: pcidevs = ast.literal_eval(pcidevs) for pcidev in pcidevs: - if not pci_strictreset: - vm.devices['pci'][('dom0', pcidev.replace(':', '_'))] = { - 'no-strict-reset': True} - else: - vm.devices['pci'][('dom0', pcidev.replace(':', '_'))] = {} + ident = pcidev.replace(':', '_') + options = {'no-strict-reset': True} if not pci_strictreset else {} + options['required'] = True + vm.devices['pci'][('dom0', ident)] = options def load(self): with open(self.store, encoding='utf-8') as fh: diff --git a/qubesadmin/backup/core3.py b/qubesadmin/backup/core3.py index 6f495c426..a9dac95a6 100644 --- a/qubesadmin/backup/core3.py +++ b/qubesadmin/backup/core3.py @@ -26,6 +26,8 @@ import qubesadmin.backup import qubesadmin.firewall +from qubesadmin import device_protocol + class Core3VM(qubesadmin.backup.BackupVM): '''VM object''' @@ -125,6 +127,8 @@ def import_core3_vm(self, element): for opt_node in node.findall('./option'): opt_name = opt_node.get('name') options[opt_name] = opt_node.text + options['required'] = device_protocol.qbool( + node.get('required', 'yes')) vm.devices[bus_name][(backend_domain, ident)] = options # extract base properties diff --git a/qubesadmin/backup/restore.py b/qubesadmin/backup/restore.py index e03ec5f53..6fb8a6edc 100644 --- a/qubesadmin/backup/restore.py +++ b/qubesadmin/backup/restore.py @@ -2087,16 +2087,24 @@ def _restore_vms_metadata(self, restore_info): for bus in vm.devices: for backend_domain, ident in vm.devices[bus]: options = vm.devices[bus][(backend_domain, ident)] + if 'required' in options: + required = options['required'] + del options['required'] + else: + required = False assignment = DeviceAssignment( - backend_domain=backend_domain, + backend_domain=self.app.domains[backend_domain], ident=ident, + devclass=bus, options=options, - attach_automatically=True) + attach_automatically=True, + required=required, + ) try: if not self.options.verify_only: - new_vm.devices[bus].attach(assignment) + new_vm.devices[bus].assign(assignment) except Exception as err: # pylint: disable=broad-except - self.log.error('Error attaching device %s:%s to %s: %s', + self.log.error('Error assigning device %s:%s to %s: %s', bus, ident, vm.name, err) # Set VM dependencies - only non-default setting diff --git a/qubesadmin/device_protocol.py b/qubesadmin/device_protocol.py index dcf5329b1..0858d4626 100644 --- a/qubesadmin/device_protocol.py +++ b/qubesadmin/device_protocol.py @@ -87,16 +87,20 @@ def __hash__(self): return hash((str(self.backend_domain), self.ident)) def __eq__(self, other): - return ( - self.backend_domain == other.backend_domain and - self.ident == other.ident - ) + if isinstance(other, Device): + return ( + self.backend_domain == other.backend_domain and + self.ident == other.ident + ) + raise TypeError(f"Comparing instances of 'Device' and '{type(other)}' " + "is not supported") def __lt__(self, other): if isinstance(other, Device): return (self.backend_domain.name, self.ident) < \ (other.backend_domain.name, other.ident) - raise NotImplementedError() + raise TypeError(f"Comparing instances of 'Device' and '{type(other)}' " + "is not supported") def __repr__(self): return "[%s]:%s" % (self.backend_domain, self.ident) diff --git a/qubesadmin/tests/backup/backupcompatibility.py b/qubesadmin/tests/backup/backupcompatibility.py index c170ed086..c629247c9 100644 --- a/qubesadmin/tests/backup/backupcompatibility.py +++ b/qubesadmin/tests/backup/backupcompatibility.py @@ -184,8 +184,8 @@ 'provides_network': True}, 'devices': { 'pci': { - ('dom0', '02_00.0'): {}, - ('dom0', '03_00.0'): {}, + ('dom0', '02_00.0'): {'required': True}, + ('dom0', '03_00.0'): {'required': True}, } }, 'tags': set(), @@ -371,8 +371,8 @@ 'provides_network': True}, 'devices': { 'pci': { - ('dom0', '02_00.0'): {}, - ('dom0', '03_00.0'): {}, + ('dom0', '02_00.0'): {'required': True}, + ('dom0', '03_00.0'): {'required': True}, } }, 'tags': set(), @@ -490,7 +490,7 @@ 'provides_network': 'True'}, 'devices': { 'pci': { - ('dom0', '02_00.0'): {}, + ('dom0', '02_00.0'): {'required': True}, } }, 'tags': set(), @@ -704,7 +704,7 @@ }, 'devices': { 'pci': { - ('dom0', '03_00.0'): {}, + ('dom0', '03_00.0'): {'required': True}, } }, 'tags': set(), @@ -1434,13 +1434,12 @@ 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['required'] = True - # all_options['attach_automatically'] = True # TODO - encoded_options = ' '.join('{}={}'.format(key, value) for - key, value in all_options.items()).encode() + encoded_options = \ + (f"required='yes' attach_automatically='yes' " + f"ident='{ident}' devclass='{bus}' backend_domain='{backend_domain}'" + f" frontend_domain='{name}'".encode()) self.app.expected_calls[ - (name, 'admin.vm.device.{}.Attach'.format(bus), + (name, 'admin.vm.device.{}.Assign'.format(bus), '{}+{}'.format(backend_domain, ident), encoded_options)] = b'0\0'