Skip to content

Commit e09ab84

Browse files
committed
q-dev: add flag to device listing
Listing all assignments by default could be overwhelming. Now to see all assignment a flag `-s` must be used. Assignments are indicated by '*' before qube name.
1 parent 469ee24 commit e09ab84

File tree

2 files changed

+50
-27
lines changed

2 files changed

+50
-27
lines changed

doc/manpages/qvm-device.rst

+5-1
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,14 @@ Commands
4141
list
4242
^^^^
4343

44-
| :command:`qvm-device` *DEVICE_CLASS* list [-h] [--verbose] [--quiet] [*VMNAME* [*VMNAME* ...]]
44+
| :command:`qvm-device` *DEVICE_CLASS* list [-h] [--verbose] [--quiet] [-s] [*VMNAME* [*VMNAME* ...]]
4545
4646
List devices.
4747

48+
.. option:: --assignments, -s
49+
50+
Include info about device assignments, indicated by '*' before qube name.
51+
4852
.. option:: --all
4953

5054
List devices from all qubes. You can use :option:`--exclude` to limit the

qubesadmin/tools/qvm_device.py

+45-26
Original file line numberDiff line numberDiff line change
@@ -69,42 +69,51 @@ class Line:
6969
"""Helper class to hold single device info for listing"""
7070

7171
# pylint: disable=too-few-public-methods
72-
def __init__(self, device: DeviceInfo, attached_to=None):
72+
def __init__(self, device: DeviceInfo, assignment=False):
7373
self.ident = "{!s}:{!s}".format(
7474
device.backend_domain, device.port_id)
7575
self.description = device.description
76-
self.attached_to = attached_to if attached_to else ""
76+
self.assignment = assignment
7777
self.frontends = []
7878

7979
@property
8080
def assignments(self):
8181
"""list of frontends the device is assigned to"""
82-
return ', '.join(self.frontends)
82+
fronts = (f'{"*" if self.assignment else ""}' + front
83+
for front in self.frontends)
84+
return ', '.join(fronts)
8385

8486

8587
def list_devices(args):
8688
"""
8789
Called by the parser to execute the qubes-devices list subcommand. """
88-
app = args.app
89-
9090
domains = args.domains if hasattr(args, 'domains') else None
91-
devices = _load_devices(app, domains, args.devclass)
92-
93-
result = {dev: Line(dev) for dev in devices}
94-
91+
lines = _load_lines(args.app, domains, args.devclass, actual_devices=True)
92+
lines = list(lines.values())
93+
if args.assignments:
94+
extra_lines = _load_lines(
95+
args.app, domains, args.devclass, actual_devices=False)
96+
lines += list(extra_lines.values())
97+
qubesadmin.tools.print_table(prepare_table(lines))
98+
99+
100+
def _load_lines(app, domains, devclass, actual_devices: bool):
101+
devices = _load_devices(app, domains, devclass, actual_devices)
102+
result = {dev: Line(dev, not actual_devices) for dev in devices}
95103
for dev in result:
96104
for vm in app.domains:
97-
frontends = _load_frontends_info(vm, dev, args.devclass)
105+
frontends = _load_frontends_info(vm, dev, devclass, actual_devices)
98106
result[dev].frontends.extend(frontends)
107+
return result
99108

100-
qubesadmin.tools.print_table(prepare_table(result.values()))
101109

102-
103-
def _load_devices(app, domains, devclass):
110+
def _load_devices(app, domains, devclass, actual_devices):
104111
"""
105112
Loads device exposed or connected to given domains.
106113
107114
If `domains` is empty/`None` load all devices.
115+
If `actual_devices` is True only devices currently present will be included,
116+
otherwise only device assignments
108117
"""
109118
devices = set()
110119
if domains:
@@ -115,12 +124,14 @@ def _load_devices(app, domains, devclass):
115124
try:
116125
for vm in domains:
117126
try:
118-
for ass in vm.devices[devclass].get_attached_devices():
119-
devices.add(ass.device)
120-
for ass in vm.devices[devclass].get_assigned_devices():
121-
devices.add(ass.virtual_device)
122-
for dev in vm.devices[devclass].get_exposed_devices():
123-
devices.add(dev)
127+
if actual_devices:
128+
for ass in vm.devices[devclass].get_attached_devices():
129+
devices.add(ass.device)
130+
for dev in vm.devices[devclass].get_exposed_devices():
131+
devices.add(dev)
132+
else:
133+
for ass in vm.devices[devclass].get_assigned_devices():
134+
devices.add(ass.virtual_device)
124135
except qubesadmin.exc.QubesVMNotFoundError:
125136
if ignore_errors:
126137
continue
@@ -132,20 +143,22 @@ def _load_devices(app, domains, devclass):
132143
return devices
133144

134145

135-
def _load_frontends_info(vm, dev, devclass):
146+
def _load_frontends_info(vm, dev, devclass, actual_devices):
136147
"""
137148
Returns string of vms to which a device is connected or `None`.
138149
"""
139150
if vm == dev.backend_domain:
140151
return
141152

142153
try:
143-
for assignment in vm.devices[devclass].get_attached_devices():
144-
if dev in assignment.devices:
145-
yield _frontend_desc(vm, assignment)
146-
for assignment in vm.devices[devclass].get_assigned_devices():
147-
if dev == assignment.virtual_device:
148-
yield _frontend_desc(vm, assignment)
154+
if actual_devices:
155+
for assignment in vm.devices[devclass].get_attached_devices():
156+
if dev in assignment.devices:
157+
yield _frontend_desc(vm, assignment)
158+
else:
159+
for assignment in vm.devices[devclass].get_assigned_devices():
160+
if dev == assignment.virtual_device:
161+
yield _frontend_desc(vm, assignment)
149162
except qubesadmin.exc.QubesVMNotFoundError:
150163
pass
151164

@@ -402,6 +415,12 @@ def init_list_parser(sub_parsers):
402415
list_parser = sub_parsers.add_parser('list', aliases=('ls', 'l'),
403416
help='list devices')
404417

418+
list_parser.add_argument('--assignments', '-s',
419+
action='store_true',
420+
default=False,
421+
help="Include info about device assignments, "
422+
"indicated by '*' before qube name.")
423+
405424
vm_name_group = qubesadmin.tools.VmNameGroup(
406425
list_parser, required=False, vm_action=qubesadmin.tools.VmNameAction,
407426
help='list devices assigned to specific domain(s)')

0 commit comments

Comments
 (0)