2013-03-18 01:06:52 +04:00
#
# Some code for parsing libvirt's capabilities XML
#
2014-03-12 15:36:17 +04:00
# Copyright 2007, 2012-2014 Red Hat, Inc.
2013-03-18 01:06:52 +04:00
# Mark McLoughlin <markmc@redhat.com>
#
2018-04-04 16:35:41 +03:00
# This work is licensed under the GNU GPLv2 or later.
2018-03-20 22:00:02 +03:00
# See the COPYING file in the top-level directory.
2013-03-18 01:06:52 +04:00
2015-09-16 23:55:37 +03:00
import logging
2013-03-18 01:06:52 +04:00
2018-03-20 22:10:04 +03:00
from . domain import DomainCpu
2015-04-22 21:44:52 +03:00
from . xmlbuilder import XMLBuilder , XMLChildProperty , XMLProperty
2013-07-06 22:12:13 +04:00
2015-04-03 23:09:25 +03:00
###################################
# capabilities host <cpu> parsing #
###################################
2013-04-13 22:34:52 +04:00
2018-03-20 22:10:04 +03:00
class _CapsCPU ( DomainCpu ) :
2015-04-03 19:40:16 +03:00
arch = XMLProperty ( " ./arch " )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
# 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 )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
##############
# Public API #
##############
2013-04-13 22:34:52 +04:00
2015-04-03 19:40:16 +03:00
def has_feature ( self , name ) :
if name == " svm " and self . _svm_bool :
return True
if name == " vmx " and self . _vmx_bool :
return True
return name in [ f . name for f in self . features ]
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
###########################
# Caps <topology> parsers #
###########################
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
class _CapsTopologyCPU ( XMLBuilder ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " cpu "
2015-04-03 19:40:16 +03:00
id = XMLProperty ( " ./@id " )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
class _TopologyCell ( XMLBuilder ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " cell "
2015-04-03 19:40:16 +03:00
cpus = XMLChildProperty ( _CapsTopologyCPU , relative_xpath = " ./cpus " )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
class _CapsTopology ( XMLBuilder ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " topology "
2015-04-03 19:40:16 +03:00
cells = XMLChildProperty ( _TopologyCell , relative_xpath = " ./cells " )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
######################################
# Caps <host> and <secmodel> parsers #
######################################
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
class _CapsSecmodelBaselabel ( XMLBuilder ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " baselabel "
2015-04-03 19:40:16 +03:00
type = XMLProperty ( " ./@type " )
content = XMLProperty ( " . " )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
class _CapsSecmodel ( XMLBuilder ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " secmodel "
2015-04-03 19:40:16 +03:00
model = XMLProperty ( " ./model " )
baselabels = XMLChildProperty ( _CapsSecmodelBaselabel )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
class _CapsHost ( XMLBuilder ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " host "
2015-04-03 19:40:16 +03:00
secmodels = XMLChildProperty ( _CapsSecmodel )
cpu = XMLChildProperty ( _CapsCPU , is_single = True )
topology = XMLChildProperty ( _CapsTopology , is_single = True )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
################################
# <guest> and <domain> parsers #
################################
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
class _CapsMachine ( XMLBuilder ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " machine "
2015-04-03 19:40:16 +03:00
name = XMLProperty ( " . " )
canonical = XMLProperty ( " ./@canonical " )
2014-09-24 00:05:48 +04:00
2015-04-03 19:40:16 +03:00
class _CapsDomain ( XMLBuilder ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " domain "
2015-04-03 19:40:16 +03:00
hypervisor_type = XMLProperty ( " ./@type " )
emulator = XMLProperty ( " ./emulator " )
2018-02-14 03:42:47 +03:00
machines = XMLChildProperty ( _CapsMachine )
2013-03-18 01:06:52 +04:00
2014-09-24 00:05:48 +04:00
2015-04-03 19:40:16 +03:00
class _CapsGuestFeatures ( XMLBuilder ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " features "
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
pae = XMLProperty ( " ./pae " , is_bool = True )
2015-07-10 13:06:49 +03:00
acpi = XMLProperty ( " ./acpi/@default " , is_onoff = True )
apic = XMLProperty ( " ./apic/@default " , is_onoff = True )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
class _CapsGuest ( XMLBuilder ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " guest "
2013-04-13 22:34:52 +04:00
2015-04-03 19:40:16 +03:00
os_type = XMLProperty ( " ./os_type " )
arch = XMLProperty ( " ./arch/@name " )
loader = XMLProperty ( " ./arch/loader " )
2018-02-14 03:42:47 +03:00
emulator = XMLProperty ( " ./arch/emulator " )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
domains = XMLChildProperty ( _CapsDomain , relative_xpath = " ./arch " )
features = XMLChildProperty ( _CapsGuestFeatures , is_single = True )
2018-02-14 03:42:47 +03:00
machines = XMLChildProperty ( _CapsMachine , relative_xpath = " ./arch " )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
###############
# Public APIs #
###############
2013-03-18 01:06:52 +04:00
2018-02-14 03:42:47 +03:00
def all_machine_names ( self , domain ) :
2015-04-03 19:40:16 +03:00
"""
2018-02-14 03:42:47 +03:00
Return all machine string names , including canonical aliases for
the guest + domain combo
2015-04-03 19:40:16 +03:00
"""
2018-02-14 03:42:47 +03:00
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
2015-04-03 19:40:16 +03:00
2015-07-10 13:06:48 +03:00
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
"""
if self . os_type != " hvm " :
return False
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 )
2015-07-10 13:06:49 +03:00
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 )
2015-04-03 19:40:16 +03:00
############################
# Main capabilities object #
############################
2015-04-04 00:24:32 +03:00
class _CapsInfo ( object ) :
"""
Container object to hold the results of guest_lookup , so users don ' t
need to juggle two objects
"""
2015-04-04 00:33:45 +03:00
def __init__ ( self , conn , guest , domain , requested_machine ) :
self . conn = conn
2015-07-10 13:06:48 +03:00
self . guest = guest
self . domain = domain
2015-04-04 00:33:45 +03:00
self . _requested_machine = requested_machine
2015-04-04 00:24:32 +03:00
2015-07-10 13:06:48 +03:00
self . hypervisor_type = self . domain . hypervisor_type
self . os_type = self . guest . os_type
self . arch = self . guest . arch
self . loader = self . guest . loader
2015-04-04 00:24:32 +03:00
2018-02-14 03:42:47 +03:00
self . emulator = self . domain . emulator or self . guest . emulator
self . machines = self . guest . all_machine_names ( self . domain )
2015-04-04 00:24:32 +03:00
def get_recommended_machine ( self ) :
2015-04-04 00:33:45 +03:00
"""
Return the recommended machine type .
However , if the user already requested an explicit machine type ,
via guest_lookup , return that instead .
"""
if self . _requested_machine :
return self . _requested_machine
# For any other HV just let libvirt get us the default, these
# are the only ones we've tested.
2016-06-16 13:00:51 +03:00
if ( not self . conn . is_test ( ) and
not self . conn . is_qemu ( ) and
not self . conn . is_xen ( ) ) :
2015-04-04 00:33:45 +03:00
return None
2016-06-16 13:00:51 +03:00
if self . conn . is_xen ( ) and len ( self . machines ) :
return self . machines [ 0 ]
2015-04-04 00:33:45 +03:00
if ( self . arch in [ " ppc64 " , " ppc64le " ] and
" pseries " in self . machines ) :
return " pseries "
if self . arch in [ " armv7l " , " aarch64 " ] :
if " virt " in self . machines :
return " virt "
if " vexpress-a15 " in self . machines :
return " vexpress-a15 "
2015-07-13 14:35:24 +03:00
if self . arch in [ " s390x " ] :
if " s390-ccw-virtio " in self . machines :
return " s390-ccw-virtio "
2015-04-04 00:33:45 +03:00
return None
2015-04-04 00:24:32 +03:00
2015-04-03 19:40:16 +03:00
class Capabilities ( XMLBuilder ) :
def __init__ ( self , * args , * * kwargs ) :
XMLBuilder . __init__ ( self , * args , * * kwargs )
2017-07-17 18:29:54 +03:00
self . _cpu_models_cache = { }
2013-03-18 01:06:52 +04:00
2018-03-21 17:53:34 +03:00
XML_NAME = " capabilities "
2015-04-03 19:40:16 +03:00
host = XMLChildProperty ( _CapsHost , is_single = True )
guests = XMLChildProperty ( _CapsGuest )
###################
# Private helpers #
###################
2013-03-18 01:06:52 +04:00
def _is_xen ( self ) :
for g in self . guests :
if g . os_type != " xen " :
continue
for d in g . domains :
if d . hypervisor_type == " xen " :
return True
return False
2015-04-03 19:40:16 +03:00
##############
# Public API #
##############
2015-04-03 23:09:25 +03:00
def get_cpu_values ( self , arch ) :
2014-03-20 21:48:49 +04:00
if not arch :
return [ ]
2017-07-17 18:29:54 +03:00
if not self . conn . check_support ( self . conn . SUPPORT_CONN_CPU_MODEL_NAMES ) :
return [ ]
if arch in self . _cpu_models_cache :
return self . _cpu_models_cache [ arch ]
2015-04-03 23:09:25 +03:00
2017-07-17 18:29:54 +03:00
try :
names = self . conn . getCPUModelNames ( arch , 0 )
if names == - 1 :
names = [ ]
except Exception as e :
logging . debug ( " Error fetching CPU model names for arch= %s : %s " ,
arch , e )
names = [ ]
self . _cpu_models_cache [ arch ] = names
return names
2013-04-13 22:34:52 +04:00
2015-04-03 19:40:16 +03:00
############################
# Public XML building APIs #
############################
2018-02-14 03:42:47 +03:00
def _guestForOSType ( self , os_type , arch ) :
2015-04-03 19:40:16 +03:00
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 :
2018-02-14 03:42:47 +03:00
if ( ( os_type is None or g . os_type == os_type ) and
2015-04-03 19:40:16 +03:00
( a is None or g . arch == a ) ) :
return g
2018-02-14 03:42:47 +03:00
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 ]
2014-02-17 20:43:53 +04:00
def guest_lookup ( self , os_type = None , arch = None , typ = None , machine = None ) :
2013-07-06 22:12:13 +04:00
"""
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 .
2018-02-14 15:17:31 +03:00
: 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
2013-07-06 22:12:13 +04:00
2018-02-14 15:17:31 +03:00
: returns : A _CapsInfo object containing the found guest and domain
2013-07-06 22:12:13 +04:00
"""
2015-09-22 17:47:58 +03:00
# 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 "
2014-02-17 20:43:53 +04:00
guest = self . _guestForOSType ( os_type , arch )
2013-07-06 22:12:13 +04:00
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 " ) %
2017-08-05 09:39:32 +03:00
{ ' virttype ' : osstr , ' arch ' : archstr } )
2013-07-06 22:12:13 +04:00
2018-02-14 03:42:47 +03:00
domain = self . _bestDomainType ( guest , typ , machine )
2013-07-06 22:12:13 +04:00
if domain is None :
2014-02-17 20:43:53 +04:00
machinestr = " with machine ' %s ' " % machine
2013-07-06 22:12:13 +04:00
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 } )
2015-04-04 00:33:45 +03:00
capsinfo = _CapsInfo ( self . conn , guest , domain , machine )
2015-04-04 00:24:32 +03:00
return capsinfo
2013-07-17 15:53:47 +04:00
2015-04-04 00:24:32 +03:00
def build_virtinst_guest ( self , capsinfo ) :
"""
Fill in a new Guest ( ) object from the results of guest_lookup
"""
2015-04-03 19:40:16 +03:00
from . guest import Guest
2015-04-04 00:24:32 +03:00
gobj = Guest ( self . conn )
gobj . type = capsinfo . hypervisor_type
gobj . os . os_type = capsinfo . os_type
gobj . os . arch = capsinfo . arch
gobj . os . loader = capsinfo . loader
gobj . emulator = capsinfo . emulator
gobj . os . machine = capsinfo . get_recommended_machine ( )
2013-07-17 15:53:47 +04:00
2015-07-10 13:06:48 +03:00
gobj . capsinfo = capsinfo
2013-07-17 15:53:47 +04:00
return gobj
2015-04-04 00:24:32 +03:00
def lookup_virtinst_guest ( self , * args , * * kwargs ) :
"""
Call guest_lookup and pass the results to build_virtinst_guest .
This is a shortcut for API users that don ' t need to do anything
with the output from guest_lookup
"""
capsinfo = self . guest_lookup ( * args , * * kwargs )
return self . build_virtinst_guest ( capsinfo )