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
|
'''Qubes Virtual Machines
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
import asyncio
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
|
|
||||||
@ -265,6 +266,8 @@ class BaseVM(qubes.PropertyHolder):
|
|||||||
# self.app must be set before super().__init__, because some property
|
# self.app must be set before super().__init__, because some property
|
||||||
# setters need working .app attribute
|
# setters need working .app attribute
|
||||||
#: mother :py:class:`qubes.Qubes` object
|
#: mother :py:class:`qubes.Qubes` object
|
||||||
|
self._qdb_watch_paths = set()
|
||||||
|
self._qdb_connection_watch = None
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
||||||
super(BaseVM, self).__init__(xml, **kwargs)
|
super(BaseVM, self).__init__(xml, **kwargs)
|
||||||
@ -387,6 +390,55 @@ class BaseVM(qubes.PropertyHolder):
|
|||||||
]).render(vm=self)
|
]).render(vm=self)
|
||||||
return domain_config
|
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):
|
class VMProperty(qubes.property):
|
||||||
'''Property that is referring to a VM
|
'''Property that is referring to a VM
|
||||||
|
@ -55,6 +55,9 @@ class AdminVM(qubes.vm.BaseVM):
|
|||||||
self._qdb_connection = None
|
self._qdb_connection = None
|
||||||
self._libvirt_domain = None
|
self._libvirt_domain = None
|
||||||
|
|
||||||
|
if not self.app.vmm.offline_mode:
|
||||||
|
self.start_qdb_watch('dom0')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
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.
|
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)
|
.. event:: backup-get-files (subject, event)
|
||||||
|
|
||||||
Collects additional file to be included in a backup.
|
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:
|
if self.storage is None:
|
||||||
self.storage = qubes.storage.Storage(self)
|
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')
|
@qubes.events.handler('property-set:label')
|
||||||
def on_property_set_label(self, event, name, newvalue, oldvalue=None):
|
def on_property_set_label(self, event, name, newvalue, oldvalue=None):
|
||||||
# pylint: disable=unused-argument
|
# 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.fire_event('domain-qdb-create')
|
||||||
|
|
||||||
|
self.start_qdb_watch(self.name)
|
||||||
|
|
||||||
# TODO async; update this in constructor
|
# TODO async; update this in constructor
|
||||||
def _update_libvirt_domain(self):
|
def _update_libvirt_domain(self):
|
||||||
'''Re-initialise :py:attr:`libvirt_domain`.'''
|
'''Re-initialise :py:attr:`libvirt_domain`.'''
|
||||||
|
Loading…
Reference in New Issue
Block a user