diff --git a/virtManager/connection.py b/virtManager/connection.py index 8c7bb9b88..a301ccf7c 100644 --- a/virtManager/connection.py +++ b/virtManager/connection.py @@ -32,6 +32,7 @@ import traceback import libvirt import virtinst +from virtinst import pollhelpers from virtManager import util from virtManager import connectauth @@ -945,211 +946,37 @@ class vmmConnection(vmmGObject): # Tick/Update methods # ####################### - def _poll_helper(self, - origlist, typename, check_support, - active_list, inactive_list, - lookup_func, build_func): - """ - Helper routine for old style split API libvirt polling. - @origlist: Pre-existing mapping of objects, with key->obj mapping - objects must have an is_active and set_active API - @typename: string describing type of objects we are polling for use - in debug messages. - @active_list: Function that returns the list of active objects - @inactive_list: Function that returns the list of inactive objects - @lookup_func: Function to get an object handle for the passed name - @build_func: Function that builds a new object class. It is passed - args of (raw libvirt object, key (usually UUID), bool is_active) - """ - current = {} - new = {} - newActiveNames = [] - newInactiveNames = [] - - if not check_support(): - return (origlist, new, current) - - try: - newActiveNames = active_list() - except Exception, e: - logging.debug("Unable to list active %ss: %s", typename, e) - try: - newInactiveNames = inactive_list() - except Exception, e: - logging.debug("Unable to list inactive %ss: %s", typename, e) - - def check_obj(key): - if key not in origlist: - try: - obj = lookup_func(key) - except Exception, e: - logging.debug("Could not fetch %s '%s': %s", - typename, key, e) - return - - # Object is brand new this tick period - current[key] = build_func(obj, key) - new[key] = current[key] - else: - # Previously known object, see if it changed state - current[key] = origlist[key] - del origlist[key] - - for name in newActiveNames + newInactiveNames: - try: - check_obj(name) - except: - logging.exception("Couldn't fetch %s '%s'", typename, name) - - return (origlist, new, current) - def _update_nets(self, dopoll): - orig = self.nets.copy() - if not dopoll: - return {}, {}, orig - - name = "network" - active_list = self._backend.listNetworks - inactive_list = self._backend.listDefinedNetworks - check_support = self.is_network_capable - lookup_func = self._backend.networkLookupByName - build_func = (lambda obj, key: vmmNetwork(self, obj, key)) - - return self._poll_helper(orig, name, check_support, - active_list, inactive_list, - lookup_func, build_func) + if not dopoll or not self.is_network_capable(): + return {}, {}, self.nets + return pollhelpers.fetch_nets(self._backend, self.nets.copy(), + (lambda obj, key: vmmNetwork(self, obj, key))) def _update_pools(self, dopoll): - orig = self.pools.copy() - if not dopoll: - return {}, {}, orig - - name = "pool" - active_list = self._backend.listStoragePools - inactive_list = self._backend.listDefinedStoragePools - check_support = self.is_storage_capable - lookup_func = self._backend.storagePoolLookupByName - build_func = (lambda obj, key: vmmStoragePool(self, obj, key)) - - return self._poll_helper(orig, name, check_support, - active_list, inactive_list, - lookup_func, build_func) + if not dopoll or not self.is_storage_capable(): + return {}, {}, self.pools + return pollhelpers.fetch_pools(self._backend, self.pools.copy(), + (lambda obj, key: vmmStoragePool(self, obj, key))) def _update_interfaces(self, dopoll): - orig = self.interfaces.copy() - if not dopoll: - return {}, {}, orig - - name = "interface" - active_list = self._backend.listInterfaces - inactive_list = self._backend.listDefinedInterfaces - check_support = self.is_interface_capable - lookup_func = self._backend.interfaceLookupByName - build_func = (lambda obj, key: vmmInterface(self, obj, key)) - - return self._poll_helper(orig, name, check_support, - active_list, inactive_list, - lookup_func, build_func) - + if not dopoll or not self.is_interface_capable(): + return {}, {}, self.interfaces + return pollhelpers.fetch_interfaces(self._backend, + self.interfaces.copy(), + (lambda obj, key: vmmInterface(self, obj, key))) def _update_nodedevs(self, dopoll): - orig = self.nodedevs.copy() - if not dopoll: - return {}, {}, orig - - name = "nodedev" - active_list = lambda: self._backend.listDevices(None, 0) - inactive_list = lambda: [] - check_support = self.is_nodedev_capable - lookup_func = self._backend.nodeDeviceLookupByName - build_func = lambda obj, key: vmmNodeDevice(self, obj, key) - - return self._poll_helper(orig, name, check_support, - active_list, inactive_list, - lookup_func, build_func) + if not dopoll or not self.is_nodedev_capable(): + return {}, {}, self.nodedevs + return pollhelpers.fetch_nodedevs(self._backend, self.nodedevs.copy(), + (lambda obj, key: vmmNodeDevice(self, obj, key))) def _update_vms(self, dopoll): - # We can't easily use _poll_helper here because the domain API - # doesn't always return names like other objects, it returns - # IDs for active VMs - - newActiveIDs = [] - newInactiveNames = [] - oldActiveIDs = {} - oldInactiveNames = {} - - origlist = self.vms.copy() - current = {} - new = {} if not dopoll: - return current, new, origlist + return {}, {}, self.vms + return pollhelpers.fetch_vms(self._backend, self.vms.copy(), + (lambda obj, key: vmmDomain(self, obj, key))) - # Build list of previous vms with proper id/name mappings - for uuid in origlist: - vm = origlist[uuid] - if vm.is_active(): - oldActiveIDs[vm.get_id()] = vm - else: - oldInactiveNames[vm.get_name()] = vm - - try: - newActiveIDs = self._backend.listDomainsID() - except Exception, e: - logging.debug("Unable to list active domains: %s", e) - - try: - newInactiveNames = self._backend.listDefinedDomains() - except Exception, e: - logging.exception("Unable to list inactive domains: %s", e) - - def add_vm(vm): - uuid = vm.get_uuid() - - current[uuid] = vm - del(origlist[uuid]) - - def check_new(rawvm, uuid): - if uuid in origlist: - vm = origlist[uuid] - del(origlist[uuid]) - else: - vm = vmmDomain(self, rawvm, uuid) - new[uuid] = vm - - current[uuid] = vm - - for _id in newActiveIDs: - if _id in oldActiveIDs: - # No change, copy across existing VM object - vm = oldActiveIDs[_id] - add_vm(vm) - else: - # Check if domain is brand new, or old one that changed state - try: - vm = self._backend.lookupByID(_id) - uuid = util.uuidstr(vm.UUID()) - - check_new(vm, uuid) - except: - logging.exception("Couldn't fetch domain id '%s'", _id) - - - for name in newInactiveNames: - if name in oldInactiveNames: - # No change, copy across existing VM object - vm = oldInactiveNames[name] - add_vm(vm) - else: - # Check if domain is brand new, or old one that changed state - try: - vm = self._backend.lookupByName(name) - uuid = util.uuidstr(vm.UUID()) - - check_new(vm, uuid) - except: - logging.exception("Couldn't fetch domain '%s'", name) - - return (origlist, new, current) def _obj_signal_proxy(self, obj, signal, key): ignore = obj diff --git a/virtManager/util.py b/virtManager/util.py index 15fc6005f..7a0e12843 100644 --- a/virtManager/util.py +++ b/virtManager/util.py @@ -281,18 +281,6 @@ def pretty_hv(gtype, domtype): return label -def uuidstr(rawuuid): - hx = ['0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'] - uuid = [] - for i in range(16): - uuid.append(hx[((ord(rawuuid[i]) >> 4) & 0xf)]) - uuid.append(hx[(ord(rawuuid[i]) & 0xf)]) - if i == 3 or i == 5 or i == 7 or i == 9: - uuid.append('-') - return "".join(uuid) - - def iface_in_use_by(conn, name): use_str = "" for i in conn.list_interface_names(): diff --git a/virtinst/pollhelpers.py b/virtinst/pollhelpers.py new file mode 100644 index 000000000..3a2bb01b8 --- /dev/null +++ b/virtinst/pollhelpers.py @@ -0,0 +1,263 @@ +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA. +# + +import logging + +from virtinst import util + + +def _new_poll_helper(origlist, typename, listfunc, keyfunc, buildfunc): + """ + Helper for new style listAll* APIs + """ + current = {} + new = {} + objs = [] + + try: + objs = listfunc() + except Exception, e: + logging.debug("Unable to list all %ss: %s", typename, e) + + for obj in objs: + key = getattr(obj, keyfunc)() + + if key not in origlist: + # Object is brand new this period + current[key] = buildfunc(obj, key) + new[key] = current[key] + else: + # Previously known object + current[key] = origlist[key] + del origlist[key] + + return (origlist, new, current) + + +def _old_poll_helper(origlist, typename, + active_list, inactive_list, + lookup_func, build_func): + """ + Helper routine for old style split API libvirt polling. + @origlist: Pre-existing mapping of objects, with key->obj mapping + objects must have an is_active and set_active API + @typename: string describing type of objects we are polling for use + in debug messages. + @active_list: Function that returns the list of active objects + @inactive_list: Function that returns the list of inactive objects + @lookup_func: Function to get an object handle for the passed name + @build_func: Function that builds a new object class. It is passed + args of (raw libvirt object, key (usually UUID), bool is_active) + """ + current = {} + new = {} + newActiveNames = [] + newInactiveNames = [] + + try: + newActiveNames = active_list() + except Exception, e: + logging.debug("Unable to list active %ss: %s", typename, e) + try: + newInactiveNames = inactive_list() + except Exception, e: + logging.debug("Unable to list inactive %ss: %s", typename, e) + + def check_obj(key): + if key not in origlist: + try: + obj = lookup_func(key) + except Exception, e: + logging.debug("Could not fetch %s '%s': %s", + typename, key, e) + return + + # Object is brand new this period + current[key] = build_func(obj, key) + new[key] = current[key] + else: + # Previously known object + current[key] = origlist[key] + del origlist[key] + + for name in newActiveNames + newInactiveNames: + try: + check_obj(name) + except: + logging.exception("Couldn't fetch %s '%s'", typename, name) + + return (origlist, new, current) + + +def fetch_nets(backend, orig, build_func): + name = "network" + + if backend.check_conn_support( + backend.SUPPORT_CONN_LISTALLNETWORKS): + return _new_poll_helper(orig, name, + backend.listAllNetworks, + "UUIDString", build_func) + else: + active_list = backend.listNetworks + inactive_list = backend.listDefinedNetworks + lookup_func = backend.networkLookupByName + + return _old_poll_helper(orig, name, + active_list, inactive_list, + lookup_func, build_func) + + +def fetch_pools(backend, orig, build_func): + name = "pool" + + if backend.check_conn_support( + backend.SUPPORT_CONN_LISTALLSTORAGEPOOLS): + return _new_poll_helper(orig, name, + backend.listAllStoragePools, + "UUIDString", build_func) + else: + active_list = backend.listStoragePools + inactive_list = backend.listDefinedStoragePools + lookup_func = backend.storagePoolLookupByName + + return _old_poll_helper(orig, name, + active_list, inactive_list, + lookup_func, build_func) + + +def fetch_interfaces(backend, orig, build_func): + name = "interface" + + # Doesn't work on F19, not sure if a transient bug or if it has + # never worked: https://bugzilla.redhat.com/show_bug.cgi?id=982014 + if False and backend.check_conn_support( + backend.SUPPORT_CONN_LISTALLINTERFACES): + return _new_poll_helper(orig, name, + backend.listAllInterfaces, + "name", build_func) + else: + active_list = backend.listInterfaces + inactive_list = backend.listDefinedInterfaces + lookup_func = backend.interfaceLookupByName + + return _old_poll_helper(orig, name, + active_list, inactive_list, + lookup_func, build_func) + + +def fetch_nodedevs(backend, orig, build_func): + name = "nodedev" + active_list = lambda: backend.listDevices(None, 0) + inactive_list = lambda: [] + lookup_func = backend.nodeDeviceLookupByName + + return _old_poll_helper(orig, name, + active_list, inactive_list, + lookup_func, build_func) + + +def _old_fetch_vms(backend, origlist, build_func): + # We can't easily use _old_poll_helper here because the domain API + # doesn't always return names like other objects, it returns + # IDs for active VMs + + newActiveIDs = [] + newInactiveNames = [] + oldActiveIDs = {} + oldInactiveNames = {} + current = {} + new = {} + + # Build list of previous vms with proper id/name mappings + for uuid in origlist: + vm = origlist[uuid] + if vm.is_active(): + oldActiveIDs[vm.get_id()] = vm + else: + oldInactiveNames[vm.get_name()] = vm + + try: + newActiveIDs = backend.listDomainsID() + except Exception, e: + logging.debug("Unable to list active domains: %s", e) + + try: + newInactiveNames = backend.listDefinedDomains() + except Exception, e: + logging.exception("Unable to list inactive domains: %s", e) + + def add_vm(vm): + uuid = vm.get_uuid() + + current[uuid] = vm + del(origlist[uuid]) + + def check_new(rawvm, uuid): + if uuid in origlist: + vm = origlist[uuid] + del(origlist[uuid]) + else: + vm = build_func(rawvm, uuid) + new[uuid] = vm + + current[uuid] = vm + + for _id in newActiveIDs: + if _id in oldActiveIDs: + # No change, copy across existing VM object + vm = oldActiveIDs[_id] + add_vm(vm) + else: + # Check if domain is brand new, or old one that changed state + try: + vm = backend.lookupByID(_id) + uuid = util.uuidstr(vm.UUID()) + + check_new(vm, uuid) + except: + logging.exception("Couldn't fetch domain id '%s'", _id) + + + for name in newInactiveNames: + if name in oldInactiveNames: + # No change, copy across existing VM object + vm = oldInactiveNames[name] + add_vm(vm) + else: + # Check if domain is brand new, or old one that changed state + try: + vm = backend.lookupByName(name) + uuid = util.uuidstr(vm.UUID()) + + check_new(vm, uuid) + except: + logging.exception("Couldn't fetch domain '%s'", name) + + return (origlist, new, current) + + +def fetch_vms(backend, origlist, build_func): + name = "domain" + if backend.check_conn_support( + backend.SUPPORT_CONN_LISTALLDOMAINS): + return _new_poll_helper(origlist, name, + backend.listAllDomains, + "UUIDString", build_func) + else: + return _old_fetch_vms(backend, origlist, build_func) diff --git a/virtinst/support.py b/virtinst/support.py index 6b6f4eff6..27f27344c 100644 --- a/virtinst/support.py +++ b/virtinst/support.py @@ -35,7 +35,11 @@ from virtinst import util SUPPORT_CONN_MAXVCPUS_XML, SUPPORT_CONN_STREAM, SUPPORT_CONN_GETVERSION, - SUPPORT_CONN_LIBVERSION) = range(1, 13) + SUPPORT_CONN_LIBVERSION, + SUPPORT_CONN_LISTALLDOMAINS, + SUPPORT_CONN_LISTALLNETWORKS, + SUPPORT_CONN_LISTALLSTORAGEPOOLS, + SUPPORT_CONN_LISTALLINTERFACES) = range(1, 17) # Flags for check_domain_support (SUPPORT_DOMAIN_GETVCPUS, @@ -174,6 +178,23 @@ _support_dict = { "args": (), }, + SUPPORT_CONN_LISTALLDOMAINS : { + "function": "virConnect.listAllDomains", + "args": (), + }, + SUPPORT_CONN_LISTALLNETWORKS : { + "function": "virConnect.listAllNetworks", + "args": (), + }, + SUPPORT_CONN_LISTALLSTORAGEPOOLS : { + "function": "virConnect.listAllStoragePools", + "args": (), + }, + SUPPORT_CONN_LISTALLINTERFACES : { + "function": "virConnect.listAllInterfaces", + "args": (), + }, + ################# # Domain checks # diff --git a/virtinst/util.py b/virtinst/util.py index 2b83ccacf..8c65e7f7b 100644 --- a/virtinst/util.py +++ b/virtinst/util.py @@ -572,3 +572,15 @@ def local_libvirt_version(): if not hasattr(libvirt, key): setattr(libvirt, key, libvirt.getVersion()) return getattr(libvirt, key) + + +def uuidstr(rawuuid): + hx = ['0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'] + uuid = [] + for i in range(16): + uuid.append(hx[((ord(rawuuid[i]) >> 4) & 0xf)]) + uuid.append(hx[(ord(rawuuid[i]) & 0xf)]) + if i == 3 or i == 5 or i == 7 or i == 9: + uuid.append('-') + return "".join(uuid)