2006-06-29 03:50:17 +08:00
|
|
|
#
|
2015-02-24 20:21:24 +08:00
|
|
|
# Copyright (C) 2006, 2013, 2014, 2015 Red Hat, Inc.
|
2006-06-29 03:50:17 +08:00
|
|
|
# Copyright (C) 2006 Daniel P. Berrange <berrange@redhat.com>
|
|
|
|
#
|
|
|
|
# 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
|
2007-11-21 00:12:20 +08:00
|
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
# MA 02110-1301 USA.
|
2006-06-29 03:50:17 +08:00
|
|
|
#
|
2006-06-14 22:59:40 +08:00
|
|
|
|
2012-05-14 21:24:56 +08:00
|
|
|
from gi.repository import GObject
|
|
|
|
|
2006-10-26 00:18:06 +08:00
|
|
|
import logging
|
2010-12-11 00:47:07 +08:00
|
|
|
import os
|
2015-04-02 07:10:16 +08:00
|
|
|
import threading
|
2011-04-29 05:11:04 +08:00
|
|
|
import time
|
2012-01-28 07:31:21 +08:00
|
|
|
import traceback
|
2010-01-07 02:09:33 +08:00
|
|
|
|
|
|
|
import libvirt
|
2008-02-23 00:48:29 +08:00
|
|
|
import virtinst
|
2013-07-08 01:53:37 +08:00
|
|
|
from virtinst import pollhelpers
|
2015-04-02 07:10:16 +08:00
|
|
|
from virtinst import support
|
2013-08-09 21:23:01 +08:00
|
|
|
from virtinst import util
|
2006-06-14 22:59:40 +08:00
|
|
|
|
2014-09-13 04:10:45 +08:00
|
|
|
from . import connectauth
|
|
|
|
from .baseclass import vmmGObject
|
|
|
|
from .domain import vmmDomain
|
|
|
|
from .interface import vmmInterface
|
|
|
|
from .network import vmmNetwork
|
|
|
|
from .nodedev import vmmNodeDevice
|
|
|
|
from .storagepool import vmmStoragePool
|
2006-06-14 22:59:40 +08:00
|
|
|
|
2012-01-28 07:31:21 +08:00
|
|
|
|
2014-06-03 00:00:07 +08:00
|
|
|
# debugging helper to turn off events
|
2015-09-18 03:18:22 +08:00
|
|
|
# Can be enabled with virt-manager --test-no-events
|
|
|
|
FORCE_DISABLE_EVENTS = False
|
2014-06-03 00:00:07 +08:00
|
|
|
|
|
|
|
|
2015-04-10 22:33:04 +08:00
|
|
|
class _ObjectList(vmmGObject):
|
|
|
|
"""
|
|
|
|
Class that wraps our internal list of libvirt objects
|
|
|
|
"""
|
|
|
|
def __init__(self):
|
|
|
|
vmmGObject.__init__(self)
|
|
|
|
|
|
|
|
self._objects = []
|
2015-09-18 03:48:42 +08:00
|
|
|
self._blacklist = []
|
2015-04-10 22:33:04 +08:00
|
|
|
self._lock = threading.Lock()
|
|
|
|
|
|
|
|
def _cleanup(self):
|
|
|
|
try:
|
|
|
|
self._lock.acquire()
|
|
|
|
|
|
|
|
for obj in self._objects:
|
|
|
|
try:
|
|
|
|
obj.cleanup()
|
|
|
|
except:
|
|
|
|
logging.debug("Failed to cleanup %s", exc_info=True)
|
|
|
|
self._objects = []
|
|
|
|
finally:
|
|
|
|
self._lock.release()
|
|
|
|
|
2015-09-18 03:48:42 +08:00
|
|
|
def _blacklist_key(self, obj):
|
|
|
|
return str(obj.__class__) + obj.get_connkey()
|
|
|
|
|
|
|
|
def add_blacklist(self, obj):
|
|
|
|
"""
|
|
|
|
Add an object to the blacklist. Basically a list of objects we
|
|
|
|
choose not to poll, because they threw an error at init time
|
|
|
|
|
|
|
|
:param obj: vmmLibvirtObject to blacklist
|
|
|
|
:returns: True if object added, False if object was already in list
|
|
|
|
"""
|
|
|
|
if self.in_blacklist(obj):
|
|
|
|
return False
|
|
|
|
self._blacklist.append(self._blacklist_key(obj))
|
|
|
|
return True
|
|
|
|
|
|
|
|
def in_blacklist(self, obj):
|
|
|
|
"""
|
|
|
|
:param obj: vmmLibvirtObject to check
|
|
|
|
:returns: True if object is in the blacklist
|
|
|
|
"""
|
|
|
|
return self._blacklist_key(obj) in self._blacklist
|
|
|
|
|
2015-04-10 22:33:04 +08:00
|
|
|
def remove(self, obj):
|
|
|
|
"""
|
|
|
|
Remove an object from the list.
|
|
|
|
|
|
|
|
:param obj: vmmLibvirtObject to remove
|
|
|
|
:returns: True if object removed, False if object was not found
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
self._lock.acquire()
|
|
|
|
|
|
|
|
# Identity check is sufficient here, since we should never be
|
|
|
|
# asked to remove an object that wasn't at one point in the list.
|
|
|
|
if obj not in self._objects:
|
2015-09-18 03:48:42 +08:00
|
|
|
if self.in_blacklist(obj):
|
|
|
|
self._blacklist.remove(self._blacklist_key(obj))
|
|
|
|
return True
|
|
|
|
|
2015-04-10 22:33:04 +08:00
|
|
|
return False
|
|
|
|
|
|
|
|
self._objects.remove(obj)
|
|
|
|
return True
|
|
|
|
finally:
|
|
|
|
self._lock.release()
|
|
|
|
|
|
|
|
def add(self, obj):
|
|
|
|
"""
|
|
|
|
Add an object to the list.
|
|
|
|
|
|
|
|
:param obj: vmmLibvirtObject to add
|
|
|
|
:returns: True if object added, False if object already in the list
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
self._lock.acquire()
|
|
|
|
|
|
|
|
# We don't look up based on identity here, to prevent tick()
|
|
|
|
# races from adding the same domain twice
|
|
|
|
#
|
|
|
|
# We don't use lookup_object here since we need to hold the
|
|
|
|
# lock the whole time to prevent a 'time of check' issue
|
|
|
|
for checkobj in self._objects:
|
|
|
|
if (checkobj.__class__ == obj.__class__ and
|
|
|
|
checkobj.get_connkey() == obj.get_connkey()):
|
|
|
|
return False
|
|
|
|
if obj in self._objects:
|
|
|
|
return False
|
|
|
|
|
|
|
|
self._objects.append(obj)
|
|
|
|
return True
|
|
|
|
finally:
|
|
|
|
self._lock.release()
|
|
|
|
|
|
|
|
def get_objects_for_class(self, classobj):
|
|
|
|
"""
|
|
|
|
Return all objects over the passed vmmLibvirtObject class
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
self._lock.acquire()
|
|
|
|
return [o for o in self._objects if o.__class__ is classobj]
|
|
|
|
finally:
|
|
|
|
self._lock.release()
|
|
|
|
|
|
|
|
def lookup_object(self, classobj, connkey):
|
|
|
|
"""
|
|
|
|
Lookup an object with the passed classobj + connkey
|
|
|
|
"""
|
|
|
|
# Doesn't require locking, since get_objects_for_class covers us
|
|
|
|
for obj in self.get_objects_for_class(classobj):
|
|
|
|
if obj.get_connkey() == connkey:
|
|
|
|
return obj
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
2010-12-10 01:37:48 +08:00
|
|
|
class vmmConnection(vmmGObject):
|
2012-05-14 21:24:56 +08:00
|
|
|
__gsignals__ = {
|
|
|
|
"vm-added": (GObject.SignalFlags.RUN_FIRST, None, [str]),
|
|
|
|
"vm-removed": (GObject.SignalFlags.RUN_FIRST, None, [str]),
|
|
|
|
"net-added": (GObject.SignalFlags.RUN_FIRST, None, [str]),
|
|
|
|
"net-removed": (GObject.SignalFlags.RUN_FIRST, None, [str]),
|
|
|
|
"pool-added": (GObject.SignalFlags.RUN_FIRST, None, [str]),
|
|
|
|
"pool-removed": (GObject.SignalFlags.RUN_FIRST, None, [str]),
|
|
|
|
"interface-added": (GObject.SignalFlags.RUN_FIRST, None, [str]),
|
|
|
|
"interface-removed": (GObject.SignalFlags.RUN_FIRST, None, [str]),
|
|
|
|
"nodedev-added": (GObject.SignalFlags.RUN_FIRST, None, [str]),
|
|
|
|
"nodedev-removed": (GObject.SignalFlags.RUN_FIRST, None, [str]),
|
|
|
|
"resources-sampled": (GObject.SignalFlags.RUN_FIRST, None, []),
|
|
|
|
"state-changed": (GObject.SignalFlags.RUN_FIRST, None, []),
|
2013-07-07 08:03:42 +08:00
|
|
|
"connect-error": (GObject.SignalFlags.RUN_FIRST, None,
|
|
|
|
[str, str, bool]),
|
|
|
|
"priority-tick": (GObject.SignalFlags.RUN_FIRST, None, [object]),
|
2012-05-14 21:24:56 +08:00
|
|
|
}
|
2006-06-15 01:52:46 +08:00
|
|
|
|
2014-09-12 06:16:21 +08:00
|
|
|
(_STATE_DISCONNECTED,
|
|
|
|
_STATE_CONNECTING,
|
|
|
|
_STATE_ACTIVE) = range(1, 4)
|
2007-09-10 10:57:24 +08:00
|
|
|
|
2013-07-04 01:56:43 +08:00
|
|
|
def __init__(self, uri):
|
2010-12-10 01:37:48 +08:00
|
|
|
vmmGObject.__init__(self)
|
2009-07-02 02:59:13 +08:00
|
|
|
|
2011-05-04 01:11:33 +08:00
|
|
|
self._uri = uri
|
|
|
|
if self._uri is None or self._uri.lower() == "xen":
|
|
|
|
self._uri = "xen:///"
|
2007-04-13 07:43:31 +08:00
|
|
|
|
2014-09-12 06:16:21 +08:00
|
|
|
self._state = self._STATE_DISCONNECTED
|
2013-07-05 20:59:58 +08:00
|
|
|
self._backend = virtinst.VirtualConnection(self._uri)
|
2014-04-16 22:32:52 +08:00
|
|
|
self._closing = False
|
2009-11-20 06:38:43 +08:00
|
|
|
|
2015-04-11 02:08:25 +08:00
|
|
|
self._init_object_count = None
|
|
|
|
self._init_object_event = None
|
|
|
|
|
2013-07-07 05:24:51 +08:00
|
|
|
self._network_capable = None
|
2011-04-08 05:57:19 +08:00
|
|
|
self._storage_capable = None
|
2013-07-07 05:24:51 +08:00
|
|
|
self._interface_capable = None
|
2009-11-26 03:50:27 +08:00
|
|
|
self._nodedev_capable = None
|
2014-02-12 02:19:15 +08:00
|
|
|
|
2014-02-12 01:07:13 +08:00
|
|
|
self.using_domain_events = False
|
2014-04-01 00:43:49 +08:00
|
|
|
self._domain_cb_ids = []
|
2014-02-12 02:19:15 +08:00
|
|
|
self.using_network_events = False
|
2014-04-01 00:43:49 +08:00
|
|
|
self._network_cb_ids = []
|
2010-02-26 08:35:01 +08:00
|
|
|
|
|
|
|
self._xml_flags = {}
|
2006-06-14 22:59:40 +08:00
|
|
|
|
2015-04-10 22:33:04 +08:00
|
|
|
self._objects = _ObjectList()
|
|
|
|
|
2015-05-05 06:16:09 +08:00
|
|
|
self._stats = []
|
|
|
|
self._hostinfo = None
|
2006-06-14 22:59:40 +08:00
|
|
|
|
2015-04-12 01:39:25 +08:00
|
|
|
self.add_gsettings_handle(
|
|
|
|
self._on_config_pretty_name_changed(
|
|
|
|
self._config_pretty_name_changed_cb))
|
|
|
|
|
2013-07-08 06:54:08 +08:00
|
|
|
self._init_virtconn()
|
|
|
|
|
2013-07-05 20:59:58 +08:00
|
|
|
|
2014-01-27 06:42:24 +08:00
|
|
|
@staticmethod
|
|
|
|
def pretty_hv(gtype, domtype):
|
|
|
|
"""
|
|
|
|
Convert XML <domain type='foo'> and <os><type>bar</type>
|
|
|
|
into a more human relevant string.
|
|
|
|
"""
|
|
|
|
|
|
|
|
gtype = gtype.lower()
|
|
|
|
domtype = domtype.lower()
|
|
|
|
|
|
|
|
label = domtype
|
2015-04-04 04:25:57 +08:00
|
|
|
if domtype == "xen":
|
2014-01-27 06:42:24 +08:00
|
|
|
if gtype == "xen":
|
|
|
|
label = "xen (paravirt)"
|
|
|
|
elif gtype == "hvm":
|
|
|
|
label = "xen (fullvirt)"
|
|
|
|
elif domtype == "test":
|
|
|
|
if gtype == "xen":
|
|
|
|
label = "test (xen)"
|
|
|
|
elif gtype == "hvm":
|
|
|
|
label = "test (hvm)"
|
2015-09-15 06:31:56 +08:00
|
|
|
elif domtype == "qemu":
|
|
|
|
label = "QEMU TCG"
|
|
|
|
elif domtype == "kvm":
|
|
|
|
label = "KVM"
|
2014-01-27 06:42:24 +08:00
|
|
|
|
|
|
|
return label
|
|
|
|
|
2009-11-26 06:07:12 +08:00
|
|
|
#################
|
|
|
|
# Init routines #
|
|
|
|
#################
|
|
|
|
|
2013-07-08 06:54:08 +08:00
|
|
|
def _init_virtconn(self):
|
2013-07-10 07:50:49 +08:00
|
|
|
self._backend.cb_fetch_all_guests = (
|
2013-09-29 08:05:13 +08:00
|
|
|
lambda: [obj.get_xmlobj(refresh_if_nec=False)
|
2015-04-10 22:33:04 +08:00
|
|
|
for obj in self.list_vms()])
|
2013-09-29 08:05:13 +08:00
|
|
|
self._backend.cb_fetch_all_pools = (
|
|
|
|
lambda: [obj.get_xmlobj(refresh_if_nec=False)
|
2015-04-10 22:33:04 +08:00
|
|
|
for obj in self.list_pools()])
|
2014-09-20 23:30:24 +08:00
|
|
|
self._backend.cb_fetch_all_nodedevs = (
|
|
|
|
lambda: [obj.get_xmlobj(refresh_if_nec=False)
|
2015-04-10 22:33:04 +08:00
|
|
|
for obj in self.list_nodedevs()])
|
2014-02-28 02:16:21 +08:00
|
|
|
|
|
|
|
def fetch_all_vols():
|
|
|
|
ret = []
|
2015-04-10 22:33:04 +08:00
|
|
|
for pool in self.list_pools():
|
2015-04-11 00:52:42 +08:00
|
|
|
for vol in pool.get_volumes():
|
2014-02-28 02:16:21 +08:00
|
|
|
try:
|
|
|
|
ret.append(vol.get_xmlobj(refresh_if_nec=False))
|
2014-03-07 01:04:08 +08:00
|
|
|
except Exception, e:
|
2014-02-28 02:16:21 +08:00
|
|
|
logging.debug("Fetching volume XML failed: %s", e)
|
|
|
|
return ret
|
|
|
|
self._backend.cb_fetch_all_vols = fetch_all_vols
|
2013-09-29 08:05:13 +08:00
|
|
|
|
2014-01-19 03:57:39 +08:00
|
|
|
def clear_cache(pools=False):
|
|
|
|
if not pools:
|
|
|
|
return
|
|
|
|
|
2015-04-14 04:56:46 +08:00
|
|
|
# This isn't synchronous, so any virtinst callers need to
|
|
|
|
# take that into account.
|
|
|
|
self.schedule_priority_tick(pollpool=True)
|
2014-01-19 03:57:39 +08:00
|
|
|
|
|
|
|
self._backend.cb_clear_cache = clear_cache
|
|
|
|
|
2013-04-16 07:34:10 +08:00
|
|
|
|
2009-11-26 06:07:12 +08:00
|
|
|
########################
|
|
|
|
# General data getters #
|
|
|
|
########################
|
2009-11-26 05:12:03 +08:00
|
|
|
|
2009-11-26 05:13:46 +08:00
|
|
|
def get_uri(self):
|
2011-05-04 01:11:33 +08:00
|
|
|
return self._uri
|
2013-07-05 20:59:58 +08:00
|
|
|
def get_backend(self):
|
|
|
|
return self._backend
|
2009-03-10 04:16:45 +08:00
|
|
|
|
2010-12-09 01:17:14 +08:00
|
|
|
def invalidate_caps(self):
|
2013-07-07 02:12:13 +08:00
|
|
|
return self._backend.invalidate_caps()
|
|
|
|
caps = property(lambda self: getattr(self, "_backend").caps)
|
2009-11-26 05:13:46 +08:00
|
|
|
|
|
|
|
def host_memory_size(self):
|
2013-07-05 20:59:58 +08:00
|
|
|
if not self._backend.is_open():
|
2009-11-26 05:13:46 +08:00
|
|
|
return 0
|
2015-05-05 06:16:09 +08:00
|
|
|
return self._hostinfo[1] * 1024
|
2009-11-26 05:13:46 +08:00
|
|
|
|
|
|
|
def host_active_processor_count(self):
|
2013-07-05 20:59:58 +08:00
|
|
|
if not self._backend.is_open():
|
2009-11-26 05:13:46 +08:00
|
|
|
return 0
|
2015-05-05 06:16:09 +08:00
|
|
|
return self._hostinfo[2]
|
2009-11-26 05:13:46 +08:00
|
|
|
|
2009-12-01 00:56:41 +08:00
|
|
|
def connect(self, name, callback, *args):
|
2011-04-12 01:06:59 +08:00
|
|
|
handle_id = vmmGObject.connect(self, name, callback, *args)
|
2009-11-26 05:13:46 +08:00
|
|
|
|
|
|
|
if name == "vm-added":
|
2015-04-10 22:33:04 +08:00
|
|
|
for vm in self.list_vms():
|
|
|
|
self.emit("vm-added", vm.get_connkey())
|
2009-11-26 05:13:46 +08:00
|
|
|
|
|
|
|
return handle_id
|
|
|
|
|
2015-04-08 01:56:48 +08:00
|
|
|
|
2009-11-26 05:13:46 +08:00
|
|
|
##########################
|
|
|
|
# URI + hostname helpers #
|
|
|
|
##########################
|
2009-11-16 04:17:03 +08:00
|
|
|
|
2015-04-16 23:21:34 +08:00
|
|
|
def libvirt_gethostname(self):
|
|
|
|
return self._backend.getHostname()
|
2006-11-06 23:34:54 +08:00
|
|
|
|
2013-07-06 08:36:28 +08:00
|
|
|
get_uri_hostname = property(lambda s:
|
2015-04-12 00:08:57 +08:00
|
|
|
getattr(s, "_backend").get_uri_hostname)
|
2015-04-12 00:57:32 +08:00
|
|
|
get_uri_username = property(lambda s:
|
|
|
|
getattr(s, "_backend").get_uri_username)
|
2015-04-12 00:08:57 +08:00
|
|
|
get_uri_transport = property(lambda s:
|
|
|
|
getattr(s, "_backend").get_uri_transport)
|
|
|
|
get_uri_port = property(lambda s: getattr(s, "_backend").get_uri_port)
|
2013-07-06 08:36:28 +08:00
|
|
|
get_driver = property(lambda s: getattr(s, "_backend").get_uri_driver)
|
|
|
|
is_container = property(lambda s: getattr(s, "_backend").is_container)
|
|
|
|
is_lxc = property(lambda s: getattr(s, "_backend").is_lxc)
|
|
|
|
is_openvz = property(lambda s: getattr(s, "_backend").is_openvz)
|
|
|
|
is_xen = property(lambda s: getattr(s, "_backend").is_xen)
|
|
|
|
is_remote = property(lambda s: getattr(s, "_backend").is_remote)
|
|
|
|
is_qemu = property(lambda s: getattr(s, "_backend").is_qemu)
|
|
|
|
is_qemu_system = property(lambda s: getattr(s, "_backend").is_qemu_system)
|
|
|
|
is_qemu_session = property(lambda s:
|
|
|
|
getattr(s, "_backend").is_qemu_session)
|
|
|
|
is_test_conn = property(lambda s: getattr(s, "_backend").is_test)
|
|
|
|
is_session_uri = property(lambda s: getattr(s, "_backend").is_session_uri)
|
2009-03-10 04:16:45 +08:00
|
|
|
|
2010-12-11 07:03:51 +08:00
|
|
|
|
2010-02-11 09:26:40 +08:00
|
|
|
# Connection capabilities debug helpers
|
2015-02-24 20:21:24 +08:00
|
|
|
def stable_defaults(self, *args, **kwargs):
|
|
|
|
return self._backend.stable_defaults(*args, **kwargs)
|
2011-09-27 06:53:00 +08:00
|
|
|
|
2013-10-01 09:41:10 +08:00
|
|
|
def get_cache_dir(self):
|
|
|
|
uri = self.get_uri().replace("/", "_")
|
|
|
|
ret = os.path.join(util.get_cache_dir(), uri)
|
|
|
|
if not os.path.exists(ret):
|
2013-11-01 20:26:24 +08:00
|
|
|
os.makedirs(ret, 0755)
|
2013-10-01 09:41:10 +08:00
|
|
|
return ret
|
|
|
|
|
2013-10-03 03:17:15 +08:00
|
|
|
def get_default_storage_format(self):
|
|
|
|
raw = self.config.get_default_storage_format(raw=True)
|
|
|
|
if raw != "default":
|
|
|
|
return raw
|
|
|
|
|
|
|
|
fmt = self.config.get_default_storage_format()
|
|
|
|
if fmt != "qcow2":
|
2014-02-11 07:47:58 +08:00
|
|
|
return fmt
|
2013-10-03 03:17:15 +08:00
|
|
|
|
2013-10-06 22:08:04 +08:00
|
|
|
if self.check_support(self._backend.SUPPORT_CONN_DEFAULT_QCOW2):
|
2013-10-03 03:17:15 +08:00
|
|
|
return fmt
|
|
|
|
return None
|
2010-02-11 09:26:40 +08:00
|
|
|
|
2014-02-11 07:47:58 +08:00
|
|
|
|
2013-07-07 02:12:13 +08:00
|
|
|
####################################
|
|
|
|
# Connection pretty print routines #
|
|
|
|
####################################
|
2010-02-11 09:26:40 +08:00
|
|
|
|
2015-04-12 00:57:32 +08:00
|
|
|
def get_pretty_desc(self):
|
2014-07-08 05:57:50 +08:00
|
|
|
"""
|
2015-04-12 00:57:32 +08:00
|
|
|
Return a pretty label for use in the manager view, and various
|
|
|
|
connection lists.
|
2014-07-08 05:57:50 +08:00
|
|
|
"""
|
2015-04-12 01:39:25 +08:00
|
|
|
if self._get_config_pretty_name():
|
|
|
|
return self._get_config_pretty_name()
|
2009-07-29 07:21:56 +08:00
|
|
|
|
2011-06-08 07:41:02 +08:00
|
|
|
pretty_map = {
|
|
|
|
"esx" : "ESX",
|
|
|
|
"gsx" : "GSX",
|
|
|
|
"libxl" : "libxl",
|
|
|
|
"lxc" : "LXC",
|
|
|
|
"openvz" : "OpenVZ",
|
|
|
|
"phyp" : "phyp",
|
2015-04-12 00:57:32 +08:00
|
|
|
"qemu" : "QEMU/KVM",
|
2011-06-08 07:41:02 +08:00
|
|
|
"test" : "test",
|
|
|
|
"uml" : "UML",
|
|
|
|
"vbox" : "VBox",
|
|
|
|
"vmware" : "VMWare",
|
2015-09-16 07:30:09 +08:00
|
|
|
"xen" : "Xen",
|
2011-06-08 07:41:02 +08:00
|
|
|
"xenapi" : "XenAPI",
|
|
|
|
}
|
|
|
|
|
2015-04-12 00:57:32 +08:00
|
|
|
hv = pretty_map.get(self.get_driver(), self.get_driver())
|
|
|
|
hostname = self.get_uri_hostname()
|
|
|
|
path = self.get_backend().get_uri_path()
|
|
|
|
is_session = self.get_backend().is_session_uri()
|
2014-07-08 05:57:50 +08:00
|
|
|
|
2015-04-12 00:57:32 +08:00
|
|
|
ret = hv
|
2010-12-12 12:51:20 +08:00
|
|
|
|
2015-04-12 00:57:32 +08:00
|
|
|
if is_session:
|
2015-05-03 07:20:18 +08:00
|
|
|
ret += " User session"
|
2015-09-16 07:30:09 +08:00
|
|
|
elif (path and path != "/system" and os.path.basename(path)):
|
2015-04-12 00:57:32 +08:00
|
|
|
# Used by test URIs to report what XML file they are using
|
|
|
|
ret += " %s" % os.path.basename(path)
|
2009-07-21 02:47:50 +08:00
|
|
|
|
2015-04-12 00:57:32 +08:00
|
|
|
if hostname:
|
2015-05-03 07:20:18 +08:00
|
|
|
ret += ": %s" % hostname
|
2013-07-17 05:15:51 +08:00
|
|
|
|
2015-04-12 00:57:32 +08:00
|
|
|
return ret
|
2009-07-21 02:47:50 +08:00
|
|
|
|
2009-11-26 05:13:46 +08:00
|
|
|
|
|
|
|
#######################
|
|
|
|
# API support helpers #
|
|
|
|
#######################
|
|
|
|
|
2013-07-06 23:20:28 +08:00
|
|
|
for _supportname in [_supportname for _supportname in
|
|
|
|
dir(virtinst.VirtualConnection) if
|
|
|
|
_supportname.startswith("SUPPORT_")]:
|
|
|
|
locals()[_supportname] = getattr(virtinst.VirtualConnection,
|
|
|
|
_supportname)
|
2013-10-06 22:08:04 +08:00
|
|
|
def check_support(self, *args):
|
|
|
|
return self._backend.check_support(*args)
|
2013-07-06 23:20:28 +08:00
|
|
|
|
2009-11-26 05:13:46 +08:00
|
|
|
def is_storage_capable(self):
|
2012-11-08 21:15:02 +08:00
|
|
|
if self._storage_capable is None:
|
2013-10-06 22:08:04 +08:00
|
|
|
self._storage_capable = self.check_support(
|
2013-07-06 23:20:28 +08:00
|
|
|
self._backend.SUPPORT_CONN_STORAGE)
|
2011-04-08 05:57:19 +08:00
|
|
|
if self._storage_capable is False:
|
|
|
|
logging.debug("Connection doesn't seem to support storage "
|
|
|
|
"APIs. Skipping all storage polling.")
|
|
|
|
else:
|
|
|
|
# Try to create the default storage pool
|
|
|
|
try:
|
2013-09-20 08:18:12 +08:00
|
|
|
virtinst.StoragePool.build_default_pool(self.get_backend())
|
2011-04-08 05:57:19 +08:00
|
|
|
except Exception, e:
|
2012-01-17 11:04:40 +08:00
|
|
|
logging.debug("Building default pool failed: %s", str(e))
|
2011-04-08 05:57:19 +08:00
|
|
|
|
|
|
|
return self._storage_capable
|
2009-11-26 05:13:46 +08:00
|
|
|
|
2011-04-10 09:51:50 +08:00
|
|
|
def is_network_capable(self):
|
2013-07-07 05:24:51 +08:00
|
|
|
if self._network_capable is None:
|
2013-10-06 22:08:04 +08:00
|
|
|
self._network_capable = self.check_support(
|
2013-07-06 23:20:28 +08:00
|
|
|
self._backend.SUPPORT_CONN_NETWORK)
|
2013-07-07 05:24:51 +08:00
|
|
|
if self._network_capable is False:
|
2011-04-10 09:51:50 +08:00
|
|
|
logging.debug("Connection doesn't seem to support network "
|
|
|
|
"APIs. Skipping all network polling.")
|
|
|
|
|
2013-07-07 05:24:51 +08:00
|
|
|
return self._network_capable
|
2011-04-10 09:51:50 +08:00
|
|
|
|
|
|
|
def is_interface_capable(self):
|
2013-07-07 05:24:51 +08:00
|
|
|
if self._interface_capable is None:
|
2013-10-06 22:08:04 +08:00
|
|
|
self._interface_capable = self.check_support(
|
2013-07-06 23:20:28 +08:00
|
|
|
self._backend.SUPPORT_CONN_INTERFACE)
|
2013-07-07 05:24:51 +08:00
|
|
|
if self._interface_capable is False:
|
2011-04-10 09:51:50 +08:00
|
|
|
logging.debug("Connection doesn't seem to support interface "
|
|
|
|
"APIs. Skipping all interface polling.")
|
|
|
|
|
2013-07-07 05:24:51 +08:00
|
|
|
return self._interface_capable
|
2011-04-10 09:51:50 +08:00
|
|
|
|
2009-11-26 05:13:46 +08:00
|
|
|
def is_nodedev_capable(self):
|
2012-11-08 21:15:02 +08:00
|
|
|
if self._nodedev_capable is None:
|
2013-10-06 22:08:04 +08:00
|
|
|
self._nodedev_capable = self.check_support(
|
2013-07-06 23:20:28 +08:00
|
|
|
self._backend.SUPPORT_CONN_NODEDEV)
|
2009-11-26 03:50:27 +08:00
|
|
|
return self._nodedev_capable
|
2009-11-26 05:13:46 +08:00
|
|
|
|
2010-02-26 08:35:01 +08:00
|
|
|
def _get_flags_helper(self, obj, key, check_func):
|
2010-12-10 00:22:35 +08:00
|
|
|
ignore = obj
|
2010-02-26 08:35:01 +08:00
|
|
|
flags_dict = self._xml_flags.get(key)
|
2009-11-26 05:13:46 +08:00
|
|
|
|
2012-11-08 21:15:02 +08:00
|
|
|
if flags_dict is None:
|
2010-02-26 08:35:01 +08:00
|
|
|
# Flags already set
|
|
|
|
inact, act = check_func()
|
|
|
|
flags_dict = {}
|
|
|
|
flags_dict["active"] = act
|
|
|
|
flags_dict["inactive"] = inact
|
|
|
|
|
|
|
|
self._xml_flags[key] = flags_dict
|
|
|
|
|
|
|
|
active_flags = flags_dict["active"]
|
|
|
|
inactive_flags = flags_dict["inactive"]
|
|
|
|
|
|
|
|
return (inactive_flags, active_flags)
|
|
|
|
|
|
|
|
def get_dom_flags(self, vm):
|
|
|
|
key = "domain"
|
|
|
|
|
|
|
|
def check_func():
|
|
|
|
act = 0
|
|
|
|
inact = 0
|
|
|
|
|
2013-10-06 22:08:04 +08:00
|
|
|
if self.check_support(
|
|
|
|
self._backend.SUPPORT_DOMAIN_XML_INACTIVE, vm):
|
2010-02-26 08:35:01 +08:00
|
|
|
inact = libvirt.VIR_DOMAIN_XML_INACTIVE
|
|
|
|
else:
|
|
|
|
logging.debug("Domain XML inactive flag not supported.")
|
|
|
|
|
2013-10-06 22:08:04 +08:00
|
|
|
if self.check_support(
|
|
|
|
self._backend.SUPPORT_DOMAIN_XML_SECURE, vm):
|
2010-02-26 08:35:01 +08:00
|
|
|
inact |= libvirt.VIR_DOMAIN_XML_SECURE
|
|
|
|
act = libvirt.VIR_DOMAIN_XML_SECURE
|
|
|
|
else:
|
|
|
|
logging.debug("Domain XML secure flag not supported.")
|
|
|
|
|
|
|
|
return inact, act
|
|
|
|
|
|
|
|
return self._get_flags_helper(vm, key, check_func)
|
2009-11-26 05:13:46 +08:00
|
|
|
|
2010-02-26 08:35:01 +08:00
|
|
|
def get_interface_flags(self, iface):
|
|
|
|
key = "interface"
|
|
|
|
|
|
|
|
def check_func():
|
|
|
|
act = 0
|
|
|
|
inact = 0
|
|
|
|
|
2013-10-06 22:08:04 +08:00
|
|
|
if self.check_support(
|
|
|
|
self._backend.SUPPORT_INTERFACE_XML_INACTIVE, iface):
|
2010-02-26 08:35:01 +08:00
|
|
|
inact = libvirt.VIR_INTERFACE_XML_INACTIVE
|
|
|
|
else:
|
|
|
|
logging.debug("Interface XML inactive flag not supported.")
|
2009-11-26 05:13:46 +08:00
|
|
|
|
2010-02-26 08:35:01 +08:00
|
|
|
return (inact, act)
|
2009-11-26 05:13:46 +08:00
|
|
|
|
2010-02-26 08:35:01 +08:00
|
|
|
return self._get_flags_helper(iface, key, check_func)
|
2009-11-26 05:13:46 +08:00
|
|
|
|
2014-06-03 05:17:47 +08:00
|
|
|
def get_default_pool(self):
|
2015-04-10 22:33:04 +08:00
|
|
|
for p in self.list_pools():
|
2014-06-03 05:17:47 +08:00
|
|
|
if p.get_name() == "default":
|
|
|
|
return p
|
|
|
|
return None
|
|
|
|
|
|
|
|
def get_vol_by_path(self, path):
|
2015-04-10 22:33:04 +08:00
|
|
|
for pool in self.list_pools():
|
2015-04-11 00:52:42 +08:00
|
|
|
for vol in pool.get_volumes():
|
2014-12-05 09:38:55 +08:00
|
|
|
try:
|
|
|
|
if vol.get_target_path() == path:
|
|
|
|
return vol
|
|
|
|
except Exception, e:
|
|
|
|
# Errors can happen if the volume disappeared, bug 1092739
|
|
|
|
logging.debug("Error looking up volume from path=%s: %s",
|
|
|
|
path, e)
|
2014-06-03 05:17:47 +08:00
|
|
|
return None
|
|
|
|
|
2013-07-06 23:20:28 +08:00
|
|
|
|
2009-11-26 05:13:46 +08:00
|
|
|
###################################
|
|
|
|
# Connection state getter/setters #
|
|
|
|
###################################
|
|
|
|
|
|
|
|
def _change_state(self, newstate):
|
2014-09-12 06:16:21 +08:00
|
|
|
if self._state != newstate:
|
|
|
|
self._state = newstate
|
2014-09-12 06:48:04 +08:00
|
|
|
logging.debug("conn=%s changed to state=%s",
|
|
|
|
self.get_uri(), self.get_state_text())
|
2009-11-26 05:13:46 +08:00
|
|
|
self.emit("state-changed")
|
|
|
|
|
2014-09-12 06:16:21 +08:00
|
|
|
def is_active(self):
|
|
|
|
return self._state == self._STATE_ACTIVE
|
|
|
|
def is_disconnected(self):
|
|
|
|
return self._state == self._STATE_DISCONNECTED
|
|
|
|
def is_connecting(self):
|
|
|
|
return self._state == self._STATE_CONNECTING
|
2009-11-26 05:13:46 +08:00
|
|
|
|
|
|
|
def get_state_text(self):
|
2014-09-12 06:16:21 +08:00
|
|
|
if self.is_disconnected():
|
2009-11-26 05:13:46 +08:00
|
|
|
return _("Disconnected")
|
2014-09-12 06:16:21 +08:00
|
|
|
elif self.is_connecting():
|
2009-11-26 05:13:46 +08:00
|
|
|
return _("Connecting")
|
2014-09-12 06:16:21 +08:00
|
|
|
elif self.is_active():
|
2013-07-04 01:56:43 +08:00
|
|
|
return _("Active")
|
2009-11-26 05:13:46 +08:00
|
|
|
else:
|
|
|
|
return _("Unknown")
|
|
|
|
|
2014-06-03 05:17:47 +08:00
|
|
|
|
2009-11-26 05:13:46 +08:00
|
|
|
#################################
|
|
|
|
# Libvirt object lookup methods #
|
|
|
|
#################################
|
2006-06-15 02:36:26 +08:00
|
|
|
|
2014-06-03 05:17:47 +08:00
|
|
|
def get_vm(self, connkey):
|
2015-04-10 22:33:04 +08:00
|
|
|
return self._objects.lookup_object(vmmDomain, connkey)
|
2014-06-03 05:17:47 +08:00
|
|
|
def list_vms(self):
|
2015-04-10 22:33:04 +08:00
|
|
|
return self._objects.get_objects_for_class(vmmDomain)
|
2014-06-03 05:17:47 +08:00
|
|
|
|
|
|
|
def get_net(self, connkey):
|
2015-04-10 22:33:04 +08:00
|
|
|
return self._objects.lookup_object(vmmNetwork, connkey)
|
2014-06-03 05:17:47 +08:00
|
|
|
def list_nets(self):
|
2015-04-10 22:33:04 +08:00
|
|
|
return self._objects.get_objects_for_class(vmmNetwork)
|
2014-06-03 05:17:47 +08:00
|
|
|
|
|
|
|
def get_pool(self, connkey):
|
2015-04-10 22:33:04 +08:00
|
|
|
return self._objects.lookup_object(vmmStoragePool, connkey)
|
2014-06-03 05:17:47 +08:00
|
|
|
def list_pools(self):
|
2015-04-10 22:33:04 +08:00
|
|
|
return self._objects.get_objects_for_class(vmmStoragePool)
|
2014-06-03 05:17:47 +08:00
|
|
|
|
|
|
|
def get_interface(self, connkey):
|
2015-04-10 22:33:04 +08:00
|
|
|
return self._objects.lookup_object(vmmInterface, connkey)
|
2014-06-03 05:17:47 +08:00
|
|
|
def list_interfaces(self):
|
2015-04-10 22:33:04 +08:00
|
|
|
return self._objects.get_objects_for_class(vmmInterface)
|
2014-06-03 05:17:47 +08:00
|
|
|
|
|
|
|
def get_nodedev(self, connkey):
|
2015-04-10 22:33:04 +08:00
|
|
|
return self._objects.lookup_object(vmmNodeDevice, connkey)
|
|
|
|
def list_nodedevs(self):
|
|
|
|
return self._objects.get_objects_for_class(vmmNodeDevice)
|
2015-04-10 21:37:03 +08:00
|
|
|
|
|
|
|
|
|
|
|
############################
|
|
|
|
# nodedev helper functions #
|
|
|
|
############################
|
|
|
|
|
|
|
|
def filter_nodedevs(self, devtype=None, devcap=None):
|
2009-03-10 04:20:23 +08:00
|
|
|
retdevs = []
|
2015-04-10 22:33:04 +08:00
|
|
|
for dev in self.list_nodedevs():
|
2015-06-07 02:20:21 +08:00
|
|
|
try:
|
|
|
|
xmlobj = dev.get_xmlobj()
|
|
|
|
except libvirt.libvirtError, e:
|
2015-06-09 23:41:39 +08:00
|
|
|
# Libvirt nodedev XML fetching can be busted
|
|
|
|
# https://bugzilla.redhat.com/show_bug.cgi?id=1225771
|
|
|
|
if e.get_error_code() != libvirt.VIR_ERR_NO_NODE_DEVICE:
|
|
|
|
logging.debug("Error fetching nodedev XML", exc_info=True)
|
|
|
|
continue
|
2015-06-07 02:20:21 +08:00
|
|
|
|
2013-10-06 02:40:38 +08:00
|
|
|
if devtype and xmlobj.device_type != devtype:
|
2009-03-10 04:20:23 +08:00
|
|
|
continue
|
|
|
|
|
2009-11-26 03:50:27 +08:00
|
|
|
if devcap:
|
2013-10-06 02:40:38 +08:00
|
|
|
if (not hasattr(xmlobj, "capability_type") or
|
|
|
|
xmlobj.capability_type != devcap):
|
2009-11-26 03:50:27 +08:00
|
|
|
continue
|
|
|
|
|
2013-10-06 02:40:38 +08:00
|
|
|
if (devtype == "usb_device" and
|
|
|
|
(("Linux Foundation" in str(xmlobj.vendor_name) or
|
|
|
|
("Linux" in str(xmlobj.vendor_name) and
|
|
|
|
xmlobj.vendor_id == "0x1d6b")) and
|
|
|
|
("root hub" in str(xmlobj.product_name) or
|
|
|
|
("host controller" in str(xmlobj.product_name).lower() and
|
|
|
|
str(xmlobj.product_id).startswith("0x000"))))):
|
|
|
|
continue
|
|
|
|
|
2015-04-08 02:12:00 +08:00
|
|
|
retdevs.append(dev)
|
2009-03-10 04:20:23 +08:00
|
|
|
|
|
|
|
return retdevs
|
|
|
|
|
2015-04-10 21:37:03 +08:00
|
|
|
def get_nodedev_count(self, devtype, vendor, product):
|
2013-04-30 22:53:10 +08:00
|
|
|
count = 0
|
2015-04-10 21:37:03 +08:00
|
|
|
devs = self.filter_nodedevs(devtype)
|
2013-04-30 22:53:10 +08:00
|
|
|
|
|
|
|
for dev in devs:
|
2015-04-08 02:12:00 +08:00
|
|
|
if (vendor == dev.xmlobj.vendor_id and
|
|
|
|
product == dev.xmlobj.product_id):
|
2013-04-30 22:53:10 +08:00
|
|
|
count += 1
|
|
|
|
|
|
|
|
logging.debug("There are %d node devices with "
|
|
|
|
"vendorId: %s, productId: %s",
|
|
|
|
count, vendor, product)
|
|
|
|
|
|
|
|
return count
|
|
|
|
|
2009-11-26 05:13:46 +08:00
|
|
|
|
|
|
|
###################################
|
|
|
|
# Libvirt object creation methods #
|
|
|
|
###################################
|
|
|
|
|
2013-09-30 00:14:00 +08:00
|
|
|
def define_domain(self, xml):
|
|
|
|
return self._backend.defineXML(xml)
|
|
|
|
def define_network(self, xml):
|
|
|
|
return self._backend.networkDefineXML(xml)
|
|
|
|
def define_pool(self, xml):
|
|
|
|
return self._backend.storagePoolDefineXML(xml, 0)
|
|
|
|
def define_interface(self, xml):
|
|
|
|
return self._backend.interfaceDefineXML(xml, 0)
|
2009-12-12 02:45:53 +08:00
|
|
|
|
2014-09-21 04:55:11 +08:00
|
|
|
def rename_object(self, obj, origxml, newxml, oldname, newname):
|
2015-04-10 22:33:04 +08:00
|
|
|
ignore = oldname
|
|
|
|
ignore = newname
|
|
|
|
|
2015-04-10 21:15:44 +08:00
|
|
|
if obj.class_name() == "domain":
|
2014-09-21 04:55:11 +08:00
|
|
|
define_cb = self.define_domain
|
2015-04-10 21:15:44 +08:00
|
|
|
elif obj.class_name() == "pool":
|
2014-09-21 04:55:11 +08:00
|
|
|
define_cb = self.define_pool
|
2015-04-10 21:15:44 +08:00
|
|
|
elif obj.class_name() == "network":
|
2014-09-21 04:55:11 +08:00
|
|
|
define_cb = self.define_network
|
|
|
|
else:
|
|
|
|
raise RuntimeError("programming error: rename_object "
|
|
|
|
"helper doesn't support object class %s" % obj.__class__)
|
|
|
|
|
2013-09-30 00:14:00 +08:00
|
|
|
# Undefine the original object
|
2013-10-01 03:23:14 +08:00
|
|
|
obj.delete(force=False)
|
2011-03-24 04:56:12 +08:00
|
|
|
|
|
|
|
newobj = None
|
|
|
|
try:
|
2014-09-21 04:55:11 +08:00
|
|
|
# Redefine new domain
|
|
|
|
newobj = define_cb(newxml)
|
|
|
|
except Exception, renameerr:
|
2011-03-24 04:56:12 +08:00
|
|
|
try:
|
2015-04-10 21:15:44 +08:00
|
|
|
logging.debug("Error defining new name %s XML",
|
|
|
|
obj.class_name(), exc_info=True)
|
2014-09-21 04:55:11 +08:00
|
|
|
newobj = define_cb(origxml)
|
|
|
|
except Exception, fixerr:
|
2015-04-10 21:15:44 +08:00
|
|
|
logging.debug("Failed to redefine original %s!",
|
|
|
|
obj.class_name(), exc_info=True)
|
2014-09-21 04:55:11 +08:00
|
|
|
raise RuntimeError(
|
|
|
|
_("%s rename failed. Attempting to recover also "
|
|
|
|
"failed.\n\n"
|
|
|
|
"Original error: %s\n\n"
|
|
|
|
"Recover error: %s" %
|
2015-04-10 21:15:44 +08:00
|
|
|
(obj.class_name(), str(renameerr), str(fixerr))))
|
2014-09-21 04:55:11 +08:00
|
|
|
raise
|
2011-03-24 04:56:12 +08:00
|
|
|
finally:
|
|
|
|
if newobj:
|
2013-09-30 00:14:00 +08:00
|
|
|
# Reinsert handle into new obj
|
|
|
|
obj.change_name_backend(newobj)
|
|
|
|
|
2009-11-26 05:13:46 +08:00
|
|
|
|
2014-02-12 01:07:13 +08:00
|
|
|
#########################
|
|
|
|
# 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.
|
|
|
|
|
2014-04-01 00:43:49 +08:00
|
|
|
def _domain_xml_misc_event(self, conn, domain, *args):
|
|
|
|
# Just trigger a domain XML refresh for hotplug type events
|
|
|
|
ignore = conn
|
|
|
|
|
2015-04-14 06:02:16 +08:00
|
|
|
name = domain.name()
|
|
|
|
logging.debug("domain xmlmisc event: domain=%s args=%s ", name, args)
|
|
|
|
obj = self.get_vm(name)
|
2014-04-01 00:43:49 +08:00
|
|
|
if not obj:
|
|
|
|
return
|
2015-04-10 06:02:42 +08:00
|
|
|
|
2015-04-12 05:18:25 +08:00
|
|
|
self.idle_add(obj.refresh_from_event_loop)
|
2014-04-01 00:43:49 +08:00
|
|
|
|
2014-02-12 01:07:13 +08:00
|
|
|
def _domain_lifecycle_event(self, conn, domain, event, reason, userdata):
|
|
|
|
ignore = conn
|
|
|
|
ignore = userdata
|
2015-04-14 06:02:16 +08:00
|
|
|
|
|
|
|
name = domain.name()
|
|
|
|
logging.debug("domain lifecycle event: domain=%s event=%s "
|
|
|
|
"reason=%s", name, event, reason)
|
|
|
|
obj = self.get_vm(name)
|
2014-02-12 01:07:13 +08:00
|
|
|
|
2014-02-12 02:19:15 +08:00
|
|
|
if obj:
|
2015-04-12 05:18:25 +08:00
|
|
|
self.idle_add(obj.refresh_from_event_loop)
|
2014-02-12 01:07:13 +08:00
|
|
|
else:
|
|
|
|
self.schedule_priority_tick(pollvm=True, force=True)
|
|
|
|
|
2014-02-12 02:19:15 +08:00
|
|
|
def _network_lifecycle_event(self, conn, network, event, reason, userdata):
|
|
|
|
ignore = conn
|
|
|
|
ignore = userdata
|
2015-04-14 06:02:16 +08:00
|
|
|
|
|
|
|
name = network.name()
|
|
|
|
logging.debug("network lifecycle event: network=%s event=%s "
|
|
|
|
"reason=%s", name, event, reason)
|
|
|
|
obj = self.get_net(name)
|
2014-02-12 02:19:15 +08:00
|
|
|
|
|
|
|
if obj:
|
2015-04-12 05:18:25 +08:00
|
|
|
self.idle_add(obj.refresh_from_event_loop)
|
2014-02-12 02:19:15 +08:00
|
|
|
else:
|
|
|
|
self.schedule_priority_tick(pollnet=True, force=True)
|
2014-02-12 01:07:13 +08:00
|
|
|
|
2014-02-12 02:19:15 +08:00
|
|
|
def _add_conn_events(self):
|
2015-02-25 23:25:21 +08:00
|
|
|
if not self.check_support(support.SUPPORT_CONN_WORKING_XEN_EVENTS):
|
|
|
|
return
|
|
|
|
|
2014-02-12 01:07:13 +08:00
|
|
|
try:
|
2015-09-18 03:18:22 +08:00
|
|
|
if FORCE_DISABLE_EVENTS:
|
|
|
|
raise RuntimeError("FORCE_DISABLE_EVENTS = True")
|
2014-06-03 00:00:07 +08:00
|
|
|
|
2014-04-01 00:43:49 +08:00
|
|
|
self._domain_cb_ids.append(
|
|
|
|
self.get_backend().domainEventRegisterAny(
|
2014-02-12 02:27:59 +08:00
|
|
|
None, libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE,
|
2014-04-01 00:43:49 +08:00
|
|
|
self._domain_lifecycle_event, None))
|
2014-02-12 01:07:13 +08:00
|
|
|
self.using_domain_events = True
|
2014-02-12 02:27:59 +08:00
|
|
|
logging.debug("Using domain events")
|
2014-02-12 01:07:13 +08:00
|
|
|
except Exception, e:
|
|
|
|
self.using_domain_events = False
|
|
|
|
logging.debug("Error registering domain events: %s", e)
|
|
|
|
|
2014-04-01 00:43:49 +08:00
|
|
|
def _add_domain_xml_event(eventid, typestr):
|
|
|
|
if not self.using_domain_events:
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
self._domain_cb_ids.append(
|
|
|
|
self.get_backend().domainEventRegisterAny(
|
|
|
|
None, eventid, self._domain_xml_misc_event, None))
|
|
|
|
except Exception, e:
|
|
|
|
logging.debug("Error registering domain %s event: %s",
|
|
|
|
typestr, e)
|
|
|
|
|
|
|
|
_add_domain_xml_event(
|
|
|
|
getattr(libvirt, "VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE", 13),
|
|
|
|
"balloon")
|
|
|
|
_add_domain_xml_event(
|
|
|
|
getattr(libvirt, "VIR_DOMAIN_EVENT_ID_TRAY_CHANGE", 10), "tray")
|
|
|
|
_add_domain_xml_event(
|
|
|
|
getattr(libvirt, "VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED", 15),
|
|
|
|
"device removed")
|
2015-04-29 17:00:40 +08:00
|
|
|
_add_domain_xml_event(
|
|
|
|
getattr(libvirt, "VIR_DOMAIN_EVENT_ID_DEVICE_ADDED", 19),
|
|
|
|
"device added")
|
2014-04-01 00:43:49 +08:00
|
|
|
|
2014-02-12 02:19:15 +08:00
|
|
|
try:
|
2015-09-18 03:18:22 +08:00
|
|
|
if FORCE_DISABLE_EVENTS:
|
|
|
|
raise RuntimeError("FORCE_DISABLE_EVENTS = True")
|
2014-06-03 00:00:07 +08:00
|
|
|
|
2014-03-22 23:21:19 +08:00
|
|
|
eventid = getattr(libvirt, "VIR_NETWORK_EVENT_ID_LIFECYCLE", 0)
|
2014-04-01 00:43:49 +08:00
|
|
|
self._network_cb_ids.append(
|
|
|
|
self.get_backend().networkEventRegisterAny(
|
|
|
|
None, eventid, self._network_lifecycle_event, None))
|
2014-02-12 02:19:15 +08:00
|
|
|
self.using_network_events = True
|
2014-02-12 02:27:59 +08:00
|
|
|
logging.debug("Using network events")
|
2014-02-12 02:19:15 +08:00
|
|
|
except Exception, e:
|
|
|
|
self.using_network_events = False
|
|
|
|
logging.debug("Error registering network events: %s", e)
|
|
|
|
|
2013-07-07 09:31:50 +08:00
|
|
|
|
2009-11-26 05:13:46 +08:00
|
|
|
######################################
|
|
|
|
# Connection closing/opening methods #
|
|
|
|
######################################
|
|
|
|
|
2014-09-12 06:48:04 +08:00
|
|
|
def _schedule_close(self):
|
|
|
|
self._closing = True
|
|
|
|
self.idle_add(self.close)
|
|
|
|
|
2009-11-26 05:13:46 +08:00
|
|
|
def close(self):
|
2014-09-12 06:16:21 +08:00
|
|
|
if not self.is_disconnected():
|
2014-04-16 22:32:52 +08:00
|
|
|
logging.debug("conn.close() uri=%s", self.get_uri())
|
|
|
|
self._closing = True
|
|
|
|
|
2014-03-10 21:33:04 +08:00
|
|
|
try:
|
|
|
|
if not self._backend.is_closed():
|
2014-04-01 00:43:49 +08:00
|
|
|
for eid in self._domain_cb_ids:
|
|
|
|
self._backend.domainEventDeregisterAny(eid)
|
|
|
|
for eid in self._network_cb_ids:
|
|
|
|
self._backend.networkEventDeregisterAny(eid)
|
2014-03-10 21:33:04 +08:00
|
|
|
except:
|
|
|
|
logging.debug("Failed to deregister events in conn cleanup",
|
|
|
|
exc_info=True)
|
2014-04-17 00:23:57 +08:00
|
|
|
finally:
|
|
|
|
self._domain_cb_ids = []
|
|
|
|
self._network_cb_ids = []
|
2014-02-12 02:27:59 +08:00
|
|
|
|
2013-07-05 20:59:58 +08:00
|
|
|
self._backend.close()
|
2015-05-05 06:16:09 +08:00
|
|
|
self._stats = []
|
2011-04-11 23:00:57 +08:00
|
|
|
|
2015-04-11 02:08:25 +08:00
|
|
|
if self._init_object_event:
|
|
|
|
self._init_object_event.clear()
|
|
|
|
|
2015-04-10 22:33:04 +08:00
|
|
|
self._objects.cleanup()
|
|
|
|
self._objects = _ObjectList()
|
2011-03-23 22:07:23 +08:00
|
|
|
|
2014-09-12 06:16:21 +08:00
|
|
|
self._change_state(self._STATE_DISCONNECTED)
|
2014-04-16 22:32:52 +08:00
|
|
|
self._closing = False
|
2009-11-26 05:13:46 +08:00
|
|
|
|
2011-07-24 09:16:54 +08:00
|
|
|
def _cleanup(self):
|
2011-04-13 21:27:02 +08:00
|
|
|
self.close()
|
|
|
|
|
2015-09-21 02:20:50 +08:00
|
|
|
self._objects = None
|
|
|
|
self._backend.cb_fetch_all_guests = None
|
|
|
|
self._backend.cb_fetch_all_pools = None
|
|
|
|
self._backend.cb_fetch_all_nodedevs = None
|
|
|
|
self._backend.cb_fetch_all_vols = None
|
|
|
|
self._backend.cb_clear_cache = None
|
|
|
|
|
2015-04-11 01:26:54 +08:00
|
|
|
def open(self):
|
2014-09-12 06:16:21 +08:00
|
|
|
if not self.is_disconnected():
|
2007-09-10 10:57:24 +08:00
|
|
|
return
|
|
|
|
|
2014-09-12 06:16:21 +08:00
|
|
|
self._change_state(self._STATE_CONNECTING)
|
2007-09-11 08:56:01 +08:00
|
|
|
|
2015-04-11 01:26:54 +08:00
|
|
|
logging.debug("Scheduling background open thread for " +
|
|
|
|
self.get_uri())
|
|
|
|
self._start_thread(self._open_thread, "Connect %s" % self.get_uri())
|
2007-09-11 08:56:01 +08:00
|
|
|
|
2013-07-05 20:59:58 +08:00
|
|
|
def _do_creds_password(self, creds):
|
2008-02-01 00:39:10 +08:00
|
|
|
try:
|
2015-04-12 00:08:57 +08:00
|
|
|
return connectauth.creds_dialog(self, creds)
|
2014-07-05 05:37:22 +08:00
|
|
|
except:
|
|
|
|
logging.debug("Launching creds dialog failed", exc_info=True)
|
2008-02-01 00:39:10 +08:00
|
|
|
return -1
|
2007-09-11 08:56:01 +08:00
|
|
|
|
2014-09-12 06:48:04 +08:00
|
|
|
def _do_open(self, retry_for_tgt=True):
|
|
|
|
warnconsole = False
|
|
|
|
libvirt_error_code = None
|
|
|
|
libvirt_error_message = None
|
2009-03-10 04:21:32 +08:00
|
|
|
|
2014-09-12 06:48:04 +08:00
|
|
|
try:
|
|
|
|
self._backend.open(self._do_creds_password)
|
2014-09-12 23:30:09 +08:00
|
|
|
return True, None
|
2014-09-12 06:48:04 +08:00
|
|
|
except Exception, exc:
|
|
|
|
tb = "".join(traceback.format_exc())
|
|
|
|
if type(exc) is libvirt.libvirtError:
|
|
|
|
libvirt_error_code = exc.get_error_code()
|
|
|
|
libvirt_error_message = exc.get_error_message()
|
|
|
|
|
|
|
|
if (libvirt_error_code ==
|
|
|
|
getattr(libvirt, "VIR_ERR_AUTH_CANCELLED", None)):
|
|
|
|
logging.debug("User cancelled auth, not raising any error.")
|
2014-09-12 23:30:09 +08:00
|
|
|
return False, None
|
2007-09-11 08:56:01 +08:00
|
|
|
|
2014-09-12 06:48:04 +08:00
|
|
|
if (libvirt_error_code == libvirt.VIR_ERR_AUTH_FAILED and
|
|
|
|
"not authorized" in libvirt_error_message.lower()):
|
|
|
|
logging.debug("Looks like we might have failed policykit "
|
|
|
|
"auth. Checking to see if we have a valid "
|
|
|
|
"console session")
|
|
|
|
if (not self.is_remote() and
|
|
|
|
not connectauth.do_we_have_session()):
|
|
|
|
warnconsole = True
|
|
|
|
|
|
|
|
if (libvirt_error_code == libvirt.VIR_ERR_AUTH_FAILED and
|
|
|
|
"GSSAPI Error" in libvirt_error_message and
|
|
|
|
"No credentials cache found" in libvirt_error_message):
|
|
|
|
if retry_for_tgt and connectauth.acquire_tgt():
|
|
|
|
self._do_open(retry_for_tgt=False)
|
|
|
|
|
2014-09-12 23:30:09 +08:00
|
|
|
connectError = (str(exc), tb, warnconsole)
|
|
|
|
return False, connectError
|
2014-09-12 06:48:04 +08:00
|
|
|
|
|
|
|
def _populate_initial_state(self):
|
|
|
|
logging.debug("libvirt version=%s",
|
|
|
|
self._backend.local_libvirt_version())
|
|
|
|
logging.debug("daemon version=%s",
|
|
|
|
self._backend.daemon_version())
|
|
|
|
logging.debug("conn version=%s", self._backend.conn_version())
|
|
|
|
logging.debug("%s capabilities:\n%s",
|
2015-04-04 00:40:16 +08:00
|
|
|
self.get_uri(), self.caps.get_xml_config())
|
2014-09-12 06:48:04 +08:00
|
|
|
self._add_conn_events()
|
2007-09-11 08:56:01 +08:00
|
|
|
|
2014-09-12 21:36:09 +08:00
|
|
|
# Prime CPU cache
|
2015-04-04 04:09:25 +08:00
|
|
|
self.caps.get_cpu_values("x86_64")
|
2014-09-12 21:36:09 +08:00
|
|
|
|
2014-09-12 06:48:04 +08:00
|
|
|
try:
|
|
|
|
self._backend.setKeepAlive(20, 1)
|
|
|
|
except Exception, e:
|
|
|
|
if (type(e) is not AttributeError and
|
|
|
|
not util.is_error_nosupport(e)):
|
|
|
|
raise
|
|
|
|
logging.debug("Connection doesn't support KeepAlive, "
|
|
|
|
"skipping")
|
2009-07-12 09:23:16 +08:00
|
|
|
|
2015-04-11 02:08:25 +08:00
|
|
|
# The initial tick will set up a threading event that will only
|
|
|
|
# trigger after all the polled libvirt objects are fully initialized.
|
|
|
|
# That way we only report the connection is open when everything is
|
|
|
|
# nicely setup for the rest of the app.
|
|
|
|
|
|
|
|
self._init_object_event = threading.Event()
|
|
|
|
self._init_object_count = 0
|
|
|
|
|
|
|
|
self.schedule_priority_tick(stats_update=True,
|
|
|
|
pollvm=True, pollnet=True,
|
|
|
|
pollpool=True, polliface=True,
|
|
|
|
pollnodedev=True, force=True, initial_poll=True)
|
|
|
|
|
|
|
|
self._init_object_event.wait()
|
|
|
|
self._init_object_event = None
|
|
|
|
self._init_object_count = None
|
|
|
|
|
2014-09-12 06:48:04 +08:00
|
|
|
def _open_thread(self):
|
2014-07-05 05:37:42 +08:00
|
|
|
try:
|
2014-09-12 23:30:09 +08:00
|
|
|
is_active, connectError = self._do_open()
|
2014-09-12 06:48:04 +08:00
|
|
|
if is_active:
|
|
|
|
self._populate_initial_state()
|
2014-07-05 05:43:24 +08:00
|
|
|
|
2015-04-11 02:08:25 +08:00
|
|
|
self.idle_add(self._change_state, is_active and
|
|
|
|
self._STATE_ACTIVE or self._STATE_DISCONNECTED)
|
2014-07-05 05:37:42 +08:00
|
|
|
except Exception, e:
|
2014-09-12 06:48:04 +08:00
|
|
|
is_active = False
|
|
|
|
self._schedule_close()
|
2014-09-12 23:30:09 +08:00
|
|
|
connectError = (str(e), "".join(traceback.format_exc()), False)
|
2010-11-25 03:01:51 +08:00
|
|
|
|
2014-09-12 06:48:04 +08:00
|
|
|
if not is_active:
|
2014-09-12 23:30:09 +08:00
|
|
|
if connectError:
|
|
|
|
self.idle_emit("connect-error", *connectError)
|
2007-09-11 08:56:01 +08:00
|
|
|
|
2006-10-11 05:24:59 +08:00
|
|
|
|
2009-11-26 05:13:46 +08:00
|
|
|
#######################
|
|
|
|
# Tick/Update methods #
|
|
|
|
#######################
|
2006-06-14 22:59:40 +08:00
|
|
|
|
2015-04-11 02:08:25 +08:00
|
|
|
def _gone_object_signals(self, gone_objects):
|
2015-04-10 22:33:04 +08:00
|
|
|
"""
|
|
|
|
Responsible for signaling the UI for any updates. All possible UI
|
|
|
|
updates need to go here to enable threading that doesn't block the
|
|
|
|
app with long tick operations.
|
|
|
|
"""
|
|
|
|
if not self._backend.is_open():
|
|
|
|
return
|
|
|
|
|
2015-04-10 21:15:44 +08:00
|
|
|
for obj in gone_objects:
|
2015-04-11 02:08:25 +08:00
|
|
|
class_name = obj.class_name()
|
2015-05-08 00:30:28 +08:00
|
|
|
try:
|
|
|
|
name = obj.get_name()
|
|
|
|
except:
|
|
|
|
name = str(obj)
|
2015-04-11 02:08:25 +08:00
|
|
|
|
2015-04-10 22:33:04 +08:00
|
|
|
if not self._objects.remove(obj):
|
2015-04-11 02:08:25 +08:00
|
|
|
logging.debug("Requested removal of %s=%s, but it's "
|
2015-05-08 00:30:28 +08:00
|
|
|
"not in our object list.", class_name, name)
|
2015-04-10 22:33:04 +08:00
|
|
|
continue
|
|
|
|
|
2015-05-08 00:30:28 +08:00
|
|
|
logging.debug("%s=%s removed", class_name, name)
|
2015-04-10 21:15:44 +08:00
|
|
|
if class_name == "domain":
|
|
|
|
self.emit("vm-removed", obj.get_connkey())
|
|
|
|
elif class_name == "network":
|
|
|
|
self.emit("net-removed", obj.get_connkey())
|
|
|
|
elif class_name == "pool":
|
|
|
|
self.emit("pool-removed", obj.get_connkey())
|
|
|
|
elif class_name == "interface":
|
|
|
|
self.emit("interface-removed", obj.get_connkey())
|
|
|
|
elif class_name == "nodedev":
|
|
|
|
self.emit("nodedev-removed", obj.get_connkey())
|
|
|
|
obj.cleanup()
|
|
|
|
|
2015-09-18 03:48:42 +08:00
|
|
|
def _new_object_cb(self, obj, initialize_failed):
|
2015-04-11 02:08:25 +08:00
|
|
|
if not self._backend.is_open():
|
|
|
|
return
|
2015-04-10 22:33:04 +08:00
|
|
|
|
2015-04-11 02:08:25 +08:00
|
|
|
try:
|
2015-04-10 21:15:44 +08:00
|
|
|
class_name = obj.class_name()
|
2015-04-11 02:08:25 +08:00
|
|
|
|
2015-09-18 03:48:42 +08:00
|
|
|
if initialize_failed:
|
|
|
|
logging.debug("Blacklisting %s=%s", class_name, obj.get_name())
|
|
|
|
if self._objects.add_blacklist(obj) is False:
|
|
|
|
logging.debug("Object already blacklisted?")
|
|
|
|
return
|
|
|
|
|
2015-04-11 02:08:25 +08:00
|
|
|
if not self._objects.add(obj):
|
|
|
|
logging.debug("New %s=%s requested, but it's already tracked.",
|
|
|
|
class_name, obj.get_name())
|
|
|
|
return
|
|
|
|
|
2015-04-10 21:15:44 +08:00
|
|
|
if class_name != "nodedev":
|
|
|
|
# Skip nodedev logging since it's noisy and not interesting
|
|
|
|
logging.debug("%s=%s status=%s added", class_name,
|
|
|
|
obj.get_name(), obj.run_status())
|
|
|
|
if class_name == "domain":
|
|
|
|
self.emit("vm-added", obj.get_connkey())
|
|
|
|
elif class_name == "network":
|
|
|
|
self.emit("net-added", obj.get_connkey())
|
|
|
|
elif class_name == "pool":
|
|
|
|
self.emit("pool-added", obj.get_connkey())
|
|
|
|
elif class_name == "interface":
|
|
|
|
self.emit("interface-added", obj.get_connkey())
|
|
|
|
elif class_name == "nodedev":
|
|
|
|
self.emit("nodedev-added", obj.get_connkey())
|
2015-04-11 02:08:25 +08:00
|
|
|
finally:
|
|
|
|
if self._init_object_event:
|
|
|
|
self._init_object_count -= 1
|
|
|
|
if self._init_object_count <= 0:
|
|
|
|
self._init_object_event.set()
|
2014-09-12 23:55:34 +08:00
|
|
|
|
2015-04-11 00:52:42 +08:00
|
|
|
def _update_nets(self, dopoll):
|
|
|
|
keymap = dict((o.get_connkey(), o) for o in self.list_nets())
|
|
|
|
if not dopoll or not self.is_network_capable():
|
|
|
|
return [], [], keymap.values()
|
|
|
|
return pollhelpers.fetch_nets(self._backend, keymap,
|
|
|
|
(lambda obj, key: vmmNetwork(self, obj, key)))
|
|
|
|
|
|
|
|
def _update_pools(self, dopoll):
|
|
|
|
keymap = dict((o.get_connkey(), o) for o in self.list_pools())
|
|
|
|
if not dopoll or not self.is_storage_capable():
|
|
|
|
return [], [], keymap.values()
|
|
|
|
return pollhelpers.fetch_pools(self._backend, keymap,
|
|
|
|
(lambda obj, key: vmmStoragePool(self, obj, key)))
|
|
|
|
|
|
|
|
def _update_interfaces(self, dopoll):
|
|
|
|
keymap = dict((o.get_connkey(), o) for o in self.list_interfaces())
|
|
|
|
if not dopoll or not self.is_interface_capable():
|
|
|
|
return [], [], keymap.values()
|
|
|
|
return pollhelpers.fetch_interfaces(self._backend, keymap,
|
|
|
|
(lambda obj, key: vmmInterface(self, obj, key)))
|
|
|
|
|
|
|
|
def _update_nodedevs(self, dopoll):
|
|
|
|
keymap = dict((o.get_connkey(), o) for o in self.list_nodedevs())
|
|
|
|
if not dopoll or not self.is_nodedev_capable():
|
|
|
|
return [], [], keymap.values()
|
|
|
|
return pollhelpers.fetch_nodedevs(self._backend, keymap,
|
|
|
|
(lambda obj, key: vmmNodeDevice(self, obj, key)))
|
|
|
|
|
|
|
|
def _update_vms(self, dopoll):
|
|
|
|
keymap = dict((o.get_connkey(), o) for o in self.list_vms())
|
|
|
|
if not dopoll:
|
|
|
|
return [], [], keymap.values()
|
|
|
|
return pollhelpers.fetch_vms(self._backend, keymap,
|
|
|
|
(lambda obj, key: vmmDomain(self, obj, key)))
|
|
|
|
|
2015-04-11 02:08:25 +08:00
|
|
|
def _poll(self, initial_poll,
|
|
|
|
pollvm, pollnet, pollpool, polliface, pollnodedev):
|
2015-04-11 00:52:42 +08:00
|
|
|
"""
|
|
|
|
Helper called from tick() to do necessary polling and return
|
|
|
|
the relevant object lists
|
|
|
|
"""
|
|
|
|
gone_objects = []
|
|
|
|
preexisting_objects = []
|
|
|
|
|
|
|
|
def _process_objects(polloutput):
|
|
|
|
gone, new, master = polloutput
|
2015-04-11 02:08:25 +08:00
|
|
|
|
|
|
|
if initial_poll:
|
|
|
|
self._init_object_count += len(new)
|
|
|
|
|
2015-04-11 00:52:42 +08:00
|
|
|
gone_objects.extend(gone)
|
|
|
|
preexisting_objects.extend([o for o in master if o not in new])
|
2015-09-18 03:48:42 +08:00
|
|
|
new = [n for n in new if not self._objects.in_blacklist(n)]
|
2015-04-11 00:52:42 +08:00
|
|
|
return new
|
|
|
|
|
2015-04-11 02:08:25 +08:00
|
|
|
new_vms = _process_objects(self._update_vms(pollvm))
|
|
|
|
new_nets = _process_objects(self._update_nets(pollnet))
|
|
|
|
new_pools = _process_objects(self._update_pools(pollpool))
|
|
|
|
new_ifaces = _process_objects(self._update_interfaces(polliface))
|
|
|
|
new_nodedevs = _process_objects(self._update_nodedevs(pollnodedev))
|
|
|
|
|
|
|
|
# Kick off one thread per object type to handle the initial
|
|
|
|
# XML fetching. Going any more fine grained then this probably
|
|
|
|
# won't be that useful due to libvirt's locking structure.
|
|
|
|
#
|
|
|
|
# Would prefer to start refreshing some objects before all polling
|
|
|
|
# is complete, but we need init_object_count to be fully accurate
|
|
|
|
# before we start initializing objects
|
|
|
|
for newlist in [new_vms, new_nets, new_pools,
|
|
|
|
new_ifaces, new_nodedevs]:
|
|
|
|
if not newlist:
|
|
|
|
continue
|
|
|
|
|
|
|
|
def cb(lst):
|
|
|
|
for obj in lst:
|
|
|
|
obj.connect_once("initialized", self._new_object_cb)
|
|
|
|
obj.init_libvirt_state()
|
2015-04-11 00:52:42 +08:00
|
|
|
|
2015-04-11 02:08:25 +08:00
|
|
|
self._start_thread(cb,
|
|
|
|
"refreshing xml for new %s" % newlist[0].class_name(),
|
|
|
|
args=(newlist,))
|
2015-04-11 00:52:42 +08:00
|
|
|
|
2015-04-11 02:08:25 +08:00
|
|
|
return gone_objects, preexisting_objects
|
2015-04-11 00:52:42 +08:00
|
|
|
|
2015-04-14 04:56:46 +08:00
|
|
|
def _tick(self, stats_update=False,
|
2013-07-07 23:06:15 +08:00
|
|
|
pollvm=False, pollnet=False,
|
|
|
|
pollpool=False, polliface=False,
|
2015-04-08 01:56:48 +08:00
|
|
|
pollnodedev=False,
|
2015-04-11 02:08:25 +08:00
|
|
|
force=False, initial_poll=False):
|
2014-02-12 01:07:13 +08:00
|
|
|
"""
|
|
|
|
main update function: polls for new objects, updates stats, ...
|
2015-04-11 00:52:42 +08:00
|
|
|
|
|
|
|
:param force: Perform the requested polling even if async events
|
2015-04-11 02:08:25 +08:00
|
|
|
are in use.
|
2014-02-12 01:07:13 +08:00
|
|
|
"""
|
2014-09-12 07:12:43 +08:00
|
|
|
if self._closing:
|
2008-07-10 05:00:49 +08:00
|
|
|
return
|
2014-09-12 07:12:43 +08:00
|
|
|
if self.is_disconnected():
|
|
|
|
return
|
2015-04-11 02:08:25 +08:00
|
|
|
if self.is_connecting() and not force:
|
|
|
|
return
|
2006-10-10 01:28:13 +08:00
|
|
|
|
2015-04-11 00:52:42 +08:00
|
|
|
# We need to set this before the event check, since stats polling
|
|
|
|
# is independent of events
|
2013-07-07 23:06:15 +08:00
|
|
|
if not pollvm:
|
|
|
|
stats_update = False
|
2014-02-12 02:19:15 +08:00
|
|
|
|
2014-02-12 01:07:13 +08:00
|
|
|
if self.using_domain_events and not force:
|
|
|
|
pollvm = False
|
2014-02-12 02:19:15 +08:00
|
|
|
if self.using_network_events and not force:
|
|
|
|
pollnet = False
|
2013-07-07 23:06:15 +08:00
|
|
|
|
2015-05-05 06:16:09 +08:00
|
|
|
self._hostinfo = self._backend.getInfo()
|
2008-10-07 01:21:06 +08:00
|
|
|
|
2015-04-11 02:08:25 +08:00
|
|
|
gone_objects, preexisting_objects = self._poll(
|
|
|
|
initial_poll, pollvm, pollnet, pollpool, polliface, pollnodedev)
|
|
|
|
self.idle_add(self._gone_object_signals, gone_objects)
|
2008-08-08 05:37:16 +08:00
|
|
|
|
2015-04-11 00:52:42 +08:00
|
|
|
# Only tick() pre-existing objects, since new objects will be
|
|
|
|
# initialized asynchronously and tick() would be redundant
|
|
|
|
for obj in preexisting_objects:
|
2009-09-17 04:02:19 +08:00
|
|
|
try:
|
2015-04-11 00:52:42 +08:00
|
|
|
if obj.reports_stats() and stats_update:
|
|
|
|
pass
|
|
|
|
elif obj.__class__ is vmmDomain and not pollvm:
|
|
|
|
continue
|
|
|
|
elif obj.__class__ is vmmNetwork and not pollnet:
|
|
|
|
continue
|
|
|
|
elif obj.__class__ is vmmStoragePool and not pollpool:
|
|
|
|
continue
|
|
|
|
elif obj.__class__ is vmmInterface and not polliface:
|
|
|
|
continue
|
|
|
|
elif obj.__class__ is vmmNodeDevice and not pollnodedev:
|
|
|
|
continue
|
|
|
|
|
2015-04-10 21:15:44 +08:00
|
|
|
obj.tick(stats_update=stats_update)
|
2009-09-17 04:02:19 +08:00
|
|
|
except Exception, e:
|
2013-07-07 20:05:23 +08:00
|
|
|
logging.exception("Tick for %s failed", obj)
|
2012-01-30 00:26:24 +08:00
|
|
|
if (isinstance(e, libvirt.libvirtError) and
|
2012-02-01 08:07:32 +08:00
|
|
|
(getattr(e, "get_error_code")() ==
|
|
|
|
libvirt.VIR_ERR_SYSTEM_ERROR)):
|
2012-01-30 00:26:24 +08:00
|
|
|
# Try a simple getInfo call to see if conn was dropped
|
2013-07-05 20:59:58 +08:00
|
|
|
self._backend.getInfo()
|
2012-01-30 00:26:24 +08:00
|
|
|
logging.debug("vm tick raised system error but "
|
|
|
|
"connection doesn't seem to have dropped. "
|
|
|
|
"Ignoring.")
|
2007-03-28 07:52:00 +08:00
|
|
|
|
2013-07-07 21:42:21 +08:00
|
|
|
if stats_update:
|
2015-04-11 00:52:42 +08:00
|
|
|
self._recalculate_stats(
|
|
|
|
[o for o in preexisting_objects if o.reports_stats()])
|
2011-04-18 23:12:36 +08:00
|
|
|
self.idle_emit("resources-sampled")
|
2009-07-12 09:23:16 +08:00
|
|
|
|
2013-07-07 20:12:15 +08:00
|
|
|
def _recalculate_stats(self, vms):
|
2013-07-05 20:59:58 +08:00
|
|
|
if not self._backend.is_open():
|
2010-02-12 04:25:41 +08:00
|
|
|
return
|
|
|
|
|
2013-07-07 20:12:15 +08:00
|
|
|
now = time.time()
|
2007-03-28 07:52:00 +08:00
|
|
|
expected = self.config.get_stats_history_length()
|
2015-05-05 06:16:09 +08:00
|
|
|
current = len(self._stats)
|
2007-03-28 07:52:00 +08:00
|
|
|
if current > expected:
|
2015-05-05 06:16:09 +08:00
|
|
|
del self._stats[expected:current]
|
2007-03-28 07:52:00 +08:00
|
|
|
|
|
|
|
mem = 0
|
|
|
|
cpuTime = 0
|
2008-10-19 03:21:33 +08:00
|
|
|
rdRate = 0
|
|
|
|
wrRate = 0
|
|
|
|
rxRate = 0
|
|
|
|
txRate = 0
|
2011-07-26 03:09:42 +08:00
|
|
|
diskMaxRate = self.disk_io_max_rate() or 10.0
|
|
|
|
netMaxRate = self.network_traffic_max_rate() or 10.0
|
2007-03-28 07:52:00 +08:00
|
|
|
|
2013-07-07 09:42:41 +08:00
|
|
|
for vm in vms:
|
2011-04-10 10:40:22 +08:00
|
|
|
if not vm.is_active():
|
|
|
|
continue
|
|
|
|
|
|
|
|
cpuTime += vm.cpu_time()
|
2011-04-10 12:02:39 +08:00
|
|
|
mem += vm.stats_memory()
|
2011-04-10 10:40:22 +08:00
|
|
|
rdRate += vm.disk_read_rate()
|
|
|
|
wrRate += vm.disk_write_rate()
|
|
|
|
rxRate += vm.network_rx_rate()
|
|
|
|
txRate += vm.network_tx_rate()
|
2007-03-28 07:52:00 +08:00
|
|
|
|
2011-07-26 03:09:42 +08:00
|
|
|
netMaxRate = max(netMaxRate, vm.network_traffic_max_rate())
|
|
|
|
diskMaxRate = max(diskMaxRate, vm.disk_io_max_rate())
|
|
|
|
|
2011-07-12 09:22:50 +08:00
|
|
|
pcentHostCpu = 0
|
|
|
|
pcentMem = mem * 100.0 / self.host_memory_size()
|
|
|
|
|
2015-05-05 06:16:09 +08:00
|
|
|
if len(self._stats) > 0:
|
|
|
|
prevTimestamp = self._stats[0]["timestamp"]
|
2010-12-11 00:47:07 +08:00
|
|
|
host_cpus = self.host_active_processor_count()
|
|
|
|
|
2011-07-12 09:22:50 +08:00
|
|
|
pcentHostCpu = ((cpuTime) * 100.0 /
|
2010-12-11 00:47:07 +08:00
|
|
|
((now - prevTimestamp) *
|
|
|
|
1000.0 * 1000.0 * 1000.0 * host_cpus))
|
2007-03-28 07:52:00 +08:00
|
|
|
|
2011-07-12 09:22:50 +08:00
|
|
|
pcentHostCpu = max(0.0, min(100.0, pcentHostCpu))
|
|
|
|
pcentMem = max(0.0, min(100.0, pcentMem))
|
2007-03-28 07:52:00 +08:00
|
|
|
|
|
|
|
newStats = {
|
|
|
|
"timestamp": now,
|
|
|
|
"memory": mem,
|
|
|
|
"memoryPercent": pcentMem,
|
|
|
|
"cpuTime": cpuTime,
|
2011-07-12 09:22:50 +08:00
|
|
|
"cpuHostPercent": pcentHostCpu,
|
2008-10-19 03:21:33 +08:00
|
|
|
"diskRdRate" : rdRate,
|
|
|
|
"diskWrRate" : wrRate,
|
|
|
|
"netRxRate" : rxRate,
|
|
|
|
"netTxRate" : txRate,
|
2011-07-26 03:09:42 +08:00
|
|
|
"diskMaxRate" : diskMaxRate,
|
|
|
|
"netMaxRate" : netMaxRate,
|
2007-03-28 07:52:00 +08:00
|
|
|
}
|
|
|
|
|
2015-05-05 06:16:09 +08:00
|
|
|
self._stats.insert(0, newStats)
|
2007-03-28 07:52:00 +08:00
|
|
|
|
2009-11-26 05:13:46 +08:00
|
|
|
|
2015-04-11 00:52:42 +08:00
|
|
|
def schedule_priority_tick(self, **kwargs):
|
|
|
|
self.idle_emit("priority-tick", kwargs)
|
|
|
|
|
2015-04-14 04:56:46 +08:00
|
|
|
def tick_from_engine(self, *args, **kwargs):
|
2015-04-11 00:52:42 +08:00
|
|
|
e = None
|
|
|
|
try:
|
|
|
|
self._tick(*args, **kwargs)
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
raise
|
|
|
|
except Exception, e:
|
|
|
|
pass
|
|
|
|
|
|
|
|
if e is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
from_remote = getattr(libvirt, "VIR_FROM_REMOTE", None)
|
|
|
|
from_rpc = getattr(libvirt, "VIR_FROM_RPC", None)
|
|
|
|
sys_error = getattr(libvirt, "VIR_ERR_SYSTEM_ERROR", None)
|
|
|
|
|
|
|
|
dom = -1
|
|
|
|
code = -1
|
|
|
|
if isinstance(e, libvirt.libvirtError):
|
|
|
|
dom = e.get_error_domain()
|
|
|
|
code = e.get_error_code()
|
|
|
|
|
|
|
|
logging.debug("Error polling connection %s",
|
|
|
|
self.get_uri(), exc_info=True)
|
|
|
|
|
|
|
|
if (dom in [from_remote, from_rpc] and
|
|
|
|
code in [sys_error]):
|
|
|
|
e = None
|
|
|
|
logging.debug("Not showing user error since libvirtd "
|
|
|
|
"appears to have stopped.")
|
|
|
|
|
|
|
|
self._schedule_close()
|
|
|
|
if e:
|
|
|
|
raise e # pylint: disable=raising-bad-type
|
|
|
|
|
|
|
|
|
2009-11-26 05:13:46 +08:00
|
|
|
########################
|
|
|
|
# Stats getter methods #
|
|
|
|
########################
|
2012-05-14 21:24:56 +08:00
|
|
|
|
2015-05-05 04:33:56 +08:00
|
|
|
def _get_record_helper(self, record_name):
|
2015-05-05 06:16:09 +08:00
|
|
|
if len(self._stats) == 0:
|
2015-05-05 04:33:56 +08:00
|
|
|
return 0
|
2015-05-05 06:16:09 +08:00
|
|
|
return self._stats[0][record_name]
|
2015-05-05 04:33:56 +08:00
|
|
|
|
|
|
|
def _vector_helper(self, record_name, limit, ceil=100.0):
|
2007-03-28 07:52:00 +08:00
|
|
|
vector = []
|
2015-05-05 04:33:56 +08:00
|
|
|
statslen = self.config.get_stats_history_length() + 1
|
|
|
|
if limit is not None:
|
|
|
|
statslen = min(statslen, limit)
|
|
|
|
|
|
|
|
for i in range(statslen):
|
2015-05-05 06:16:09 +08:00
|
|
|
if i < len(self._stats):
|
|
|
|
vector.append(self._stats[i][record_name] / ceil)
|
2007-03-28 07:52:00 +08:00
|
|
|
else:
|
|
|
|
vector.append(0)
|
2013-04-12 20:26:21 +08:00
|
|
|
|
2015-05-05 04:33:56 +08:00
|
|
|
return vector
|
2011-04-15 00:20:02 +08:00
|
|
|
|
2015-05-05 04:33:56 +08:00
|
|
|
def stats_memory_vector(self, limit=None):
|
|
|
|
return self._vector_helper("memoryPercent", limit)
|
|
|
|
def host_cpu_time_vector(self, limit=None):
|
|
|
|
return self._vector_helper("cpuHostPercent", limit)
|
2011-04-15 00:20:02 +08:00
|
|
|
|
|
|
|
def stats_memory(self):
|
|
|
|
return self._get_record_helper("memory")
|
2011-07-12 09:22:50 +08:00
|
|
|
def host_cpu_time_percentage(self):
|
|
|
|
return self._get_record_helper("cpuHostPercent")
|
2015-05-05 04:33:56 +08:00
|
|
|
def guest_cpu_time_percentage(self):
|
|
|
|
return self.host_cpu_time_percentage()
|
2008-10-19 03:21:33 +08:00
|
|
|
def network_traffic_rate(self):
|
2015-05-05 04:33:56 +08:00
|
|
|
return (self._get_record_helper("netRxRate") +
|
|
|
|
self._get_record_helper("netTxRate"))
|
|
|
|
def disk_io_rate(self):
|
|
|
|
return (self._get_record_helper("diskRdRate") +
|
|
|
|
self._get_record_helper("diskWrRate"))
|
|
|
|
|
2011-07-26 03:09:42 +08:00
|
|
|
def network_traffic_max_rate(self):
|
|
|
|
return self._get_record_helper("netMaxRate")
|
|
|
|
def disk_io_max_rate(self):
|
|
|
|
return self._get_record_helper("diskMaxRate")
|
2015-04-12 01:39:25 +08:00
|
|
|
|
|
|
|
|
|
|
|
###########################
|
|
|
|
# Per-conn config helpers #
|
|
|
|
###########################
|
|
|
|
|
|
|
|
def get_autoconnect(self):
|
|
|
|
return self.config.get_conn_autoconnect(self.get_uri())
|
|
|
|
def set_autoconnect(self, val):
|
|
|
|
self.config.set_conn_autoconnect(self.get_uri(), val)
|
|
|
|
|
|
|
|
def set_config_pretty_name(self, value):
|
2015-09-16 07:35:30 +08:00
|
|
|
cfgname = self._get_config_pretty_name()
|
|
|
|
if value == cfgname:
|
|
|
|
return
|
|
|
|
if not cfgname and value == self.get_pretty_desc():
|
|
|
|
# Don't encode the default connection value into gconf right
|
|
|
|
# away, require the user to edit it first
|
|
|
|
return
|
|
|
|
self.config.set_perconn(self.get_uri(), "/pretty-name", value)
|
2015-04-12 01:39:25 +08:00
|
|
|
def _get_config_pretty_name(self):
|
|
|
|
return self.config.get_perconn(self.get_uri(), "/pretty-name")
|
|
|
|
def _on_config_pretty_name_changed(self, *args, **kwargs):
|
|
|
|
return self.config.listen_perconn(self.get_uri(), "/pretty-name",
|
|
|
|
*args, **kwargs)
|
|
|
|
def _config_pretty_name_changed_cb(self):
|
|
|
|
self.emit("state-changed")
|