libvirtobject: Unify internal status APIs and signals

Drop config-changes vs. status-changed and just use one signal, since they
are largely the same code paths for all users.
This commit is contained in:
Cole Robinson 2015-04-09 18:02:42 -04:00
parent 01bf07ba11
commit a3f8d73a9c
15 changed files with 181 additions and 216 deletions

View File

@ -678,6 +678,8 @@ class vmmConnection(vmmGObject):
obj = self._vms.get(domain.name(), None)
if not obj:
return
# Uses forcesignal=True
self.idle_add(obj.refresh_xml, True)
def _domain_lifecycle_event(self, conn, domain, event, reason, userdata):
@ -692,6 +694,7 @@ class vmmConnection(vmmGObject):
self.idle_add(obj.force_update_status, True)
if event == libvirt.VIR_DOMAIN_EVENT_DEFINED:
# Uses forcesignal=True
self.idle_add(obj.refresh_xml, True)
else:
self.schedule_priority_tick(pollvm=True, force=True)
@ -706,6 +709,7 @@ class vmmConnection(vmmGObject):
self.idle_add(obj.force_update_status, True)
if event == getattr(libvirt, "VIR_NETWORK_EVENT_DEFINED", 0):
# Uses forcesignal=True
self.idle_add(obj.refresh_xml, True)
else:
self.schedule_priority_tick(pollnet=True, force=True)

View File

@ -51,7 +51,7 @@ class vmmConsolePages(vmmGObjectUI):
self.vm = vm
self._pointer_is_grabbed = False
self._change_title()
self.vm.connect("config-changed", self._change_title)
self.vm.connect("state-changed", self._change_title)
self._force_resize = False
# State for disabling modifiers when keyboard is grabbed

View File

@ -1891,7 +1891,7 @@ class vmmCreate(vmmGObjectUI):
# Register a status listener, which will restart the
# guest after the install has finished
def cb():
vm.connect_opt_out("status-changed",
vm.connect_opt_out("state-changed",
self.check_install_status, guest)
return False
self.idle_add(cb)
@ -1925,7 +1925,7 @@ class vmmCreate(vmmGObjectUI):
# will force one final restart.
virtinst_guest.continue_install()
vm.connect_opt_out("status-changed",
vm.connect_opt_out("state-changed",
self.check_install_status, None)
return True

View File

@ -574,8 +574,7 @@ class vmmDetails(vmmGObjectUI):
})
# Deliberately keep all this after signal connection
self.vm.connect("status-changed", self.refresh_vm_state)
self.vm.connect("config-changed", self.refresh_vm_state)
self.vm.connect("state-changed", self.refresh_vm_state)
self.vm.connect("resources-sampled", self.refresh_resources)
self.populate_hw_list()

View File

@ -177,6 +177,8 @@ class vmmDomainSnapshot(vmmLibvirtObject):
def _XMLDesc(self, flags):
return self._backend.getXMLDesc(flags=flags)
def _get_backend_status(self):
return self._STATUS_ACTIVE
def delete(self, force=True):
ignore = force
@ -214,6 +216,8 @@ class vmmDomain(vmmLibvirtObject):
"pre-startup": (GObject.SignalFlags.RUN_FIRST, None, [object]),
}
_conn_tick_poll_param = "pollvm"
@staticmethod
def pretty_run_status(status, has_saved=False):
if status == libvirt.VIR_DOMAIN_RUNNING:
@ -302,10 +306,7 @@ class vmmDomain(vmmLibvirtObject):
self._snapshot_list = None
self._autostart = None
self._domain_caps = None
self.lastStatus = libvirt.VIR_DOMAIN_SHUTOFF
self._lastStatusReason = getattr(libvirt, "VIR_DOMAIN_SHUTOFF_SHUTDOWN",
1)
self._status_reason = None
self.managedsave_supported = False
self.remote_console_supported = False
@ -362,7 +363,7 @@ class vmmDomain(vmmLibvirtObject):
self.toggle_sample_mem_stats()
self.toggle_sample_cpu_stats()
self.force_update_status(from_event=True, log=False)
self.force_update_status(from_event=True)
# Prime caches
self.refresh_xml()
@ -426,10 +427,14 @@ class vmmDomain(vmmLibvirtObject):
return self._id
def status(self):
return self.lastStatus
return self._normalize_status(self._get_status())
def status_reason(self):
return self._lastStatusReason
if self._status_reason is None:
self._status_reason = 1
if self.domain_state_supported:
self._status_reason = self._backend.state()[1]
return self._status_reason
def get_cloning(self):
return self.cloning
@ -520,6 +525,9 @@ class vmmDomain(vmmLibvirtObject):
def _invalidate_xml(self):
vmmLibvirtObject._invalidate_xml(self)
self._id = None
self._has_managed_save = None
self._status_reason = None
self._has_managed_save = None
def _lookup_device_to_define(self, origdev, guest=None):
if guest is None:
@ -1034,6 +1042,8 @@ class vmmDomain(vmmLibvirtObject):
self.conn.define_domain(newxml)
def _XMLDesc(self, flags):
return self._backend.XMLDesc(flags)
def _get_backend_status(self):
return self._backend.info()[0]
def get_autostart(self):
if self._autostart is None:
@ -1345,7 +1355,7 @@ class vmmDomain(vmmLibvirtObject):
self.shutdown()
def add_reboot():
self.reboot_listener = self.connect_opt_out("status-changed",
self.reboot_listener = self.connect_opt_out("state-changed",
reboot_listener, self)
self.idle_add(add_reboot)
@ -1716,48 +1726,6 @@ class vmmDomain(vmmLibvirtObject):
status = libvirt.VIR_DOMAIN_NOSTATE
return vm_status_icons[status]
def force_update_status(self, from_event=False, log=True):
"""
Fetch current domain state and clear status cache
"""
if not from_event and self._using_events():
return
try:
info = self._backend.info()
if log:
logging.debug("domain=%s status changed to %d=%s",
self.get_name(), info[0], self.pretty_run_status(info[0]))
self._update_status(info[0])
except libvirt.libvirtError, e:
# Transient domain might have disappeared, tell the connection
# to update the domain list
logging.debug("Error setting domain status: %s\nDomain might "
"have disappeared, triggering connection tick", e)
self.conn.schedule_priority_tick(pollvm=True, force=True)
def _update_status(self, status):
"""
Internal helper to change cached status to 'status' and signal
clients if we actually changed state
"""
status = self._normalize_status(status)
if status == self.lastStatus:
return
self.lastStatus = status
if self.domain_state_supported:
self._lastStatusReason = self._backend.state()[1]
self._has_managed_save = None
# Send 'config-changed' before a status-update, so users
# are operating with fresh XML
self.refresh_xml()
self.idle_emit("status-changed")
def inspection_data_updated(self):
self.idle_emit("inspection-changed")
@ -1934,8 +1902,8 @@ class vmmDomain(vmmLibvirtObject):
if stats_update:
self._tick_stats(info)
if not self._using_events():
self._update_status(info[0])
# This is a no-op if using events
self.force_update_status(newstatus=info[0])
if stats_update:
self.idle_emit("resources-sampled")
@ -2015,7 +1983,7 @@ class vmmDomainVirtinst(vmmDomain):
return self._backend.autostart
def set_autostart(self, val):
self._backend.autostart = bool(val)
self.emit("config-changed")
self.emit("state-changed")
def _using_events(self):
return False
@ -2038,7 +2006,7 @@ class vmmDomainVirtinst(vmmDomain):
def _define(self, newxml):
ignore = newxml
self.emit("config-changed")
self.emit("state-changed")
def _invalidate_xml(self):
vmmDomain._invalidate_xml(self)

View File

@ -703,7 +703,7 @@ class vmmHost(vmmGObjectUI):
net.disconnect_by_func(self.refresh_network)
except:
pass
net.connect("status-changed", self.refresh_network)
net.connect("state-changed", self.refresh_network)
model.append([net.get_connkey(), net.get_name(), "network-idle",
Gtk.IconSize.LARGE_TOOLBAR,
bool(net.is_active())])
@ -955,7 +955,7 @@ class vmmHost(vmmGObjectUI):
iface.disconnect_by_func(self.refresh_interface)
except:
pass
iface.connect("status-changed", self.refresh_interface)
iface.connect("state-changed", self.refresh_interface)
model.append([iface.get_connkey(), iface.get_name(),
"network-idle", Gtk.IconSize.LARGE_TOOLBAR,
bool(iface.is_active())])

View File

@ -24,16 +24,14 @@ from .libvirtobject import vmmLibvirtObject
class vmmInterface(vmmLibvirtObject):
_conn_tick_poll_param = "polliface"
def __init__(self, conn, backend, key):
vmmLibvirtObject.__init__(self, conn, backend, key, Interface)
self._active = True
(self._inactive_xml_flags,
self._active_xml_flags) = self.conn.get_interface_flags(self._backend)
self._support_isactive = None
self.tick()
# Routines from vmmLibvirtObject
@ -41,36 +39,21 @@ class vmmInterface(vmmLibvirtObject):
return self._backend.XMLDesc(flags)
def _define(self, xml):
return self.conn.define_interface(xml)
def _set_active(self, state):
if state == self._active:
return
self._active = state
self._invalidate_xml()
self.idle_emit("status-changed")
def _backend_get_active(self):
ret = True
if self._support_isactive is None:
self._support_isactive = self.conn.check_support(
self.conn.SUPPORT_INTERFACE_ISACTIVE, self._backend)
if not self._support_isactive:
return True
return bool(self._backend.isActive())
def tick(self):
self._set_active(self._backend_get_active())
def is_active(self):
return self._active
def get_mac(self):
return self.get_xmlobj().macaddr
def _check_supports_isactive(self):
return self.conn.check_support(
self.conn.SUPPORT_INTERFACE_ISACTIVE, self._backend)
def _get_backend_status(self):
return self._backend_get_active()
def _kick_conn(self):
self.conn.schedule_priority_tick(polliface=True)
def tick(self):
self.force_update_status()
#####################
# Object operations #
#####################
def start(self):
self._backend.create(0)
@ -87,6 +70,14 @@ class vmmInterface(vmmLibvirtObject):
self._backend.undefine()
self._kick_conn()
################
# XML routines #
################
def get_mac(self):
return self.get_xmlobj().macaddr
def is_bridge(self):
typ = self.get_type()
return typ == "bridge"

View File

@ -27,12 +27,18 @@ from .baseclass import vmmGObject
class vmmLibvirtObject(vmmGObject):
__gsignals__ = {
"status-changed": (GObject.SignalFlags.RUN_FIRST, None, []),
"config-changed": (GObject.SignalFlags.RUN_FIRST, None, []),
"state-changed": (GObject.SignalFlags.RUN_FIRST, None, []),
"started": (GObject.SignalFlags.RUN_FIRST, None, []),
"stopped": (GObject.SignalFlags.RUN_FIRST, None, []),
}
_STATUS_ACTIVE = 1
_STATUS_INACTIVE = 2
# The parameter name for conn.tick() object polling. So
# for vmmDomain == "pollvm"
_conn_tick_poll_param = None
def __init__(self, conn, backend, key, parseclass):
vmmGObject.__init__(self)
self._conn = conn
@ -40,6 +46,9 @@ class vmmLibvirtObject(vmmGObject):
self._key = key
self._parseclass = parseclass
self.__status = self._STATUS_ACTIVE
self._support_isactive = None
self._xmlobj = None
self._xmlobj_to_define = None
self._is_xml_valid = False
@ -52,6 +61,7 @@ class vmmLibvirtObject(vmmGObject):
self._name = None
self.get_name()
@staticmethod
def log_redefine_xml_diff(obj, origxml, newxml):
objname = "<%s name=%s>" % (obj.__class__.__name__, obj.get_name())
@ -105,7 +115,7 @@ class vmmLibvirtObject(vmmGObject):
finally:
self._invalidate_xml()
self.emit("config-changed")
self.emit("state-changed")
#############################################################
@ -116,6 +126,10 @@ class vmmLibvirtObject(vmmGObject):
raise NotImplementedError()
def _using_events(self):
return False
def _check_supports_isactive(self):
return False
def _get_backend_status(self):
raise NotImplementedError()
def _define(self, xml):
ignore = xml
@ -124,10 +138,6 @@ class vmmLibvirtObject(vmmGObject):
def delete(self, force=True):
ignore = force
def force_update_status(self, from_event=False, log=True):
ignore = from_event
ignore = log
def get_name(self):
if self._name is None:
self._name = self._backend_get_name()
@ -137,16 +147,65 @@ class vmmLibvirtObject(vmmGObject):
return self._backend.name()
###################
# Status handling #
###################
def _get_status(self):
return self.__status
def is_active(self):
# vmmDomain overwrites this since it has more fine grained statuses
return self._get_status() == self._STATUS_ACTIVE
def force_update_status(self, from_event=False, newstatus=None):
"""
:param newstatus: Used by vmmDomain as a small optimization to
avoid polling info() twice
"""
if self._using_events() and not from_event:
return
try:
status = newstatus
if newstatus is None:
status = self._get_backend_status()
if status == self.__status:
return
self.__status = status
# This will send state-change for us
self.refresh_xml(forcesignal=True)
except:
# If we hit an exception here, it's often that the object
# disappeared, so request the poll loop to be updated
if self._conn_tick_poll_param:
logging.debug("force_update_status: Triggering %s "
"list refresh", self.__class__)
kwargs = {"force": True, self._conn_tick_poll_param: True}
self.conn.schedule_priority_tick(**kwargs)
def _backend_get_active(self):
if self._support_isactive is None:
self._support_isactive = self._check_supports_isactive()
if not self._support_isactive:
return self._STATUS_ACTIVE
return (bool(self._backend.isActive()) and
self._STATUS_ACTIVE or
self._STATUS_INACTIVE)
##################
# Public XML API #
##################
def refresh_xml(self, forcesignal=False):
"""
Force an xml update. Signal 'config-changed' if domain xml has
Force an xml update. Signal 'state-changed' if domain xml has
changed since last refresh
:param forcesignal: Send config-changed unconditionally
:param forcesignal: Send state-changed unconditionally
"""
origxml = None
if self._xmlobj:
@ -159,7 +218,7 @@ class vmmLibvirtObject(vmmGObject):
self._is_xml_valid = True
if forcesignal or origxml != active_xml:
self.idle_emit("config-changed")
self.idle_emit("state-changed")
def get_xmlobj(self, inactive=False, refresh_if_nec=True):
"""

View File

@ -588,8 +588,7 @@ class vmmManager(vmmGObjectUI):
if self.vm_row_key(vm) in self.rows:
return
vm.connect("config-changed", self.vm_config_changed)
vm.connect("status-changed", self.vm_status_changed)
vm.connect("state-changed", self.vm_changed)
vm.connect("resources-sampled", self.vm_row_updated)
vm.connect("inspection-changed", self.vm_inspection_changed)
@ -778,12 +777,15 @@ class vmmManager(vmmGObjectUI):
return
self.widget("vm-list").get_model().row_changed(row.path, row.iter)
def vm_config_changed(self, vm):
def vm_changed(self, vm):
row = self.rows.get(self.vm_row_key(vm), None)
if row is None:
return
try:
if vm == self.current_vm():
self.update_current_selection()
name = vm.get_name_or_title()
status = vm.run_status()
@ -803,25 +805,6 @@ class vmmManager(vmmGObjectUI):
self.vm_row_updated(vm)
def vm_status_changed(self, vm):
parent = self.rows[vm.conn.get_uri()].iter
vmlist = self.widget("vm-list")
model = vmlist.get_model()
missing = True
for row in range(model.iter_n_children(parent)):
_iter = model.iter_nth_child(parent, row)
if model[_iter][ROW_HANDLE] == vm:
missing = False
break
if missing:
self._append_vm(model, vm, vm.conn)
# Update run/shutdown/pause button states
self.update_current_selection()
self.vm_config_changed(vm)
def vm_inspection_changed(self, vm):
row = self.rows.get(self.vm_row_key(vm), None)
if row is None:

View File

@ -18,8 +18,6 @@
# MA 02110-1301 USA.
#
import logging
import ipaddr
from virtinst import Network
@ -41,11 +39,10 @@ def _make_addr_str(addrStr, prefix, netmaskStr):
class vmmNetwork(vmmLibvirtObject):
_conn_tick_poll_param = "pollnet"
def __init__(self, conn, backend, key):
vmmLibvirtObject.__init__(self, conn, backend, key, Network)
self._active = True
self._support_isactive = None
self.force_update_status(from_event=True)
@ -60,47 +57,22 @@ class vmmNetwork(vmmLibvirtObject):
return self.conn.define_network(xml)
def _using_events(self):
return self.conn.using_network_events
def _check_supports_isactive(self):
return self.conn.check_support(
self.conn.SUPPORT_NET_ISACTIVE, self._backend)
def _get_backend_status(self):
return self._backend_get_active()
def _kick_conn(self):
self.conn.schedule_priority_tick(pollnet=True)
def tick(self):
self.force_update_status()
###########
# Actions #
###########
def _backend_get_active(self):
if self._support_isactive is None:
self._support_isactive = self.conn.check_support(
self.conn.SUPPORT_NET_ISACTIVE, self._backend)
if not self._support_isactive:
return True
return bool(self._backend.isActive())
def _set_active(self, state):
if state == self._active:
return
self._active = state
self._invalidate_xml()
self.idle_emit("status-changed")
def force_update_status(self, from_event=False, log=True):
ignore = log
if self._using_events() and not from_event:
return
try:
self._set_active(self._backend_get_active())
except:
logging.debug("force_update_status: Triggering network "
"list refresh")
self.conn.schedule_priority_tick(pollnet=True, force=True)
def is_active(self):
return self._active
def _kick_conn(self):
self.conn.schedule_priority_tick(pollnet=True)
def start(self):
self._backend.create()
self._kick_conn()
@ -115,14 +87,16 @@ class vmmNetwork(vmmLibvirtObject):
self._backend = None
self._kick_conn()
###############################
# XML/config handling parsing #
###############################
def get_autostart(self):
return self._backend.autostart()
def set_autostart(self, value):
self._backend.setAutostart(value)
def tick(self):
self.force_update_status()
def set_qos(self, **kwargs):
xmlobj = self._get_xmlobj_to_define()
q = xmlobj.bandwidth
@ -132,11 +106,6 @@ class vmmNetwork(vmmLibvirtObject):
self.redefine_cached()
return self.is_active()
###############
# XML parsing #
###############
def get_uuid(self):
return self.get_xmlobj().uuid
def get_bridge_device(self):

View File

@ -28,12 +28,16 @@ def _parse_convert(conn, parsexml=None):
class vmmNodeDevice(vmmLibvirtObject):
_conn_tick_poll_param = "pollnodedev"
def __init__(self, conn, backend, key):
vmmLibvirtObject.__init__(self, conn, backend, key, _parse_convert)
self._name = key
def _XMLDesc(self, flags):
return self._backend.XMLDesc(flags)
def _get_backend_status(self):
return self._STATUS_ACTIVE
def is_active(self):
return True

View File

@ -320,7 +320,7 @@ class vmmSerialConsole(vmmGObject):
self.error_label = None
self.init_ui()
self.vm.connect("status-changed", self.vm_status_changed)
self.vm.connect("state-changed", self.vm_status_changed)
def init_terminal(self):
self.terminal = Vte.Terminal()

View File

@ -384,7 +384,7 @@ class vmmStorageList(vmmGObjectUI):
pool.disconnect_by_func(self._pool_changed)
except:
pass
pool.connect("status-changed", self._pool_changed)
pool.connect("state-changed", self._pool_changed)
pool.connect("refreshed", self._pool_changed)
name = pool.get_name()

View File

@ -47,6 +47,9 @@ class vmmStorageVolume(vmmLibvirtObject):
self._backend.key(), e)
raise
def _get_backend_status(self):
return self._STATUS_ACTIVE
###########
# Actions #
@ -100,13 +103,12 @@ class vmmStoragePool(vmmLibvirtObject):
"refreshed": (GObject.SignalFlags.RUN_FIRST, None, [])
}
_conn_tick_poll_param = "pollpool"
def __init__(self, conn, backend, key):
vmmLibvirtObject.__init__(self, conn, backend, key, StoragePool)
self._active = True
self._support_isactive = None
self._last_refresh_time = 0
self._volumes = {}
self.tick()
@ -120,47 +122,22 @@ class vmmStoragePool(vmmLibvirtObject):
return self._backend.XMLDesc(flags)
def _define(self, xml):
return self.conn.define_pool(xml)
def _check_supports_isactive(self):
return self.conn.check_support(
self.conn.SUPPORT_POOL_ISACTIVE, self._backend)
def _get_backend_status(self):
return self._backend_get_active()
def _kick_conn(self):
self.conn.schedule_priority_tick(pollpool=True)
def tick(self):
self.force_update_status()
###########
# Actions #
###########
def is_active(self):
return self._active
def _backend_get_active(self):
if self._support_isactive is None:
self._support_isactive = self.conn.check_support(
self.conn.SUPPORT_POOL_ISACTIVE, self._backend)
if not self._support_isactive:
return True
return bool(self._backend.isActive())
def _set_active(self, state):
if state == self._active:
return
self._active = state
self._invalidate_xml()
self.idle_emit("status-changed")
def _kick_conn(self):
self.conn.schedule_priority_tick(pollpool=True)
def tick(self):
self._set_active(self._backend_get_active())
def set_autostart(self, value):
self._backend.setAutostart(value)
def get_autostart(self):
return self._backend.autostart()
def can_change_alloc(self):
typ = self.get_type()
return (typ in [StoragePool.TYPE_LOGICAL])
def supports_volume_creation(self):
return self.get_xmlobj().supports_volume_creation()
def start(self):
self._backend.create(0)
self._kick_conn()
@ -212,9 +189,20 @@ class vmmStoragePool(vmmLibvirtObject):
self._volumes = allvols
#################
# XML accessors #
#################
#########################
# XML/config operations #
#########################
def set_autostart(self, value):
self._backend.setAutostart(value)
def get_autostart(self):
return self._backend.autostart()
def can_change_alloc(self):
typ = self.get_type()
return (typ in [StoragePool.TYPE_LOGICAL])
def supports_volume_creation(self):
return self.get_xmlobj().supports_volume_creation()
def get_type(self):
return self.get_xmlobj().type

View File

@ -280,7 +280,7 @@ class vmmSystray(vmmGObject):
vm = conn.get_vm(connkey)
if not vm:
return
vm.connect("status-changed", self.vm_state_changed)
vm.connect("state-changed", self.vm_state_changed)
vm_mappings = self.conn_vm_menuitems[uri]
if connkey in vm_mappings: