devices: implement DeviceCollection.update_persistent()

Allow attached device to be converted from persistent to non-persistent
and the other way around.
This is to allow starting a VM with some device attached temporarily.
When VM is not running, it is possible to attach device only
persistently, so this change will allow to do that, then, after starting
the VM, change it to non-persistent - so it will not be attached again
at further startups.

QubesOS/qubes-issues#3055
This commit is contained in:
Marek Marczykowski-Górecki 2017-09-01 12:42:02 +02:00
부모 1f5d43a094
커밋 9d062c4c66
No known key found for this signature in database
GPG 키 ID: 063938BA42CFA724
2개의 변경된 파일70개의 추가작업 그리고 2개의 파일을 삭제

파일 보기

@ -270,6 +270,27 @@ class DeviceCollection(object):
device_assignment.bus = self._bus
self._set.add(device_assignment)
def update_persistent(self, device: DeviceInfo, persistent: bool):
'''Update `persistent` flag of already attached device.
'''
if self._vm.is_halted():
raise qubes.exc.QubesVMNotStartedError(self._vm,
'VM must be running to modify device persistence flag')
assignments = [a for a in self.assignments() if a.device == device]
if not assignments:
raise qubes.exc.QubesValueError('Device not assigned')
assert len(assignments) == 1
assignment = assignments[0]
# be careful to use already present assignment, not the provided one
# - to not change options as a side effect
if persistent and device not in self._set:
assignment.persistent = True
self._set.add(assignment)
elif not persistent and device in self._set:
self._set.discard(assignment)
@asyncio.coroutine
def detach(self, device_assignment: DeviceAssignment):
'''Detach (remove) device from domain.

파일 보기

@ -71,6 +71,9 @@ class TestVM(qubes.tests.TestEmitter):
def is_halted(self):
return not self.running
def is_running(self):
return self.running
class TC_00_DeviceCollection(qubes.tests.QubesTestCase):
@ -130,8 +133,6 @@ class TC_00_DeviceCollection(qubes.tests.QubesTestCase):
self.loop.run_until_complete(self.collection.attach(self.assignment))
self.assertEventFired(self.emitter, 'device-list-attached:testclass')
self.assertEqual({self.device}, set(self.collection.persistent()))
self.assertEqual({self.device},
set(self.collection.persistent()))
self.assertEqual(set([]),
set(self.collection.attached()))
@ -153,6 +154,52 @@ class TC_00_DeviceCollection(qubes.tests.QubesTestCase):
self.assertEqual({self.device}, set(self.collection))
self.assertEventFired(self.emitter, 'device-list:testclass')
def test_020_update_persistent_to_false(self):
self.emitter.running = True
self.assertEqual(set([]), set(self.collection.persistent()))
self.loop.run_until_complete(self.collection.attach(self.assignment))
# device-attach event not implemented, so manipulate object manually
self.device.frontend_domain = self.emitter
self.assertEqual({self.device}, set(self.collection.persistent()))
self.assertEqual({self.device}, set(self.collection.attached()))
self.assertEqual({self.device}, set(self.collection.persistent()))
self.assertEqual({self.device}, set(self.collection.attached()))
self.collection.update_persistent(self.device, False)
self.assertEqual(set(), set(self.collection.persistent()))
self.assertEqual({self.device}, set(self.collection.attached()))
def test_021_update_persistent_to_true(self):
self.assignment.persistent = False
self.emitter.running = True
self.assertEqual(set([]), set(self.collection.persistent()))
self.loop.run_until_complete(self.collection.attach(self.assignment))
# device-attach event not implemented, so manipulate object manually
self.device.frontend_domain = self.emitter
self.assertEqual(set(), set(self.collection.persistent()))
self.assertEqual({self.device}, set(self.collection.attached()))
self.assertEqual(set(), set(self.collection.persistent()))
self.assertEqual({self.device}, set(self.collection.attached()))
self.collection.update_persistent(self.device, True)
self.assertEqual({self.device}, set(self.collection.persistent()))
self.assertEqual({self.device}, set(self.collection.attached()))
def test_022_update_persistent_reject_not_running(self):
self.assertEqual(set([]), set(self.collection.persistent()))
self.loop.run_until_complete(self.collection.attach(self.assignment))
self.assertEqual({self.device}, set(self.collection.persistent()))
self.assertEqual(set(), set(self.collection.attached()))
with self.assertRaises(qubes.exc.QubesVMNotStartedError):
self.collection.update_persistent(self.device, False)
def test_023_update_persistent_reject_not_attached(self):
self.assertEqual(set(), set(self.collection.persistent()))
self.assertEqual(set(), set(self.collection.attached()))
self.emitter.running = True
with self.assertRaises(qubes.exc.QubesValueError):
self.collection.update_persistent(self.device, True)
with self.assertRaises(qubes.exc.QubesValueError):
self.collection.update_persistent(self.device, False)
class TC_01_DeviceManager(qubes.tests.QubesTestCase):
def setUp(self):