@@ -63,7 +63,7 @@ def main():
6363from unittest import mock
6464from copy import deepcopy
6565
66- from typing import List , Optional , Dict , Tuple
66+ from typing import List , Optional , Dict , Tuple , Any
6767
6868import qubesadmin .events
6969from 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
579581class 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
652677class 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 ()
0 commit comments