Skip to content

Commit 9308882

Browse files
committed
Redefine DeviceCategories and improvements in MockDevices
- changed DeviceCategory listings to more closely align with desired GUI list of blockable devices - improved mocks to include better MockDevices
1 parent e6034fd commit 9308882

3 files changed

Lines changed: 115 additions & 64 deletions

File tree

qubesadmin/device_protocol.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -641,29 +641,31 @@ class DeviceCategory(Enum):
641641
"""
642642

643643
# pylint: disable=invalid-name
644-
Other = "*******"
644+
Other = "*******" # also matches all devices, if used to block attachment
645645

646-
Communication = ("u02****", "p07****") # eg. modems
646+
# The following devices are used in GUI for blocks; take note when changing
647+
648+
# modems, WiFi and Ethernet adapters
649+
Network = ("u02****", "p07****", "p02****")
647650
Input = ("u03****", "p09****") # HID etc.
648651
Keyboard = ("u03**01", "p0900**")
649652
Mouse = ("u03**02", "p0902**")
650653
Printer = ("u07****",)
651-
Scanner = ("p0903**",)
652-
Microphone = ("m******",)
654+
Image_Input = ("p0903**", "u06****", "u0e****") # cameras and scanners
655+
653656
# Multimedia = Audio, Video, Displays etc.
654-
Multimedia = (
655-
"u06****",
656-
"u10****",
657-
"p03****",
658-
"p04****",
659-
)
660-
Audio = ("p0403**", "p0401**", "u01****")
661-
Display = ("p0300**", "p0380**")
662-
Video = ("p0400**", "u0e****")
663-
Wireless = ("ue0****", "p0d****")
664-
Bluetooth = ("ue00101", "p0d11**")
657+
Multimedia_Output = ("u10****", "p03****", "p04****")
658+
Audio_Output = ("p0403**", "p0401**", "u01****")
659+
Audio = ("p0403**", "p0401**", "u01****", "m******")
660+
Microphone = ("m******",)
661+
USB_Storage = ("u08****")
662+
Block_Storage = ("b******")
665663
Storage = ("b******", "u08****", "p01****")
666-
Network = ("p02****",)
664+
Bluetooth = ("ue00101", "p0d11**")
665+
Smart_Card_Readers = ("u0b****")
666+
667+
Display = ("p0300**", "p0380**") # PCI screens?
668+
Wireless = ("ue0****", "p0d****")
667669
Memory = ("p05****",)
668670
PCI_Bridge = ("p06****",)
669671
Docking_Station = ("p0a****",)

qubesadmin/tests/mock_app.py

Lines changed: 94 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def main():
6363
from unittest import mock
6464
from copy import deepcopy
6565

66-
from typing import List, Optional, Dict, Tuple
66+
from typing import List, Optional, Dict, Tuple, Any
6767

6868
import qubesadmin.events
6969
from qubesadmin.tests import QubesTest
@@ -116,6 +116,7 @@ def default_string(self):
116116
"debug": Property("False", "bool", True),
117117
"default_dispvm": Property("default-dvm", "vm", False),
118118
"default_user": Property("user", "str", True),
119+
"devices_denied": Property("", "str", True),
119120
"dns": Property("10.139.1.1 10.139.1.2", "str", True),
120121
"gateway": Property("", "str", True),
121122
"gateway6": Property("", "str", True),
@@ -389,9 +390,9 @@ def update_calls(self):
389390

390391
# create all propertyget calls
391392
for prop, value in self.properties.items():
392-
if ((prop == 'template') and \
393+
if ((prop == 'template') and
393394
self.klass in ("TemplateVM", "StandaloneVM")) \
394-
or ((prop == 'appvm_default_bootmode') and \
395+
or ((prop == 'appvm_default_bootmode') and
395396
self.klass in ("AppVM",)):
396397
self.qapp.expected_calls[
397398
(self.name, "admin.vm.property.Get", prop, None)] = \
@@ -576,59 +577,73 @@ def __init__(self, qapp):
576577
continue
577578

578579

580+
# pylint: disable=too-many-positional-arguments
579581
class MockDevice:
580582
"""helper for adding a device to a qubes test instance"""
581-
def __init__(self, qapp: QubesTest, dev_class: str,
582-
description: str, dev_id: str, backend_vm: str,
583-
attached: Optional[str] = None):
583+
def __init__(self,
584+
qapp: QubesTest,
585+
dev_class: str,
586+
device_id: str,
587+
backend_vm: str,
588+
port: str,
589+
product: str,
590+
vendor: str,
591+
attached: Optional[str] = None,
592+
assigned: Optional[List[Tuple[str, str, list[Any] | None]]] =
593+
None):
584594
"""
585595
:param qapp: QubesTest object
586596
:param dev_class: block / mic / usb
587-
:param description: device description
588-
:param dev_id: dev id (such as sda, 2-1, mic)
597+
:param device_id: device_id
598+
:param port: port
589599
:param backend_vm: name of the vm providing this device
590-
:param attached: name of the qube to which the device is attached,
591-
if any
600+
:param attached: name of the qube to which the device is
601+
currently attached,
602+
:param assigned: list of the qubes to which the device is currently
603+
assigned, tupled with mode ('ask-to-attach' or 'auto-attach')
592604
"""
593605
self.qapp = qapp
594606
self.dev_class = dev_class
595-
self.description = description
596-
self.dev_id = dev_id
607+
self.device_id = device_id
608+
self.port = port
597609
self.backend_vm = backend_vm
598610
self.attached = attached
611+
self.assigned = assigned
612+
self.product = product
613+
self.vendor = vendor
614+
615+
self.interface = self.device_id.split(":")[-1]
599616

600617
self.update_calls()
601618

602619
def device_string(self):
603-
if self.dev_class == 'block':
604-
port, d_id = self.dev_id.split(":", 1)
605-
return f"{self.dev_id} device_id='{d_id}' port_id='{port}' " \
606-
f"devclass='block' backend_domain='{self.backend_vm}' " \
607-
f"serial='root/test.img' manufacturer='{self.description}' " \
608-
"interfaces='b******'\n"
609-
if self.dev_class == 'pci':
610-
_, d_id = self.dev_id.split(":")
611-
port = self.dev_id.replace(':', '_')
612-
return f"{port} device_id='{d_id}' " \
613-
f"port_id='{port}' devclass='pci' " \
614-
f"product='{self.description}' vendor='{self.description}' " \
615-
f"interfaces='p0c0500' backend_domain='{self.backend_vm}'\n"
616-
return f"{self.dev_id} description='{self.description}'\n"
620+
return f"{self.port} device_id='{self.device_id}' " \
621+
f"port_id='{self.port}' devclass='{self.dev_class}' " \
622+
f"product='{self.product}' vendor='{self.vendor}' " \
623+
f"interfaces='{self.interface}' " \
624+
f"backend_domain='{self.backend_vm}'\n"
617625

618626
def attachment_string(self):
619-
if ":" in self.dev_id:
620-
port, _ = self.dev_id.split(":")
621-
elif self.dev_class == 'mic':
622-
port = "mic"
623-
else:
624-
port = "00"
625-
string = (f"{self.backend_vm}+{self.dev_id} "
626-
f"port_id='{port}' devclass='{self.dev_class}' "
627-
f"backend_domain='{self.backend_vm}' mode='required' "
627+
string = (f"{self.backend_vm}+{self.port} "
628+
f"port_id='{self.port}' devclass='{self.dev_class}' "
629+
f"backend_domain='{self.backend_vm}' mode='manual' "
628630
f"frontend_domain='{self.attached}'")
629631
string += "\n"
630632
return string
631633

634+
def assignment_string(self, vm, mode, opts):
635+
string = (f"{self.backend_vm}+{self.port} device_id='"
636+
f"{self.device_id}' "
637+
f"port_id='{self.port}' devclass='{self.dev_class}' "
638+
f"backend_domain='{self.backend_vm}' mode='{mode}' "
639+
f"frontend_domain='{vm}'")
640+
if opts:
641+
for opt in opts:
642+
string += f" _{opt}='True'"
643+
string += "\n"
644+
return string
645+
646+
632647
def update_calls(self):
633648
# modify call
634649
current_response = self.qapp.expected_calls[
@@ -641,13 +656,23 @@ def update_calls(self):
641656

642657
if self.attached:
643658
current_response = self.qapp.expected_calls[
644-
(self.attached, f"admin.vm.device.{self.dev_class}.Assigned",
659+
(self.attached, f"admin.vm.device.{self.dev_class}.Attached",
645660
None, None)]
646661
self.qapp.expected_calls[
647-
(self.attached, f"admin.vm.device.{self.dev_class}.Assigned",
662+
(self.attached, f"admin.vm.device.{self.dev_class}.Attached",
648663
None, None)] = current_response + \
649664
self.attachment_string().encode()
650665

666+
if self.assigned:
667+
for vm, mode, opts in self.assigned:
668+
current_response = self.qapp.expected_calls[
669+
(vm, f"admin.vm.device.{self.dev_class}.Assigned",
670+
None, None)]
671+
self.qapp.expected_calls[
672+
(vm, f"admin.vm.device.{self.dev_class}.Assigned",
673+
None, None)] = current_response + \
674+
self.assignment_string(vm, mode, opts).encode()
675+
651676

652677
class QubesTestWrapper(QubesTest):
653678
def __init__(self):
@@ -822,16 +847,40 @@ def __init__(self):
822847

823848
# also add a bunch of devices
824849
self._devices = [
825-
MockDevice(self, 'mic', 'Internal Microphone', 'mic', 'dom0',
850+
MockDevice(self, dev_class='mic', device_id='dom0:mic::m000000',
851+
backend_vm='dom0', port='mic',
852+
product='Internal Mic', vendor='ACME',
826853
attached='test-blue'),
827854
# the usb stick appears as multiple devices, as they are wont to
828-
MockDevice(self, 'usb', 'My USB Drive', '2-1', 'sys-usb'),
829-
MockDevice(self, 'block', ' ()', 'sda::0', 'sys-usb'),
830-
MockDevice(self, 'block', '(USB DISK)', 'sda1::1', 'sys-usb'),
831-
MockDevice(self, 'usb', 'Internal Camera', '2-10', 'sys-usb'),
832-
MockDevice(self, 'pci', 'Host bridge', '00:00.0', 'dom0'),
833-
MockDevice(self, 'pci', 'PCI Bridge', '00:02.2', 'dom0'),
834-
MockDevice(self, 'pci', 'USB Controller', '00:03.2', 'dom0'),
855+
MockDevice(self, dev_class='usb',
856+
device_id='1d6b:0104:CAFEBABE:u030101u300000',
857+
backend_vm='sys-usb', port='2-1',
858+
product='My USB Drive', vendor='SCP Foundation'),
859+
MockDevice(self, dev_class='block',
860+
device_id='1d6b:0104:CAFEBABE:b123456',
861+
backend_vm='sys-usb', port='sda',
862+
product='(USB)', vendor='SCP Foundation'),
863+
MockDevice(self, dev_class='block',
864+
device_id='1d6b:0104:CAFEBABE:b123456',
865+
backend_vm='sys-usb', port='sda',
866+
product='()', vendor='SCP Foundation'),
867+
MockDevice(self, dev_class='usb',
868+
device_id='04f2:b684:01.00.00:u0e0101u0e0201',
869+
backend_vm='sys-usb', port='2-7',
870+
product='Internal Camera', vendor='Saruman Industries'),
871+
872+
MockDevice(self, dev_class='pci',
873+
device_id='0x8086:0x4621::p060000',
874+
backend_vm='dom0', port='00_00.0',
875+
product='PCI Bridge', vendor='Unnamed Industries'),
876+
MockDevice(self, dev_class='pci',
877+
device_id='0x8086:0x51e9::p0c8000',
878+
backend_vm='dom0', port='00_02.0',
879+
product='I2C Controller', vendor='Unnamed Industries'),
880+
MockDevice(self, dev_class='pci',
881+
device_id='0x8086:0x461e::p0c0330',
882+
backend_vm='dom0', port='00_03.0',
883+
product='I2C Controller', vendor='Unnamed Industries'),
835884
]
836885

837886
self.update_vm_calls()

qubesadmin/tests/tools/qvm_device.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def test_000_list_all(self):
8080
['testclass', 'list'], app=self.app)
8181
self.assertEqual(
8282
[x.rstrip() for x in buf.getvalue().splitlines()],
83-
['test-vm1:dev1 Audio: itl test-device',
83+
['test-vm1:dev1 Audio_Output: itl test-device',
8484
'test-vm2:dev2 ?******: ? ` test-device']
8585
)
8686

@@ -155,7 +155,7 @@ def test_002_list_attach(self):
155155
['testclass', 'list', 'test-vm3'], app=self.app)
156156
self.assertEqual(
157157
buf.getvalue(),
158-
'test-vm1:dev1 Audio: itl test-device '
158+
'test-vm1:dev1 Audio_Output: itl test-device '
159159
'test-vm3 (attached)\n'
160160
)
161161

@@ -573,7 +573,7 @@ def test_060_device_info(self):
573573
qubesadmin.tools.qvm_device.main(
574574
['testclass', 'info', 'test-vm1:dev1'],
575575
app=self.app)
576-
self.assertIn('Audio: itl test-device\n'
576+
self.assertIn('Audio_Output: itl test-device\n'
577577
'device ID: dead:beef:babe:u012345',
578578
buf.getvalue())
579579
self.assertAllCalled()

0 commit comments

Comments
 (0)