Use libvirt events for domain lifecycle tracking (bz 836703)

When events were successfully registered, we skip the VM listing on
every tick, and instead trigger a manual refresh whenever a VM event
is received. Not as efficient as it should be, but saves us a lot of
API calls.
This commit is contained in:
Cole Robinson 2014-02-11 12:07:13 -05:00
parent 020957c756
commit c7cd6ca2d3
3 changed files with 74 additions and 16 deletions

View File

@ -97,6 +97,7 @@ class vmmConnection(vmmGObject):
self._storage_capable = None
self._interface_capable = None
self._nodedev_capable = None
self.using_domain_events = False
self._xml_flags = {}
@ -819,6 +820,42 @@ class vmmConnection(vmmGObject):
return self._rename_helper("storagepool", self.define_pool,
obj, origxml, newxml)
#########################
# Domain event handling #
#########################
# Our strategy here isn't the most efficient: since we need to keep the
# poll helpers around for compat with old libvirt, switching to a fully
# event driven setup is hard, so we end up doing more polling than
# necessary on most events.
def _domain_lifecycle_event(self, conn, domain, event, reason, userdata):
ignore = conn
ignore = reason
ignore = userdata
domobj = self.vms.get(domain.UUIDString(), None)
if domobj:
# If the domain disappeared, this will catch it and trigger
# a domain list refresh
self.idle_add(domobj.force_update_status, True)
if event == libvirt.VIR_DOMAIN_EVENT_DEFINED:
self.idle_add(domobj.refresh_xml)
else:
self.schedule_priority_tick(pollvm=True, force=True)
def _add_conn_domain_event(self):
try:
self.get_backend().domainEventRegisterAny(None,
libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE,
self._domain_lifecycle_event, None)
self.using_domain_events = True
except Exception, e:
self.using_domain_events = False
logging.debug("Error registering domain events: %s", e)
####################
# Update listeners #
@ -883,6 +920,7 @@ class vmmConnection(vmmGObject):
cleanup(self.vms)
self.vms = {}
self.using_domain_events = False
self._change_state(self.STATE_DISCONNECTED)
def _cleanup(self):
@ -987,10 +1025,12 @@ class vmmConnection(vmmGObject):
logging.debug("conn version=%s", self._backend.conn_version())
logging.debug("%s capabilities:\n%s",
self.get_uri(), self.caps.xml)
self._add_conn_domain_event()
self.schedule_priority_tick(stats_update=True,
pollvm=True, pollnet=True,
pollpool=True, polliface=True,
pollnodedev=True, pollmedia=True)
pollnodedev=True, pollmedia=True,
force=True)
if self.state == self.STATE_DISCONNECTED:
if self.connectError:
@ -1047,13 +1087,19 @@ class vmmConnection(vmmGObject):
def tick(self, stats_update,
pollvm=False, pollnet=False,
pollpool=False, polliface=False,
pollnodedev=False, pollmedia=False):
""" main update function: polls for new objects, updates stats, ..."""
pollnodedev=False, pollmedia=False,
force=False):
"""
main update function: polls for new objects, updates stats, ...
@force: Perform the requested polling even if async events are in use
"""
if self.state != self.STATE_ACTIVE:
return
if not pollvm:
stats_update = False
if self.using_domain_events and not force:
pollvm = False
self.hostinfo = self._backend.getInfo()
@ -1146,7 +1192,7 @@ class vmmConnection(vmmGObject):
if stats_update:
updateVMs = vms
if pollvm:
if stats_update:
for key in vms:
if key in updateVMs:
add_to_ticklist([vms[key]], (True,))

View File

@ -304,7 +304,7 @@ class vmmDomain(vmmLibvirtObject):
self.toggle_sample_disk_io()
self.toggle_sample_mem_stats()
self.force_update_status()
self.force_update_status(from_event=True)
# Hook up listeners that need to be cleaned up
self.add_gconf_handle(
@ -352,6 +352,9 @@ class vmmDomain(vmmLibvirtObject):
# Misc API getter methods #
###########################
def _using_events(self):
return self.conn.using_domain_events
def get_name(self):
if self._name is None:
self._name = self._backend.name()
@ -1642,18 +1645,22 @@ class vmmDomain(vmmLibvirtObject):
status = libvirt.VIR_DOMAIN_NOSTATE
return vm_status_icons[status]
def force_update_status(self):
def force_update_status(self, from_event=False):
"""
Fetch current domain state and clear status cache
"""
if not from_event and self._using_events():
return
try:
info = self._backend.info()
self._update_status(info[0])
except libvirt.libvirtError, e:
if util.exception_is_libvirt_error(e, "VIR_ERR_NO_DOMAIN"):
return
raise
except libvirt.libvirtError:
# Transient domain might have disappeared, tell the connection
# to update the domain list
logging.debug("force_update_status: Triggering domain "
"list refresh")
self.conn.schedule_priority_tick(pollvm=True, force=True)
def _update_status(self, status):
"""
@ -1828,13 +1835,16 @@ class vmmDomain(vmmLibvirtObject):
def tick(self, stats_update=True):
self._invalidate_xml()
if not self._using_events():
self._invalidate_xml()
info = self._backend.info()
if stats_update:
self._tick_stats(info)
self._update_status(info[0])
if not self._using_events():
self._update_status(info[0])
if stats_update:
self.idle_emit("resources-sampled")

View File

@ -113,6 +113,8 @@ class vmmLibvirtObject(vmmGObject):
raise NotImplementedError()
def _XMLDesc(self, flags):
raise NotImplementedError()
def _using_events(self):
return False
def _define(self, xml):
ignore = xml
@ -224,9 +226,9 @@ class vmmLibvirtObject(vmmGObject):
if origxml != newxml:
self._define(newxml)
# Make sure we have latest XML
self.refresh_xml(forcesignal=True)
return
if not self._using_events():
# Make sure we have latest XML
self.refresh_xml(forcesignal=True)
def _redefine_xml(self, newxml):
origxml = self._xml_to_redefine()