VirtualConnection: cache capabilities XML

This means we can drop passing around the pre-parsed caps info everywhere.
This commit is contained in:
Cole Robinson 2013-07-06 14:12:13 -04:00
parent f5ef1edeea
commit 94f8d4cfa9
39 changed files with 229 additions and 362 deletions

View File

@ -45,7 +45,7 @@ class TestCapabilities(unittest.TestCase):
path = os.path.join("tests/capabilities-xml", filename)
xml = file(path).read()
return capabilities.parse(xml)
return capabilities.Capabilities(xml)
def _testCapabilities(self, path, (host_arch, host_features), guests,
secmodel=None):

View File

@ -40,7 +40,7 @@ import os
# We install several storage pools on the connection to ensure
# we aren't bumping up against errors in that department.
testconn = utils.open_testdriver()
testcaps = virtinst.CapabilitiesParser.parse(testconn.getCapabilities())
testcaps = testconn.caps
virtimage = virtinst.ImageParser.parse_file("tests/image-xml/image.xml")

View File

@ -25,7 +25,6 @@ import urlgrabber.progress as progress
import virtinst
import virtinst.ImageParser
import virtinst.CapabilitiesParser
import virtinst.cli as cli
from virtinst.cli import fail, print_stdout, print_stderr
@ -140,7 +139,7 @@ def main(conn=None):
try:
image = virtinst.ImageParser.parse_file(options.image)
except virtinst.ImageParser.ParserException, msg:
except RuntimeError, msg:
fail("%s '%s': %s" % (_("Cannot parse"), options.image, msg))
if options.boot is not None:

View File

@ -31,7 +31,6 @@ import optparse
import urlgrabber.progress as progress
import virtinst
import virtinst.CapabilitiesParser
import virtinst.cli as cli
import virtinst.util as util
from virtinst import VirtualCharDevice
@ -258,15 +257,12 @@ def prompt_virt(caps, arch, req_virt_type, req_accel):
def get_virt_type(conn, options):
# Set up all virt/hypervisor parameters
if sum([bool(f) for f in [options.fullvirt,
options.paravirt,
options.container]]) > 1:
fail(_("Can't do more than one of --hvm, --paravirt, or --container"))
capabilities = virtinst.CapabilitiesParser.parse(conn.getCapabilities())
req_accel = True
req_hv_type = options.hv_type and options.hv_type.lower() or None
if options.fullvirt:
@ -282,7 +278,7 @@ def get_virt_type(conn, options):
if cli.is_prompt():
# User requested prompting but passed no virt type flag, ask for
# needed info
req_virt_type, req_accel = prompt_virt(capabilities, options.arch,
req_virt_type, req_accel = prompt_virt(conn.caps, options.arch,
req_virt_type, req_accel)
logging.debug("Requesting virt method '%s', hv type '%s'.",
@ -295,9 +291,7 @@ def get_virt_type(conn, options):
try:
(capsguest,
capsdomain) = virtinst.CapabilitiesParser.guest_lookup(
conn=conn,
caps=capabilities,
capsdomain) = conn.caps.guest_lookup(
os_type=req_virt_type,
arch=arch,
typ=req_hv_type,

View File

@ -187,23 +187,8 @@ class vmmConnection(vmmGObject):
return self._backend
def invalidate_caps(self):
self._caps_xml = None
self._caps = None
def _check_caps(self):
if not (self._caps_xml or self._caps):
self._caps_xml = self._backend.getCapabilities()
self._caps = virtinst.CapabilitiesParser.parse(self._caps_xml)
def get_capabilities_xml(self):
if not self._caps_xml:
self._check_caps()
return self._caps_xml
def get_capabilities(self):
if not self._caps:
self._check_caps()
return self._caps
return self._backend.invalidate_caps()
caps = property(lambda self: getattr(self, "_backend").caps)
def get_host_info(self):
return self.hostinfo
@ -305,26 +290,16 @@ class vmmConnection(vmmGObject):
return self.config.rhel6_defaults
def rhel6_defaults_caps(self):
caps = self.get_capabilities()
for guest in caps.guests:
for guest in self.caps.guests:
for dom in guest.domains:
if dom.emulator.startswith("/usr/libexec"):
return self.config.rhel6_defaults
return True
def is_kvm_supported(self):
return self.get_capabilities().is_kvm_available()
def no_install_options(self):
return self.get_capabilities().no_install_options()
def hw_virt_supported(self):
return self.get_capabilities().hw_virt_supported()
def is_bios_virt_disabled(self):
return self.get_capabilities().is_bios_virt_disabled()
# Connection pretty print routines
####################################
# Connection pretty print routines #
####################################
def _get_pretty_desc(self, active, shorthost, show_trans):
def match_whole_string(orig, reg):
@ -380,7 +355,7 @@ class vmmConnection(vmmGObject):
if scheme in pretty_map:
hv = pretty_map[scheme]
if hv == "QEMU" and active and self.is_kvm_supported():
if hv == "QEMU" and active and self.caps.is_kvm_available():
hv += "/KVM"
if show_trans:
@ -968,10 +943,8 @@ class vmmConnection(vmmGObject):
self.idle_emit("state-changed")
if self.state == self.STATE_ACTIVE:
caps = self.get_capabilities_xml()
logging.debug("%s capabilities:\n%s",
self.get_uri(), caps)
self.get_uri(), self.caps.xml)
self.tick()
if self.state == self.STATE_DISCONNECTED:

View File

@ -417,7 +417,7 @@ class vmmCreate(vmmGObjectUI):
# Update all state that has some dependency on the current connection
self.widget("create-forward").set_sensitive(True)
if self.conn.no_install_options():
if self.conn.caps.no_install_options():
error = _("No hypervisor options were found for this "
"connection.")
@ -431,13 +431,13 @@ class vmmCreate(vmmGObjectUI):
# A bit out of order, but populate arch + hv lists so we can
# determine a default
self.conn.invalidate_caps()
self.caps = self.conn.get_capabilities()
self.caps = self.conn.caps
self.change_caps()
self.populate_hv()
if self.conn.is_xen():
if self.conn.hw_virt_supported():
if self.conn.is_bios_virt_disabled():
if self.conn.caps.hw_virt_supported():
if self.conn.caps.is_bios_virt_disabled():
error = _("Host supports full virtualization, but "
"no related install options are available. "
"This may mean support is disabled in your "
@ -450,7 +450,7 @@ class vmmCreate(vmmGObjectUI):
self.startup_warning(error)
elif self.conn.is_qemu():
if not self.conn.is_kvm_supported():
if not self.conn.caps.is_kvm_available():
error = _("KVM is not available. This may mean the KVM "
"package is not installed, or the KVM kernel modules "
"are not loaded. Your virtual machines may perform poorly.")
@ -811,10 +811,7 @@ class vmmCreate(vmmGObjectUI):
gtype = "hvm"
break
(newg, newdom) = virtinst.CapabilitiesParser.guest_lookup(
conn=self.conn.get_backend(),
caps=self.caps,
os_type=gtype,
(newg, newdom) = self.caps.guest_lookup(os_type=gtype,
typ=dtype,
accelerated=True,
arch=arch)

View File

@ -785,7 +785,7 @@ class vmmDetails(vmmGObjectUI):
clock_model.append([offset])
arch = self.vm.get_arch()
caps = self.vm.conn.get_capabilities()
caps = self.vm.conn.caps
machines = []
if len(caps.guests) > 0:
@ -819,7 +819,7 @@ class vmmDetails(vmmGObjectUI):
# VCPU Pinning list
generate_cpuset = self.widget("config-vcpupin-generate")
generate_warn = self.widget("config-vcpupin-generate-err")
if not self.conn.get_capabilities().host.topology:
if not self.conn.caps.host.topology:
generate_cpuset.set_sensitive(False)
generate_warn.show()
generate_warn.set_tooltip_text(_("Libvirt did not detect NUMA capabilities."))
@ -883,7 +883,7 @@ class vmmDetails(vmmGObjectUI):
no_default = not self.is_customize_dialog
# CPU features
caps = self.vm.conn.get_capabilities()
caps = self.vm.conn.caps
cpu_values = None
cpu_names = []
all_features = []
@ -2740,7 +2740,7 @@ class vmmDetails(vmmGObjectUI):
# Security details
semodel, sectype, vmlabel, relabel = self.vm.get_seclabel()
caps = self.vm.conn.get_capabilities()
caps = self.vm.conn.caps
if caps.host.secmodel and caps.host.secmodel.model:
semodel = caps.host.secmodel.model
@ -2877,7 +2877,7 @@ class vmmDetails(vmmGObjectUI):
def _refresh_cpu_config(self, cpu):
feature_ui = self.widget("cpu-features")
model = cpu.model or ""
caps = self.vm.conn.get_capabilities()
caps = self.vm.conn.caps
capscpu = None
try:

View File

@ -408,8 +408,7 @@ class vmmDomain(vmmLibvirtObject):
def _build_guest(self, xml):
return virtinst.Guest(self.conn.get_backend(),
parsexml=xml,
caps=self.conn.get_capabilities())
parsexml=xml)
def _reparse_xml(self, ignore=None):
self._guest = self._build_guest(self._get_domain_xml())

View File

@ -205,7 +205,7 @@ class virtimage_parser(formats.parser):
f.close()
ImageParser.parse(output, input_file)
except ImageParser.ParserException:
except RuntimeError:
return False
return True

View File

@ -35,9 +35,9 @@ class Boot(XMLBuilderDomain.XMLBuilderDomain):
BOOT_DEVICE_FLOPPY, BOOT_DEVICE_NETWORK]
_dumpxml_xpath = "/domain/os"
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
def __init__(self, conn, parsexml=None, parsexmlnode=None):
XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml,
parsexmlnode, caps)
parsexmlnode)
self._bootorder = []
self._enable_bootmenu = None

View File

@ -34,9 +34,9 @@ class CPUFeature(XMLBuilderDomain.XMLBuilderDomain):
POLICIES = ["force", "require", "optional", "disable", "forbid"]
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
def __init__(self, conn, parsexml=None, parsexmlnode=None):
XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml,
parsexmlnode, caps)
parsexmlnode)
self._name = None
self._policy = None
@ -79,7 +79,7 @@ class CPU(XMLBuilderDomain.XMLBuilderDomain):
MATCHS = ["minimum", "exact", "strict"]
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
def __init__(self, conn, parsexml=None, parsexmlnode=None):
self._model = None
self._match = None
self._vendor = None
@ -91,7 +91,7 @@ class CPU(XMLBuilderDomain.XMLBuilderDomain):
self._threads = None
XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml,
parsexmlnode, caps)
parsexmlnode)
if self._is_parse():
return
@ -201,7 +201,7 @@ class CPU(XMLBuilderDomain.XMLBuilderDomain):
Enact the equivalent of qemu -cpu host, pulling all info
from capabilities about the host CPU
"""
cpu = self._get_caps().host.cpu
cpu = self.conn.caps.host.cpu
if not cpu.model:
raise ValueError(_("No host CPU reported in capabilities"))

View File

@ -23,16 +23,18 @@ import re
from virtinst import util
class CapabilitiesParserException(Exception):
def __init__(self, msg):
Exception.__init__(self, msg)
# Whether a guest can be created with a certain feature on resp. off
FEATURE_ON = 0x01
FEATURE_OFF = 0x02
def xpathString(node, path, default=None):
result = node.xpathEval("string(%s)" % path)
if len(result) == 0:
result = default
return result
class CPUValuesModel(object):
"""
Single <model> definition from cpu_map
@ -118,7 +120,7 @@ class CPUValues(object):
util.parse_node_helper(xml, "cpus",
self._parseXML,
CapabilitiesParserException)
RuntimeError)
def _parseXML(self, node):
child = node.children
@ -203,7 +205,9 @@ class CapabilityFeatures(Features):
elif default == "off":
d[feature] = FEATURE_OFF
else:
raise CapabilitiesParserException("Feature %s: value of default must be 'on' or 'off', but is '%s'" % (feature, default))
raise RuntimeError("Feature %s: value of default must "
"be 'on' or 'off', but is '%s'" %
(feature, default))
if toggle == "yes":
d[feature] |= d[feature] ^ (FEATURE_ON | FEATURE_OFF)
else:
@ -395,7 +399,7 @@ class Guest(object):
{'type': self.os_type, 'arch': self.arch})
error += domainerr
error += machineerr
raise CapabilitiesParserException(error)
raise RuntimeError(error)
return self._favoredDomain(accelerated, domains)
@ -495,16 +499,16 @@ class SecurityModel(object):
class Capabilities(object):
def __init__(self, node=None):
def __init__(self, xml):
self.host = None
self.guests = []
self.xml = xml
self._topology = None
self._cpu_values = None
if not node is None:
self.parseXML(node)
util.parse_node_helper(self.xml, "capabilities",
self.parseXML,
RuntimeError)
self._fixBrokenEmulator()
def _is_xen(self):
@ -688,83 +692,63 @@ class Capabilities(object):
return self._cpu_values.get_arch(arch)
def parse(xml):
return util.parse_node_helper(xml, "capabilities",
Capabilities,
CapabilitiesParserException)
def guest_lookup(self, os_type=None, arch=None, typ=None,
accelerated=False, machine=None):
"""
Simple virtualization availability lookup
Convenience function for looking up 'Guest' and 'Domain' capabilities
objects for the desired virt type. If type, arch, or os_type are none,
we return the default virt type associated with those values. These are
typically:
def guest_lookup(conn, caps=None, os_type=None, arch=None, typ=None,
accelerated=False, machine=None):
"""
Simple virtualization availability lookup
- os_type : hvm, then xen
- typ : kvm over plain qemu
- arch : host arch over all others
Convenience function for looking up 'Guest' and 'Domain' capabilities
objects for the desired virt type. If type, arch, or os_type are none,
we return the default virt type associated with those values. These are
typically:
Otherwise the default will be the first listed in the capabilities xml.
This function throws C{ValueError}s if any of the requested values are
not found.
- os_type : hvm, then xen
- typ : kvm over plain qemu
- arch : host arch over all others
@param typ: Virtualization type ('hvm', 'xen', ...)
@type typ: C{str}
@param arch: Guest architecture ('x86_64', 'i686' ...)
@type arch: C{str}
@param os_type: Hypervisor name ('qemu', 'kvm', 'xen', ...)
@type os_type: C{str}
@param accelerated: Whether to look for accelerated domain if none is
specifically requested
@type accelerated: C{bool}
@param machine: Optional machine type to emulate
@type machine: C{str}
Otherwise the default will be the first listed in the capabilities xml.
This function throws C{ValueError}s if any of the requested values are
not found.
@returns: A (Capabilities Guest, Capabilities Domain) tuple
"""
guest = self.guestForOSType(os_type, arch)
if not guest:
archstr = _("for arch '%s'") % arch
if not arch:
archstr = ""
@param conn: libvirt connection
@param caps: Optional L{Capabilities} instance (saves a lookup)
@type caps: L{Capabilities}
@param typ: Virtualization type ('hvm', 'xen', ...)
@type typ: C{str}
@param arch: Guest architecture ('x86_64', 'i686' ...)
@type arch: C{str}
@param os_type: Hypervisor name ('qemu', 'kvm', 'xen', ...)
@type os_type: C{str}
@param accelerated: Whether to look for accelerated domain if none is
specifically requested
@type accelerated: C{bool}
@param machine: Optional machine type to emulate
@type machine: C{str}
osstr = _("virtualization type '%s'") % os_type
if not os_type:
osstr = _("any virtualization options")
@returns: A (Capabilities Guest, Capabilities Domain) tuple
"""
raise ValueError(_("Host does not support %(virttype)s %(arch)s") %
{'virttype' : osstr, 'arch' : archstr})
if not caps:
caps = parse(conn.getCapabilities())
domain = guest.bestDomainType(accelerated=accelerated,
dtype=typ,
machine=machine)
guest = caps.guestForOSType(os_type, arch)
if not guest:
archstr = _("for arch '%s'") % arch
if not arch:
archstr = ""
if domain is None:
machinestr = "with machine '%s'" % machine
if not machine:
machinestr = ""
raise ValueError(_("Host does not support domain type %(domain)s"
"%(machine)s for virtualization type "
"'%(virttype)s' arch '%(arch)s'") %
{'domain': typ, 'virttype': guest.os_type,
'arch': guest.arch, 'machine': machinestr})
osstr = _("virtualization type '%s'") % os_type
if not os_type:
osstr = _("any virtualization options")
raise ValueError(_("Host does not support %(virttype)s %(arch)s") %
{'virttype' : osstr, 'arch' : archstr})
domain = guest.bestDomainType(accelerated=accelerated,
dtype=typ,
machine=machine)
if domain is None:
machinestr = "with machine '%s'" % machine
if not machine:
machinestr = ""
raise ValueError(_("Host does not support domain type %(domain)s"
"%(machine)s for virtualization type "
"'%(virttype)s' arch '%(arch)s'") %
{'domain': typ, 'virttype': guest.os_type,
'arch': guest.arch, 'machine': machinestr})
return (guest, domain)
def xpathString(node, path, default=None):
result = node.xpathEval("string(%s)" % path)
if len(result) == 0:
result = default
return result
return (guest, domain)

View File

@ -27,9 +27,9 @@ class Clock(XMLBuilderDomain.XMLBuilderDomain):
"""
_dumpxml_xpath = "/domain/clock"
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
def __init__(self, conn, parsexml=None, parsexmlnode=None):
XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml,
parsexmlnode, caps)
parsexmlnode)
self._offset = None

View File

@ -155,12 +155,12 @@ def _upload_file(conn, meter, destpool, src):
class DistroInstaller(Installer.Installer):
def __init__(self, conn, type="xen", location=None,
extraargs=None, os_type=None, caps=None):
extraargs=None, os_type=None):
# pylint: disable=W0622
# Redefining built-in 'type', but it matches the XML so keep it
Installer.Installer.__init__(self, conn, type, location, extraargs,
os_type, caps=caps)
os_type)
self._livecd = False

View File

@ -33,9 +33,9 @@ class DomainFeatures(XMLBuilderDomain.XMLBuilderDomain):
"""
_dumpxml_xpath = "/domain/features"
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
def __init__(self, conn, parsexml=None, parsexmlnode=None):
XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml,
parsexmlnode, caps)
parsexmlnode)
self._acpi = None
self._apic = None

View File

@ -97,12 +97,12 @@ class DomainNumatune(XMLBuilderDomain.XMLBuilderDomain):
MEMORY_MODES = ["interleave", "strict", "preferred"]
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
def __init__(self, conn, parsexml=None, parsexmlnode=None):
self._memory_nodeset = None
self._memory_mode = None
XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml,
parsexmlnode, caps)
parsexmlnode)
if self._is_parse():
return

View File

@ -29,7 +29,6 @@ import libvirt
import libxml2
from virtinst import util
from virtinst import CapabilitiesParser
from virtinst import support
from virtinst import XMLBuilderDomain
import virtinst
@ -124,12 +123,10 @@ class Guest(XMLBuilderDomain.XMLBuilderDomain):
If host doesn't have a suitable NUMA configuration, a RuntimeError
is thrown.
"""
caps = CapabilitiesParser.parse(conn.getCapabilities())
if caps.host.topology is None:
if conn.caps.host.topology is None:
raise RuntimeError(_("No topology section in capabilities xml."))
cells = caps.host.topology.cells
cells = conn.caps.host.topology.cells
if len(cells) <= 1:
raise RuntimeError(_("Capabilities only show <= 1 cell. "
"Not NUMA capable"))
@ -171,7 +168,7 @@ class Guest(XMLBuilderDomain.XMLBuilderDomain):
return cpustr
def __init__(self, conn, type=None,
installer=None, parsexml=None, caps=None):
installer=None, parsexml=None):
# pylint: disable=W0622
# Redefining built-in 'type', but it matches the XML so keep it
@ -210,21 +207,14 @@ class Guest(XMLBuilderDomain.XMLBuilderDomain):
self._default_input_device = None
self._default_console_device = None
# pylint: disable=W0212
# Access to protected member _get_caps
caps = caps or (self._installer and self._installer._get_caps())
# pylint: enable=W0212
XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml,
caps=caps)
XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml)
if self._is_parse():
return
if not self.installer:
i = virtinst.DistroInstaller(conn,
type=type,
os_type=self._default_os_type,
caps=self._get_caps())
os_type=self._default_os_type)
self.installer = i
# Add default devices (if applicable)
@ -660,7 +650,6 @@ class Guest(XMLBuilderDomain.XMLBuilderDomain):
}
# Hand off all child element parsing to relevant classes
caps = self._get_caps()
for node in self._xml_node.children:
if node.name != "devices":
continue
@ -673,24 +662,20 @@ class Guest(XMLBuilderDomain.XMLBuilderDomain):
if objclass == virtinst.VirtualCharDevice:
dev = objclass(self.conn, devnode.name,
parsexmlnode=devnode, caps=caps)
parsexmlnode=devnode)
else:
dev = objclass(self.conn,
parsexmlnode=devnode, caps=caps)
dev = objclass(self.conn, parsexmlnode=devnode)
self._add_device(dev)
self._installer = virtinst.Installer.Installer(self.conn,
parsexmlnode=self._xml_node,
caps=caps)
parsexmlnode=self._xml_node)
self._features = DomainFeatures(self.conn,
parsexmlnode=self._xml_node,
caps=caps)
self._clock = Clock(self.conn, parsexmlnode=self._xml_node, caps=caps)
self._seclabel = Seclabel(self.conn, parsexmlnode=self._xml_node,
caps=caps)
self._cpu = CPU(self.conn, parsexmlnode=self._xml_node, caps=caps)
parsexmlnode=self._xml_node)
self._clock = Clock(self.conn, parsexmlnode=self._xml_node)
self._seclabel = Seclabel(self.conn, parsexmlnode=self._xml_node)
self._cpu = CPU(self.conn, parsexmlnode=self._xml_node)
self._numatune = DomainNumatune(self.conn,
parsexmlnode=self._xml_node, caps=caps)
parsexmlnode=self._xml_node)
def _get_default_input_device(self):
"""
@ -766,7 +751,7 @@ class Guest(XMLBuilderDomain.XMLBuilderDomain):
if (not self.emulator and
self.installer.is_hvm() and
self.type == "xen"):
if self._get_caps().host.arch in ("x86_64"):
if self.conn.caps.host.arch in ("x86_64"):
emulator = "/usr/lib64/xen/bin/qemu-dm"
else:
emulator = "/usr/lib/xen/bin/qemu-dm"
@ -1298,11 +1283,11 @@ class Guest(XMLBuilderDomain.XMLBuilderDomain):
features["acpi"] = self._lookup_osdict_key("acpi")
if features["apic"] is None:
features["apic"] = self._lookup_osdict_key("apic")
if features["pae"] is None and self._get_caps():
features["pae"] = self._get_caps().support_pae()
if features["pae"] is None:
features["pae"] = self.conn.caps.support_pae()
if (self.installer.machine is None and
self._get_caps().host.arch == "ppc64"):
self.conn.caps.host.arch == "ppc64"):
self.installer.machine = "pseries"
def _set_pv_defaults(self, devlist_func, remove_func):

View File

@ -22,7 +22,7 @@ import os
from virtinst import Installer
from virtinst import ImageParser
from virtinst import CapabilitiesParser as Cap
from virtinst import CapabilitiesParser
from virtinst.VirtualDisk import VirtualDisk
@ -40,7 +40,7 @@ class ImageInstaller(Installer.Installer):
# Set boot _boot_caps/_boot_parameters
if boot_index is None:
self._boot_caps = match_boots(self._get_caps(),
self._boot_caps = match_boots(self.conn.caps,
self.image.domain.boots)
if self._boot_caps is None:
raise RuntimeError(_("Could not find suitable boot "
@ -52,7 +52,7 @@ class ImageInstaller(Installer.Installer):
self._boot_caps = image.domain.boots[boot_index]
# Set up internal caps.guest object
self._guest = self._get_caps().guestForOSType(self.boot_caps.type,
self._guest = self.conn.caps.guestForOSType(self.boot_caps.type,
self.boot_caps.arch)
if self._guest is None:
raise RuntimeError(_("Unsupported virtualization type: %s %s" %
@ -88,9 +88,9 @@ class ImageInstaller(Installer.Installer):
self._make_disks()
for f in ['pae', 'acpi', 'apic']:
if self.boot_caps.features[f] & Cap.FEATURE_ON:
if self.boot_caps.features[f] & CapabilitiesParser.FEATURE_ON:
guest.features[f] = True
elif self.boot_caps.features[f] & Cap.FEATURE_OFF:
elif self.boot_caps.features[f] & CapabilitiesParser.FEATURE_OFF:
guest.features[f] = False
self.bootconfig.kernel = self.boot_caps.kernel

View File

@ -1,5 +1,5 @@
# Sample code to parse an image XML description and
# spit out libvirt XML; will be hooked into virt-install
# spit out libvirt XML
#
# Copyright 2007, 2013 Red Hat, Inc.
# David Lutterkort <dlutter@redhat.com>
@ -22,19 +22,13 @@
import logging
import os
import libxml2
import urlgrabber
from virtinst import CapabilitiesParser
from virtinst import util
class ParserException(Exception):
def __init__(self, msg):
Exception.__init__(self, msg)
class Image:
class Image(object):
"""The toplevel object representing a VM image"""
def __init__(self, node=None, base=None, filename=None):
self.storage = {}
@ -77,24 +71,23 @@ class Image:
disk.id = "disk%d.img" % len(self.storage)
disk.file = "disk%d.img" % (len(self.storage) + 1)
if disk.id in self.storage:
raise ParserException("Disk file '%s' defined twice"
% disk.file)
raise RuntimeError("Disk file '%s' defined twice" % disk.file)
self.storage[disk.id] = disk
lm = node.xpathEval("domain")
if len(lm) == 1:
self.domain = Domain(lm[0])
else:
raise ParserException(_("Expected exactly one 'domain' element"))
raise RuntimeError(_("Expected exactly one 'domain' element"))
# Connect the disk maps to the disk definitions
for boot in self.domain.boots:
for d in boot.drives:
if d.disk_id not in self.storage:
raise ParserException(_("Disk entry for '%s' not found")
% d.disk_id)
raise RuntimeError(_("Disk entry for '%s' not found")
% d.disk_id)
d.disk = self.storage[d.disk_id]
class Domain:
class Domain(object):
"""The description of a virtual domain as part of an image"""
def __init__(self, node=None):
self.boots = []
@ -116,8 +109,8 @@ class Domain:
try:
self.memory = int(tmpmem)
except ValueError:
raise ParserException(_("Memory must be an integer, "
"but is '%s'") % self.memory)
raise RuntimeError(_("Memory must be an integer, "
"but is '%s'") % self.memory)
else:
tmpmem = 0
@ -134,10 +127,12 @@ class ImageFeatures(CapabilitiesParser.Features):
elif state == "off":
d[feature] = CapabilitiesParser.FEATURE_OFF
else:
raise ParserException("The state for feature %s must be either 'on' or 'off', but is '%s'" % (feature, state))
raise RuntimeError("The state for feature %s must be "
"either 'on' or 'off', but is '%s'" %
(feature, state))
class Boot:
class Boot(object):
"""The overall description of how the image can be booted, including
required capabilities of the host and mapping of disks into the VM"""
def __init__(self, node=None):
@ -168,7 +163,9 @@ class Boot:
fl = node.xpathEval("guest/features")
if len(fl) > 1:
raise ParserException("Expected at most one <features> element in %s boot descriptor for %s" % (self.type, self.arch))
raise RuntimeError("Expected at most one <features> element "
"in %s boot descriptor for %s" %
(self.type, self.arch))
elif len(fl) == 1:
self.features = ImageFeatures(fl[0])
@ -188,7 +185,7 @@ class Boot:
# and without a loader
class Drive:
class Drive(object):
"""The mapping of a disk from the storage section to a virtual drive
in a guest"""
def __init__(self, node=None):
@ -203,7 +200,7 @@ class Drive:
self.target = xpathString(node, "@target")
class Disk:
class Disk(object):
FORMAT_RAW = "raw"
FORMAT_ISO = "iso"
FORMAT_QCOW = "qcow"
@ -298,7 +295,7 @@ class Disk:
def validate(cond, msg):
if not cond:
raise ParserException(msg)
raise RuntimeError(msg)
def xpathString(node, path, default=None):
@ -312,35 +309,9 @@ def parse(xml, filename):
"""Parse the XML description of a VM image into a data structure. Returns
an object of class Image. BASE should be the directory where the disk
image files for this image can be found"""
class ErrorHandler:
def __init__(self):
self.msg = ""
def handler(self, ignore, s):
self.msg += s
error = ErrorHandler()
libxml2.registerErrorHandler(error.handler, None)
try:
try:
doc = libxml2.readMemory(xml, len(xml),
None, None,
libxml2.XML_PARSE_NOBLANKS)
except (libxml2.parserError, libxml2.treeError), e:
raise ParserException("%s\n%s" % (e, error.msg))
finally:
libxml2.registerErrorHandler(None, None)
try:
root = doc.getRootElement()
if root.name != "image":
raise ParserException(_("Root element is not 'image'"))
image = Image(root, filename=filename)
finally:
doc.freeDoc()
return image
def cb(x):
return Image(x, filename=filename)
return util.parse_node_helper(xml, "image", cb, RuntimeError)
def parse_file(filename):

View File

@ -28,7 +28,6 @@ from virtinst import util
import virtinst
from virtinst import XMLBuilderDomain
from virtinst.XMLBuilderDomain import _xml_property
from virtinst import CapabilitiesParser
from virtinst.Boot import Boot
XEN_SCRATCH = "/var/lib/xen"
@ -65,12 +64,12 @@ class Installer(XMLBuilderDomain.XMLBuilderDomain):
def __init__(self, conn, type="xen", location=None,
extraargs=None, os_type=None,
parsexml=None, parsexmlnode=None, caps=None):
parsexml=None, parsexmlnode=None):
# pylint: disable=W0622
# Redefining built-in 'type', but it matches the XML so keep it
XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml,
parsexmlnode, caps=caps)
parsexmlnode)
self._type = None
self._location = None
@ -91,10 +90,7 @@ class Installer(XMLBuilderDomain.XMLBuilderDomain):
if self._is_parse():
return
# FIXME: Better solution? Skip validating this since we may not be
# able to install a VM of the host arch
if self._get_caps():
self._arch = self._get_caps().host.arch
self._arch = self.conn.caps.host.arch
if type is None:
type = "xen"
@ -414,12 +410,10 @@ class Installer(XMLBuilderDomain.XMLBuilderDomain):
if not self.conn:
raise ValueError(_("A connection must be specified."))
guest, domain = CapabilitiesParser.guest_lookup(conn=self.conn,
caps=self._get_caps(),
os_type=self.os_type,
typ=self.type,
arch=self.arch,
machine=self.machine)
guest, domain = self.conn.caps.guest_lookup(os_type=self.os_type,
typ=self.type,
arch=self.arch,
machine=self.machine)
gobj = virtinst.Guest(self.conn, installer=self)
gobj.arch = guest.arch

View File

@ -42,9 +42,9 @@ class Seclabel(XMLBuilderDomain.XMLBuilderDomain):
SECLABEL_MODEL_NONE]
_dumpxml_xpath = "/domain/seclabel"
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
def __init__(self, conn, parsexml=None, parsexmlnode=None):
XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml,
parsexmlnode, caps)
parsexmlnode)
self._type = None
self._model = None
@ -59,23 +59,19 @@ class Seclabel(XMLBuilderDomain.XMLBuilderDomain):
self.type = self.SECLABEL_TYPE_DEFAULT
def _get_default_model(self):
caps = self._get_caps()
if caps:
if (self.SECLABEL_MODEL_TEST in
[x.model for x in caps.host.secmodels]):
return self.SECLABEL_MODEL_TEST
if (self.SECLABEL_MODEL_TEST in
[x.model for x in self.conn.caps.host.secmodels]):
return self.SECLABEL_MODEL_TEST
for model in self.SECLABEL_MODELS:
if model in [x.model for x in caps.host.secmodels]:
return model
for model in self.SECLABEL_MODELS:
if model in [x.model for x in self.conn.caps.host.secmodels]:
return model
raise RuntimeError("No supported model found in capabilities")
def _guess_secmodel(self, label, imagelabel):
# We always want the testSecurity model when running tests
caps = self._get_caps()
if (caps and
self.SECLABEL_MODEL_TEST in
[x.model for x in caps.host.secmodels]):
if (self.SECLABEL_MODEL_TEST in
[x.model for x in self.conn.caps.host.secmodels]):
return self.SECLABEL_MODEL_TEST
if not label and not imagelabel:

View File

@ -28,9 +28,8 @@ class VirtualAudio(VirtualDevice):
MODEL_DEFAULT = "default"
MODELS = ["es1370", "sb16", "pcspk", "ac97", "ich6", MODEL_DEFAULT]
def __init__(self, conn, model=None,
parsexml=None, parsexmlnode=None, caps=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps)
def __init__(self, conn, model=None, parsexml=None, parsexmlnode=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._model = None
if self._is_parse():

View File

@ -175,15 +175,13 @@ class VirtualCharDevice(VirtualDevice):
return c(conn, dev_type)
get_dev_instance = staticmethod(get_dev_instance)
def __init__(self, conn, dev_type,
parsexml=None, parsexmlnode=None, caps=None):
def __init__(self, conn, dev_type, parsexml=None, parsexmlnode=None):
if dev_type not in self.dev_types:
raise ValueError(_("Unknown character device type '%s'") % dev_type)
self._dev_type = dev_type
self._virtual_device_type = self._dev_type
VirtualDevice.__init__(self, conn,
parsexml, parsexmlnode, caps)
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
# Init
self._source_path = None
@ -626,8 +624,8 @@ class VirtualCharSpicevmcDevice(VirtualCharDevice):
has_target = True
def __init__(self, conn, dev_type=VirtualCharDevice.DEV_CHANNEL,
parsexml=None, parsexmlnode=None, caps=None):
parsexml=None, parsexmlnode=None):
VirtualCharDevice.__init__(self, conn, dev_type,
parsexml, parsexmlnode, caps)
parsexml, parsexmlnode)
self._target_type = VirtualCharDevice.CHAR_CHANNEL_TARGET_VIRTIO
self._target_name = "com.redhat.spice.0"

View File

@ -76,10 +76,8 @@ class VirtualController(VirtualDevice):
_controller_type = None
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None,
model=None):
VirtualDevice.__init__(self, conn,
parsexml, parsexmlnode, caps)
def __init__(self, conn, parsexml=None, parsexmlnode=None, model=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._index = 0
self._ports = None
@ -87,8 +85,7 @@ class VirtualController(VirtualDevice):
self._model = None
self._master = VirtualDeviceMaster(conn,
parsexml=parsexml,
parsexmlnode=parsexmlnode,
caps=caps)
parsexmlnode=parsexmlnode)
if self._is_parse():
return
@ -187,9 +184,8 @@ class VirtualControllerUSB(VirtualController):
class VirtualDeviceMaster(XMLBuilderDomain):
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode,
caps=caps)
def __init__(self, conn, parsexml=None, parsexmlnode=None):
XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode)
self._startport = None

View File

@ -71,23 +71,20 @@ class VirtualDevice(XMLBuilderDomain):
# General device type (disk, interface, etc.)
_virtual_device_type = None
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
def __init__(self, conn, parsexml=None, parsexmlnode=None):
"""
Initialize device state
@param conn: libvirt connection to validate device against
"""
XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode,
caps=caps)
XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode)
self.alias = VirtualDeviceAlias(conn,
parsexml=parsexml,
parsexmlnode=parsexmlnode,
caps=caps)
parsexmlnode=parsexmlnode)
self.address = VirtualDeviceAddress(conn,
parsexml=parsexml,
parsexmlnode=parsexmlnode,
caps=caps)
parsexmlnode=parsexmlnode)
if not self._virtual_device_type:
raise ValueError(_("Virtual device type must be set in subclass."))
@ -121,9 +118,8 @@ class VirtualDevice(XMLBuilderDomain):
class VirtualDeviceAlias(XMLBuilderDomain):
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode,
caps=caps)
def __init__(self, conn, parsexml=None, parsexmlnode=None):
XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode)
self._name = None
@ -150,10 +146,9 @@ class VirtualDeviceAddress(XMLBuilderDomain):
ADDRESS_TYPE_VIRTIO_SERIAL, ADDRESS_TYPE_CCID,
ADDRESS_TYPE_SPAPR_VIO]
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None,
def __init__(self, conn, parsexml=None, parsexmlnode=None,
addrstr=None):
XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode,
caps=caps)
XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode)
self._type = None

View File

@ -535,7 +535,7 @@ class VirtualDisk(VirtualDevice):
readOnly=False, sparse=True, volObject=None,
volInstall=None, volName=None, bus=None, shareable=False,
driverCache=None, format=None,
validate=True, parsexml=None, parsexmlnode=None, caps=None,
validate=True, parsexml=None, parsexmlnode=None,
driverIO=None, sizebytes=None, nomanaged=False):
"""
@param path: filesystem path to the disk image.
@ -583,8 +583,7 @@ class VirtualDisk(VirtualDevice):
"""
VirtualDevice.__init__(self, conn=conn,
parsexml=parsexml, parsexmlnode=parsexmlnode,
caps=caps)
parsexml=parsexml, parsexmlnode=parsexmlnode)
self._path = None
self._size = None

View File

@ -70,9 +70,8 @@ class VirtualFilesystem(VirtualDevice):
return "dev"
return "dir"
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
VirtualDevice.__init__(self, conn, parsexml,
parsexmlnode, caps)
def __init__(self, conn, parsexml=None, parsexmlnode=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._type = None
self._mode = None

View File

@ -93,11 +93,11 @@ class VirtualGraphics(VirtualDevice):
def __init__(self, conn, type=TYPE_VNC, port=-1, listen=None, passwd=None,
keymap=KEYMAP_DEFAULT, parsexml=None,
parsexmlnode=None, tlsPort=-1, channels=None,
caps=None, passwdValidTo=None):
passwdValidTo=None):
# pylint: disable=W0622
# Redefining built-in 'type', but it matches the XML so keep it
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps)
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._type = None
self._port = None

View File

@ -71,7 +71,7 @@ class VirtualHostDevice(VirtualDevice):
device_from_node = staticmethod(device_from_node)
def __init__(self, conn, nodedev=None,
parsexml=None, parsexmlnode=None, caps=None):
parsexml=None, parsexmlnode=None):
"""
@param conn: Connection the device/guest will be installed on
@type conn: libvirt.virConnect
@ -79,8 +79,7 @@ class VirtualHostDevice(VirtualDevice):
attached to the guest
@type nodedev: L{virtinst.NodeDeviceParser.NodeDevice}
"""
VirtualDevice.__init__(self, conn, parsexml,
parsexmlnode, caps)
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._mode = None
self._type = None

View File

@ -37,9 +37,8 @@ class VirtualInputDevice(VirtualDevice):
input_buses = [INPUT_BUS_PS2, INPUT_BUS_USB, INPUT_BUS_XEN,
INPUT_BUS_DEFAULT]
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
VirtualDevice.__init__(self, conn, parsexml,
parsexmlnode, caps)
def __init__(self, conn, parsexml=None, parsexmlnode=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._type = None
self._bus = None

View File

@ -29,9 +29,8 @@ class VirtualMemballoon(VirtualDevice):
MODELS = ["xen", "none", MODEL_DEFAULT]
def __init__(self, conn=None, model=MODEL_DEFAULT,
parsexml=None, parsexmlnode=None, caps=None):
VirtualDevice.__init__(self, conn, parsexml,
parsexmlnode, caps)
parsexml=None, parsexmlnode=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._model = None

View File

@ -68,9 +68,9 @@ def _countMACaddr(vms, searchmac):
class VirtualPort(XMLBuilderDomain.XMLBuilderDomain):
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
def __init__(self, conn, parsexml=None, parsexmlnode=None):
XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml,
parsexmlnode, caps=caps)
parsexmlnode)
self._type = None
self._managerid = None
self._typeid = None
@ -147,11 +147,11 @@ class VirtualNetworkInterface(VirtualDevice):
def __init__(self, conn, macaddr=None, type=TYPE_BRIDGE, bridge=None,
network=None, model=None,
parsexml=None, parsexmlnode=None, caps=None):
parsexml=None, parsexmlnode=None):
# pylint: disable=W0622
# Redefining built-in 'type', but it matches the XML so keep it
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps)
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._network = None
self._bridge = None
@ -161,7 +161,7 @@ class VirtualNetworkInterface(VirtualDevice):
self._target_dev = None
self._source_dev = None
self._source_mode = "vepa"
self._virtualport = VirtualPort(conn, parsexml, parsexmlnode, caps)
self._virtualport = VirtualPort(conn, parsexml, parsexmlnode)
# Generate _random_mac
self._random_mac = None

View File

@ -35,12 +35,8 @@ class VirtualRedirDevice(VirtualDevice):
_types = ["tcp", "spicevmc", None]
def __init__(self, conn, bus=BUS_DEFAULT, stype=TYPE_DEFAULT,
parsexml=None, parsexmlnode=None, caps=None):
"""
@param conn: Connection the device/guest will be installed on
@type conn: libvirt.virConnect
"""
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps)
parsexml=None, parsexmlnode=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._type = None
self._bus = None

View File

@ -35,9 +35,8 @@ class VirtualSmartCardDevice(VirtualDevice):
_types = ["tcp", "spicevmc", None]
def __init__(self, conn, mode=MODE_DEFAULT,
parsexml=None, parsexmlnode=None, caps=None):
VirtualDevice.__init__(self, conn,
parsexml, parsexmlnode, caps)
parsexml=None, parsexmlnode=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._mode = None
self._type = None

View File

@ -59,8 +59,8 @@ class VirtualTPMDevice(VirtualDevice):
get_dev_instance = staticmethod(get_dev_instance)
def __init__(self, conn, typ=TYPE_DEFAULT,
parsexml=None, parsexmlnode=None, caps=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps)
parsexml=None, parsexmlnode=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._type = None
self._model = self.TPM_TIS

View File

@ -35,9 +35,8 @@ class VirtualVideoDevice(VirtualDevice):
return model.upper()
return model.capitalize()
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
VirtualDevice.__init__(self, conn,
parsexml, parsexmlnode, caps)
def __init__(self, conn, parsexml=None, parsexmlnode=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._model_type = None
self._vram = None

View File

@ -55,9 +55,8 @@ class VirtualWatchdog(VirtualDevice):
else:
return action
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
VirtualDevice.__init__(self, conn, parsexml,
parsexmlnode, caps)
def __init__(self, conn, parsexml=None, parsexmlnode=None):
VirtualDevice.__init__(self, conn, parsexml, parsexmlnode)
self._model = None
self._action = None

View File

@ -24,7 +24,6 @@ import threading
import libxml2
from virtinst import CapabilitiesParser
from virtinst import util
_xml_refs_lock = threading.Lock()
@ -389,7 +388,7 @@ class XMLBuilderDomain(object):
"""
_dumpxml_xpath = "."
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
def __init__(self, conn, parsexml=None, parsexmlnode=None):
"""
Initialize state
@ -398,20 +397,12 @@ class XMLBuilderDomain(object):
@param parsexml: Optional XML string to parse
@type parsexml: C{str}
@param parsexmlnode: Option xpathNode to use
@param caps: Capabilities() instance
"""
self._conn = conn
self.__caps = None
self._xml_node = None
self._xml_ctx = None
if caps:
if not isinstance(caps, CapabilitiesParser.Capabilities):
raise ValueError("caps must be a Capabilities instance")
self.__caps = caps
if parsexml or parsexmlnode:
self._parsexml(parsexml, parsexmlnode)
@ -447,11 +438,6 @@ class XMLBuilderDomain(object):
return self._conn
conn = property(_get_conn)
def _get_caps(self):
if not self.__caps:
self.__caps = CapabilitiesParser.parse(self.conn.getCapabilities())
return self.__caps
def _check_bool(self, val, name):
if val not in [True, False]:
raise ValueError(_("'%s' must be True or False" % name))

View File

@ -23,6 +23,7 @@ import re
import libvirt
from virtinst import support
from virtinst import CapabilitiesParser
from virtinst.cli import parse_optstr
from virtinst.util import uri_split
@ -54,6 +55,8 @@ class VirtualConnection(object):
self._libvirtconn = None
self._urisplits = uri_split(self._uri)
self._caps = None
# Just proxy virConnect access for now
def __getattr__(self, attr):
@ -73,6 +76,13 @@ class VirtualConnection(object):
libvirtconn = property(lambda self: getattr(self, "_libvirtconn"))
def _get_caps(self):
if not self._caps:
self._caps = CapabilitiesParser.Capabilities(
self.libvirtconn.getCapabilities())
return self._caps
caps = property(_get_caps)
##############
# Public API #
@ -82,6 +92,9 @@ class VirtualConnection(object):
self._libvirtconn = None
self._uri = None
def invalidate_caps(self):
self._caps = None
def is_open(self):
return bool(self._libvirtconn)