commit 6f52a3bbe00121acb5f11d9ec131c90700b27419 Author: Radek Novacek rnovacek@redhat.com Date: Tue Jul 1 10:57:58 2014 +0200
libvirtd: use event loop instead of reconnecting all the time
tests/test_libvirtd.py | 24 +++++++----- virt/libvirtd/libvirtd.py | 86 ++++++++++++++++++++++++-------------------- 2 files changed, 61 insertions(+), 49 deletions(-) --- diff --git a/tests/test_libvirtd.py b/tests/test_libvirtd.py index 6077702..bb3f428 100644 --- a/tests/test_libvirtd.py +++ b/tests/test_libvirtd.py @@ -54,24 +54,28 @@ class TestLibvirtd(unittest.TestCase): self.assertRaises(VirtError, libvirtd.listDomains)
@patch('libvirt.openReadOnly') - def test_monitoring(self, virt): + @patch('threading.Thread') + def test_monitoring(self, thread, virt): event = threading.Event() LibvirtMonitor().set_event(event) - LibvirtMonitor()._prepare() - LibvirtMonitor()._checkChange() + LibvirtMonitor().check() + + thread.assert_called()
virt.assert_called_with('') - virt.return_value.listDomainsID.assert_called() - virt.return_value.listDefinedDomains.assert_called() - virt.return_value.closed.assert_called() + virt.return_value.domainEventRegister.assert_called() + virt.return_value.setKeepAlive.assert_called() self.assertFalse(event.is_set())
- virt.return_value.listDomainsID.return_value = [1] - LibvirtMonitor()._checkChange() + LibvirtMonitor()._callback() + LibvirtMonitor().check() self.assertTrue(event.is_set()) event.clear()
- virt.return_value.listDomainsID.return_value = [] - LibvirtMonitor()._checkChange() + LibvirtMonitor().check() + self.assertFalse(event.is_set()) + event.clear() + + LibvirtMonitor()._callback() self.assertTrue(event.is_set()) event.clear() diff --git a/virt/libvirtd/libvirtd.py b/virt/libvirtd/libvirtd.py index 1e37759..2b7a3b3 100644 --- a/virt/libvirtd/libvirtd.py +++ b/virt/libvirtd/libvirtd.py @@ -29,65 +29,72 @@ import virt eventLoopThread = None
-class LibvirtMonitor(threading.Thread): +def virEventLoopNativeRun(): + while True: + libvirt.virEventRunDefaultImpl() + + +class LibvirtMonitor(object): """ Singleton class that performs background event monitoring. """ _instance = None
def __new__(cls, *args, **kwargs): if not cls._instance: + print "Creating new instance of LibvirtMonitor" cls._instance = super(LibvirtMonitor, cls).__new__(cls, *args, **kwargs) + cls._instance.logger = logging.getLogger("rhsm-app") cls._instance.event = None + cls._instance.eventLoopThread = None cls._instance.terminate = threading.Event() cls._instance.domainIds = [] cls._instance.definedDomains = [] + cls._instance.vc = None return cls._instance
STATUS_NOT_STARTED, STATUS_RUNNING, STATUS_DISABLED = range(3)
def _prepare(self): - self.logger = logging.getLogger("rhsm-app") self.running = threading.Event() self.status = LibvirtMonitor.STATUS_NOT_STARTED
- def run(self): - self._prepare() - while not self.terminate.isSet(): - self._checkChange() - time.sleep(5) - - def _checkChange(self): + def _loop_start(self): + libvirt.virEventRegisterDefaultImpl() + if self.eventLoopThread is not None and self.eventLoopThread.isAlive(): + self.eventLoopThread.terminate() + self.eventLoopThread = threading.Thread(target=virEventLoopNativeRun, name="libvirtEventLoop") + self.eventLoopThread.setDaemon(True) + self.eventLoopThread.start() + self._create_connection() + + def _create_connection(self): + self.vc = libvirt.openReadOnly('') try: - virt = libvirt.openReadOnly('') - except libvirt.libvirtError, e: - # Show error only once - if self.status != LibvirtMonitor.STATUS_DISABLED: - self.logger.debug("Unable to connect to libvirtd, disabling event monitoring") - self.logger.exception(e) - self.status = LibvirtMonitor.STATUS_DISABLED - return - - domainIds = virt.listDomainsID() - definedDomains = virt.listDefinedDomains() - - changed = domainIds != self.domainIds or definedDomains != self.definedDomains - - self.domainIds = domainIds - self.definedDomains = definedDomains - - virt.close() - - if changed and self.event is not None and self.status != LibvirtMonitor.STATUS_NOT_STARTED: - self.event.set() - - if self.status == LibvirtMonitor.STATUS_DISABLED: - self.logger.debug("Event monitoring resumed") - self.status = LibvirtMonitor.STATUS_RUNNING + self.vc.registerCloseCallback(self._close_callback, None, None) + except AttributeError: + self.logger.warn("Can't monitor libvirtd restarts due to bug in libvirt-python") + self.vc.domainEventRegister(self._callback, None) + self.vc.setKeepAlive(5, 3) + + def check(self): + if self.eventLoopThread is None or not self.eventLoopThread.isAlive(): + self.logger.debug("Starting libvirt monitoring event loop") + self._loop_start() + if self.vc is None or not self.vc.isAlive(): + self.logger.debug("Reconnecting to libvirtd") + self._create_connection() + + def _callback(self, *args, **kwargs): + self.event.set() + + def _close_callback(self, *args, **kwargs): + self.logger.info("Connection to libvirtd lost, reconnecting") + self._create_connection()
def set_event(self, event): self.event = event
- def stop(self): - self.terminate.set() + def isAlive(self): + return self.eventLoopThread is not None and self.eventLoopThread.isAlive()
class Libvirtd(virt.DirectVirt): @@ -129,7 +136,9 @@ class Libvirtd(virt.DirectVirt): domains.append(virt.Domain(self.virt, domain)) self.logger.debug("Virtual machine found: %s: %s" % (domainName, domain.UUIDString())) except libvirt.libvirtError, e: + self.virt.close() raise virt.VirtError(str(e)) + self.virt.close() return domains
def canMonitor(self): @@ -137,9 +146,8 @@ class Libvirtd(virt.DirectVirt):
def startMonitoring(self, event): monitor = LibvirtMonitor() - if not monitor.isAlive(): - monitor.set_event(event) - monitor.start() + monitor.set_event(event) + monitor.check()
def eventToString(event):
virt-who-devel@lists.fedorahosted.org