Skip to content

Commit 1d0b2ad

Browse files
committed
q-dev: assignment
1 parent 167ec8d commit 1d0b2ad

File tree

7 files changed

+76
-87
lines changed

7 files changed

+76
-87
lines changed

qubes/device_protocol.py

+19-27
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,13 @@ def __init__(self, backend_domain, ident, *, devclass, **kwargs):
792792
super().__init__(port, **kwargs)
793793

794794

795+
class AssignmentMode(Enum):
796+
MANUAL = "manual"
797+
ASK = "ask-to-attach"
798+
AUTO = "auto-attach"
799+
REQUIRED = "required"
800+
801+
795802
class DeviceAssignment(Port):
796803
""" Maps a device to a frontend_domain.
797804
@@ -812,29 +819,20 @@ class DeviceAssignment(Port):
812819
and required to start domain.
813820
"""
814821

815-
class AssignmentType(Enum):
816-
MANUAL = 0
817-
ASK = 1
818-
AUTO = 2
819-
REQUIRED = 3
820-
821822
def __init__(
822823
self,
823824
port: Port,
825+
device_id=None,
824826
frontend_domain=None,
825827
options=None,
826-
required=False,
827-
attach_automatically=False
828+
mode: Union[str, AssignmentMode] = "manual",
828829
):
829830
super().__init__(port.backend_domain, port.ident, port.devclass)
830831
self.__options = options or {}
831-
if required:
832-
assert attach_automatically
833-
self.type = DeviceAssignment.AssignmentType.REQUIRED
834-
elif attach_automatically:
835-
self.type = DeviceAssignment.AssignmentType.AUTO
832+
if isinstance(mode, AssignmentMode):
833+
self.mode = mode
836834
else:
837-
self.type = DeviceAssignment.AssignmentType.MANUAL
835+
self.mode = AssignmentMode(mode)
838836
self.frontend_domain = frontend_domain
839837

840838
def clone(self, **kwargs):
@@ -885,26 +883,26 @@ def required(self) -> bool:
885883
Is the presence of this device required for the domain to start? If yes,
886884
it will be attached automatically.
887885
"""
888-
return self.type == DeviceAssignment.AssignmentType.REQUIRED
886+
return self.mode == AssignmentMode.REQUIRED
889887

890888
@required.setter
891889
def required(self, required: bool):
892-
self.type = DeviceAssignment.AssignmentType.REQUIRED
890+
self.mode = AssignmentMode.REQUIRED
893891

894892
@property
895893
def attach_automatically(self) -> bool:
896894
"""
897895
Should this device automatically connect to the frontend domain when
898896
available and not connected to other qubes?
899897
"""
900-
return self.type in (
901-
DeviceAssignment.AssignmentType.AUTO,
902-
DeviceAssignment.AssignmentType.REQUIRED
898+
return self.mode in (
899+
AssignmentMode.AUTO,
900+
AssignmentMode.REQUIRED
903901
)
904902

905903
@attach_automatically.setter
906904
def attach_automatically(self, attach_automatically: bool):
907-
self.type = DeviceAssignment.AssignmentType.AUTO
905+
self.mode = AssignmentMode.AUTO
908906

909907
@property
910908
def options(self) -> Dict[str, Any]:
@@ -923,9 +921,7 @@ def serialize(self) -> bytes:
923921
properties = b' '.join(
924922
self.pack_property(key, value)
925923
for key, value in (
926-
('required', 'yes' if self.required else 'no'),
927-
('attach_automatically',
928-
'yes' if self.attach_automatically else 'no'),
924+
('mode', self.mode.value),
929925
('ident', self.ident),
930926
('devclass', self.devclass)))
931927

@@ -973,8 +969,4 @@ def _deserialize(
973969
del properties['ident']
974970
del properties['devclass']
975971

976-
properties['attach_automatically'] = qbool(
977-
properties.get('attach_automatically', 'no'))
978-
properties['required'] = qbool(properties.get('required', 'no'))
979-
980972
return cls(expected_port, **properties)

qubes/devices.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,8 @@ async def assign(self, assignment: DeviceAssignment):
229229
device = assignment.device
230230
if device in self.get_assigned_devices():
231231
raise DeviceAlreadyAssigned(
232-
'device {!s} of class {} already assigned to {!s}'.format(
233-
device, self._bus, self._vm))
232+
'{} device {!s} already assigned to {!s}'.format(
233+
self._bus, device, self._vm))
234234

235235
self._set.add(assignment)
236236

@@ -355,8 +355,7 @@ def get_attached_devices(self) -> Iterable[DeviceAssignment]:
355355
),
356356
frontend_domain=self._vm,
357357
options=options,
358-
attach_automatically=False,
359-
required=False,
358+
mode='manual',
360359
)
361360

362361
def get_assigned_devices(

qubes/tests/api_admin.py

+15-25
Original file line numberDiff line numberDiff line change
@@ -1787,7 +1787,7 @@ def test_462_vm_device_available_invalid(self):
17871787
def test_470_vm_device_list_assigned(self):
17881788
assignment = qubes.device_protocol.DeviceAssignment(
17891789
qubes.device_protocol.Port(self.vm, '1234', 'test'),
1790-
attach_automatically=True, required=True)
1790+
mode='required')
17911791
self.loop.run_until_complete(
17921792
self.vm.devices['testclass'].assign(assignment))
17931793
value = self.call_mgmt_func(b'admin.vm.device.testclass.Assigned',
@@ -1800,12 +1800,11 @@ def test_470_vm_device_list_assigned(self):
18001800
def test_471_vm_device_list_assigned_options(self):
18011801
assignment = qubes.device_protocol.DeviceAssignment(
18021802
qubes.device_protocol.Port(self.vm, '1234', 'test'),
1803-
attach_automatically=True, required=True, options={'opt1': 'value'})
1803+
mode='required', options={'opt1': 'value'})
18041804
self.loop.run_until_complete(
18051805
self.vm.devices['testclass'].assign(assignment))
18061806
assignment = qubes.device_protocol.DeviceAssignment(
1807-
self.vm, '4321', 'test',
1808-
attach_automatically=True, required=True)
1807+
self.vm, '4321', 'test', mode='required')
18091808
self.loop.run_until_complete(
18101809
self.vm.devices['testclass'].assign(assignment))
18111810
value = self.call_mgmt_func(b'admin.vm.device.testclass.Assigned',
@@ -1838,12 +1837,12 @@ def test_472_vm_device_list_attached(self):
18381837
def test_473_vm_device_list_assigned_specific(self):
18391838
assignment = qubes.device_protocol.DeviceAssignment(
18401839
qubes.device_protocol.Port(self.vm, '1234', 'test'),
1841-
attach_automatically=True, required=True)
1840+
mode='required')
18421841
self.loop.run_until_complete(
18431842
self.vm.devices['testclass'].assign(assignment))
18441843
assignment = qubes.device_protocol.DeviceAssignment(
18451844
qubes.device_protocol.Port(self.vm, '4321', 'test'),
1846-
attach_automatically=True, required=True)
1845+
mode='required')
18471846
self.loop.run_until_complete(
18481847
self.vm.devices['testclass'].assign(assignment))
18491848
value = self.call_mgmt_func(b'admin.vm.device.testclass.Assigned',
@@ -2027,8 +2026,7 @@ def test_488_vm_device_assign_options(self):
20272026
def test_490_vm_device_unassign_from_running(self):
20282027
assignment = qubes.device_protocol.DeviceAssignment(
20292028
qubes.device_protocol.Port(self.vm, '1234', 'test'),
2030-
attach_automatically=True, required=False,
2031-
options={'opt1': 'value'})
2029+
mode='auto-attach', options={'opt1': 'value'})
20322030
self.loop.run_until_complete(
20332031
self.vm.devices['testclass'].assign(assignment))
20342032
mock_action = unittest.mock.Mock()
@@ -2048,8 +2046,7 @@ def test_490_vm_device_unassign_from_running(self):
20482046
def test_491_vm_device_unassign_required_from_running(self):
20492047
assignment = qubes.device_protocol.DeviceAssignment(
20502048
qubes.device_protocol.Port(self.vm, '1234', 'test'),
2051-
attach_automatically=True, required=True,
2052-
options={'opt1': 'value'})
2049+
mode='required', options={'opt1': 'value'})
20532050
self.loop.run_until_complete(
20542051
self.vm.devices['testclass'].assign(assignment))
20552052
mock_action = unittest.mock.Mock()
@@ -2070,8 +2067,7 @@ def test_491_vm_device_unassign_required_from_running(self):
20702067
def test_492_vm_device_unassign_from_halted(self):
20712068
assignment = qubes.device_protocol.DeviceAssignment(
20722069
qubes.device_protocol.Port(self.vm, '1234', 'test'),
2073-
attach_automatically=True, required=False,
2074-
options={'opt1': 'value'})
2070+
mode='required', options={'opt1': 'value'})
20752071
self.loop.run_until_complete(
20762072
self.vm.devices['testclass'].assign(assignment))
20772073
mock_action = unittest.mock.Mock()
@@ -2089,8 +2085,7 @@ def test_492_vm_device_unassign_from_halted(self):
20892085
def test_493_vm_device_unassign_required_from_halted(self):
20902086
assignment = qubes.device_protocol.DeviceAssignment(
20912087
qubes.device_protocol.Port(self.vm, '1234', 'test'),
2092-
attach_automatically=True, required=True,
2093-
options={'opt1': 'value'})
2088+
mode='required', options={'opt1': 'value'})
20942089
self.loop.run_until_complete(
20952090
self.vm.devices['testclass'].assign(assignment))
20962091
mock_action = unittest.mock.Mock()
@@ -2111,8 +2106,7 @@ def test_494_vm_device_unassign_attached(self):
21112106
self.device_list_single_attached_testclass)
21122107
assignment = qubes.device_protocol.DeviceAssignment(
21132108
qubes.device_protocol.Port(self.vm, '1234', 'test'),
2114-
attach_automatically=True, required=False,
2115-
options={'opt1': 'value'})
2109+
mode='auto-attach', options={'opt1': 'value'})
21162110
self.loop.run_until_complete(
21172111
self.vm.devices['testclass'].assign(assignment))
21182112
mock_action = unittest.mock.Mock()
@@ -2199,7 +2193,7 @@ def test_502_vm_remove_attached(self, mock_rmtree, mock_remove):
21992193
self.setup_for_clone()
22002194
assignment = qubes.device_protocol.DeviceAssignment(
22012195
qubes.device_protocol.Port(self.vm, '1234', 'test'),
2202-
attach_automatically=True, required=True)
2196+
mode='required')
22032197
self.loop.run_until_complete(
22042198
self.vm2.devices['testclass'].assign(assignment))
22052199

@@ -2919,8 +2913,7 @@ def test_642_vm_create_disposable_not_allowed(self, storage_mock):
29192913
def test_650_vm_device_set_required_true(self):
29202914
assignment = qubes.device_protocol.DeviceAssignment(
29212915
qubes.device_protocol.Port(self.vm, '1234', 'test'),
2922-
attach_automatically=True, required=False,
2923-
options={'opt1': 'value'})
2916+
mode='auto-attach', options={'opt1': 'value'})
29242917
self.loop.run_until_complete(
29252918
self.vm.devices['testclass'].assign(assignment))
29262919
mock_action = unittest.mock.Mock()
@@ -2950,8 +2943,7 @@ def test_650_vm_device_set_required_true(self):
29502943
def test_651_vm_device_set_required_false(self):
29512944
assignment = qubes.device_protocol.DeviceAssignment(
29522945
qubes.device_protocol.Port(self.vm, '1234', 'test'),
2953-
attach_automatically=True, required=True,
2954-
options={'opt1': 'value'})
2946+
mode='required', options={'opt1': 'value'})
29552947
self.loop.run_until_complete(
29562948
self.vm.devices['testclass'].assign(assignment))
29572949
mock_action = unittest.mock.Mock()
@@ -2981,8 +2973,7 @@ def test_651_vm_device_set_required_false(self):
29812973
def test_652_vm_device_set_required_true_unchanged(self):
29822974
assignment = qubes.device_protocol.DeviceAssignment(
29832975
qubes.device_protocol.Port(self.vm, '1234', 'test'),
2984-
attach_automatically=True, required=True,
2985-
options={'opt1': 'value'})
2976+
mode='required', options={'opt1': 'value'})
29862977
self.loop.run_until_complete(
29872978
self.vm.devices['testclass'].assign(assignment))
29882979
with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
@@ -3000,8 +2991,7 @@ def test_652_vm_device_set_required_true_unchanged(self):
30002991
def test_653_vm_device_set_required_false_unchanged(self):
30012992
assignment = qubes.device_protocol.DeviceAssignment(
30022993
qubes.device_protocol.Port(self.vm, '1234', 'test'),
3003-
attach_automatically=True, required=False,
3004-
options={'opt1': 'value'})
2994+
mode='auto-attach', options={'opt1': 'value'})
30052995
self.loop.run_until_complete(
30062996
self.vm.devices['testclass'].assign(assignment))
30072997
with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,

qubes/tests/devices.py

+5-13
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,7 @@ def setUp(self):
9090
self.app.domains['vm'] = self.emitter
9191
self.device = self.emitter.device
9292
self.collection = self.emitter.devices['testclass']
93-
self.assignment = DeviceAssignment(
94-
self.device,
95-
attach_automatically=True,
96-
required=True,
97-
)
93+
self.assignment = DeviceAssignment(self.device, mode='required')
9894

9995
def attach(self):
10096
self.emitter.running = True
@@ -339,8 +335,7 @@ def test_000_init(self):
339335

340336
def test_001_missing(self):
341337
device = TestDevice(self.emitter.app.domains['vm'], 'testdev')
342-
assignment = DeviceAssignment(
343-
device, attach_automatically=True, required=True)
338+
assignment = DeviceAssignment(device, mode='required')
344339
self.loop.run_until_complete(
345340
self.manager['testclass'].assign(assignment))
346341
self.assertEqual(
@@ -532,8 +527,7 @@ def test_011_serialize_required(self):
532527
ident="1-1.1.1",
533528
devclass="bus",
534529
),
535-
attach_automatically=True,
536-
required=True,
530+
mode='required',
537531
)
538532
actual = assignment.serialize()
539533
expected = (
@@ -603,8 +597,7 @@ def test_020_deserialize(self):
603597
devclass="bus",
604598
),
605599
frontend_domain=self.vm,
606-
attach_automatically=True,
607-
required=False,
600+
mode='auto-attach',
608601
options={'read-only': 'yes'},
609602
)
610603

@@ -642,8 +635,7 @@ def test_030_serialize_and_deserialize(self):
642635
devclass="bus",
643636
),
644637
frontend_domain=self.vm,
645-
attach_automatically=True,
646-
required=False,
638+
mode='auto-attach',
647639
options={'read-only': 'yes'},
648640
)
649641
serialized = expected.serialize()

qubes/tests/integ/devices_pci.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,10 @@ def setUp(self):
3939
pcidev = os.environ['QUBES_TEST_PCIDEV']
4040
self.dev = self.app.domains[0].devices['pci'][pcidev]
4141
self.assignment = DeviceAssignment(
42-
self.dev, attach_automatically=True
42+
self.dev, mode='auto-attach'
4343
)
4444
self.required_assignment = DeviceAssignment(
45-
self.dev,
46-
attach_automatically=True,
47-
required=True,
45+
self.dev, mode='required',
4846
)
4947
if isinstance(self.dev, qubes.device_protocol.UnknownDevice):
5048
self.skipTest('Specified device {} does not exists'.format(pcidev))

qubes/tests/vm/qubesvm.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -1317,8 +1317,7 @@ def test_600_libvirt_xml_hvm_pcidev(self):
13171317
ident='00_00.0',
13181318
devclass="pci",
13191319
),
1320-
attach_automatically=True,
1321-
required=True,
1320+
mode='required',
13221321
)
13231322
vm.devices['pci']._set.add(
13241323
assignment)
@@ -1405,7 +1404,7 @@ def test_600_libvirt_xml_hvm_pcidev_s0ix(self):
14051404
ident='00_00.0',
14061405
devclass="pci",
14071406
),
1408-
attach_automatically=True, required=True)
1407+
mode='required')
14091408
vm.devices['pci']._set.add(
14101409
assignment)
14111410
libvirt_xml = vm.create_config_file()
@@ -1493,7 +1492,7 @@ def test_600_libvirt_xml_hvm_cdrom_boot(self):
14931492
devclass="block",
14941493
),
14951494
options={'devtype': 'cdrom', 'read-only': 'yes'},
1496-
attach_automatically=True, required=True)
1495+
mode='required')
14971496
self.loop.run_until_complete(vm.devices['block'].assign(dev))
14981497
libvirt_xml = vm.create_config_file()
14991498
self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
@@ -1602,7 +1601,7 @@ def test_600_libvirt_xml_hvm_cdrom_dom0_kernel_boot(self):
16021601
devclass="block",
16031602
),
16041603
options={'devtype': 'cdrom', 'read-only': 'yes'},
1605-
attach_automatically=True, required=True)
1604+
mode='required')
16061605
self.loop.run_until_complete(vm.devices['block'].assign(dev))
16071606
libvirt_xml = vm.create_config_file()
16081607
self.assertXMLEqual(lxml.etree.XML(libvirt_xml),

0 commit comments

Comments
 (0)