mirror of
https://github.com/virt-manager/virt-manager.git
synced 2024-12-24 21:34:47 +03:00
94f8d4cfa9
This means we can drop passing around the pre-parsed caps info everywhere.
317 lines
9.2 KiB
Python
317 lines
9.2 KiB
Python
#
|
|
# Copyright 2010 Red Hat, Inc.
|
|
# Cole Robinson <crobinso@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
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# 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.
|
|
|
|
from virtinst import XMLBuilderDomain
|
|
from virtinst.XMLBuilderDomain import _xml_property
|
|
|
|
import libxml2
|
|
|
|
|
|
def _int_or_none(val):
|
|
return val and int(val) or val
|
|
|
|
|
|
class CPUFeature(XMLBuilderDomain.XMLBuilderDomain):
|
|
"""
|
|
Class for generating <cpu> child <feature> XML
|
|
"""
|
|
|
|
POLICIES = ["force", "require", "optional", "disable", "forbid"]
|
|
|
|
def __init__(self, conn, parsexml=None, parsexmlnode=None):
|
|
XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml,
|
|
parsexmlnode)
|
|
|
|
self._name = None
|
|
self._policy = None
|
|
|
|
if self._is_parse():
|
|
return
|
|
|
|
def _get_name(self):
|
|
return self._name
|
|
def _set_name(self, val):
|
|
self._name = val
|
|
name = _xml_property(_get_name, _set_name,
|
|
xpath="./@name")
|
|
|
|
def _get_policy(self):
|
|
return self._policy
|
|
def _set_policy(self, val):
|
|
self._policy = val
|
|
policy = _xml_property(_get_policy, _set_policy,
|
|
xpath="./@policy")
|
|
|
|
def _get_xml_config(self):
|
|
if not self.name:
|
|
return ""
|
|
|
|
xml = " <feature"
|
|
if self.policy:
|
|
xml += " policy='%s'" % self.policy
|
|
xml += " name='%s'/>" % self.name
|
|
|
|
return xml
|
|
|
|
|
|
class CPU(XMLBuilderDomain.XMLBuilderDomain):
|
|
"""
|
|
Class for generating <cpu> XML
|
|
"""
|
|
|
|
_dumpxml_xpath = "/domain/cpu"
|
|
|
|
MATCHS = ["minimum", "exact", "strict"]
|
|
|
|
def __init__(self, conn, parsexml=None, parsexmlnode=None):
|
|
self._model = None
|
|
self._match = None
|
|
self._vendor = None
|
|
self._mode = None
|
|
self._features = []
|
|
|
|
self._sockets = None
|
|
self._cores = None
|
|
self._threads = None
|
|
|
|
XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml,
|
|
parsexmlnode)
|
|
if self._is_parse():
|
|
return
|
|
|
|
def _parsexml(self, xml, node):
|
|
XMLBuilderDomain.XMLBuilderDomain._parsexml(self, xml, node)
|
|
|
|
for node in self._xml_node.children:
|
|
if node.name != "feature":
|
|
continue
|
|
feature = CPUFeature(self.conn, parsexmlnode=node)
|
|
self._features.append(feature)
|
|
|
|
def _get_features(self):
|
|
return self._features[:]
|
|
features = _xml_property(_get_features)
|
|
|
|
def add_feature(self, name, policy="require"):
|
|
feature = CPUFeature(self.conn)
|
|
feature.name = name
|
|
feature.policy = policy
|
|
|
|
if self._is_parse():
|
|
xml = feature.get_xml_config()
|
|
node = libxml2.parseDoc(xml).children
|
|
feature.set_xml_node(node)
|
|
self._add_child_node("./cpu", node)
|
|
|
|
self._features.append(feature)
|
|
|
|
def remove_feature(self, feature):
|
|
if self._is_parse() and feature in self._features:
|
|
xpath = feature.get_xml_node_path()
|
|
if xpath:
|
|
self._remove_child_xpath(xpath)
|
|
|
|
self._features.remove(feature)
|
|
|
|
|
|
def _get_model(self):
|
|
return self._model
|
|
def _set_model(self, val):
|
|
if val:
|
|
self.mode = "custom"
|
|
if val and not self.match:
|
|
self.match = "exact"
|
|
self._model = val
|
|
model = _xml_property(_get_model, _set_model,
|
|
xpath="./cpu/model")
|
|
|
|
def _get_match(self):
|
|
return self._match
|
|
def _set_match(self, val):
|
|
self._match = val
|
|
match = _xml_property(_get_match, _set_match,
|
|
xpath="./cpu/@match")
|
|
|
|
def _get_vendor(self):
|
|
return self._vendor
|
|
def _set_vendor(self, val):
|
|
self._vendor = val
|
|
vendor = _xml_property(_get_vendor, _set_vendor,
|
|
xpath="./cpu/vendor")
|
|
|
|
def _get_mode(self):
|
|
return self._mode
|
|
def _set_mode(self, val):
|
|
self._mode = val
|
|
mode = _xml_property(_get_mode, _set_mode,
|
|
xpath="./cpu/@mode")
|
|
|
|
# Topology properties
|
|
def _get_sockets(self):
|
|
return self._sockets
|
|
def _set_sockets(self, val):
|
|
self._sockets = _int_or_none(val)
|
|
sockets = _xml_property(_get_sockets, _set_sockets,
|
|
get_converter=lambda s, x: _int_or_none(x),
|
|
xpath="./cpu/topology/@sockets")
|
|
|
|
def _get_cores(self):
|
|
return self._cores
|
|
def _set_cores(self, val):
|
|
self._cores = _int_or_none(val)
|
|
cores = _xml_property(_get_cores, _set_cores,
|
|
get_converter=lambda s, x: _int_or_none(x),
|
|
xpath="./cpu/topology/@cores")
|
|
|
|
def _get_threads(self):
|
|
return self._threads
|
|
def _set_threads(self, val):
|
|
self._threads = _int_or_none(val)
|
|
threads = _xml_property(_get_threads, _set_threads,
|
|
get_converter=lambda s, x: _int_or_none(x),
|
|
xpath="./cpu/topology/@threads")
|
|
|
|
def clear_attrs(self):
|
|
self.match = None
|
|
self.mode = None
|
|
self.vendor = None
|
|
self.model = None
|
|
|
|
for feature in self.features:
|
|
self.remove_feature(feature)
|
|
|
|
def copy_host_cpu(self):
|
|
"""
|
|
Enact the equivalent of qemu -cpu host, pulling all info
|
|
from capabilities about the host CPU
|
|
"""
|
|
cpu = self.conn.caps.host.cpu
|
|
if not cpu.model:
|
|
raise ValueError(_("No host CPU reported in capabilities"))
|
|
|
|
self.mode = "custom"
|
|
self.match = "exact"
|
|
self.model = cpu.model
|
|
self.vendor = cpu.vendor
|
|
|
|
for feature in self.features:
|
|
self.remove_feature(feature)
|
|
for name in cpu.features.names():
|
|
self.add_feature(name)
|
|
|
|
def vcpus_from_topology(self):
|
|
"""
|
|
Determine the CPU count represented by topology, or 1 if
|
|
no topology is set
|
|
"""
|
|
self.set_topology_defaults()
|
|
if self.sockets:
|
|
return self.sockets * self.cores * self.threads
|
|
return 1
|
|
|
|
def set_topology_defaults(self, vcpus=None):
|
|
"""
|
|
Fill in unset topology values, using the passed vcpus count if
|
|
required
|
|
"""
|
|
if (self.sockets is None and
|
|
self.cores is None and
|
|
self.threads is None):
|
|
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:
|
|
self.sockets = vcpus / self.threads
|
|
else:
|
|
self.sockets = vcpus / self.cores
|
|
|
|
if not self.cores:
|
|
if not self.threads:
|
|
self.cores = vcpus / self.sockets
|
|
else:
|
|
self.cores = vcpus / (self.sockets * self.threads)
|
|
|
|
if not self.threads:
|
|
self.threads = vcpus / (self.sockets * self.cores)
|
|
|
|
return
|
|
|
|
def _get_topology_xml(self):
|
|
xml = ""
|
|
if self.sockets:
|
|
xml += " sockets='%s'" % self.sockets
|
|
if self.cores:
|
|
xml += " cores='%s'" % self.cores
|
|
if self.threads:
|
|
xml += " threads='%s'" % self.threads
|
|
|
|
if not xml:
|
|
return ""
|
|
return " <topology%s/>\n" % xml
|
|
|
|
def _get_feature_xml(self):
|
|
xml = ""
|
|
for feature in self._features:
|
|
xml += feature.get_xml_config() + "\n"
|
|
return xml
|
|
|
|
def _get_xml_config(self):
|
|
top_xml = self._get_topology_xml()
|
|
feature_xml = self._get_feature_xml()
|
|
mode_xml = ""
|
|
match_xml = ""
|
|
if self.match:
|
|
match_xml = " match='%s'" % self.match
|
|
xml = ""
|
|
|
|
if self.model == "host-passthrough":
|
|
self.mode = "host-passthrough"
|
|
mode_xml = " mode='%s'" % self.mode
|
|
xml += " <cpu%s/>" % mode_xml
|
|
return xml
|
|
else:
|
|
self.mode = "custom"
|
|
mode_xml = " mode='%s'" % self.mode
|
|
|
|
if not (self.model or top_xml or feature_xml):
|
|
return ""
|
|
|
|
# Simple topology XML mode
|
|
xml += " <cpu%s%s>\n" % (mode_xml, match_xml)
|
|
if self.model:
|
|
xml += " <model>%s</model>\n" % self.model
|
|
if self.vendor:
|
|
xml += " <vendor>%s</vendor>\n" % self.vendor
|
|
if top_xml:
|
|
xml += top_xml
|
|
if feature_xml:
|
|
xml += feature_xml
|
|
|
|
xml += " </cpu>"
|
|
return xml
|