[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