2013-03-17 17:06:52 -04:00
#
# Some code for parsing libvirt's capabilities XML
#
2014-03-12 12:36:17 +01:00
# Copyright 2007, 2012-2014 Red Hat, Inc.
2013-03-17 17:06:52 -04:00
#
2018-04-04 14:35:41 +01:00
# This work is licensed under the GNU GPLv2 or later.
2018-03-20 15:00:02 -04:00
# See the COPYING file in the top-level directory.
2013-03-17 17:06:52 -04:00
2018-10-11 18:52:45 -04:00
import pwd
2013-03-17 17:06:52 -04:00
2019-06-16 21:12:39 -04:00
from . logger import log
2015-04-22 14:44:52 -04:00
from . xmlbuilder import XMLBuilder , XMLChildProperty , XMLProperty
2013-07-06 14:12:13 -04:00
2015-04-03 16:09:25 -04:00
###################################
# capabilities host <cpu> parsing #
###################################
2013-04-13 14:34:52 -04:00
2022-02-16 10:38:30 -05:00
class _CapsCPU ( XMLBuilder ) :
2018-03-21 10:53:34 -04:00
XML_NAME = " cpu "
2022-02-16 10:38:30 -05:00
arch = XMLProperty ( " ./arch " )
model = XMLProperty ( " ./model " )
2024-08-26 11:35:20 +02:00
vendor = XMLProperty ( " ./vendor " )
2013-03-17 17:06:52 -04:00
2015-04-03 12:40:16 -04:00
######################################
# Caps <host> and <secmodel> parsers #
######################################
2013-03-17 17:06:52 -04:00
2015-04-03 12:40:16 -04:00
class _CapsSecmodelBaselabel ( XMLBuilder ) :
2018-03-21 10:53:34 -04:00
XML_NAME = " baselabel "
2015-04-03 12:40:16 -04:00
type = XMLProperty ( " ./@type " )
content = XMLProperty ( " . " )
2013-03-17 17:06:52 -04:00
2015-04-03 12:40:16 -04:00
class _CapsSecmodel ( XMLBuilder ) :
2018-03-21 10:53:34 -04:00
XML_NAME = " secmodel "
2015-04-03 12:40:16 -04:00
model = XMLProperty ( " ./model " )
baselabels = XMLChildProperty ( _CapsSecmodelBaselabel )
2013-03-17 17:06:52 -04:00
2015-04-03 12:40:16 -04:00
class _CapsHost ( XMLBuilder ) :
2018-03-21 10:53:34 -04:00
XML_NAME = " host "
2015-04-03 12:40:16 -04:00
secmodels = XMLChildProperty ( _CapsSecmodel )
cpu = XMLChildProperty ( _CapsCPU , is_single = True )
2013-03-17 17:06:52 -04:00
2018-10-11 18:52:45 -04:00
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 :
2019-06-10 14:15:50 -04:00
continue # pragma: no cover
2018-10-11 18:52:45 -04:00
# 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
2020-01-30 16:17:42 -05:00
except Exception :
2019-06-16 21:12:39 -04:00
log . debug ( " Exception parsing qemu dac baselabel= %s " ,
2018-10-11 18:52:45 -04:00
label , exc_info = True )
2020-01-30 16:17:42 -05:00
return None , None
2018-10-11 18:52:45 -04:00
2013-03-17 17:06:52 -04:00
2015-04-03 12:40:16 -04:00
################################
# <guest> and <domain> parsers #
################################
2013-03-17 17:06:52 -04:00
2015-04-03 12:40:16 -04:00
class _CapsMachine ( XMLBuilder ) :
2018-03-21 10:53:34 -04:00
XML_NAME = " machine "
2015-04-03 12:40:16 -04:00
name = XMLProperty ( " . " )
canonical = XMLProperty ( " ./@canonical " )
2014-09-23 16:05:48 -04:00
2015-04-03 12:40:16 -04:00
class _CapsDomain ( XMLBuilder ) :
2018-03-21 10:53:34 -04:00
XML_NAME = " domain "
2015-04-03 12:40:16 -04:00
hypervisor_type = XMLProperty ( " ./@type " )
emulator = XMLProperty ( " ./emulator " )
2018-02-13 19:42:47 -05:00
machines = XMLChildProperty ( _CapsMachine )
2013-03-17 17:06:52 -04:00
2014-09-23 16:05:48 -04:00
2015-04-03 12:40:16 -04:00
class _CapsGuestFeatures ( XMLBuilder ) :
2018-03-21 10:53:34 -04:00
XML_NAME = " features "
2013-03-17 17:06:52 -04:00
2015-04-03 12:40:16 -04:00
pae = XMLProperty ( " ./pae " , is_bool = True )
2015-07-10 12:06:49 +02:00
acpi = XMLProperty ( " ./acpi/@default " , is_onoff = True )
apic = XMLProperty ( " ./apic/@default " , is_onoff = True )
2024-01-23 12:02:43 +01:00
externalSnapshot = XMLProperty ( " ./externalSnapshot " , is_bool = True )
2013-03-17 17:06:52 -04:00
2015-04-03 12:40:16 -04:00
class _CapsGuest ( XMLBuilder ) :
2018-03-21 10:53:34 -04:00
XML_NAME = " guest "
2013-04-13 14:34:52 -04:00
2015-04-03 12:40:16 -04:00
os_type = XMLProperty ( " ./os_type " )
arch = XMLProperty ( " ./arch/@name " )
loader = XMLProperty ( " ./arch/loader " )
2018-02-13 19:42:47 -05:00
emulator = XMLProperty ( " ./arch/emulator " )
2013-03-17 17:06:52 -04:00
2015-04-03 12:40:16 -04:00
domains = XMLChildProperty ( _CapsDomain , relative_xpath = " ./arch " )
features = XMLChildProperty ( _CapsGuestFeatures , is_single = True )
2018-02-13 19:42:47 -05:00
machines = XMLChildProperty ( _CapsMachine , relative_xpath = " ./arch " )
2013-03-17 17:06:52 -04:00
2015-04-03 12:40:16 -04:00
###############
# Public APIs #
###############
2013-03-17 17:06:52 -04:00
2018-02-13 19:42:47 -05:00
def all_machine_names ( self , domain ) :
2015-04-03 12:40:16 -04:00
"""
2018-02-13 19:42:47 -05:00
Return all machine string names , including canonical aliases for
2020-07-25 00:11:38 +09:00
the guest + domain combo but avoiding duplicates
2015-04-03 12:40:16 -04:00
"""
2018-02-13 19:42:47 -05:00
mobjs = ( domain and domain . machines ) or self . machines
ret = [ ]
for m in mobjs :
ret . append ( m . name )
2020-07-25 00:11:38 +09:00
if m . canonical and m . canonical not in ret :
2024-09-10 13:13:46 -04:00
ret . append ( m . canonical ) # pragma: no cover
2018-02-13 19:42:47 -05:00
return ret
2015-04-03 12:40:16 -04:00
2022-06-28 21:54:07 +01:00
def is_machine_alias ( self , domain , src , tgt ) :
"""
Determine if machine @src is an alias for machine @tgt
"""
mobjs = ( domain and domain . machines ) or self . machines
for m in mobjs :
if m . name == src and m . canonical == tgt :
return True
return False
2015-07-10 12:06:48 +02:00
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 )
2015-07-10 12:06:49 +02: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 )
2024-01-23 12:02:43 +01:00
def supports_externalSnapshot ( self ) :
"""
Return True if capabilities report support for external snapshots
"""
return bool ( self . features . externalSnapshot )
2015-04-03 12:40:16 -04:00
############################
# Main capabilities object #
############################
2015-04-03 17:24:32 -04:00
class _CapsInfo ( object ) :
"""
Container object to hold the results of guest_lookup , so users don ' t
need to juggle two objects
"""
2018-09-06 19:07:15 -04:00
def __init__ ( self , conn , guest , domain ) :
2015-04-03 17:33:45 -04:00
self . conn = conn
2015-07-10 12:06:48 +02:00
self . guest = guest
self . domain = domain
2015-04-03 17:24:32 -04:00
2015-07-10 12:06:48 +02: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-03 17:24:32 -04:00
2018-02-13 19:42:47 -05:00
self . emulator = self . domain . emulator or self . guest . emulator
self . machines = self . guest . all_machine_names ( self . domain )
2015-04-03 17:24:32 -04:00
2022-06-28 21:54:07 +01:00
def is_machine_alias ( self , src , tgt ) :
return self . guest . is_machine_alias ( self . domain , src , tgt )
2015-04-03 17:24:32 -04:00
2015-04-03 12:40:16 -04:00
class Capabilities ( XMLBuilder ) :
def __init__ ( self , * args , * * kwargs ) :
XMLBuilder . __init__ ( self , * args , * * kwargs )
2017-07-17 11:29:54 -04:00
self . _cpu_models_cache = { }
2013-03-17 17:06:52 -04:00
2018-03-21 10:53:34 -04:00
XML_NAME = " capabilities "
2015-04-03 12:40:16 -04:00
host = XMLChildProperty ( _CapsHost , is_single = True )
guests = XMLChildProperty ( _CapsGuest )
############################
# Public XML building APIs #
############################
2018-02-13 19:42:47 -05:00
def _guestForOSType ( self , os_type , arch ) :
2015-04-03 12:40:16 -04:00
archs = [ arch ]
if arch is None :
archs = [ self . host . cpu . arch , None ]
for a in archs :
for g in self . guests :
2018-02-13 19:42:47 -05:00
if ( ( os_type is None or g . os_type == os_type ) and
2015-04-03 12:40:16 -04:00
( a is None or g . arch == a ) ) :
return g
2018-02-13 19:42:47 -05: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
2023-09-17 17:34:22 +04:00
priority = [ " kvm " , " xen " , " hvf " , " qemu " ]
2018-02-13 19:42:47 -05:00
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 ]
2020-08-30 09:29:55 -04:00
def has_install_options ( self ) :
"""
Return True if there are any install options available
"""
for guest in self . guests :
if guest . domains :
return True
return False
2014-02-17 11:43:53 -05:00
def guest_lookup ( self , os_type = None , arch = None , typ = None , machine = None ) :
2013-07-06 14: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 07:17:31 -05: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 14:12:13 -04:00
2018-02-14 07:17:31 -05:00
: returns : A _CapsInfo object containing the found guest and domain
2013-07-06 14:12:13 -04:00
"""
2015-09-22 10:47:58 -04: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 11:43:53 -05:00
guest = self . _guestForOSType ( os_type , arch )
2013-07-06 14:12:13 -04:00
if not guest :
2020-09-17 08:44:06 +02:00
if arch and os_type :
msg = ( _ ( " Host does not support virtualization type "
" ' %(virttype)s ' for architecture ' %(arch)s ' " ) %
{ ' virttype ' : os_type , ' arch ' : arch } )
elif arch :
msg = ( _ ( " Host does not support any virtualization options "
" for architecture ' %(arch)s ' " ) %
{ ' arch ' : arch } )
elif os_type :
msg = ( _ ( " Host does not support virtualization type "
" ' %(virttype)s ' " ) %
{ ' virttype ' : os_type } )
else :
msg = _ ( " Host does not support any virtualization options " )
raise ValueError ( msg )
2013-07-06 14:12:13 -04:00
2018-02-13 19:42:47 -05:00
domain = self . _bestDomainType ( guest , typ , machine )
2013-07-06 14:12:13 -04:00
if domain is None :
2020-09-17 08:44:06 +02:00
if machine :
msg = ( _ ( " Host does not support domain type %(domain)s with "
" machine ' %(machine)s ' for virtualization type "
" ' %(virttype)s ' with architecture ' %(arch)s ' " ) %
{ ' domain ' : typ , ' virttype ' : guest . os_type ,
' arch ' : guest . arch , ' machine ' : machine } )
else :
msg = ( _ ( " Host does not support domain type %(domain)s for "
" virtualization type ' %(virttype)s ' with "
" architecture ' %(arch)s ' " ) %
{ ' domain ' : typ , ' virttype ' : guest . os_type ,
' arch ' : guest . arch } )
raise ValueError ( msg )
2013-07-06 14:12:13 -04:00
2018-09-06 19:07:15 -04:00
capsinfo = _CapsInfo ( self . conn , guest , domain )
2015-04-03 17:24:32 -04:00
return capsinfo