VirtualConnection: cache version lookups

This commit is contained in:
Cole Robinson 2013-07-06 15:39:00 -04:00
parent c0deb97dba
commit c406a6433c
8 changed files with 144 additions and 146 deletions

View File

@ -18,7 +18,6 @@ import unittest
import os
import logging
import libvirt
import urlgrabber.progress as progress
import virtinst
@ -454,13 +453,9 @@ class TestXMLConfig(unittest.TestCase):
g = build_guest()
self._compare(g, "install-f11-ac97", False)
oldver = libvirt.getVersion
try:
utils.set_conn(utils.open_plainkvm(libver=5000))
g = build_guest()
self._compare(g, "install-f11-noac97", False)
finally:
libvirt.getVersion = oldver
utils.set_conn(utils.open_plainkvm(libver=5000))
g = build_guest()
self._compare(g, "install-f11-noac97", False)
utils.set_conn(utils.open_plainkvm(connver=10000))
g = build_guest()

View File

@ -32,6 +32,7 @@ from gi.repository import GObject
from gi.repository import LibvirtGLib
# pylint: enable=E0611
from virtinst import util as virtinstutil
from virtinst import cli as virtinstcli
from virtcli import cliutils, cliconfig
@ -245,7 +246,8 @@ def main():
os.path.join(cliconfig.asset_dir, "ui"),
options.testfirstrun)
if not config.support_threading:
if not virtinstutil.local_libvirt_version() >= 6000:
# We need this version for threaded virConnect access
_show_startup_error(
_("virt-manager requires libvirt 0.6.0 or later."), "")
return

View File

@ -123,8 +123,7 @@ def _simple_async(callback, args, title, text, parent, errorintro,
docb = tmpcb
asyncjob = vmmAsyncJob(docb, args, title, text, parent.topwin,
show_progress=show_progress,
async=parent.config.support_threading)
show_progress=show_progress)
error, details = asyncjob.run()
if error is None:
return

View File

@ -26,8 +26,6 @@ from gi.repository import GLib
from gi.repository import Gtk
# pylint: enable=E0611
import virtinst
from virtManager.keyring import vmmKeyring, vmmSecret
@ -171,17 +169,12 @@ class vmmConfig(object):
self._objects = []
self.support_threading = virtinst.support.support_threading()
self.support_inspection = self.check_inspection(self.support_threading)
self.support_inspection = self.check_inspection()
self._spice_error = None
def check_inspection(self, support_threading):
if not support_threading:
return False
def check_inspection(self):
try:
# Check we can open the Python guestfs module.
from guestfs import GuestFS # pylint: disable=F0401

View File

@ -105,8 +105,6 @@ class vmmEngine(vmmGObject):
self._tick_thread = None
self._tick_thread_slow = False
if not self.config.support_threading:
logging.debug("Libvirt doesn't support threading, skipping.")
self.inspection = None
self._create_inspection_thread()
@ -276,9 +274,6 @@ class vmmEngine(vmmGObject):
self.timer = self.timeout_add(interval, self.tick)
def tick(self):
if not self.config.support_threading:
return self._tick()
if self._tick_thread and self._tick_thread.isAlive():
if not self._tick_thread_slow:
logging.debug("Tick is slow, not running at requested rate.")

View File

@ -23,9 +23,9 @@ import re
import libvirt
from virtinst import support
from virtinst import util
from virtinst import CapabilitiesParser
from virtinst.cli import parse_optstr
from virtinst.util import uri_split
_virtinst_uri_magic = "__virtinst_test__"
@ -53,7 +53,12 @@ class VirtualConnection(object):
self._test_opts = {}
self._libvirtconn = None
self._urisplits = uri_split(self._uri)
self._urisplits = util.uri_split(self._uri)
self._fake_libvirt_version = None
self._fake_conn_version = None
self._daemon_version = None
self._conn_version = None
self._caps = None
@ -114,6 +119,41 @@ class VirtualConnection(object):
self._libvirtconn = conn
#########################
# Public version checks #
#########################
def local_libvirt_version(self):
if self._fake_libvirt_version is not None:
return self._fake_libvirt_version
# This handles caching for us
return util.local_libvirt_version()
def daemon_version(self):
if self._fake_libvirt_version is not None:
return self._fake_libvirt_version
if not self.is_remote():
return self.local_libvirt_version()
if not self._daemon_version:
if not self.check_conn_support(support.SUPPORT_CONN_LIBVERSION):
self._daemon_version = 0
else:
self._daemon_version = self.libvirtconn.getLibVersion()
return self._daemon_version
def conn_version(self):
if self._fake_conn_version is not None:
return self._fake_conn_version
if not self._conn_version:
if not self.check_conn_support(support.SUPPORT_CONN_GETVERSION):
self._conn_version = 0
else:
self._conn_version = self.libvirtconn.getVersion()
return self._conn_version
###################
# Public URI bits #
###################
@ -259,7 +299,7 @@ class VirtualConnection(object):
opts.pop("xen", None)
opts.pop("lxc", None)
conn.getVersion = lambda: 10000000000
self._fake_conn_version = 10000000000
conn.getURI = self._virtinst_uri_make_fake
origcreate = conn.createLinux
@ -273,22 +313,12 @@ class VirtualConnection(object):
conn.createLinux = newcreate
conn.defineXML = newdefine
# These need to come after the HV setter, since that sets a default
# conn version
if "connver" in opts:
ver = int(opts.pop("connver"))
def newconnversion():
return ver
conn.getVersion = newconnversion
self._fake_conn_version = int(opts.pop("connver"))
if "libver" in opts:
ver = int(opts.pop("libver"))
def newlibversion(drv=None):
if drv:
return (ver, ver)
return ver
libvirt.getVersion = newlibversion
self._fake_libvirt_version = int(opts.pop("libver"))
if opts:
raise RuntimeError("Unhandled virtinst test uri options %s" % opts)

View File

@ -33,7 +33,9 @@ from virtinst import util
SUPPORT_CONN_NETWORK,
SUPPORT_CONN_INTERFACE,
SUPPORT_CONN_MAXVCPUS_XML,
SUPPORT_CONN_STREAM) = range(10)
SUPPORT_CONN_STREAM,
SUPPORT_CONN_GETVERSION,
SUPPORT_CONN_LIBVERSION) = range(12)
# Flags for check_domain_support
(SUPPORT_DOMAIN_GETVCPUS,
@ -79,7 +81,7 @@ from virtinst import util
# "force_version" : Demand that version check is met for the checked
# libvirt version. Normally we will make a best effort
# attempt, because determining the daemon version depends
# on a fairly new API call getLibVersion. So for things like
# on an api call from 2010. So for things like
# testing API availability (e.g. createXMLFrom) we won't
# force the check, but for things like XML options (AC97)
# we want to be ABSOLUTELY SURE it is supported so we
@ -112,158 +114,184 @@ _support_dict = {
SUPPORT_CONN_STORAGE : {
"function" : "virConnect.listStoragePools",
"args" : (),
},
},
SUPPORT_CONN_NODEDEV : {
"function" : "virConnect.listDevices",
"args" : (None, 0),
},
},
SUPPORT_CONN_FINDPOOLSOURCES : {
"function" : "virConnect.findStoragePoolSources",
},
},
SUPPORT_CONN_KEYMAP_AUTODETECT : {
"drv_version" : [("qemu", 11000)],
},
},
SUPPORT_CONN_GETHOSTNAME : {
"function" : "virConnect.getHostname()",
"args" : (),
},
},
SUPPORT_CONN_DOMAIN_VIDEO : {
"version" : 6005,
},
},
SUPPORT_CONN_NETWORK : {
"function" : "virConnect.listNetworks",
"args" : (),
},
},
SUPPORT_CONN_INTERFACE : {
"function" : "virConnect.listInterfaces",
"args" : (),
},
},
SUPPORT_CONN_MAXVCPUS_XML : {
"version" : 8005,
},
},
SUPPORT_CONN_STREAM : {
# Earliest version with working bindings
"version" : 9003,
"function" : "virConnect.newStream",
"args" : (0,),
},
},
SUPPORT_CONN_GETVERSION : {
"function": "virConnect.getVersion",
"args": (),
},
SUPPORT_CONN_LIBVERSION : {
"function": "virConnect.getLibVersion",
"args": (),
},
# Domain checks
#################
# Domain checks #
#################
SUPPORT_DOMAIN_GETVCPUS : {
"function" : "virDomain.vcpus",
"args" : (),
},
},
SUPPORT_DOMAIN_XML_INACTIVE : {
"function" : "virDomain.XMLDesc",
"args" : (),
"flag" : "VIR_DOMAIN_XML_INACTIVE",
},
},
SUPPORT_DOMAIN_XML_SECURE : {
"function" : "virDomain.XMLDesc",
"args" : (),
"flag" : "VIR_DOMAIN_XML_SECURE",
},
},
SUPPORT_DOMAIN_MANAGED_SAVE : {
"function" : "virDomain.hasManagedSaveImage",
"args" : (0,),
},
},
SUPPORT_DOMAIN_MIGRATE_DOWNTIME : {
"function" : "virDomain.migrateSetMaxDowntime",
# Use a bogus flags value, so that we don't overwrite existing
# downtime value
"args" : (30, 12345678),
},
},
SUPPORT_DOMAIN_JOB_INFO : {
"function" : "virDomain.jobInfo",
"args" : (),
},
},
SUPPORT_DOMAIN_CONSOLE_STREAM : {
"version" : 9003,
},
},
SUPPORT_DOMAIN_SET_METADATA : {
"version" : 9010,
},
},
SUPPORT_DOMAIN_CPU_HOST_MODEL : {
SUPPORT_DOMAIN_CPU_HOST_MODEL : {
"version" : 9010,
},
},
# Pool checks
###############
# Pool checks #
###############
# This can't ever require a pool object for back compat reasons
SUPPORT_STORAGE_CREATEVOLFROM : {
"function" : "virStoragePool.createXMLFrom",
"version" : 6004,
},
},
##################
# Nodedev checks #
##################
# Nodedev checks
# This can't ever require a nodedev object for back compat reasons
SUPPORT_NODEDEV_PCI_DETACH : {
"function" : "virNodeDevice.dettach",
"version" : 6001,
},
},
####################
# Interface checks #
####################
# Interface checks
SUPPORT_INTERFACE_XML_INACTIVE : {
"function" : "virInterface.XMLDesc",
"args" : (),
"flag" : "VIR_INTERFACE_XML_INACTIVE",
},
},
##################
# Conn HV checks #
##################
# Conn HV checks
SUPPORT_CONN_HV_VIRTIO : {
"drv_version": [("qemu", 0)],
"hv_version" : [("kvm", 0)],
},
},
SUPPORT_CONN_HV_SKIP_DEFAULT_ACPI : {
"drv_version" : [("xen", -3001000)],
},
},
SUPPORT_CONN_HV_SOUND_AC97 : {
"version" : 6000,
"force_version" : True,
"drv_version" : [("qemu", 11000), ],
},
},
SUPPORT_CONN_HV_SOUND_ICH6 : {
"version" : 8008,
"drv_version" : [("qemu", 14000), ],
"rhel6_drv_version" : [("qemu", 12001)],
"rhel6_version" : 8007,
},
},
SUPPORT_CONN_HV_GRAPHICS_SPICE : {
"version" : 8006,
"drv_version" : [("qemu", 14000), ],
},
},
SUPPORT_CONN_HV_CHAR_SPICEVMC : {
"version" : 8008,
"drv_version" : [("qemu", 14000), ],
},
},
SUPPORT_CONN_HV_DIRECT_INTERFACE : {
"version" : 8007,
"drv_version" : [("qemu", 0), ],
},
},
SUPPORT_CONN_HV_FILESYSTEM : {
"drv_version" : [("qemu", 13000),
("lxc", 0),
@ -273,14 +301,14 @@ _support_dict = {
("lxc", 0),
("openvz", 0),
("test", 0)],
},
},
SUPPORT_STREAM_UPLOAD : {
# Latest I tested with, and since we will use it by default
# for URL installs, want to be sure it works
"version" : 9004,
},
},
}
# RHEL6 has lots of feature backports, and since libvirt doesn't
@ -330,72 +358,19 @@ def _get_flag(flag_name):
def _try_command(func, args, check_all_error=False):
try:
func(*args)
except libvirt.libvirtError, e:
if util.is_error_nosupport(e):
return False
if check_all_error:
return False
except Exception:
# Other python exceptions likely mean the bindings are horked
return False
return True
# Version of the local libvirt library
def _local_lib_ver():
return libvirt.getVersion()
# Version of libvirt library/daemon on the connection (could be remote)
def _daemon_lib_ver(conn, is_remote, force_version, minimum_libvirt_version):
# Always force the required version if it's after the version which
# has getLibVersion
if force_version or minimum_libvirt_version >= 7004:
default_ret = 0
else:
default_ret = 100000000000
if not is_remote:
return _local_lib_ver()
if not _has_command("getLibVersion", obj=conn):
return default_ret
if not _try_command(getattr(conn, "getLibVersion"), ()):
return default_ret
return conn.getLibVersion()
# Return the hypervisor version
def _hv_ver(conn, drv_type):
args = ()
cmd = _get_command("getVersion", obj=conn)
if not cmd:
cmd = _get_command("getVersion")
args = (drv_type,)
if not cmd:
return 0
if not _try_command(cmd, args):
return 0
try:
ret = cmd(*args)
if type(ret) == tuple:
ret = ret[1]
except libvirt.libvirtError:
ret = 0
return ret
def _split_function_name(function):
if not function:
return (None, None)
@ -407,7 +382,7 @@ def _split_function_name(function):
return (output[0], output[1])
def check_support(conn, feature, data=None):
def check_support(virtconn, feature, data=None):
"""
Attempt to determine if a specific libvirt feature is support given
the passed connection.
@ -421,9 +396,6 @@ def check_support(conn, feature, data=None):
@returns: True if feature is supported, False otherwise
"""
is_remote = conn.is_remote()
drv_type = conn.get_uri_driver()
conn = conn.libvirtconn
if "VirtualConnection" in repr(data):
data = data.libvirtconn
@ -455,11 +427,6 @@ def check_support(conn, feature, data=None):
args = get_value("args")
flag = get_value("flag")
actual_lib_ver = _local_lib_ver()
actual_daemon_ver = _daemon_lib_ver(conn, is_remote, force_version,
minimum_libvirt_version)
actual_drv_ver = _hv_ver(conn, drv_type)
# Make sure there are no keys left in the key_list. This will
# ensure we didn't mistype anything above, or in the support_dict
if key_list:
@ -498,6 +465,16 @@ def check_support(conn, feature, data=None):
check_all_error=bool(flag_tuple))
return ret
# Do this after the function check, since there's an ordering issue
# with VirtualConnection
drv_type = virtconn.get_uri_driver()
actual_lib_ver = virtconn.local_libvirt_version()
actual_daemon_ver = virtconn.daemon_version()
actual_drv_ver = virtconn.conn_version()
if (actual_daemon_ver == 0 and not force_version):
# This means the API may not be supported, but we don't care
actual_daemon_ver = 1000000000
# Check that local libvirt version is sufficient
if minimum_libvirt_version > actual_lib_ver:
return False
@ -564,7 +541,3 @@ def check_support(conn, feature, data=None):
return False
return True
def support_threading():
return bool(_local_lib_ver() >= 6000)

View File

@ -578,3 +578,14 @@ def is_error_nosupport(err):
return True
return False
def local_libvirt_version():
"""
Lookup the local libvirt library version, but cache the value since
it never changes.
"""
key = "__virtinst_cached_getVersion"
if not hasattr(libvirt, key):
setattr(libvirt, key, libvirt.getVersion())
return getattr(libvirt, key)