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>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
2013-10-28 00:59:47 +04:00
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
2013-03-18 01:06:52 +04:00
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA.
2015-09-16 23:55:37 +03:00
import logging
import os
2013-03-18 01:06:52 +04:00
import re
2015-04-03 19:40:16 +03:00
from . cpu import CPU as 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
##########################
# CPU model list objects #
##########################
2013-03-18 01:06:52 +04:00
2015-04-03 23:09:25 +03:00
class _CPUMapModel ( XMLBuilder ) :
2013-03-18 01:06:52 +04:00
"""
2015-04-03 23:09:25 +03:00
Single < model > instance from cpu_map . xml
2013-03-18 01:06:52 +04:00
"""
2015-04-03 23:09:25 +03:00
_XML_ROOT_NAME = " model "
name = XMLProperty ( " ./@name " )
2013-03-18 01:06:52 +04:00
2015-04-03 23:09:25 +03:00
class _CPUMapArch ( XMLBuilder ) :
2013-03-18 01:06:52 +04:00
"""
2015-04-03 23:09:25 +03:00
Single < arch > instance of valid CPU from cpu_map . xml
2013-03-18 01:06:52 +04:00
"""
2015-04-03 23:09:25 +03:00
_XML_ROOT_NAME = " arch "
arch = XMLProperty ( " ./@name " )
models = XMLChildProperty ( _CPUMapModel )
2014-03-13 15:52:51 +04:00
2015-04-03 23:09:25 +03:00
class _CPUMapFileValues ( XMLBuilder ) :
2014-03-13 15:52:51 +04:00
"""
Fallback method to lists cpu models , parsed directly from libvirt ' s local
cpu_map . xml
"""
2015-04-03 23:09:25 +03:00
# This is overwritten as part of the test suite
2014-04-04 21:13:20 +04:00
_cpu_filename = " /usr/share/libvirt/cpu_map.xml "
2015-04-03 23:09:25 +03:00
def __init__ ( self , conn ) :
2015-09-16 23:55:37 +03:00
if os . path . exists ( self . _cpu_filename ) :
xml = file ( self . _cpu_filename ) . read ( )
else :
xml = None
logging . debug ( " CPU map file not found: %s " , self . _cpu_filename )
2015-04-03 23:09:25 +03:00
XMLBuilder . __init__ ( self , conn , parsexml = xml )
2013-03-18 01:06:52 +04:00
2015-04-03 23:09:25 +03:00
self . _archmap = { }
2013-03-18 01:06:52 +04:00
2015-04-03 23:09:25 +03:00
_cpuvalues = XMLChildProperty ( _CPUMapArch )
2014-04-10 19:15:55 +04:00
2013-03-18 01:06:52 +04:00
2015-04-03 23:09:25 +03:00
##############
# Public API #
##############
2013-03-18 01:06:52 +04:00
2015-04-03 23:09:25 +03:00
def get_cpus ( self , arch ) :
2013-03-18 01:06:52 +04:00
if re . match ( r ' i[4-9]86 ' , arch ) :
arch = " x86 "
elif arch == " x86_64 " :
arch = " x86 "
2015-04-03 23:09:25 +03:00
cpumap = self . _archmap . get ( arch )
2013-03-18 01:06:52 +04:00
if not cpumap :
2015-04-03 23:09:25 +03:00
for vals in self . _cpuvalues :
if vals . arch == arch :
cpumap = vals
2013-03-18 01:06:52 +04:00
2015-04-03 23:09:25 +03:00
if not cpumap :
# Create a stub object
cpumap = _CPUMapArch ( self . conn )
self . _archmap [ arch ] = cpumap
return [ m . name for m in cpumap . models ]
class _CPUAPIValues ( object ) :
"""
Lists valid values for cpu models obtained from libvirt ' s getCPUModelNames
"""
def __init__ ( self , conn ) :
self . conn = conn
self . _cpus = None
def get_cpus ( self , arch ) :
if self . _cpus is not None :
return self . _cpus
2013-03-18 01:06:52 +04:00
2015-04-03 23:09:25 +03:00
if self . conn . check_support ( self . conn . SUPPORT_CONN_CPU_MODEL_NAMES ) :
names = self . conn . getCPUModelNames ( arch , 0 )
# Bindings were broke for a long time, so catch -1
if names != - 1 :
self . _cpus = names
return self . _cpus
return [ ]
###################################
# capabilities host <cpu> parsing #
###################################
2013-04-13 22:34:52 +04:00
2015-04-03 19:40:16 +03:00
class _CapsCPU ( DomainCPU ) :
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 ) :
_XML_ROOT_NAME = " cpu "
id = XMLProperty ( " ./@id " )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
class _TopologyCell ( XMLBuilder ) :
_XML_ROOT_NAME = " cell "
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 ) :
_XML_ROOT_NAME = " topology "
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 ) :
_XML_ROOT_NAME = " baselabel "
type = XMLProperty ( " ./@type " )
content = XMLProperty ( " . " )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
class _CapsSecmodel ( XMLBuilder ) :
_XML_ROOT_NAME = " secmodel "
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 ) :
_XML_ROOT_NAME = " host "
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 ) :
_XML_ROOT_NAME = " machine "
name = XMLProperty ( " . " )
canonical = XMLProperty ( " ./@canonical " )
2014-09-24 00:05:48 +04:00
2015-04-03 19:40:16 +03:00
class _CapsDomain ( XMLBuilder ) :
def __init__ ( self , * args , * * kwargs ) :
XMLBuilder . __init__ ( self , * args , * * kwargs )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
self . machines = [ ]
for m in self . _machines :
self . machines . append ( m . name )
if m . canonical :
self . machines . append ( m . canonical )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
_XML_ROOT_NAME = " domain "
hypervisor_type = XMLProperty ( " ./@type " )
emulator = XMLProperty ( " ./emulator " )
_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 ) :
_XML_ROOT_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 ) :
def __init__ ( self , * args , * * kwargs ) :
XMLBuilder . __init__ ( self , * args , * * kwargs )
2013-04-13 22:34:52 +04:00
2015-04-04 00:24:32 +03:00
machines = [ ]
2015-04-03 19:40:16 +03:00
for m in self . _machines :
2015-04-04 00:24:32 +03:00
machines . append ( m . name )
2015-04-03 19:40:16 +03:00
if m . canonical :
2015-04-04 00:24:32 +03:00
machines . append ( m . canonical )
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
for d in self . domains :
if not d . emulator :
2015-04-04 00:24:32 +03:00
d . emulator = self . _emulator
2015-04-03 19:40:16 +03:00
if not d . machines :
2015-04-04 00:24:32 +03:00
d . machines = machines
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
_XML_ROOT_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 " )
2015-04-04 00:24:32 +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 )
_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
2015-04-03 19:40:16 +03:00
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 :
if dtype and d . hypervisor_type != dtype . lower ( ) :
continue
if machine and machine not in d . machines :
continue
2013-03-18 01:06:52 +04:00
2015-04-03 19:40:16 +03:00
domains . append ( d )
if not domains :
return None
2015-04-03 23:26:58 +03:00
priority = [ " kvm " , " xen " , " qemu " ]
2015-04-03 19:40:16 +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 ]
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
2015-07-10 13:06:48 +03:00
self . emulator = self . domain . emulator
self . machines = self . domain . machines [ : ]
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.
if not self . conn . is_test ( ) and not self . conn . is_qemu ( ) :
return None
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 ) :
2015-04-03 23:09:25 +03:00
# Set by the test suite to force a particular code path
_force_cpumap = False
2015-04-03 19:40:16 +03:00
def __init__ ( self , * args , * * kwargs ) :
XMLBuilder . __init__ ( self , * args , * * kwargs )
2013-03-18 01:06:52 +04:00
self . _cpu_values = None
2015-04-03 19:40:16 +03:00
_XML_ROOT_NAME = " capabilities "
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 [ ]
2014-03-13 15:52:51 +04:00
if self . _cpu_values :
2015-04-03 23:09:25 +03:00
return self . _cpu_values . get_cpus ( arch )
order = [ _CPUAPIValues , _CPUMapFileValues ]
if self . _force_cpumap :
order = [ _CPUMapFileValues ]
2013-03-18 01:06:52 +04:00
2014-03-13 15:52:51 +04:00
# Iterate over the available methods until a set of CPU models is found
2015-04-03 23:09:25 +03:00
for mode in order :
cpu_values = mode ( self . conn )
cpus = cpu_values . get_cpus ( arch )
2014-03-20 21:48:49 +04:00
if len ( cpus ) > 0 :
2014-03-13 15:52:51 +04:00
self . _cpu_values = cpu_values
return cpus
2013-03-18 01:06:52 +04:00
2014-03-13 15:52:51 +04:00
return [ ]
2013-04-13 22:34:52 +04:00
2015-04-03 19:40:16 +03:00
############################
# 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
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 .
@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
2015-04-04 00:24:32 +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 " ) %
{ ' virttype ' : osstr , ' arch ' : archstr } )
2014-02-17 20:43:53 +04:00
domain = guest . bestDomainType ( dtype = typ , machine = 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 )