[Kimchi-devel] [PATCH v8 1/2] Parts to allow Kimchi to configure the cpu topology.
Christy Perez
christy at linux.vnet.ibm.com
Mon Nov 24 17:02:05 UTC 2014
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 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 | 124 ++++++++++++++++++++++++++++++++++++++++++
src/kimchi/model/templates.py | 10 ++--
6 files changed, 198 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 2c6aa44..210a036 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -899,6 +899,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 5a185e3..3592388 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
@@ -37,6 +38,7 @@ def __init__(self, model, id=None):
self.packagesupdate = PackagesUpdate(self.model)
self.repositories = Repositories(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 3d925b3..58d1f36 100644
--- a/src/kimchi/i18n.py
+++ b/src/kimchi/i18n.py
@@ -319,4 +319,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..3411ef5
--- /dev/null
+++ b/src/kimchi/model/cpuinfo.py
@@ -0,0 +1,124 @@
+#
+# 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
+
+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'
+
+
+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):
+ 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
+
+ self.conn = kargs['conn']
+ libvirt_topology = None
+ try:
+ connect = self.conn.get()
+ libvirt_topology = get_topo_capabilities(connect)
+ except Exception as e:
+ kimchi_log.info("Unable to get CPU topology capabilities: %s"
+ % e.message)
+ return
+ if libvirt_topology is None:
+ kimchi_log.info("cpu_info topology not supported.")
+ return
+
+ 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 475f878..2ca80ba 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()
--
1.9.3
More information about the Kimchi-devel
mailing list