cli: --cpu: code cleanup & reordering

Shuffling bits of code around, adding comments and grouping CLI options
to make the code easier to read and understand at a glance.

Brings the ordering of XML options in line with libvirt's own output as
implemented in `src/conf/cpu_conf.c` and `src/conf/numa_conf.c`.
This commit is contained in:
Hugues Fafard 2021-08-04 12:00:13 +02:00 committed by Cole Robinson
parent 6a2bc142eb
commit cb97f6001b
3 changed files with 250 additions and 198 deletions

View File

@ -131,6 +131,7 @@
<cpu mode="custom" match="strict" check="partial">
<model>foobar</model>
<vendor>meee</vendor>
<cache level="3" mode="emulate"/>
<feature policy="force" name="x2apic"/>
<feature policy="force" name="x2apicagain"/>
<feature policy="require" name="reqtest"/>
@ -140,12 +141,6 @@
<feature policy="forbid" name="foo"/>
<feature policy="forbid" name="bar"/>
<numa>
<interconnects>
<bandwidth initiator="0" target="0" type="access" value="204800"/>
<bandwidth initiator="0" target="2" cache="1" type="access" value="409600" unit="KiB"/>
<latency initiator="0" target="0" type="access" value="5"/>
<latency initiator="0" target="2" cache="1" type="access" value="10" unit="ns"/>
</interconnects>
<cell id="0" cpus="1,2,3" memory="1024">
<distances>
<sibling id="0" value="10"/>
@ -158,14 +153,19 @@
<sibling id="1" value="10"/>
</distances>
</cell>
<cell id="2" cpus="4" memory="256" memAccess="shared" discard="no" unit="KiB">
<cell id="2" cpus="4" memory="256" unit="KiB" memAccess="shared" discard="no">
<cache level="1" associativity="direct" policy="writeback">
<size value="256" unit="KiB"/>
<line value="256" unit="KiB"/>
</cache>
</cell>
<interconnects>
<latency initiator="0" target="0" type="access" value="5"/>
<latency initiator="0" target="2" cache="1" type="access" value="10" unit="ns"/>
<bandwidth initiator="0" target="0" type="access" value="204800"/>
<bandwidth initiator="0" target="2" cache="1" type="access" value="409600" unit="KiB"/>
</interconnects>
</numa>
<cache mode="emulate" level="3"/>
</cpu>
<clock offset="utc">
<timer name="rtc" tickpolicy="merge"/>
@ -415,6 +415,7 @@
<cpu mode="custom" match="strict" check="partial">
<model>foobar</model>
<vendor>meee</vendor>
<cache level="3" mode="emulate"/>
<feature policy="force" name="x2apic"/>
<feature policy="force" name="x2apicagain"/>
<feature policy="require" name="reqtest"/>
@ -424,12 +425,6 @@
<feature policy="forbid" name="foo"/>
<feature policy="forbid" name="bar"/>
<numa>
<interconnects>
<bandwidth initiator="0" target="0" type="access" value="204800"/>
<bandwidth initiator="0" target="2" cache="1" type="access" value="409600" unit="KiB"/>
<latency initiator="0" target="0" type="access" value="5"/>
<latency initiator="0" target="2" cache="1" type="access" value="10" unit="ns"/>
</interconnects>
<cell id="0" cpus="1,2,3" memory="1024">
<distances>
<sibling id="0" value="10"/>
@ -442,14 +437,19 @@
<sibling id="1" value="10"/>
</distances>
</cell>
<cell id="2" cpus="4" memory="256" memAccess="shared" discard="no" unit="KiB">
<cell id="2" cpus="4" memory="256" unit="KiB" memAccess="shared" discard="no">
<cache level="1" associativity="direct" policy="writeback">
<size value="256" unit="KiB"/>
<line value="256" unit="KiB"/>
</cache>
</cell>
<interconnects>
<latency initiator="0" target="0" type="access" value="5"/>
<latency initiator="0" target="2" cache="1" type="access" value="10" unit="ns"/>
<bandwidth initiator="0" target="0" type="access" value="204800"/>
<bandwidth initiator="0" target="2" cache="1" type="access" value="409600" unit="KiB"/>
</interconnects>
</numa>
<cache mode="emulate" level="3"/>
</cpu>
<clock offset="utc">
<timer name="rtc" tickpolicy="merge"/>

View File

@ -2221,6 +2221,11 @@ class ParserCPU(VirtCLIParser):
"numa.cell[0-9]*.memory": "cell[0-9]*.memory",
}
###################
# Special parsing #
###################
def _convert_old_feature_options(self):
# For old CLI compat, --cpu force=foo,force=bar should force
# enable 'foo' and 'bar' features, but that doesn't fit with the
@ -2254,9 +2259,9 @@ class ParserCPU(VirtCLIParser):
return super()._parse(inst)
###################
# Option handling #
###################
#################################
# Option multi-instance finders #
#################################
def cell_find_inst_cb(self, *args, **kwargs):
cliarg = "cell" # cell[0-9]*
@ -2294,6 +2299,11 @@ class ParserCPU(VirtCLIParser):
cb = self._make_find_inst_cb(cliarg, list_propname)
return cb(*args, **kwargs)
#############################
# Option handling callbacks #
#############################
def set_model_cb(self, inst, val, virtarg):
if val == "host":
val = inst.SPECIAL_MODE_HOST_MODEL
@ -2330,16 +2340,21 @@ class ParserCPU(VirtCLIParser):
# overridden
cls.add_arg("model.fallback", "model_fallback")
cls.add_arg("model.vendor_id", "model_vendor_id")
cls.add_arg("vendor", "vendor")
cls.add_arg("mode", "mode")
cls.add_arg("match", "match")
cls.add_arg("check", "check")
cls.add_arg("migratable", "migratable", is_onoff=True)
cls.add_arg("vendor", "vendor")
cls.add_arg("cache.mode", "cache.mode")
cls.add_arg("topology.sockets", "topology.sockets")
cls.add_arg("topology.dies", "topology.dies")
cls.add_arg("topology.cores", "topology.cores")
cls.add_arg("topology.threads", "topology.threads")
cls.add_arg("cache.level", "cache.level")
cls.add_arg("cache.mode", "cache.mode")
# CPU features
# These are handled specially in _parse
cls.add_arg("force", None, lookup_cb=None, cb=cls.set_feature_cb)
cls.add_arg("require", None, lookup_cb=None, cb=cls.set_feature_cb)
@ -2347,30 +2362,24 @@ class ParserCPU(VirtCLIParser):
cls.add_arg("disable", None, lookup_cb=None, cb=cls.set_feature_cb)
cls.add_arg("forbid", None, lookup_cb=None, cb=cls.set_feature_cb)
cls.add_arg("topology.sockets", "topology.sockets")
cls.add_arg("topology.dies", "topology.dies")
cls.add_arg("topology.cores", "topology.cores")
cls.add_arg("topology.threads", "topology.threads")
# Options for CPU.cells config
# NUMA cells
cls.add_arg("numa.cell[0-9]*.id", "id",
find_inst_cb=cls.cell_find_inst_cb)
cls.add_arg("numa.cell[0-9]*.cpus", "cpus", can_comma=True,
find_inst_cb=cls.cell_find_inst_cb)
cls.add_arg("numa.cell[0-9]*.memory", "memory",
find_inst_cb=cls.cell_find_inst_cb)
cls.add_arg("numa.cell[0-9]*.unit", "unit",
find_inst_cb=cls.cell_find_inst_cb)
cls.add_arg("numa.cell[0-9]*.memAccess", "memAccess",
find_inst_cb=cls.cell_find_inst_cb)
cls.add_arg("numa.cell[0-9]*.discard", "discard",
find_inst_cb=cls.cell_find_inst_cb, is_onoff=True)
cls.add_arg("numa.cell[0-9]*.memory", "memory",
find_inst_cb=cls.cell_find_inst_cb)
cls.add_arg("numa.cell[0-9]*.unit", "unit",
find_inst_cb=cls.cell_find_inst_cb)
cls.add_arg("numa.cell[0-9]*.distances.sibling[0-9]*.id", "id",
find_inst_cb=cls.cell_sibling_find_inst_cb)
cls.add_arg("numa.cell[0-9]*.distances.sibling[0-9]*.value", "value",
find_inst_cb=cls.cell_sibling_find_inst_cb)
# NUMA cell caches
cls.add_arg("numa.cell[0-9]*.cache[0-9]*.level", "level",
find_inst_cb=cls.cell_cache_find_inst_cb)
cls.add_arg("numa.cell[0-9]*.cache[0-9]*.associativity", "associativity",
@ -2386,6 +2395,7 @@ class ParserCPU(VirtCLIParser):
cls.add_arg("numa.cell[0-9]*.cache[0-9]*.line.unit", "line_unit",
find_inst_cb=cls.cell_cache_find_inst_cb)
# Interconnections between NUMA cells
cls.add_arg("numa.interconnects.latency[0-9]*.initiator", "initiator",
find_inst_cb=cls.latency_find_inst_cb)
cls.add_arg("numa.interconnects.latency[0-9]*.target", "target",
@ -2398,7 +2408,6 @@ class ParserCPU(VirtCLIParser):
find_inst_cb=cls.latency_find_inst_cb)
cls.add_arg("numa.interconnects.latency[0-9]*.unit", "unit",
find_inst_cb=cls.latency_find_inst_cb)
cls.add_arg("numa.interconnects.bandwidth[0-9]*.initiator", "initiator",
find_inst_cb=cls.bandwidth_find_inst_cb)
cls.add_arg("numa.interconnects.bandwidth[0-9]*.target", "target",

View File

@ -10,118 +10,13 @@ from ..xmlbuilder import XMLBuilder, XMLProperty, XMLChildProperty
from .. import xmlutil
class _NUMACellSibling(XMLBuilder):
"""
Class for generating <cpu><numa><cell><distances> child nodes <sibling>,
describing the distances to other NUMA cells.
"""
XML_NAME = "sibling"
_XML_PROP_ORDER = ["id", "value"]
id = XMLProperty("./@id", is_int=True)
value = XMLProperty("./@value", is_int=True)
class _NUMACellCache(XMLBuilder):
"""
Class for generating <cpu><numa><cell> child nodes <cache>, describing
caches for NUMA cells.
"""
XML_NAME = "cache"
_XML_PROP_ORDER = ["level", "associativity", "policy",
"size_value", "size_unit", "line_value", "line_unit"]
level = XMLProperty("./@level", is_int=True)
associativity = XMLProperty("./@associativity")
policy = XMLProperty("./@policy")
size_value = XMLProperty("./size/@value", is_int=True)
size_unit = XMLProperty("./size/@unit")
line_value = XMLProperty("./line/@value", is_int=True)
line_unit = XMLProperty("./line/@unit")
class _NUMACell(XMLBuilder):
"""
Class for generating <cpu><numa> child nodes <cell> XML, describing NUMA
cells.
"""
XML_NAME = "cell"
_XML_PROP_ORDER = ["id", "cpus", "memory", "memAccess", "discard"]
id = XMLProperty("./@id", is_int=True)
cpus = XMLProperty("./@cpus")
memory = XMLProperty("./@memory", is_int=True)
unit = XMLProperty("./@unit")
memAccess = XMLProperty("./@memAccess")
discard = XMLProperty("./@discard", is_yesno=True)
siblings = XMLChildProperty(_NUMACellSibling, relative_xpath="./distances")
caches = XMLChildProperty(_NUMACellCache)
class _NUMALatency(XMLBuilder):
"""
Class for generating <cpu><numa><cell><interconnects> child nodes
<latency>, describing latency between two NUMA memory nodes.
"""
XML_NAME = "latency"
_XML_PROP_ORDER = ["initiator", "target", "cache", "type", "value", "unit"]
initiator = XMLProperty("./@initiator", is_int=True)
target = XMLProperty("./@target", is_int=True)
cache = XMLProperty("./@cache", is_int=True)
type = XMLProperty("./@type")
value = XMLProperty("./@value", is_int=True)
unit = XMLProperty("./@unit")
class _NUMABandwidth(XMLBuilder):
"""
Class for generating <cpu><numa><cell><interconnects> child nodes
<bandwidth>, describing bandwidth between two NUMA memory nodes.
"""
XML_NAME = "bandwidth"
_XML_PROP_ORDER = ["initiator", "target", "cache", "type", "value", "unit"]
# Note: The documentation only mentions <latency> nodes havin a cache=
# attribute, but <bandwidth> and <latency> nodes are otherwise identical
# and libvirt will happily accept XML with a cache= attribute on
# <bandwidth> nodes as well, so let's leave it here for now.
initiator = XMLProperty("./@initiator", is_int=True)
target = XMLProperty("./@target", is_int=True)
cache = XMLProperty("./@cache", is_int=True)
type = XMLProperty("./@type")
value = XMLProperty("./@value", is_int=True)
unit = XMLProperty("./@unit")
class _CPUCache(XMLBuilder):
"""
Class for generating <cpu> child <cache> XML
"""
XML_NAME = "cache"
_XML_PROP_ORDER = ["mode", "level"]
mode = XMLProperty("./@mode")
level = XMLProperty("./@level", is_int=True)
class _CPUFeature(XMLBuilder):
"""
Class for generating <cpu> child <feature> XML
"""
XML_NAME = "feature"
_XML_PROP_ORDER = ["policy", "name"]
name = XMLProperty("./@name")
policy = XMLProperty("./@policy")
###################################
# Misc child nodes for CPU domain #
###################################
class _CPUTopology(XMLBuilder):
"""
Class for generating <cpu> <topology> XML
Class for generating XML for <cpu> child node <topology>.
"""
XML_NAME = "topology"
_XML_PROP_ORDER = ["sockets", "dies", "cores", "threads"]
@ -152,16 +47,177 @@ class _CPUTopology(XMLBuilder):
return
# Note: CPU cache is weird. The documentation implies that multiples instances
# can be declared, one for each cache level one wishes to define. However,
# libvirt doesn't accept more than one <cache> element, so it's implemented
# with `is_single=True` for now (see actual CPU Domain below).
class _CPUCache(XMLBuilder):
"""
Class for generating XML for <cpu> child node <cache>.
"""
XML_NAME = "cache"
_XML_PROP_ORDER = ["level", "mode"]
level = XMLProperty("./@level", is_int=True)
mode = XMLProperty("./@mode")
class _CPUFeature(XMLBuilder):
"""
Class for generating XML for <cpu> child nodes <feature>.
"""
XML_NAME = "feature"
_XML_PROP_ORDER = ["policy", "name"]
name = XMLProperty("./@name")
policy = XMLProperty("./@policy")
##############
# NUMA cells #
##############
class _NUMACellSibling(XMLBuilder):
"""
Class for generating XML for <cpu><numa><cell><distances> child nodes
<sibling>, describing the distances to other NUMA cells.
"""
XML_NAME = "sibling"
_XML_PROP_ORDER = ["id", "value"]
id = XMLProperty("./@id", is_int=True)
value = XMLProperty("./@value", is_int=True)
class _NUMACellCache(XMLBuilder):
"""
Class for generating XML for <cpu><numa><cell> child nodes <cache>,
describing caches for NUMA cells.
"""
XML_NAME = "cache"
_XML_PROP_ORDER = ["level", "associativity", "policy",
"size_value", "size_unit", "line_value", "line_unit"]
level = XMLProperty("./@level", is_int=True)
associativity = XMLProperty("./@associativity")
policy = XMLProperty("./@policy")
size_value = XMLProperty("./size/@value", is_int=True)
size_unit = XMLProperty("./size/@unit")
line_value = XMLProperty("./line/@value", is_int=True)
line_unit = XMLProperty("./line/@unit")
class _NUMACell(XMLBuilder):
"""
Class for generating XML for <cpu><numa> child nodes <cell> XML, describing
NUMA cells.
"""
XML_NAME = "cell"
_XML_PROP_ORDER = ["id", "cpus", "memory", "unit", "memAccess", "discard",
"siblings", "caches"]
id = XMLProperty("./@id", is_int=True)
cpus = XMLProperty("./@cpus")
memory = XMLProperty("./@memory", is_int=True)
unit = XMLProperty("./@unit")
memAccess = XMLProperty("./@memAccess")
discard = XMLProperty("./@discard", is_yesno=True)
siblings = XMLChildProperty(_NUMACellSibling, relative_xpath="./distances")
caches = XMLChildProperty(_NUMACellCache)
#######################################
# Interconnections between NUMA cells #
#######################################
class _NUMALatency(XMLBuilder):
"""
Class for generating XML for <cpu><numa><cell><interconnects> child nodes
<latency>, describing latency between two NUMA memory nodes.
"""
XML_NAME = "latency"
_XML_PROP_ORDER = ["initiator", "target", "cache", "type", "value", "unit"]
# Note: While libvirt happily accepts XML with a unit= property, it is
# currently ignored on <latency> nodes.
initiator = XMLProperty("./@initiator", is_int=True)
target = XMLProperty("./@target", is_int=True)
cache = XMLProperty("./@cache", is_int=True)
type = XMLProperty("./@type")
value = XMLProperty("./@value", is_int=True)
unit = XMLProperty("./@unit")
class _NUMABandwidth(XMLBuilder):
"""
Class for generating XML for <cpu><numa><cell><interconnects> child nodes
<bandwidth>, describing bandwidth between two NUMA memory nodes.
"""
XML_NAME = "bandwidth"
_XML_PROP_ORDER = ["initiator", "target", "cache", "type", "value", "unit"]
# Note: The documentation only mentions <latency> nodes having a cache=
# attribute, but <bandwidth> and <latency> nodes are otherwise identical
# and libvirt will happily accept XML with a cache= attribute on
# <bandwidth> nodes as well, so let's leave it here for now.
initiator = XMLProperty("./@initiator", is_int=True)
target = XMLProperty("./@target", is_int=True)
cache = XMLProperty("./@cache", is_int=True)
type = XMLProperty("./@type")
value = XMLProperty("./@value", is_int=True)
unit = XMLProperty("./@unit")
#####################
# Actual CPU domain #
#####################
class DomainCpu(XMLBuilder):
"""
Class for generating <cpu> XML
"""
XML_NAME = "cpu"
_XML_PROP_ORDER = ["mode", "match", "check", "migratable",
"model", "vendor", "topology", "features"]
"model", "model_fallback", "model_vendor_id", "vendor",
"topology", "cache", "features",
"cells", "latencies", "bandwidths"]
##################
# XML properties #
##################
# Note: This is not a libvirt property. This is specific to the virt-*
# tools and causes additional security features to be added to the VM.
# See the security mitigation related functions below for more details.
secure = True
mode = XMLProperty("./@mode")
match = XMLProperty("./@match")
check = XMLProperty("./@check")
migratable = XMLProperty("./@migratable", is_onoff=True)
model = XMLProperty("./model")
model_fallback = XMLProperty("./model/@fallback")
model_vendor_id = XMLProperty("./model/@vendor_id")
vendor = XMLProperty("./vendor")
topology = XMLChildProperty(_CPUTopology, is_single=True)
cache = XMLChildProperty(_CPUCache, is_single=True)
features = XMLChildProperty(_CPUFeature)
# NUMA related properties
cells = XMLChildProperty(_NUMACell, relative_xpath="./numa")
latencies = XMLChildProperty(_NUMALatency, relative_xpath="./numa/interconnects")
bandwidths = XMLChildProperty(_NUMABandwidth, relative_xpath="./numa/interconnects")
#############################
# Special CPU mode handling #
#############################
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"
@ -206,6 +262,42 @@ class DomainCpu(XMLBuilder):
self.special_mode_was_set = True
def copy_host_cpu(self, guest):
"""
Try to manually mimic host-model, copying all the info
preferably out of domcapabilities, but capabilities as fallback.
"""
domcaps = guest.lookup_domcaps()
if domcaps.supports_safe_host_model():
log.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: # pragma: no cover
raise ValueError(_("No host CPU reported in capabilities"))
self.mode = "custom"
self.match = "exact"
self.set_model(guest, model)
if fallback:
self.model_fallback = fallback
self.vendor = cpu.vendor
for feature in self.features:
self.remove_child(feature)
for feature in cpu.features:
policy = getattr(feature, "policy", "require")
self.add_feature(feature.name, policy)
########################
# Security mitigations #
########################
def _add_security_features(self, guest):
domcaps = guest.lookup_domcaps()
for feature in domcaps.get_cpu_security_features():
@ -245,6 +337,11 @@ class DomainCpu(XMLBuilder):
self.remove_child(f)
break
###########
# Helpers #
###########
def set_model(self, guest, val):
log.debug("setting cpu model %s", val)
if val:
@ -261,43 +358,6 @@ class DomainCpu(XMLBuilder):
feature = self.features.add_new()
feature.name = name
feature.policy = policy
features = XMLChildProperty(_CPUFeature)
cells = XMLChildProperty(_NUMACell, relative_xpath="./numa")
latencies = XMLChildProperty(_NUMALatency, relative_xpath="./numa/interconnects")
bandwidths = XMLChildProperty(_NUMABandwidth, relative_xpath="./numa/interconnects")
cache = XMLChildProperty(_CPUCache, is_single=True)
def copy_host_cpu(self, guest):
"""
Try to manually mimic host-model, copying all the info
preferably out of domcapabilities, but capabilities as fallback.
"""
domcaps = guest.lookup_domcaps()
if domcaps.supports_safe_host_model():
log.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: # pragma: no cover
raise ValueError(_("No host CPU reported in capabilities"))
self.mode = "custom"
self.match = "exact"
self.set_model(guest, model)
if fallback:
self.model_fallback = fallback
self.vendor = cpu.vendor
for feature in self.features:
self.remove_child(feature)
for feature in cpu.features:
policy = getattr(feature, "policy", "require")
self.add_feature(feature.name, policy)
def vcpus_from_topology(self):
"""
@ -325,23 +385,6 @@ class DomainCpu(XMLBuilder):
self.topology.set_defaults_from_vcpus(vcpus)
##################
# XML properties #
##################
topology = XMLChildProperty(_CPUTopology, is_single=True)
model = XMLProperty("./model")
model_fallback = XMLProperty("./model/@fallback")
model_vendor_id = XMLProperty("./model/@vendor_id")
match = XMLProperty("./@match")
vendor = XMLProperty("./vendor")
mode = XMLProperty("./@mode")
check = XMLProperty("./@check")
migratable = XMLProperty("./@migratable", is_onoff=True)
##################
# Default config #
##################