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
#
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
2018-10-12 01:52:45 +03:00
import pwd
2013-03-18 01:06:52 +04:00
2019-06-17 04:12:39 +03:00
from . logger import log
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
2022-02-16 18:38:30 +03:00
class _CapsCPU ( XMLBuilder ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " cpu "
2022-02-16 18:38:30 +03:00
arch = XMLProperty ( " ./arch " )
model = XMLProperty ( " ./model " )
2024-08-26 12:35:20 +03:00
vendor = XMLProperty ( " ./vendor " )
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 )
2013-03-18 01:06:52 +04:00
2018-10-12 01:52:45 +03: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 21:15:50 +03:00
continue # pragma: no cover
2018-10-12 01:52:45 +03: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-31 00:17:42 +03:00
except Exception :
2019-06-17 04:12:39 +03:00
log . debug ( " Exception parsing qemu dac baselabel= %s " ,
2018-10-12 01:52:45 +03:00
label , exc_info = True )
2020-01-31 00:17:42 +03:00
return None , None
2018-10-12 01:52:45 +03:00
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 )
2024-01-23 14:02:43 +03:00
externalSnapshot = XMLProperty ( " ./externalSnapshot " , is_bool = 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
2020-07-24 18:11:38 +03:00
the guest + domain combo but avoiding duplicates
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 )
2020-07-24 18:11:38 +03:00
if m . canonical and m . canonical not in ret :
2024-09-10 20:13:46 +03:00
ret . append ( m . canonical ) # pragma: no cover
2018-02-14 03:42:47 +03:00
return ret
2015-04-03 19:40:16 +03:00
2022-06-28 23:54:07 +03: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 13:06:48 +03: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 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 )
2024-01-23 14:02:43 +03:00
def supports_externalSnapshot ( self ) :
"""
Return True if capabilities report support for external snapshots
"""
return bool ( self . features . externalSnapshot )
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
"""
2018-09-07 02:07:15 +03:00
def __init__ ( self , conn , guest , domain ) :
2015-04-04 00:33:45 +03:00
self . conn = conn
2015-07-10 13:06:48 +03:00
self . guest = guest
self . domain = domain
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
2022-06-28 23:54:07 +03:00
def is_machine_alias ( self , src , tgt ) :
return self . guest . is_machine_alias ( self . domain , src , tgt )
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 )
############################
# 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
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
2023-09-17 16:34:22 +03:00
priority = [ " kvm " , " xen " , " hvf " , " qemu " ]
2018-02-14 03:42:47 +03: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 16:29:55 +03: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 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 :
2020-09-17 09:44:06 +03: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 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 :
2020-09-17 09:44:06 +03: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 22:12:13 +04:00
2018-09-07 02:07:15 +03:00
capsinfo = _CapsInfo ( self . conn , guest , domain )
2015-04-04 00:24:32 +03:00
return capsinfo