mirror of
https://github.com/virt-manager/virt-manager.git
synced 2024-12-23 17:34:21 +03:00
f107e39989
Init a shared log instance in virtinst/logger.py, and use that throughout the code base, so we aren't calling directly into 'logging'. This helps protect our logging output from being cluttered with other library output, as happens with some 'requests' usage
316 lines
9.3 KiB
Python
316 lines
9.3 KiB
Python
#
|
|
# Some code for parsing libvirt's capabilities XML
|
|
#
|
|
# Copyright 2007, 2012-2014 Red Hat, Inc.
|
|
#
|
|
# This work is licensed under the GNU GPLv2 or later.
|
|
# See the COPYING file in the top-level directory.
|
|
|
|
import pwd
|
|
|
|
from .domain import DomainCpu
|
|
from .logger import log
|
|
from .xmlbuilder import XMLBuilder, XMLChildProperty, XMLProperty
|
|
|
|
|
|
###################################
|
|
# capabilities host <cpu> parsing #
|
|
###################################
|
|
|
|
class _CapsCPU(DomainCpu):
|
|
arch = XMLProperty("./arch")
|
|
|
|
|
|
###########################
|
|
# Caps <topology> parsers #
|
|
###########################
|
|
|
|
class _CapsTopologyCPU(XMLBuilder):
|
|
XML_NAME = "cpu"
|
|
id = XMLProperty("./@id")
|
|
|
|
|
|
class _TopologyCell(XMLBuilder):
|
|
XML_NAME = "cell"
|
|
cpus = XMLChildProperty(_CapsTopologyCPU, relative_xpath="./cpus")
|
|
|
|
|
|
class _CapsTopology(XMLBuilder):
|
|
XML_NAME = "topology"
|
|
cells = XMLChildProperty(_TopologyCell, relative_xpath="./cells")
|
|
|
|
|
|
######################################
|
|
# Caps <host> and <secmodel> parsers #
|
|
######################################
|
|
|
|
class _CapsSecmodelBaselabel(XMLBuilder):
|
|
XML_NAME = "baselabel"
|
|
type = XMLProperty("./@type")
|
|
content = XMLProperty(".")
|
|
|
|
|
|
class _CapsSecmodel(XMLBuilder):
|
|
XML_NAME = "secmodel"
|
|
model = XMLProperty("./model")
|
|
baselabels = XMLChildProperty(_CapsSecmodelBaselabel)
|
|
|
|
|
|
class _CapsHost(XMLBuilder):
|
|
XML_NAME = "host"
|
|
secmodels = XMLChildProperty(_CapsSecmodel)
|
|
cpu = XMLChildProperty(_CapsCPU, is_single=True)
|
|
topology = XMLChildProperty(_CapsTopology, is_single=True)
|
|
|
|
def get_qemu_baselabel(self):
|
|
for secmodel in self.secmodels:
|
|
if secmodel.model != "dac":
|
|
continue
|
|
|
|
label = None
|
|
for baselabel in secmodel.baselabels:
|
|
if baselabel.type in ["qemu", "kvm"]:
|
|
label = baselabel.content
|
|
break
|
|
if not label:
|
|
continue # pragma: no cover
|
|
|
|
# XML we are looking at is like:
|
|
#
|
|
# <secmodel>
|
|
# <model>dac</model>
|
|
# <doi>0</doi>
|
|
# <baselabel type='kvm'>+107:+107</baselabel>
|
|
# <baselabel type='qemu'>+107:+107</baselabel>
|
|
# </secmodel>
|
|
try:
|
|
uid = int(label.split(":")[0].replace("+", ""))
|
|
user = pwd.getpwuid(uid)[0]
|
|
return user, uid
|
|
except Exception: # pragma: no cover
|
|
log.debug("Exception parsing qemu dac baselabel=%s",
|
|
label, exc_info=True)
|
|
return None, None # pragma: no cover
|
|
|
|
|
|
################################
|
|
# <guest> and <domain> parsers #
|
|
################################
|
|
|
|
class _CapsMachine(XMLBuilder):
|
|
XML_NAME = "machine"
|
|
name = XMLProperty(".")
|
|
canonical = XMLProperty("./@canonical")
|
|
|
|
|
|
class _CapsDomain(XMLBuilder):
|
|
XML_NAME = "domain"
|
|
hypervisor_type = XMLProperty("./@type")
|
|
emulator = XMLProperty("./emulator")
|
|
machines = XMLChildProperty(_CapsMachine)
|
|
|
|
|
|
class _CapsGuestFeatures(XMLBuilder):
|
|
XML_NAME = "features"
|
|
|
|
pae = XMLProperty("./pae", is_bool=True)
|
|
acpi = XMLProperty("./acpi/@default", is_onoff=True)
|
|
apic = XMLProperty("./apic/@default", is_onoff=True)
|
|
|
|
|
|
class _CapsGuest(XMLBuilder):
|
|
XML_NAME = "guest"
|
|
|
|
os_type = XMLProperty("./os_type")
|
|
arch = XMLProperty("./arch/@name")
|
|
loader = XMLProperty("./arch/loader")
|
|
emulator = XMLProperty("./arch/emulator")
|
|
|
|
domains = XMLChildProperty(_CapsDomain, relative_xpath="./arch")
|
|
features = XMLChildProperty(_CapsGuestFeatures, is_single=True)
|
|
machines = XMLChildProperty(_CapsMachine, relative_xpath="./arch")
|
|
|
|
|
|
###############
|
|
# Public APIs #
|
|
###############
|
|
|
|
def all_machine_names(self, domain):
|
|
"""
|
|
Return all machine string names, including canonical aliases for
|
|
the guest+domain combo
|
|
"""
|
|
mobjs = (domain and domain.machines) or self.machines
|
|
ret = []
|
|
for m in mobjs:
|
|
ret.append(m.name)
|
|
if m.canonical:
|
|
ret.append(m.canonical)
|
|
return ret
|
|
|
|
def has_install_options(self):
|
|
"""
|
|
Return True if there are any install options available
|
|
"""
|
|
return bool(len(self.domains) > 0)
|
|
|
|
def is_kvm_available(self):
|
|
"""
|
|
Return True if kvm guests can be installed
|
|
"""
|
|
for d in self.domains:
|
|
if d.hypervisor_type == "kvm":
|
|
return True
|
|
return False
|
|
|
|
def supports_pae(self):
|
|
"""
|
|
Return True if capabilities report support for PAE
|
|
"""
|
|
return bool(self.features.pae)
|
|
|
|
def supports_acpi(self):
|
|
"""
|
|
Return Tree if capabilities report support for ACPI
|
|
"""
|
|
return bool(self.features.acpi)
|
|
|
|
def supports_apic(self):
|
|
"""
|
|
Return Tree if capabilities report support for APIC
|
|
"""
|
|
return bool(self.features.apic)
|
|
|
|
|
|
############################
|
|
# Main capabilities object #
|
|
############################
|
|
|
|
class _CapsInfo(object):
|
|
"""
|
|
Container object to hold the results of guest_lookup, so users don't
|
|
need to juggle two objects
|
|
"""
|
|
def __init__(self, conn, guest, domain):
|
|
self.conn = conn
|
|
self.guest = guest
|
|
self.domain = domain
|
|
|
|
self.hypervisor_type = self.domain.hypervisor_type
|
|
self.os_type = self.guest.os_type
|
|
self.arch = self.guest.arch
|
|
self.loader = self.guest.loader
|
|
|
|
self.emulator = self.domain.emulator or self.guest.emulator
|
|
self.machines = self.guest.all_machine_names(self.domain)
|
|
|
|
|
|
class Capabilities(XMLBuilder):
|
|
def __init__(self, *args, **kwargs):
|
|
XMLBuilder.__init__(self, *args, **kwargs)
|
|
self._cpu_models_cache = {}
|
|
|
|
XML_NAME = "capabilities"
|
|
|
|
host = XMLChildProperty(_CapsHost, is_single=True)
|
|
guests = XMLChildProperty(_CapsGuest)
|
|
|
|
|
|
############################
|
|
# Public XML building APIs #
|
|
############################
|
|
|
|
def _guestForOSType(self, os_type, arch):
|
|
archs = [arch]
|
|
if arch is None:
|
|
archs = [self.host.cpu.arch, None]
|
|
|
|
for a in archs:
|
|
for g in self.guests:
|
|
if ((os_type is None or g.os_type == os_type) and
|
|
(a is None or g.arch == a)):
|
|
return g
|
|
|
|
def _bestDomainType(self, guest, dtype, machine):
|
|
"""
|
|
Return the recommended domain for use if the user does not explicitly
|
|
request one.
|
|
"""
|
|
domains = []
|
|
for d in guest.domains:
|
|
if dtype and d.hypervisor_type != dtype.lower():
|
|
continue
|
|
if machine and machine not in guest.all_machine_names(d):
|
|
continue
|
|
|
|
domains.append(d)
|
|
|
|
if not domains:
|
|
return None
|
|
|
|
priority = ["kvm", "xen", "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]
|
|
|
|
def guest_lookup(self, os_type=None, arch=None, typ=None, 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:
|
|
|
|
- os_type : hvm, then xen
|
|
- typ : kvm over plain qemu
|
|
- arch : host arch over all others
|
|
|
|
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.
|
|
|
|
:param typ: Virtualization type ('hvm', 'xen', ...)
|
|
:param arch: Guest architecture ('x86_64', 'i686' ...)
|
|
:param os_type: Hypervisor name ('qemu', 'kvm', 'xen', ...)
|
|
:param machine: Optional machine type to emulate
|
|
|
|
:returns: A _CapsInfo object containing the found guest and domain
|
|
"""
|
|
# F22 libxl xen still puts type=linux in the XML, so we need
|
|
# to handle it for caps lookup
|
|
if os_type == "linux":
|
|
os_type = "xen"
|
|
|
|
guest = self._guestForOSType(os_type, arch)
|
|
if not guest:
|
|
archstr = _("for arch '%s'") % arch
|
|
if not arch:
|
|
archstr = ""
|
|
|
|
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 = self._bestDomainType(guest, typ, 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})
|
|
|
|
capsinfo = _CapsInfo(self.conn, guest, domain)
|
|
return capsinfo
|