@@ -62,6 +62,12 @@ def __init__(self, vm, class_):
6262 self ._vm = vm
6363 self ._class = class_
6464 self ._dev_cache = {}
65+ #: attachments cache, `None` means "not cached (yet)",
66+ #: in contrast to empty list which means "cached empty list"
67+ self ._attachment_cache = None
68+ #: assignments cache, `None` means "not cached (yet)",
69+ #: in contrast to empty list which means "cached empty list"
70+ self ._assignment_cache = None
6571
6672 def attach (self , assignment : DeviceAssignment ) -> None :
6773 """
@@ -75,6 +81,9 @@ def attach(self, assignment: DeviceAssignment) -> None:
7581 "did you mean `qvm-pci assign --required ...`"
7682 )
7783 self ._add (assignment , "attach" )
84+ # clear the whole cache instead of saving provided assignment, it might
85+ # get modified before actually attaching
86+ self ._attachment_cache = None
7887
7988 def detach (self , assignment : DeviceAssignment ) -> None :
8089 """
@@ -84,6 +93,7 @@ def detach(self, assignment: DeviceAssignment) -> None:
8493 (obtained from :py:meth:`assignments`)
8594 """
8695 self ._remove (assignment , "detach" )
96+ self ._assignment_cache = None
8797
8898 def assign (self , assignment : DeviceAssignment ) -> None :
8999 """
@@ -113,6 +123,9 @@ def assign(self, assignment: DeviceAssignment) -> None:
113123 )
114124
115125 self ._add (assignment , "assign" )
126+ # clear the whole cache instead of saving provided assignment, it might
127+ # get modified before actually assigning
128+ self ._assignment_cache = None
116129
117130 def unassign (self , assignment : DeviceAssignment ) -> None :
118131 """
@@ -122,6 +135,7 @@ def unassign(self, assignment: DeviceAssignment) -> None:
122135 (obtained from :py:meth:`assignments`)
123136 """
124137 self ._remove (assignment , "unassign" )
138+ self ._assignment_cache = None
125139
126140 def _add (self , assignment : DeviceAssignment , action : str ) -> None :
127141 """
@@ -186,6 +200,10 @@ def get_attached_devices(self) -> Iterable[DeviceAssignment]:
186200 """
187201 List devices which are attached to this vm.
188202 """
203+ if self ._attachment_cache is not None :
204+ yield from self ._attachment_cache
205+ return
206+ new_cache = []
189207 assignments_str = self ._vm .qubesd_call (
190208 None , "admin.vm.device.{}.Attached" .format (self ._class )
191209 ).decode ()
@@ -195,9 +213,14 @@ def get_attached_devices(self) -> Iterable[DeviceAssignment]:
195213 head , self ._class , self ._vm .app .domains , blind = True
196214 )
197215
198- yield DeviceAssignment .deserialize (
216+ assignment = DeviceAssignment .deserialize (
199217 untrusted_rest .encode ("ascii" ), expected_device = device
200218 )
219+ new_cache .append (assignment )
220+ yield assignment
221+
222+ if self ._vm .app .cache_enabled :
223+ self ._attachment_cache = new_cache
201224
202225 def get_assigned_devices (
203226 self , required_only : bool = False
@@ -207,6 +230,12 @@ def get_assigned_devices(
207230
208231 Safe to access before libvirt bootstrap.
209232 """
233+ if self ._assignment_cache is not None :
234+ for assignment in self ._assignment_cache :
235+ if not required_only or assignment .required :
236+ yield assignment
237+ return
238+ new_cache = []
210239 assignments_str = self ._vm .qubesd_call (
211240 None , "admin.vm.device.{}.Assigned" .format (self ._class )
212241 ).decode ()
@@ -219,9 +248,13 @@ def get_assigned_devices(
219248 assignment = DeviceAssignment .deserialize (
220249 untrusted_rest .encode ("ascii" ), expected_device = device
221250 )
251+ new_cache .append (assignment )
222252 if not required_only or assignment .required :
223253 yield assignment
224254
255+ if self ._vm .app .cache_enabled :
256+ self ._assignment_cache = new_cache
257+
225258 def get_exposed_devices (self ) -> Iterable [DeviceInfo ]:
226259 """
227260 List devices exposed by this vm.
@@ -252,6 +285,7 @@ def update_assignment(self, device: Port, required: AssignmentMode):
252285 repr (device ),
253286 required .value .encode ("utf-8" ),
254287 )
288+ self ._assignment_cache = None
255289
256290 __iter__ = get_exposed_devices
257291
@@ -260,6 +294,8 @@ def clear_cache(self):
260294 Clear cache of available devices.
261295 """
262296 self ._dev_cache .clear ()
297+ self ._assignment_cache = None
298+ self ._attachment_cache = None
263299
264300 def __getitem__ (self , item ):
265301 """Get device object with given port_id.
@@ -325,3 +361,8 @@ def allow(self, *interfaces: Iterable[DeviceInterface]):
325361 None ,
326362 "" .join (repr (ifc ) for ifc in interfaces ).encode ('ascii' ),
327363 )
364+
365+ def clear_cache (self ):
366+ """Clear cache of all available device classes"""
367+ for devclass in self .values ():
368+ devclass .clear_cache ()
0 commit comments