[Kimchi-devel] [PATCH v7 1/2] Parts to allow Kimchi to configure the cpu topology.
Christy Perez
christy at linux.vnet.ibm.com
Mon Nov 24 15:58:14 UTC 2014
On 11/24/2014 09:07 AM, Aline Manera wrote:
>
> On 11/20/2014 06:30 PM, Christy Perez wrote:
>> This patch adds a host/cpuinfo API so that the topology can be
>> retrieved and then used as a guideline for what is legal
>> for this system.
>>
>> The original plan for this function was to abstract everything
>> away from users, but over many iterations, it became clear that
>> due to differences in architectures, etc., it is simpler to ask
>> a user to input cores and threads for a VM, with a few minimal
>> restrictions on threads/core and cores.
>>
>> As a result, the final version of the patch for the backend
>> contains only a small amount of validation for new templates.
>>
>> Signed-off-by: Christy Perez <christy at linux.vnet.ibm.com>
>> ---
>> docs/API.md | 24 ++++++++
>> src/kimchi/control/cpuinfo.py | 37 ++++++++++++
>> src/kimchi/control/host.py | 2 +
>> src/kimchi/i18n.py | 5 ++
>> src/kimchi/model/cpuinfo.py | 128
>> ++++++++++++++++++++++++++++++++++++++++++
>> src/kimchi/model/templates.py | 10 ++--
>> 6 files changed, 202 insertions(+), 4 deletions(-)
>> create mode 100644 src/kimchi/control/cpuinfo.py
>> create mode 100644 src/kimchi/model/cpuinfo.py
>>
>> diff --git a/docs/API.md b/docs/API.md
>> index 6c36bb1..d51e763 100644
>> --- a/docs/API.md
>> +++ b/docs/API.md
>> @@ -896,6 +896,30 @@ Contains the host sample data.
>>
>> *No actions defined*
>>
>> +### Resource: HostStats
>> +
>> +**URI:** /host/cpuinfo
>> +
>> +The cores and sockets of a hosts's CPU. Useful when sizing VMs to take
>> +advantages of the perforamance benefits of SMT (Power) or
>> Hyper-Threading (Intel).
>> +
>> +**Methods:**
>> +
>> +* **GET**: Retreives the sockets, cores, and threads values.
>> + * threading_enabled: Whether CPU topology is supported on this
>> system.
>> + * sockets: The number of total sockets on a system.
>> + * cores: The total number of cores per socket.
>> + * threads_per_core: The threads per core.
>> +
>> +**Actions (PUT):**
>> +
>> +*No actions defined*
>> +
>> +**Actions (POST):**
>> +
>> +*No actions defined*
>> +
>> +
>> ### Resource: HostStatsHistory
>>
>> **URI:** /host/stats/history
>> diff --git a/src/kimchi/control/cpuinfo.py
>> b/src/kimchi/control/cpuinfo.py
>> new file mode 100644
>> index 0000000..415dd3d
>> --- /dev/null
>> +++ b/src/kimchi/control/cpuinfo.py
>> @@ -0,0 +1,37 @@
>> +#
>> +# Project Kimchi
>> +#
>> +# Copyright IBM, Corp. 2014
>> +#
>> +# This library is free software; you can redistribute it and/or
>> +# modify it under the terms of the GNU Lesser General Public
>> +# License as published by the Free Software Foundation; either
>> +# version 2.1 of the License, or (at your option) any later version.
>> +#
>> +# This library 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
>> +# Lesser General Public License for more details.
>> +#
>> +# You should have received a copy of the GNU Lesser General Public
>> +# License along with this library; if not, write to the Free Software
>> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>> 02110-1301 USA
>> +
>> +
>> +from kimchi.control.base import Resource
>> +
>> +
>> +class CPUInfo(Resource):
>> + def __init__(self, model):
>> + super(CPUInfo, self).__init__(model)
>> + self.admin_methods = ['GET']
>> + self.role_key = 'host'
>> + self.uri_fmt = "/host/cpuinfo"
>> +
>> + @property
>> + def data(self):
>> + return {'threading_enabled': self.info['guest_threads_enabled'],
>> + 'sockets': self.info['sockets'],
>> + 'cores': self.info['cores_available'],
>> + 'threads_per_core': self.info['threads_per_core']
>> + }
>> diff --git a/src/kimchi/control/host.py b/src/kimchi/control/host.py
>> index 4362da7..9f73653 100644
>> --- a/src/kimchi/control/host.py
>> +++ b/src/kimchi/control/host.py
>> @@ -17,6 +17,7 @@
>> # License along with this library; if not, write to the Free Software
>> # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>> 02110-1301 USA
>>
>> +from kimchi.control.cpuinfo import CPUInfo
>> from kimchi.control.base import Collection, Resource, SimpleCollection
>> from kimchi.control.utils import UrlSubNode
>> from kimchi.exception import NotFoundError
>> @@ -39,6 +40,7 @@ def __init__(self, model, id=None):
>> self.users = Users(self.model)
>> self.groups = Groups(self.model)
>> self.swupdate = self.generate_action_handler_task('swupdate')
>> + self.cpuinfo = CPUInfo(self.model)
>>
>> @property
>> def data(self):
>> diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
>> index 8f6b67e..a836401 100644
>> --- a/src/kimchi/i18n.py
>> +++ b/src/kimchi/i18n.py
>> @@ -318,4 +318,9 @@
>> "KCHSNAP0006E": _("Unable to delete snapshot '%(name)s' on
>> virtual machine '%(vm)s'. Details: %(err)s"),
>> "KCHSNAP0008E": _("Unable to retrieve current snapshot on
>> virtual machine '%(vm)s'. Details: %(err)s"),
>> "KCHSNAP0009E": _("Unable to revert virtual machine '%(vm)s' to
>> snapshot '%(name)s'. Details: %(err)s"),
>> +
>> + "KCHCPUINF0001E": _("The number of vCPUs is too large for this
>> system."),
>> + "KCHCPUINF0002E": _("Invalid vCPU/topology combination."),
>> + "KCHCPUINF0003E": _("This host (or current configuration) does
>> not allow CPU topology."),
>> +
>> }
>
>> diff --git a/src/kimchi/model/cpuinfo.py b/src/kimchi/model/cpuinfo.py
>> new file mode 100644
>> index 0000000..fbae6ce
>> --- /dev/null
>> +++ b/src/kimchi/model/cpuinfo.py
>> @@ -0,0 +1,128 @@
>> +# modify it under the terms of the GNU Lesser General Public
>> +# License as published by the Free Software Foundation; either
>> +# version 2.1 of the License, or (at your option) any later version.
>> +#
>> +# This library 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
>> +# Lesser General Public License for more details.
>> +#
>> +# You should have received a copy of the GNU Lesser General Public
>> +# License along with this library; if not, write to the Free Software
>> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>> 02110-1301 USA
>> +
>
> Seems there are parts of the license header missing here.
ACK
>
>> +import platform
>> +
>> +from xml.etree import ElementTree as ET
>> +
>> +from kimchi.exception import InvalidParameter, InvalidOperation
>> +from kimchi.utils import kimchi_log, run_command
>> +
>> +ARCH = 'power' if platform.machine().startswith('ppc') else 'x86'
>> +LEGACY_CPU_MAX = 4
>> +
>> +
>> +def get_topo_capabilities(connect):
>> + """
>> + This helper function exists solely to be overridden for
>> + mockmodel tests. Since other modules use getCapabilies(),
>> + it can't be overridden directly.
>> + """
>> + xml = connect.getCapabilities()
>> + capabilities = ET.fromstring(xml)
>> + return capabilities.find('host').find('cpu').find('topology')
>> +
>> +
>> +class CPUInfoModel(object):
>> + """
>> + Get information about a CPU for hyperthreading (on x86)
>> + or SMT (on POWER) for logic when creating templates and VMs.
>> + """
>> +
>> + def __init__(self, **kargs):
>> + def set_not_supported():
>> + kimchi_log.info("cpu_info topology not supported.")
>
>> + self.guest_threads_enabled = False
>> + self.sockets = 0
>> + self.cores_present = 0
>> + self.cores_available = 0
>> + self.cores_per_socket = 0
>> + self.threads_per_core = 0
>> + self.max_threads = 0
>> +
>
> You can set those values by default and only change them when needed.
ACK
>
>> + self.conn = kargs['conn']
>> + libvirt_topology = None
>
>> + try:
>> + connect = self.conn.get()
>> + except Exception as e:
>> + kimchi_log.error("cpuinfo: Unable to get qemu connection:
>> %s"
>> + % e.message)
>> + set_not_supported()
>> +
>> + try:
>> + libvirt_topology = get_topo_capabilities(connect)
>> + if libvirt_topology is None:
>> + set_not_supported()
>> + return
>> + except Exception as e:
>> + kimchi_log.info("Unable to get CPU topology capabilities:
>> %s"
>> + % e.message)
>> + set_not_supported()
>> + return
>> +
>
> Then all that become:
>
> try:
> connect = self.conn.get()
> libvirt_topology = get_topo_capabilities()
> except Exception, e:
> kimchi_log.into(....)
I'd still need the check "if libvirt_topology is None:," but I can take
out a try/except block. Sending this change in v8.
>
>> + if ARCH == 'power':
>> + # IBM PowerPC
>> + self.guest_threads_enabled = True
>> + out, error, rc = run_command(['ppc64_cpu', '--smt'])
>> + if rc or 'on' in out:
>> + # SMT has to be disabled for guest to use threads as
>> CPUs.
>> + # rc is always zero, whether SMT is off or on.
>> + self.guest_threads_enabled = False
>> + out, error, rc = run_command(['ppc64_cpu',
>> '--cores-present'])
>> + if not rc:
>> + self.cores_present = int(out.split()[-1])
>> + out, error, rc = run_command(['ppc64_cpu', '--cores-on'])
>> + if not rc:
>> + self.cores_available = int(out.split()[-1])
>> + out, error, rc = run_command(['ppc64_cpu',
>> '--threads-per-core'])
>> + if not rc:
>> + self.threads_per_core = int(out.split()[-1])
>> + self.sockets = self.cores_present/self.threads_per_core
>> + self.cores_per_socket = self.cores_present/self.sockets
>> + else:
>> + # Intel or AMD
>> + self.guest_threads_enabled = True
>> + self.sockets = int(libvirt_topology.get('sockets'))
>> + self.cores_per_socket = int(libvirt_topology.get('cores'))
>> + self.cores_present = self.cores_per_socket * self.sockets
>> + self.cores_available = self.cores_present
>> + self.threads_per_core = int(libvirt_topology.get('threads'))
>> +
>> + def lookup(self, ident):
>> + return {
>> + 'guest_threads_enabled': self.guest_threads_enabled,
>> + 'sockets': self.sockets,
>> + 'cores_per_socket': self.cores_per_socket,
>> + 'cores_present': self.cores_present,
>> + 'cores_available': self.cores_available,
>> + 'threads_per_core': self.threads_per_core,
>> + }
>> +
>> + def check_topology(self, vcpus, topology):
>> + """
>> + param vcpus: should be an integer
>> + param iso_path: the path of the guest ISO
>> + param topology: {'sockets': x, 'cores': x, 'threads': x}
>> + """
>> + sockets = topology['sockets']
>> + cores = topology['cores']
>> + threads = topology['threads']
>> +
>> + if not self.guest_threads_enabled:
>> + raise InvalidOperation("KCHCPUINF0003E")
>> + if vcpus != sockets * cores * threads:
>> + raise InvalidParameter("KCHCPUINF0002E")
>> + if vcpus > self.cores_available * self.threads_per_core:
>> + raise InvalidParameter("KCHCPUINF0001E")
>> + if threads > self.threads_per_core:
>> + raise InvalidParameter("KCHCPUINF0002E")
>> diff --git a/src/kimchi/model/templates.py
>> b/src/kimchi/model/templates.py
>> index 6e1a571..36dff9f 100644
>> --- a/src/kimchi/model/templates.py
>> +++ b/src/kimchi/model/templates.py
>> @@ -25,6 +25,7 @@
>> from kimchi.exception import InvalidOperation, InvalidParameter
>> from kimchi.exception import NotFoundError, OperationFailed
>> from kimchi.kvmusertests import UserTests
>> +from kimchi.model.cpuinfo import CPUInfoModel
>> from kimchi.utils import pool_name_from_uri
>> from kimchi.utils import probe_file_permission_as_user
>> from kimchi.vmtemplate import VMTemplate
>> @@ -57,11 +58,12 @@ def create(self, params):
>> sockets = topology['sockets']
>> cores = topology['cores']
>> threads = topology['threads']
>> - vcpus = params.get('cpus')
>> - if vcpus is None:
>> + if params.get('cpus') is None:
>> params['cpus'] = sockets * cores * threads
>> - elif vcpus != sockets * cores * threads:
>> - raise InvalidParameter("KCHTMPL0025E")
>> + # check_topoology will raise the appropriate
>> + # exception if a topology is invalid.
>> + CPUInfoModel(conn=self.conn).\
>> + check_topology(params['cpus'], topology)
>> else:
>> params['cpu_info'] = dict()
>>
>
More information about the Kimchi-devel
mailing list