Skip to content

Commit 64e7669

Browse files
committed
q-dev: fix block auto-attach
1 parent 56559c0 commit 64e7669

File tree

5 files changed

+48
-35
lines changed

5 files changed

+48
-35
lines changed

qubes/api/admin.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1227,7 +1227,7 @@ async def vm_device_available(self, endpoint):
12271227
devices = self.fire_event_for_filter(devices, devclass=devclass)
12281228
dev_info = {f'{dev.port_id}:{dev.device_id}':
12291229
dev.serialize().decode() for dev in devices}
1230-
return ''.join('{} {}\n'.format(port_id, dev_info[port_id])
1230+
return ''.join(f'{port_id} {dev_info[port_id]}\n'
12311231
for port_id in sorted(dev_info))
12321232

12331233
@qubes.api.method('admin.vm.device.{endpoint}.Assigned', endpoints=(ep.name

qubes/device_protocol.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,7 @@ def serialize(self) -> bytes:
895895
if getattr(self, key) != getattr(default, key)))
896896

897897
if self.attachment:
898-
properties = DeviceSerializer.pack_property(
898+
properties += b' ' + DeviceSerializer.pack_property(
899899
'attachment', self.attachment.name)
900900

901901
properties += b' ' + DeviceSerializer.pack_property(
@@ -1122,6 +1122,8 @@ def devices(self) -> List[DeviceInfo]:
11221122
# could return UnknownDevice
11231123
return [self.backend_domain.devices[self.devclass][self.port_id]]
11241124
result = []
1125+
if self.device_id == "0000:0000::?******":
1126+
return result
11251127
for dev in self.backend_domain.devices[self.devclass]:
11261128
if dev.device_id == self.device_id:
11271129
result.append(dev)

qubes/ext/block.py

+30-23
Original file line numberDiff line numberDiff line change
@@ -541,34 +541,41 @@ def pre_attachment_internal(
541541
@qubes.ext.handler('domain-start')
542542
async def on_domain_start(self, vm, _event, **_kwargs):
543543
# pylint: disable=unused-argument
544-
for assignment in vm.devices['block'].get_assigned_devices():
545-
self.notify_auto_attached(vm, assignment)
546-
547-
def notify_auto_attached(self, vm, assignment):
548-
for device in assignment.devices:
549-
if not assignment.matches(device):
550-
print("Unrecognized identity, skipping attachment of device "
551-
f"from the port {assignment}", file=sys.stderr)
544+
to_attach = {}
545+
assignments = vm.devices['block'].get_assigned_devices()
546+
# the most specific assignments first
547+
for assignment in reversed(sorted(assignments)):
548+
if assignment.required:
549+
# already attached
552550
continue
553-
554-
if assignment.mode.value == "ask-to-attach":
555-
if vm.name != confirm_device_attachment(device,
556-
{vm: assignment}):
551+
# TODO: notify?
552+
for device in assignment.devices:
553+
if isinstance(device, qubes.device_protocol.UnknownDevice):
557554
continue
558-
559-
self.pre_attachment_internal(
560-
vm, device, assignment.options, expected_attachment=vm)
561-
562-
asyncio.ensure_future(vm.fire_event_async(
563-
'device-attach:block',
564-
device=device,
565-
options=assignment.options,
566-
))
555+
if not assignment.matches(device):
556+
print(
557+
"Unrecognized identity, skipping attachment of device "
558+
f"from the port {assignment}", file=sys.stderr)
559+
continue
560+
# chose first assignment (the most specific) and ignore rest
561+
if device not in to_attach:
562+
# make it unique
563+
to_attach[device] = assignment.clone(
564+
device=qubes.device_protocol.VirtualDevice(
565+
device.port, device.device_id))
566+
for assignment in to_attach.values():
567+
await self.attach_and_notify(vm, assignment)
567568

568569
async def attach_and_notify(self, vm, assignment):
569570
# bypass DeviceCollection logic preventing double attach
570-
# we expected that these devices are already attached to this vm
571-
self.notify_auto_attached(vm, assignment)
571+
device = assignment.device
572+
if assignment.mode.value == "ask-to-attach":
573+
if vm.name != confirm_device_attachment(device, {vm: assignment}):
574+
return
575+
self.on_device_pre_attached_block(
576+
vm, 'device-pre-attach:block', device, assignment.options)
577+
await vm.fire_event_async(
578+
'device-attach:block', device=str(device), options=assignment.options)
572579

573580
@qubes.ext.handler('domain-shutdown')
574581
async def on_domain_shutdown(self, vm, event, **_kwargs):

qubes/ext/utils.py

+12-9
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@ def device_list_change(
4343
compare_device_cache(vm, ext.devices_cache, current_devices))
4444

4545
# send events about devices detached/attached outside by themselves
46-
for dev_id, front_vm in detached.items():
47-
dev = device_class(vm, dev_id)
46+
for port_id, front_vm in detached.items():
47+
dev = device_class(vm, port_id)
4848
asyncio.ensure_future(front_vm.fire_event_async(
4949
f'device-detach:{devclass}', port=dev.port))
50-
for dev_id in removed:
51-
device = device_class(vm, dev_id)
52-
vm.fire_event(f'device-removed:{devclass}', device=device)
53-
for dev_id in added:
54-
device = device_class(vm, dev_id)
50+
for port_id in removed:
51+
device = device_class(vm, port_id)
52+
vm.fire_event(f'device-removed:{devclass}', port=device.port)
53+
for port_id in added:
54+
device = device_class(vm, port_id)
5555
vm.fire_event(f'device-added:{devclass}', device=device)
5656
for dev_ident, front_vm in attached.items():
5757
dev = device_class(vm, dev_ident)
@@ -73,8 +73,12 @@ def device_list_change(
7373
):
7474
frontends = to_attach.get(device.port_id, {})
7575
# make it unique
76-
frontends[front_vm] = assignment.clone(
76+
ass = assignment.clone(
7777
device=VirtualDevice(device.port, device.device_id))
78+
curr = frontends.get(front_vm, None)
79+
if curr is None or curr < ass:
80+
# chose the most specific assignment
81+
frontends[front_vm] = ass
7882
to_attach[device.port_id] = frontends
7983

8084
for port_id, frontends in to_attach.items():
@@ -137,7 +141,6 @@ def compare_device_cache(vm, devices_cache, current_devices):
137141

138142
def confirm_device_attachment(device, frontends) -> str:
139143
guivm = 'dom0' # TODO
140-
# TODO: guivm rpc?
141144

142145
try:
143146
proc = subprocess.Popen(

qubes/tests/devices_block.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ def test_060_on_qdb_change_added(self):
668668
'/qubes-block-devices/sda/size': b'1024000',
669669
'/qubes-block-devices/sda/mode': b'r',
670670
}, domain_xml=domain_xml_template.format(""))
671-
exp_dev = qubes.ext.block.BlockDevice(back_vm, 'sda')
671+
exp_dev = qubes.ext.block.BlockDevice(back_vm, 'sda')
672672

673673
self.ext.on_qdb_change(back_vm, None, None)
674674

@@ -713,6 +713,7 @@ def test_061_on_qdb_change_auto_attached(self):
713713

714714
# In the case of block devices it is the same,
715715
# but notify_auto_attached is synchronous
716+
# TODO!
716717
self.ext.attach_and_notify = self.ext.notify_auto_attached
717718
with mock.patch('asyncio.ensure_future'):
718719
self.ext.on_qdb_change(back_vm, None, None)

0 commit comments

Comments
 (0)