Skip to content

Commit 922bce8

Browse files
committed
q-dev: fix qvm-device
1 parent 250afc8 commit 922bce8

File tree

2 files changed

+63
-21
lines changed

2 files changed

+63
-21
lines changed

qubesadmin/tests/tools/qvm_device.py

+54-18
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def test_001_list_assigned_required(self):
9292
b"devclass='testclass' backend_domain='test-vm2'\n")
9393
self.expected_device_call(
9494
'test-vm3', 'Available',
95-
b"0\0dev3 port_id='dev3' device_id='0000:0000::?******' "
95+
b"0\0dev3 port_id='dev3' device_id='0000:0000::p000000' "
9696
b"devclass='testclass' backend_domain='test-vm3' "
9797
b"vendor='evil inc.' product='test-device-3'\n"
9898
)
@@ -103,9 +103,9 @@ def test_001_list_assigned_required(self):
103103
self.expected_device_call(
104104
'test-vm2', 'Assigned',
105105
b"0\0test-vm1+dev1 port_id='dev1' devclass='testclass' "
106-
b"backend_domain='test-vm1' "
107-
b"mode='required' _option='other option' _extra_opt='yes'\n"
108-
b"test-vm3+dev3 device_id='0000:0000::?******' port_id='dev3' "
106+
b"backend_domain='test-vm1' mode='required' _option='other option' "
107+
b"_extra_opt='yes'\n"
108+
b"test-vm3+dev3 device_id='0000:0000::p000000' port_id='dev3' "
109109
b"devclass='testclass' backend_domain='test-vm3' mode='required'\n"
110110
)
111111
self.expected_device_call(
@@ -116,14 +116,14 @@ def test_001_list_assigned_required(self):
116116

117117
with qubesadmin.tests.tools.StdoutBuffer() as buf:
118118
qubesadmin.tools.qvm_device.main(
119-
['testclass', 'list', 'test-vm3'], app=self.app)
119+
['testclass', 'list', '-s', 'test-vm3'], app=self.app)
120120
self.assertEqual(
121121
buf.getvalue(),
122-
'test-vm1:dev1 any device '
123-
'test-vm2 (required: option=other option, extra_opt=yes), '
124-
'test-vm3 (required: option=test option)\n'
125-
'test-vm3:dev3 ?******: evil inc. test-device-3 '
126-
'test-vm2 (required)\n'
122+
'test-vm1:dev1 any device '
123+
'*test-vm2 (required: option=other option, extra_opt=yes), '
124+
'*test-vm3 (required: option=test option)\n'
125+
'test-vm3:dev3 0000:0000::p000000 *test-vm2 (required)\n'
126+
'test-vm3:dev3 ?******: evil inc. test-device-3 \n'
127127
)
128128

129129
def test_002_list_attach(self):
@@ -152,7 +152,7 @@ def test_002_list_attach(self):
152152
self.assertEqual(
153153
buf.getvalue(),
154154
'test-vm1:dev1 Multimedia: itl test-device '
155-
'test-vm3 (required)\n'
155+
'test-vm3 (attached)\n'
156156
)
157157

158158
def test_003_list_device_classes(self):
@@ -233,7 +233,7 @@ def test_020_detach(self):
233233
""" Test detach action """
234234
self.app.expected_calls[
235235
('test-vm2', 'admin.vm.device.testclass.Detach',
236-
'test-vm1+dev1:*', None)] = b'0\0'
236+
'test-vm1+dev1:dead:beef:babe:u012345', None)] = b'0\0'
237237
qubesadmin.tools.qvm_device.main(
238238
['testclass', 'detach', 'test-vm2', 'test-vm1:dev1'], app=self.app)
239239
self.assertAllCalled()
@@ -242,7 +242,7 @@ def test_021_detach_unknown(self):
242242
""" Test detach action """
243243
self.app.expected_calls[
244244
('test-vm2', 'admin.vm.device.testclass.Detach',
245-
'test-vm1+dev7:*', None)] = b'0\0'
245+
'test-vm1+dev7:0000:0000::?******', None)] = b'0\0'
246246
qubesadmin.tools.qvm_device.main(
247247
['testclass', 'detach', 'test-vm2', 'test-vm1:dev7'], app=self.app)
248248
self.assertAllCalled()
@@ -272,6 +272,9 @@ def test_030_assign(self):
272272
b"devclass='testclass' backend_domain='test-vm1' "
273273
b"mode='auto-attach' frontend_domain='test-vm2'"
274274
)] = b'0\0'
275+
self.app.expected_calls[(
276+
'test-vm2', 'admin.vm.device.testclass.Attached', None, None
277+
)] = b'0\0'
275278
qubesadmin.tools.qvm_device.main(
276279
['testclass', 'assign', 'test-vm2', 'test-vm1:dev1'], app=self.app)
277280
self.assertAllCalled()
@@ -286,6 +289,9 @@ def test_031_assign_required(self):
286289
b"devclass='testclass' backend_domain='test-vm1' mode='required' "
287290
b"frontend_domain='test-vm2'"
288291
)] = b'0\0'
292+
self.app.expected_calls[(
293+
'test-vm2', 'admin.vm.device.testclass.Attached', None, None
294+
)] = b'0\0'
289295
qubesadmin.tools.qvm_device.main(
290296
['testclass', 'assign', '--required', 'test-vm2', 'test-vm1:dev1'], app=self.app)
291297
self.assertAllCalled()
@@ -300,6 +306,9 @@ def test_032_assign_ask_and_options(self):
300306
b"devclass='testclass' backend_domain='test-vm1' "
301307
b"mode='ask-to-attach' frontend_domain='test-vm2' _read-only='yes'"
302308
)] = b'0\0'
309+
self.app.expected_calls[(
310+
'test-vm2', 'admin.vm.device.testclass.Attached', None, None
311+
)] = b'0\0'
303312
with qubesadmin.tests.tools.StdoutBuffer() as buf:
304313
qubesadmin.tools.qvm_device.main(
305314
['testclass', 'assign', '--ro', '--ask', 'test-vm2',
@@ -324,11 +333,11 @@ def test_033_assign_invalid(self):
324333
def test_034_assign_invalid_device(self):
325334
""" Test attach action """
326335
with qubesadmin.tests.tools.StderrBuffer() as stderr:
327-
with self.assertRaises(SystemExit):
328-
qubesadmin.tools.qvm_device.main(
336+
retcode = qubesadmin.tools.qvm_device.main(
329337
['testclass', 'assign', 'test-vm2', 'test-vm1:invalid'],
330338
app=self.app)
331-
self.assertIn('doesn\'t expose testclass device', stderr.getvalue())
339+
self.assertEqual(retcode, 1)
340+
self.assertIn("doesn't expose testclass device", stderr.getvalue())
332341
self.assertAllCalled()
333342

334343
def test_035_assign_invalid_backend(self):
@@ -351,6 +360,9 @@ def test_036_assign_port(self):
351360
b"devclass='testclass' backend_domain='test-vm1' "
352361
b"mode='auto-attach' frontend_domain='test-vm2'"
353362
)] = b'0\0'
363+
self.app.expected_calls[(
364+
'test-vm2', 'admin.vm.device.testclass.Attached', None, None
365+
)] = b'0\0'
354366
qubesadmin.tools.qvm_device.main(
355367
['testclass', 'assign', 'test-vm2', 'test-vm1:dev1', '--port'],
356368
app=self.app)
@@ -366,6 +378,9 @@ def test_037_assign_port_asterisk(self):
366378
b"devclass='testclass' backend_domain='test-vm1' "
367379
b"mode='auto-attach' frontend_domain='test-vm2'"
368380
)] = b'0\0'
381+
self.app.expected_calls[(
382+
'test-vm2', 'admin.vm.device.testclass.Attached', None, None
383+
)] = b'0\0'
369384
qubesadmin.tools.qvm_device.main(
370385
['testclass', 'assign', 'test-vm2', 'test-vm1:dev1:*'],
371386
app=self.app)
@@ -381,6 +396,9 @@ def test_038_assign_device_from_port(self):
381396
b"devclass='testclass' backend_domain='test-vm1' "
382397
b"mode='auto-attach' frontend_domain='test-vm2'"
383398
)] = b'0\0'
399+
self.app.expected_calls[(
400+
'test-vm2', 'admin.vm.device.testclass.Attached', None, None
401+
)] = b'0\0'
384402
qubesadmin.tools.qvm_device.main(
385403
['testclass', 'assign', 'test-vm2', 'test-vm1:dev1', '--device'],
386404
app=self.app)
@@ -433,14 +451,17 @@ def test_041_assign_denied_device(self, mock_deny_list):
433451
qubesadmin.tools.qvm_device.main(
434452
['testclass', 'assign', '--ask', 'test-vm2', 'test-vm1:dev1'],
435453
app=self.app)
436-
self.assertIn('Attention:', buf.getvalue())
454+
self.assertIn('Warning:', buf.getvalue())
437455
self.assertAllCalled()
438456

439457
def test_050_unassign(self):
440458
""" Test unassign action """
441459
self.app.expected_calls[
442460
('test-vm2', 'admin.vm.device.testclass.Unassign',
443461
'test-vm1+dev1:dead:beef:babe:u012345', None)] = b'0\0'
462+
self.app.expected_calls[(
463+
'test-vm2', 'admin.vm.device.testclass.Attached', None, None
464+
)] = b'0\0'
444465
qubesadmin.tools.qvm_device.main(
445466
['testclass', 'unassign', 'test-vm2', 'test-vm1:dev1'],
446467
app=self.app)
@@ -451,6 +472,9 @@ def test_051_unassign_unknown(self):
451472
self.app.expected_calls[
452473
('test-vm2', 'admin.vm.device.testclass.Unassign',
453474
'test-vm1+dev7:0000:0000::?******', None)] = b'0\0'
475+
self.app.expected_calls[(
476+
'test-vm2', 'admin.vm.device.testclass.Attached', None, None
477+
)] = b'0\0'
454478
qubesadmin.tools.qvm_device.main(
455479
['testclass', 'unassign', 'test-vm2', 'test-vm1:dev7'],
456480
app=self.app)
@@ -461,6 +485,9 @@ def test_052_unassign_port(self):
461485
self.app.expected_calls[
462486
('test-vm2', 'admin.vm.device.testclass.Unassign',
463487
'test-vm1+dev1:*', None)] = b'0\0'
488+
self.app.expected_calls[(
489+
'test-vm2', 'admin.vm.device.testclass.Attached', None, None
490+
)] = b'0\0'
464491
qubesadmin.tools.qvm_device.main(
465492
['testclass', 'unassign', 'test-vm2', 'test-vm1:dev1', '--port'],
466493
app=self.app)
@@ -471,6 +498,9 @@ def test_053_unassign_device_from_port(self):
471498
self.app.expected_calls[
472499
('test-vm2', 'admin.vm.device.testclass.Unassign',
473500
'test-vm1+*:dead:beef:babe:u012345', None)] = b'0\0'
501+
self.app.expected_calls[(
502+
'test-vm2', 'admin.vm.device.testclass.Attached', None, None
503+
)] = b'0\0'
474504
qubesadmin.tools.qvm_device.main(
475505
['testclass', 'unassign', 'test-vm2', 'test-vm1:dev1', '--device'],
476506
app=self.app)
@@ -491,6 +521,9 @@ def test_055_unassign_explicit_device_port(self):
491521
self.app.expected_calls[
492522
('test-vm2', 'admin.vm.device.testclass.Unassign',
493523
'test-vm1+dev1:*', None)] = b'0\0'
524+
self.app.expected_calls[(
525+
'test-vm2', 'admin.vm.device.testclass.Attached', None, None
526+
)] = b'0\0'
494527
qubesadmin.tools.qvm_device.main(
495528
['testclass', 'unassign', 'test-vm2',
496529
'test-vm1:dev1:dead:beef:babe:u0123456', '--port'], app=self.app)
@@ -518,6 +551,9 @@ def test_057_unassign_all(self):
518551
self.app.expected_calls[
519552
('test-vm2', 'admin.vm.device.testclass.Unassign',
520553
'test-vm1+dev2:*', None)] = b'0\0'
554+
self.app.expected_calls[(
555+
'test-vm2', 'admin.vm.device.testclass.Attached', None, None
556+
)] = b'0\0'
521557
qubesadmin.tools.qvm_device.main(
522558
['testclass', 'unassign', 'test-vm2'], app=self.app)
523559
self.assertAllCalled()
@@ -528,6 +564,6 @@ def test_060_device_info(self):
528564
qubesadmin.tools.qvm_device.main(
529565
['testclass', 'info', 'test-vm1:dev1'],
530566
app=self.app)
531-
self.assertIn('dead:beef:babe:u012345: Multimedia: itl test-device',
567+
self.assertIn('Multimedia: itl test-device\ndevice ID: dead:beef:babe:u012345',
532568
buf.getvalue())
533569
self.assertAllCalled()

qubesadmin/tools/qvm_device.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,10 @@ def list_devices(args):
9191
lines = _load_lines(args.app, domains, args.devclass, actual_devices=True)
9292
lines = list(lines.values())
9393
if args.assignments:
94+
# we need to check assignments for all domains since
95+
# selected vm can be mentioned there as backend
9496
extra_lines = _load_lines(
95-
args.app, domains, args.devclass, actual_devices=False)
97+
args.app, [], args.devclass, actual_devices=False)
9698
lines += list(extra_lines.values())
9799
qubesadmin.tools.print_table(prepare_table(lines))
98100

@@ -157,7 +159,7 @@ def _load_frontends_info(vm, dev, devclass, actual_devices):
157159
yield _frontend_desc(vm, assignment)
158160
else:
159161
for assignment in vm.devices[devclass].get_assigned_devices():
160-
if dev == assignment.virtual_device:
162+
if assignment.matches(dev):
161163
yield _frontend_desc(vm, assignment, virtual=True)
162164
except qubesadmin.exc.QubesVMNotFoundError:
163165
pass
@@ -281,6 +283,10 @@ def assign_device(args):
281283
device = args.device
282284
if args.only_port:
283285
device = device.clone(device_id="*")
286+
elif device.device_id == UnknownDevice(device.port).device_id:
287+
raise qubesadmin.exc.QubesException(
288+
f"backend vm {device.backend_name} doesn't expose "
289+
f"{device.devclass} device {device.port_id!r}")
284290
if args.only_device:
285291
device = device.clone(
286292
port=Port(device.backend_domain, "*", device.devclass))
@@ -314,7 +320,7 @@ def _print_attach_hint(assignment, vm):
314320
if dev not in attached and not isinstance(dev, UnknownDevice)]
315321

316322
if ports:
317-
print("Assigned. To attach you can now restart domain or run: \n"
323+
print("Assigned. To attach you can now restart domain or run: \n" +
318324
"\n".join(ports))
319325

320326

0 commit comments

Comments
 (0)