2013-03-17 17:06:52 -04:00
#
2013-10-27 21:59:46 +01:00
# Copyright 2010, 2013 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-09-02 11:38:12 -04:00
import logging
2018-03-20 15:10:04 -04:00
from . . xmlbuilder import XMLBuilder , XMLProperty , XMLChildProperty
2013-03-17 17:06:52 -04:00
2013-04-13 14:34:52 -04:00
virtinst: Add NUMA distance support
Now that libvirt has support for administration of distances between NUMA cells
it would be nice to be able to set those with virt-install directly instead of
having to 'virsh edit' the domain XML manually after installation.
For example
--cpu cell0.memory=1234,cell0.cpus=0-3,cell1.memory=5678,cell1.cpus=4-7,\
cell0.distances.sibling0.id=0,cell0.distances.sibling0.value=10,\
cell0.distances.sibling1.id=1,cell0.distances.sibling1.value=21,\
cell1.distances.sibling0.id=0,cell1.distances.sibling0.value=21,\
cell1.distances.sibling1.id=1,cell1.distances.sibling1.value=10
would generate the following XML:
<cpu>
<numa>
<cell cpus="0-3" memory="1234">
<distances>
<sibling id="0" value="10"/>
<sibling id="1" value="21"/>
</distances>
</cell>
<cell cpus="4-7" memory="5678">
<distances>
<sibling id="0" value="21"/>
<sibling id="1" value="10"/>
</distances>
</cell>
</numa>
</cpu>
Signed-off-by: Menno Lageman <menno.lageman@oracle.com>
(crobinso: rework cli format, drop some validation, drop man changes)
2018-01-22 13:36:00 +01:00
class _CPUCellSibling ( XMLBuilder ) :
"""
Class for generating < distances > < sibling > nodes
"""
2018-03-21 10:53:34 -04:00
XML_NAME = " sibling "
virtinst: Add NUMA distance support
Now that libvirt has support for administration of distances between NUMA cells
it would be nice to be able to set those with virt-install directly instead of
having to 'virsh edit' the domain XML manually after installation.
For example
--cpu cell0.memory=1234,cell0.cpus=0-3,cell1.memory=5678,cell1.cpus=4-7,\
cell0.distances.sibling0.id=0,cell0.distances.sibling0.value=10,\
cell0.distances.sibling1.id=1,cell0.distances.sibling1.value=21,\
cell1.distances.sibling0.id=0,cell1.distances.sibling0.value=21,\
cell1.distances.sibling1.id=1,cell1.distances.sibling1.value=10
would generate the following XML:
<cpu>
<numa>
<cell cpus="0-3" memory="1234">
<distances>
<sibling id="0" value="10"/>
<sibling id="1" value="21"/>
</distances>
</cell>
<cell cpus="4-7" memory="5678">
<distances>
<sibling id="0" value="21"/>
<sibling id="1" value="10"/>
</distances>
</cell>
</numa>
</cpu>
Signed-off-by: Menno Lageman <menno.lageman@oracle.com>
(crobinso: rework cli format, drop some validation, drop man changes)
2018-01-22 13:36:00 +01:00
_XML_PROP_ORDER = [ " id " , " value " ]
id = XMLProperty ( " ./@id " , is_int = True )
value = XMLProperty ( " ./@value " , is_int = True )
2016-06-14 18:19:09 -04:00
class _CPUCell ( XMLBuilder ) :
"""
Class for generating < cpu > < numa > child < cell > XML
"""
2018-03-21 10:53:34 -04:00
XML_NAME = " cell "
2016-06-14 18:19:09 -04:00
_XML_PROP_ORDER = [ " id " , " cpus " , " memory " ]
id = XMLProperty ( " ./@id " , is_int = True )
2017-05-04 13:03:34 +02:00
cpus = XMLProperty ( " ./@cpus " )
2016-06-14 18:19:09 -04:00
memory = XMLProperty ( " ./@memory " , is_int = True )
virtinst: Add NUMA distance support
Now that libvirt has support for administration of distances between NUMA cells
it would be nice to be able to set those with virt-install directly instead of
having to 'virsh edit' the domain XML manually after installation.
For example
--cpu cell0.memory=1234,cell0.cpus=0-3,cell1.memory=5678,cell1.cpus=4-7,\
cell0.distances.sibling0.id=0,cell0.distances.sibling0.value=10,\
cell0.distances.sibling1.id=1,cell0.distances.sibling1.value=21,\
cell1.distances.sibling0.id=0,cell1.distances.sibling0.value=21,\
cell1.distances.sibling1.id=1,cell1.distances.sibling1.value=10
would generate the following XML:
<cpu>
<numa>
<cell cpus="0-3" memory="1234">
<distances>
<sibling id="0" value="10"/>
<sibling id="1" value="21"/>
</distances>
</cell>
<cell cpus="4-7" memory="5678">
<distances>
<sibling id="0" value="21"/>
<sibling id="1" value="10"/>
</distances>
</cell>
</numa>
</cpu>
Signed-off-by: Menno Lageman <menno.lageman@oracle.com>
(crobinso: rework cli format, drop some validation, drop man changes)
2018-01-22 13:36:00 +01:00
siblings = XMLChildProperty ( _CPUCellSibling , relative_xpath = " ./distances " )
2016-06-14 18:19:09 -04:00
2018-03-20 15:10:04 -04:00
class _CPUCache ( XMLBuilder ) :
2017-09-14 12:09:27 +08:00
"""
Class for generating < cpu > child < cache > XML
"""
2018-03-21 10:53:34 -04:00
XML_NAME = " cache "
2017-09-14 12:09:27 +08:00
_XML_PROP_ORDER = [ " mode " , " level " ]
mode = XMLProperty ( " ./@mode " )
level = XMLProperty ( " ./@level " , is_int = True )
2018-03-20 15:10:04 -04:00
class _CPUFeature ( XMLBuilder ) :
2013-03-17 17:06:52 -04:00
"""
Class for generating < cpu > child < feature > XML
"""
2018-03-21 10:53:34 -04:00
XML_NAME = " feature "
2014-01-31 13:44:50 -05:00
_XML_PROP_ORDER = [ " policy " , " name " ]
2013-03-17 17:06:52 -04:00
2013-09-10 18:32:10 -04:00
name = XMLProperty ( " ./@name " )
policy = XMLProperty ( " ./@policy " )
2013-03-17 17:06:52 -04:00
2018-03-20 15:10:04 -04:00
class DomainCpu ( XMLBuilder ) :
2013-03-17 17:06:52 -04:00
"""
Class for generating < cpu > XML
"""
2018-03-21 10:53:34 -04:00
XML_NAME = " cpu "
2019-03-14 10:46:02 +01:00
_XML_PROP_ORDER = [ " mode " , " match " , " model " , " vendor " ,
2013-09-10 18:32:10 -04:00
" sockets " , " cores " , " threads " , " features " ]
2013-03-17 17:06:52 -04:00
2019-03-29 10:59:25 +01:00
secure = True
2014-02-05 13:51:53 -05:00
special_mode_was_set = False
# These values are exposed on the command line, so are stable API
SPECIAL_MODE_HOST_MODEL_ONLY = " host-model-only "
SPECIAL_MODE_HV_DEFAULT = " hv-default "
SPECIAL_MODE_HOST_COPY = " host-copy "
SPECIAL_MODE_HOST_MODEL = " host-model "
SPECIAL_MODE_HOST_PASSTHROUGH = " host-passthrough "
SPECIAL_MODE_CLEAR = " clear "
2018-10-04 12:23:32 -04:00
SPECIAL_MODE_APP_DEFAULT = " default "
2014-02-05 13:51:53 -05:00
SPECIAL_MODES = [ SPECIAL_MODE_HOST_MODEL_ONLY , SPECIAL_MODE_HV_DEFAULT ,
SPECIAL_MODE_HOST_COPY , SPECIAL_MODE_HOST_MODEL ,
2018-10-04 12:23:32 -04:00
SPECIAL_MODE_HOST_PASSTHROUGH , SPECIAL_MODE_CLEAR ,
SPECIAL_MODE_APP_DEFAULT ]
2018-10-04 14:19:32 -04:00
def set_special_mode ( self , guest , val ) :
if val == self . SPECIAL_MODE_APP_DEFAULT :
# If libvirt is new enough to support reliable mode=host-model
# then use it, otherwise use previous default HOST_MODEL_ONLY
domcaps = guest . lookup_domcaps ( )
val = self . SPECIAL_MODE_HOST_MODEL_ONLY
if domcaps . supports_safe_host_model ( ) :
val = self . SPECIAL_MODE_HOST_MODEL
2014-02-05 13:51:53 -05:00
if ( val == self . SPECIAL_MODE_HOST_MODEL or
val == self . SPECIAL_MODE_HOST_PASSTHROUGH ) :
self . model = None
2015-03-26 17:08:15 -04:00
self . vendor = None
self . model_fallback = None
for f in self . features :
2018-02-07 17:27:56 -05:00
self . remove_child ( f )
2014-02-05 13:51:53 -05:00
self . mode = val
elif val == self . SPECIAL_MODE_HOST_COPY :
2018-10-13 17:47:31 -04:00
self . copy_host_cpu ( guest )
2014-02-05 13:51:53 -05:00
elif ( val == self . SPECIAL_MODE_HV_DEFAULT or
val == self . SPECIAL_MODE_CLEAR ) :
self . clear ( )
elif val == self . SPECIAL_MODE_HOST_MODEL_ONLY :
if self . conn . caps . host . cpu . model :
2017-03-05 14:03:48 -05:00
self . clear ( )
2019-03-14 10:48:21 +01:00
self . set_model ( guest , self . conn . caps . host . cpu . model )
2014-02-05 13:51:53 -05:00
else :
raise RuntimeError ( " programming error: unknown "
" special cpu mode ' %s ' " % val )
self . special_mode_was_set = True
2019-03-14 10:48:21 +01:00
def _add_security_features ( self , guest ) :
domcaps = guest . lookup_domcaps ( )
for feature in domcaps . get_cpu_security_features ( ) :
exists = False
for f in self . features :
if f . name == feature :
exists = True
break
if not exists :
self . add_feature ( feature )
2019-04-03 15:23:20 +02:00
def check_security_features ( self , guest ) :
"""
Since ' secure ' property is not exported into the domain XML
we might need to refresh its state .
"""
domcaps = guest . lookup_domcaps ( )
features = domcaps . get_cpu_security_features ( )
if len ( features ) == 0 :
self . secure = False
return
2019-04-09 13:13:46 +02:00
guestFeatures = [ f . name for f in self . features if f . policy == " require " ]
2019-04-10 20:36:31 +02:00
if self . model :
if self . model . endswith ( " IBRS " ) :
guestFeatures . append ( " spec-ctrl " )
if self . model . endswith ( " IBPB " ) :
guestFeatures . append ( " ibpb " )
2019-04-09 13:13:46 +02:00
self . secure = set ( features ) < = set ( guestFeatures )
2019-04-03 15:23:20 +02:00
def _remove_security_features ( self , guest ) :
domcaps = guest . lookup_domcaps ( )
for feature in domcaps . get_cpu_security_features ( ) :
for f in self . features :
if f . name == feature and f . policy == " require " :
self . remove_child ( f )
break
2019-03-14 10:48:21 +01:00
def set_model ( self , guest , val ) :
2019-03-14 10:46:02 +01:00
logging . debug ( " setting cpu model %s " , val )
if val :
self . mode = " custom "
if not self . match :
self . match = " exact "
2019-03-29 10:59:25 +01:00
if self . secure :
self . _add_security_features ( guest )
else :
self . _remove_security_features ( guest )
2019-03-14 10:46:02 +01:00
self . model = val
2013-03-17 17:06:52 -04:00
def add_feature ( self , name , policy = " require " ) :
2018-02-07 17:27:56 -05:00
feature = self . features . add_new ( )
2013-09-10 18:32:10 -04:00
feature . name = name
2013-03-17 17:06:52 -04:00
feature . policy = policy
2018-03-20 15:10:04 -04:00
features = XMLChildProperty ( _CPUFeature )
2013-03-17 17:06:52 -04:00
2016-06-14 18:19:09 -04:00
cells = XMLChildProperty ( _CPUCell , relative_xpath = " ./numa " )
2019-05-11 15:05:36 -04:00
cache = XMLChildProperty ( _CPUCache , is_single = True )
2017-09-14 12:09:27 +08:00
2018-10-13 17:47:31 -04:00
def copy_host_cpu ( self , guest ) :
2013-03-17 17:06:52 -04:00
"""
2018-10-13 17:47:31 -04:00
Try to manually mimic host - model , copying all the info
preferably out of domcapabilities , but capabilities as fallback .
2013-03-17 17:06:52 -04:00
"""
2018-10-13 17:47:31 -04:00
domcaps = guest . lookup_domcaps ( )
if domcaps . supports_safe_host_model ( ) :
logging . debug ( " Using domcaps for host-copy " )
cpu = domcaps . cpu . get_mode ( " host-model " )
model = cpu . models [ 0 ] . model
fallback = cpu . models [ 0 ] . fallback
else :
cpu = self . conn . caps . host . cpu
model = cpu . model
fallback = None
if not model :
raise ValueError ( _ ( " No host CPU reported in capabilities " ) )
2013-03-17 17:06:52 -04:00
2013-04-11 13:45:45 +08:00
self . mode = " custom "
2013-03-17 17:06:52 -04:00
self . match = " exact "
2019-03-14 10:48:21 +01:00
self . set_model ( guest , model )
2018-10-13 17:47:31 -04:00
if fallback :
self . model_fallback = fallback
2013-03-17 17:06:52 -04:00
self . vendor = cpu . vendor
for feature in self . features :
2018-02-07 17:27:56 -05:00
self . remove_child ( feature )
2015-07-14 15:53:25 +02:00
for feature in cpu . features :
2018-10-13 17:47:31 -04:00
policy = getattr ( feature , " policy " , " require " )
self . add_feature ( feature . name , policy )
2013-03-17 17:06:52 -04:00
def vcpus_from_topology ( self ) :
"""
Determine the CPU count represented by topology , or 1 if
no topology is set
"""
self . set_topology_defaults ( )
2019-05-11 19:05:33 -04:00
if self . has_topology ( ) :
2013-03-17 17:06:52 -04:00
return self . sockets * self . cores * self . threads
return 1
2019-05-11 19:05:33 -04:00
def has_topology ( self ) :
"""
Return True if any topology info is set
"""
return bool ( self . sockets or self . cores or self . threads )
2013-03-17 17:06:52 -04:00
def set_topology_defaults ( self , vcpus = None ) :
"""
Fill in unset topology values , using the passed vcpus count if
required
"""
2019-05-11 19:05:33 -04:00
if not self . has_topology ( ) :
2013-03-17 17:06:52 -04:00
return
if vcpus is None :
if self . sockets is None :
self . sockets = 1
if self . threads is None :
self . threads = 1
if self . cores is None :
self . cores = 1
vcpus = int ( vcpus or 0 )
if not self . sockets :
if not self . cores :
2017-10-11 12:35:55 +01:00
self . sockets = vcpus / / self . threads
2013-03-17 17:06:52 -04:00
else :
2017-10-11 12:35:55 +01:00
self . sockets = vcpus / / self . cores
2013-03-17 17:06:52 -04:00
if not self . cores :
if not self . threads :
2017-10-11 12:35:55 +01:00
self . cores = vcpus / / self . sockets
2013-03-17 17:06:52 -04:00
else :
2017-10-11 12:35:55 +01:00
self . cores = vcpus / / ( self . sockets * self . threads )
2013-03-17 17:06:52 -04:00
if not self . threads :
2017-10-11 12:35:55 +01:00
self . threads = vcpus / / ( self . sockets * self . cores )
2013-03-17 17:06:52 -04:00
return
2013-07-17 17:11:18 -04:00
##################
# XML properties #
##################
2019-03-14 10:46:02 +01:00
model = XMLProperty ( " ./model " )
2015-03-26 17:08:15 -04:00
model_fallback = XMLProperty ( " ./model/@fallback " )
2013-07-17 17:11:18 -04:00
2013-09-19 13:27:30 -04:00
match = XMLProperty ( " ./@match " )
vendor = XMLProperty ( " ./vendor " )
mode = XMLProperty ( " ./@mode " )
2013-07-17 17:11:18 -04:00
2013-09-19 13:27:30 -04:00
sockets = XMLProperty ( " ./topology/@sockets " , is_int = True )
cores = XMLProperty ( " ./topology/@cores " , is_int = True )
threads = XMLProperty ( " ./topology/@threads " , is_int = True )
2018-09-02 11:38:12 -04:00
##################
# Default config #
##################
2018-10-04 12:22:22 -04:00
def _validate_default_host_model_only ( self , guest ) :
2018-09-02 11:38:12 -04:00
# It's possible that the value HOST_MODEL_ONLY gets from
# <capabilities> is not actually supported by qemu/kvm
# combo which will be reported in <domainCapabilities>
2018-10-04 12:22:22 -04:00
if not self . model :
return
domcaps = guest . lookup_domcaps ( )
2018-09-02 11:38:12 -04:00
domcaps_mode = domcaps . cpu . get_mode ( " custom " )
if not domcaps_mode :
return
cpu_model = domcaps_mode . get_model ( self . model )
2019-03-14 14:10:26 +01:00
if cpu_model and cpu_model . usable != " no " :
2018-09-02 11:38:12 -04:00
return
logging . debug ( " Host capabilities CPU ' %s ' is not supported "
" according to domain capabilities. Unsetting CPU model " ,
self . model )
self . model = None
2018-10-04 12:22:22 -04:00
def _set_cpu_x86_kvm_default ( self , guest ) :
if guest . os . arch != self . conn . caps . host . cpu . arch :
return
mode = guest . x86_cpu_default
2018-10-04 12:23:32 -04:00
2018-10-04 14:19:32 -04:00
self . set_special_mode ( guest , mode )
2018-10-04 12:22:22 -04:00
if mode == self . SPECIAL_MODE_HOST_MODEL_ONLY :
self . _validate_default_host_model_only ( guest )
2018-09-02 11:38:12 -04:00
def set_defaults ( self , guest ) :
self . set_topology_defaults ( guest . vcpus )
if not self . conn . is_test ( ) and not self . conn . is_qemu ( ) :
return
if ( self . get_xml ( ) . strip ( ) or
self . special_mode_was_set ) :
# User already configured CPU
return
if guest . os . is_arm_machvirt ( ) and guest . type == " kvm " :
self . mode = self . SPECIAL_MODE_HOST_PASSTHROUGH
elif guest . os . is_arm64 ( ) and guest . os . is_arm_machvirt ( ) :
# -M virt defaults to a 32bit CPU, even if using aarch64
2019-03-14 10:48:21 +01:00
self . set_model ( guest , " cortex-a57 " )
2018-09-02 11:38:12 -04:00
elif guest . os . is_x86 ( ) and guest . type == " kvm " :
self . _set_cpu_x86_kvm_default ( guest )
if guest . osinfo . broken_x2apic ( ) :
self . add_feature ( " x2apic " , policy = " disable " )