vm: add API for watching changes in QubesDB
Provide an API for use QubesDB.watch() inside of qubesd. Fixes QubesOS/qubes-issues#2940
This commit is contained in:
parent
d5b94d1cbd
commit
b7f0cf7d82
@ -24,6 +24,7 @@
|
||||
'''Qubes Virtual Machines
|
||||
|
||||
'''
|
||||
import asyncio
|
||||
import re
|
||||
import string
|
||||
|
||||
@ -265,6 +266,8 @@ class BaseVM(qubes.PropertyHolder):
|
||||
# self.app must be set before super().__init__, because some property
|
||||
# setters need working .app attribute
|
||||
#: mother :py:class:`qubes.Qubes` object
|
||||
self._qdb_watch_paths = set()
|
||||
self._qdb_connection_watch = None
|
||||
self.app = app
|
||||
|
||||
super(BaseVM, self).__init__(xml, **kwargs)
|
||||
@ -387,6 +390,55 @@ class BaseVM(qubes.PropertyHolder):
|
||||
]).render(vm=self)
|
||||
return domain_config
|
||||
|
||||
def watch_qdb_path(self, path):
|
||||
'''Add a QubesDB path to be watched.
|
||||
|
||||
Each change to the path will cause `domain-qdb-change:path` event to be
|
||||
fired.
|
||||
You can call this method for example in response to
|
||||
`domain-init` and `domain-load` events.
|
||||
'''
|
||||
|
||||
if path not in self._qdb_watch_paths:
|
||||
self._qdb_watch_paths.add(path)
|
||||
if self._qdb_connection_watch:
|
||||
self._qdb_connection_watch.watch(path)
|
||||
|
||||
def _qdb_watch_reader(self, loop):
|
||||
'''Callback when self._qdb_connection_watch.watch_fd() FD is
|
||||
readable.
|
||||
|
||||
Read reported event (watched path change) and fire appropriate event.
|
||||
'''
|
||||
import qubesdb # pylint: disable=import-error
|
||||
try:
|
||||
path = self._qdb_connection_watch.read_watch()
|
||||
for watched_path in self._qdb_watch_paths:
|
||||
if watched_path == path or (
|
||||
watched_path.endswith('/') and
|
||||
path.startswith(watched_path)):
|
||||
self.fire_event('domain-qdb-change:' + watched_path,
|
||||
path=path)
|
||||
except qubesdb.DisconnectedError:
|
||||
loop.remove_reader(self._qdb_connection_watch.watch_fd())
|
||||
self._qdb_connection_watch.close()
|
||||
self._qdb_connection_watch = None
|
||||
|
||||
def start_qdb_watch(self, name, loop=None):
|
||||
'''Start watching QubesDB
|
||||
|
||||
Calling this method in appropriate time is responsibility of child
|
||||
class.
|
||||
'''
|
||||
import qubesdb # pylint: disable=import-error
|
||||
self._qdb_connection_watch = qubesdb.QubesDB(name)
|
||||
if loop is None:
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.add_reader(self._qdb_connection_watch.watch_fd(),
|
||||
self._qdb_watch_reader, loop)
|
||||
for path in self._qdb_watch_paths:
|
||||
self._qdb_connection_watch.watch(path)
|
||||
|
||||
|
||||
class VMProperty(qubes.property):
|
||||
'''Property that is referring to a VM
|
||||
|
@ -55,6 +55,9 @@ class AdminVM(qubes.vm.BaseVM):
|
||||
self._qdb_connection = None
|
||||
self._libvirt_domain = None
|
||||
|
||||
if not self.app.vmm.offline_mode:
|
||||
self.start_qdb_watch('dom0')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
@ -248,6 +248,18 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
||||
|
||||
This event is a good place to add your custom entries to the qdb.
|
||||
|
||||
.. event:: domain-qdb-change:watched-path (subject, event, path)
|
||||
|
||||
Fired when watched QubesDB entry is changed. See
|
||||
:py:meth:`watch_qdb_path`. *watched-path* part of event name is
|
||||
what path was registered for watching, *path* in event argument
|
||||
is what actually have changed (which may be different if watching a
|
||||
directory, i.e. a path with `/` at the end).
|
||||
|
||||
:param subject: Event emitter (the qube object)
|
||||
:param event: Event name (``'domain-qdb-change'``)
|
||||
:param path: changed QubesDB path
|
||||
|
||||
.. event:: backup-get-files (subject, event)
|
||||
|
||||
Collects additional file to be included in a backup.
|
||||
@ -726,6 +738,9 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
||||
if self.storage is None:
|
||||
self.storage = qubes.storage.Storage(self)
|
||||
|
||||
if not self.app.vmm.offline_mode and self.is_running():
|
||||
self.start_qdb_watch(self.name)
|
||||
|
||||
@qubes.events.handler('property-set:label')
|
||||
def on_property_set_label(self, event, name, newvalue, oldvalue=None):
|
||||
# pylint: disable=unused-argument
|
||||
@ -1770,6 +1785,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
||||
|
||||
self.fire_event('domain-qdb-create')
|
||||
|
||||
self.start_qdb_watch(self.name)
|
||||
|
||||
# TODO async; update this in constructor
|
||||
def _update_libvirt_domain(self):
|
||||
'''Re-initialise :py:attr:`libvirt_domain`.'''
|
||||
|
Loading…
Reference in New Issue
Block a user