summaryrefslogtreecommitdiff
path: root/virtinst/capabilities.py
diff options
context:
space:
mode:
authorCole Robinson <crobinso@redhat.com>2015-04-03 12:40:16 -0400
committerCole Robinson <crobinso@redhat.com>2015-04-03 16:45:42 -0400
commitcac4ac1401203e2b851f111bf81136ae0723bcbe (patch)
tree56ac50058d92a32b694cc74a059920ea0c7c72a8 /virtinst/capabilities.py
parent0492dbc1b12db1057cd8ac9132d6786d32703376 (diff)
downloadvirt-manager-cac4ac1401203e2b851f111bf81136ae0723bcbe.tar.gz
capabilities: Switch to use XMLBuilder
Just a cleanup to finally unify all the XML objects around the same API, and drop a bunch of old cruft.
Diffstat (limited to 'virtinst/capabilities.py')
-rw-r--r--virtinst/capabilities.py529
1 files changed, 193 insertions, 336 deletions
diff --git a/virtinst/capabilities.py b/virtinst/capabilities.py
index 729fa73f..ed329dd7 100644
--- a/virtinst/capabilities.py
+++ b/virtinst/capabilities.py
@@ -22,17 +22,14 @@
import re
from . import util
+from .cpu import CPU as DomainCPU
+from .xmlbuilder import XMLBuilder, XMLChildProperty
+from .xmlbuilder import XMLProperty as _XMLProperty
-# 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
+# Disable test suite property tracking
+class XMLProperty(_XMLProperty):
+ _track = False
class CPUValuesModel(object):
@@ -141,252 +138,109 @@ class _CPUMapFileValues(_CPUAPIValues):
return cpumap.cpus
-class Features(object):
- """Represent a set of features. For each feature, store a bit mask of
- FEATURE_ON and FEATURE_OFF to indicate whether the feature can
- be turned on or off. For features for which toggling doesn't make sense
- (e.g., 'vmx') store FEATURE_ON when the feature is present."""
-
- def __init__(self, node=None):
- self.features = {}
- if node is not None:
- self.parseXML(node)
-
- def __getitem__(self, feature):
- if feature in self.features:
- return self.features[feature]
- return 0
-
- def names(self):
- return self.features.keys()
-
- def parseXML(self, node):
- d = self.features
-
- feature_list = []
- if node.name == "features":
- node_list = node.xpathEval("*")
- for n in node_list:
- feature_list.append(n.name)
- else:
- # New style features
- node_list = node.xpathEval("feature/@name")
- for n in node_list:
- feature_list.append(n.content)
-
- for feature in feature_list:
- if feature not in d:
- d[feature] = 0
-
- self._extractFeature(feature, d, n)
-
- def _extractFeature(self, feature, d, node):
- """Extract the value of FEATURE from NODE and set DICT[FEATURE] to
- its value. Abstract method, must be overridden"""
- raise NotImplementedError("Abstract base class")
-
-
-class CapabilityFeatures(Features):
- def __init__(self, node=None):
- Features.__init__(self, node)
-
- def _extractFeature(self, feature, d, n):
- default = xpathString(n, "@default")
- toggle = xpathString(n, "@toggle")
-
- if default is not None:
- # Format for guest features
- if default == "on":
- d[feature] = FEATURE_ON
- elif default == "off":
- d[feature] = FEATURE_OFF
- else:
- 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:
- # Format for old HOST features, on OLD old guest features
- # back compat is just <$featurename>, like <svm/>
- if feature == "nonpae":
- d["pae"] |= FEATURE_OFF
- else:
- d[feature] |= FEATURE_ON
-
-
-class CPU(object):
- def __init__(self, node=None):
- # e.g. "i686" or "x86_64"
- self.arch = None
- self.model = None
- self.vendor = None
- self.sockets = 1
- self.cores = 1
- self.threads = 1
- self.features = CapabilityFeatures()
-
- if node is not None:
- self.parseXML(node)
-
- def parseXML(self, node):
- newstyle_features = False
-
- child = node.children
- while child:
- # Do a first pass to try and detect new style features
- if child.name == "feature":
- newstyle_features = True
- break
- child = child.next
+class _CapsCPU(DomainCPU):
+ arch = XMLProperty("./arch")
- if newstyle_features:
- self.features = CapabilityFeatures(node)
+ # capabilities used to just expose these properties as bools
+ _svm_bool = XMLProperty("./features/svm", is_bool=True)
+ _vmx_bool = XMLProperty("./features/vmx", is_bool=True)
+ _pae_bool = XMLProperty("./features/pae", is_bool=True)
+ _nonpae_bool = XMLProperty("./features/nonpae", is_bool=True)
- child = node.children
- while child:
- if child.name == "arch":
- self.arch = child.content
- elif child.name == "model":
- self.model = child.content
- elif child.name == "vendor":
- self.vendor = child.content
- elif child.name == "topology":
- self.sockets = xpathString(child, "@sockets") or 1
- self.cores = xpathString(child, "@cores") or 1
- self.threads = xpathString(child, "@threads") or 1
-
- elif child.name == "features" and not newstyle_features:
- self.features = CapabilityFeatures(child)
+ has_feature_block = XMLProperty("./features", is_bool=True)
- child = child.next
+ ##############
+ # Public API #
+ ##############
-class Host(object):
- def __init__(self, node=None):
- self.cpu = CPU()
- self.topology = None
- self.secmodels = []
+ def has_feature(self, name):
+ if name == "svm" and self._svm_bool:
+ return True
+ if name == "vmx" and self._vmx_bool:
+ return True
+ if name == "pae" and self._pae_bool:
+ return True
+ if name == "nonpae" and self._nonpae_bool:
+ return True
- if node is not None:
- self.parseXML(node)
+ return name in [f.name for f in self.features]
- def get_secmodel(self):
- return self.secmodels and self.secmodels[0] or None
- secmodel = property(get_secmodel)
- def parseXML(self, node):
- child = node.children
- while child:
- if child.name == "topology":
- self.topology = Topology(child)
+###########################
+# Caps <topology> parsers #
+###########################
- if child.name == "secmodel":
- self.secmodels.append(SecurityModel(child))
+class _CapsTopologyCPU(XMLBuilder):
+ _XML_ROOT_NAME = "cpu"
+ id = XMLProperty("./@id")
- if child.name == "cpu":
- self.cpu = CPU(child)
- child = child.next
+class _TopologyCell(XMLBuilder):
+ _XML_ROOT_NAME = "cell"
+ id = XMLProperty("./@id")
+ cpus = XMLChildProperty(_CapsTopologyCPU, relative_xpath="./cpus")
-class Guest(object):
- def __init__(self, node=None):
- # e.g. "xen" or "hvm"
- self.os_type = None
- # e.g. "i686" or "x86_64"
- self.arch = None
+class _CapsTopology(XMLBuilder):
+ _XML_ROOT_NAME = "topology"
+ cells = XMLChildProperty(_TopologyCell, relative_xpath="./cells")
- self.domains = []
- self.features = CapabilityFeatures()
+######################################
+# Caps <host> and <secmodel> parsers #
+######################################
- if node is not None:
- self.parseXML(node)
+class _CapsSecmodelBaselabel(XMLBuilder):
+ _XML_ROOT_NAME = "baselabel"
+ type = XMLProperty("./@type")
+ content = XMLProperty(".")
- def parseXML(self, node):
- child = node.children
- while child:
- if child.name == "os_type":
- self.os_type = child.content
- elif child.name == "features":
- self.features = CapabilityFeatures(child)
- elif child.name == "arch":
- self.arch = child.prop("name")
- machines = []
- emulator = None
- loader = None
- n = child.children
- while n:
- if n.name == "machine":
- machines.append(n.content)
-
- canon = n.prop("canonical")
- if canon:
- machines.append(canon)
- elif n.name == "emulator":
- emulator = n.content
- elif n.name == "loader":
- loader = n.content
- n = n.next
-
- n = child.children
- while n:
- if n.name == "domain":
- self.domains.append(Domain(n.prop("type"),
- emulator, loader, machines, n))
- n = n.next
- child = child.next
+class _CapsSecmodel(XMLBuilder):
+ _XML_ROOT_NAME = "secmodel"
+ model = XMLProperty("./model")
+ baselabels = XMLChildProperty(_CapsSecmodelBaselabel)
- def _favoredDomain(self, domains):
- """
- Return the recommended domain for use if the user does not explicitly
- request one.
- """
- if not domains:
- return None
- priority = ["kvm", "xen", "kqemu", "qemu"]
+class _CapsHost(XMLBuilder):
+ _XML_ROOT_NAME = "host"
+ secmodels = XMLChildProperty(_CapsSecmodel)
+ cpu = XMLChildProperty(_CapsCPU, is_single=True)
+ topology = XMLChildProperty(_CapsTopology, is_single=True)
- for t in priority:
- for d in domains:
- if d.hypervisor_type == t:
- return d
- # Fallback, just return last item in list
- return domains[-1]
+################################
+# <guest> and <domain> parsers #
+################################
- def bestDomainType(self, dtype=None, machine=None):
- domains = []
- for d in self.domains:
- d.set_recommended_machine(None)
+class _CapsMachine(XMLBuilder):
+ _XML_ROOT_NAME = "machine"
+ name = XMLProperty(".")
+ canonical = XMLProperty("./@canonical")
- if dtype and d.hypervisor_type != dtype.lower():
- continue
- if machine and machine not in d.machines:
- continue
- if machine:
- d.set_recommended_machine(machine)
- domains.append(d)
+class _CapsDomain(XMLBuilder):
+ def __init__(self, *args, **kwargs):
+ XMLBuilder.__init__(self, *args, **kwargs)
- return self._favoredDomain(domains)
+ self.machines = []
+ for m in self._machines:
+ self.machines.append(m.name)
+ if m.canonical:
+ self.machines.append(m.canonical)
+ self._recommended_machine = None
-class Domain(object):
- def __init__(self, hypervisor_type,
- emulator=None, loader=None,
- machines=None, node=None):
- self.hypervisor_type = hypervisor_type
- self.emulator = emulator
- self.loader = loader
- self.machines = machines
+ _XML_ROOT_NAME = "domain"
+ hypervisor_type = XMLProperty("./@type")
+ emulator = XMLProperty("./emulator")
+ _machines = XMLChildProperty(_CapsMachine)
- self._recommended_machine = None
- if node is not None:
- self.parseXML(node)
+ ###############
+ # Public APIs #
+ ###############
def get_recommended_machine(self, conn, capsguest):
if self._recommended_machine:
@@ -408,102 +262,102 @@ class Domain(object):
def set_recommended_machine(self, machine):
self._recommended_machine = machine
- def parseXML(self, node):
- child = node.children
- machines = []
- while child:
- if child.name == "emulator":
- self.emulator = child.content
- elif child.name == "machine":
- machines.append(child.content)
-
- canon = child.prop("canonical")
- if canon:
- machines.append(canon)
- machines.append(child.content)
- child = child.next
-
- if len(machines) > 0:
- self.machines = machines
-
def is_accelerated(self):
return self.hypervisor_type in ["kvm", "kqemu"]
-class Topology(object):
- def __init__(self, node=None):
- self.cells = []
+class _CapsGuestFeatures(XMLBuilder):
+ _XML_ROOT_NAME = "features"
- if node is not None:
- self.parseXML(node)
+ pae = XMLProperty("./pae", is_bool=True)
+ nonpae = XMLProperty("./nonpae", is_bool=True)
+ acpi = XMLProperty("./acpi", is_bool=True)
+ apic = XMLProperty("./apci", is_bool=True)
- def parseXML(self, node):
- child = node.children
- if child.name == "cells":
- for cell in child.children:
- if cell.name == "cell":
- self.cells.append(TopologyCell(cell))
+class _CapsGuest(XMLBuilder):
+ def __init__(self, *args, **kwargs):
+ XMLBuilder.__init__(self, *args, **kwargs)
-class TopologyCell(object):
- def __init__(self, node=None):
- self.id = None
- self.cpus = []
+ self.machines = []
+ for m in self._machines:
+ self.machines.append(m.name)
+ if m.canonical:
+ self.machines.append(m.canonical)
- if node is not None:
- self.parseXML(node)
+ for d in self.domains:
+ if not d.emulator:
+ d.emulator = self.emulator
+ if not d.machines:
+ d.machines = self.machines
- def parseXML(self, node):
- self.id = int(node.prop("id"))
- for child in node.children:
- if child.name == "cpus":
- for cpu in child.children:
- if cpu.name == "cpu":
- self.cpus.append(TopologyCPU(cpu))
+ _XML_ROOT_NAME = "guest"
-class TopologyCPU(object):
- def __init__(self, node=None):
- self.id = None
+ os_type = XMLProperty("./os_type")
+ arch = XMLProperty("./arch/@name")
+ loader = XMLProperty("./arch/loader")
+ emulator = XMLProperty("./arch/emulator")
- if node is not None:
- self.parseXML(node)
+ domains = XMLChildProperty(_CapsDomain, relative_xpath="./arch")
+ features = XMLChildProperty(_CapsGuestFeatures, is_single=True)
+ _machines = XMLChildProperty(_CapsMachine, relative_xpath="./arch")
- def parseXML(self, node):
- self.id = int(node.prop("id"))
+ ###############
+ # Public APIs #
+ ###############
-class SecurityModel(object):
- def __init__(self, node=None):
- self.model = None
- self.doi = None
- self.baselabels = {}
+ def bestDomainType(self, dtype=None, machine=None):
+ """
+ Return the recommended domain for use if the user does not explicitly
+ request one.
+ """
+ domains = []
+ for d in self.domains:
+ d.set_recommended_machine(None)
- if node is not None:
- self.parseXML(node)
+ if dtype and d.hypervisor_type != dtype.lower():
+ continue
+ if machine and machine not in d.machines:
+ continue
- def parseXML(self, node):
- for child in node.children or []:
- if child.name == "model":
- self.model = child.content
- elif child.name == "doi":
- self.doi = child.content
- elif child.name == "baselabel":
- typ = child.prop("type")
- self.baselabels[typ] = child.content
-
-
-class Capabilities(object):
- def __init__(self, xml):
- self.host = None
- self.guests = []
- self.xml = xml
- self._topology = None
+ if machine:
+ d.set_recommended_machine(machine)
+ domains.append(d)
+
+ if not domains:
+ return None
+
+ priority = ["kvm", "xen", "kqemu", "qemu"]
+
+ for t in priority:
+ for d in domains:
+ if d.hypervisor_type == t:
+ return d
+
+ # Fallback, just return last item in list
+ return domains[-1]
+
+
+############################
+# Main capabilities object #
+############################
+
+class Capabilities(XMLBuilder):
+ def __init__(self, *args, **kwargs):
+ XMLBuilder.__init__(self, *args, **kwargs)
self._cpu_values = None
- util.parse_node_helper(self.xml, "capabilities",
- self.parseXML,
- RuntimeError)
+ _XML_ROOT_NAME = "capabilities"
+
+ host = XMLChildProperty(_CapsHost, is_single=True)
+ guests = XMLChildProperty(_CapsGuest)
+
+
+ ###################
+ # Private helpers #
+ ###################
def _is_xen(self):
for g in self.guests:
@@ -516,6 +370,11 @@ class Capabilities(object):
return False
+
+ ##############
+ # Public API #
+ ##############
+
def no_install_options(self):
"""
Return True if there are no install options available
@@ -534,17 +393,17 @@ class Capabilities(object):
sufficiently provided, so we will return True in cases that we
aren't sure.
"""
+ # Obvious case of feature being specified
+ if (self.host.cpu.has_feature("vmx") or
+ self.host.cpu.has_feature("svm")):
+ return True
+
has_hvm_guests = False
for g in self.guests:
if g.os_type == "hvm":
has_hvm_guests = True
break
- # Obvious case of feature being specified
- if (self.host.cpu.features["vmx"] == FEATURE_ON or
- self.host.cpu.features["svm"] == FEATURE_ON):
- return True
-
# Xen seems to block the vmx/svm feature bits from cpuinfo?
# so make sure no hvm guests are listed
if self._is_xen() and has_hvm_guests:
@@ -552,7 +411,7 @@ class Capabilities(object):
# If there is other features, but no virt bit, then HW virt
# isn't supported
- if len(self.host.cpu.features.names()):
+ if self.host.cpu.has_feature_block:
return False
# Xen caps have always shown this info, so if we didn't find any
@@ -617,36 +476,15 @@ class Capabilities(object):
return True
- def support_pae(self):
+ def supports_pae(self):
+ """
+ Return True if capabilities report support for PAE
+ """
for g in self.guests:
- if "pae" in g.features.names():
+ if g.features.pae:
return True
return False
- def _guestForOSType(self, typ=None, arch=None):
- if self.host is None:
- return None
-
- if arch is None:
- archs = [self.host.cpu.arch, None]
- else:
- archs = [arch]
-
- for a in archs:
- for g in self.guests:
- if (typ is None or g.os_type == typ) and \
- (a is None or g.arch == a):
- return g
-
- def parseXML(self, node):
- child = node.children
- while child:
- if child.name == "host":
- self.host = Host(child)
- elif child.name == "guest":
- self.guests.append(Guest(child))
- child = child.next
-
def get_cpu_values(self, conn, arch):
if not arch:
return []
@@ -663,6 +501,25 @@ class Capabilities(object):
return []
+
+ ############################
+ # Public XML building APIs #
+ ############################
+
+ def _guestForOSType(self, typ=None, arch=None):
+ if self.host is None:
+ return None
+
+ archs = [arch]
+ if arch is None:
+ archs = [self.host.cpu.arch, None]
+
+ for a in archs:
+ for g in self.guests:
+ if ((typ is None or g.os_type == typ) and
+ (a is None or g.arch == a)):
+ return g
+
def guest_lookup(self, os_type=None, arch=None, typ=None, machine=None):
"""
Simple virtualization availability lookup
@@ -714,12 +571,12 @@ class Capabilities(object):
return (guest, domain)
def build_virtinst_guest(self, conn, guest, domain):
- from .guest import Guest as VMGuest
- gobj = VMGuest(conn)
+ from .guest import Guest
+ gobj = Guest(conn)
gobj.type = domain.hypervisor_type
gobj.os.os_type = guest.os_type
gobj.os.arch = guest.arch
- gobj.os.loader = domain.loader
+ gobj.os.loader = guest.loader
gobj.emulator = domain.emulator
gobj.os.machine = domain.get_recommended_machine(conn, guest)