forked from kimchi-project/kimchi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cpuinfo.py
157 lines (140 loc) · 5.82 KB
/
cpuinfo.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#
# Project Kimchi
#
# Copyright IBM Corp, 2015-2016
#
# 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 wok.exception import InvalidOperation
from wok.exception import InvalidParameter
from wok.utils import run_command
from wok.utils import wok_log
ARCH = 'power' if platform.machine().startswith('ppc') else 'x86'
MAX_PPC_VCPUS = 255
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']
try:
connect = self.conn.get()
libvirt_topology = get_topo_capabilities(connect)
except Exception as e:
wok_log.info(f'Unable to get CPU topology capabilities: {str(e)}')
return
if libvirt_topology is None:
wok_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
if self.sockets == 0:
self.sockets = 1
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_cpu_info(self, cpu_info):
"""
param cpu_info: topology definition dict: {
'maxvcpus': integer
'vcpus': integer
'topology': {
'sockets': integer,
'cores': integer,
'threads': integer
}
}
"""
maxvcpus = cpu_info.get('maxvcpus')
vcpus = cpu_info.get('vcpus')
topology = cpu_info.get('topology')
if topology:
# sockets, cores and threads are required when topology is defined
if (
'sockets' not in topology
or 'cores' not in topology
or 'threads' not in topology
):
raise InvalidOperation('KCHCPUINF0007E')
sockets = topology['sockets']
cores = topology['cores']
threads = topology['threads']
if not self.guest_threads_enabled:
raise InvalidOperation('KCHCPUINF0003E')
if maxvcpus != sockets * cores * threads:
raise InvalidParameter('KCHCPUINF0002E')
if vcpus % threads != 0:
raise InvalidParameter('KCHCPUINF0005E')
if maxvcpus > self.get_host_max_vcpus():
raise InvalidParameter('KCHCPUINF0004E')
if vcpus > maxvcpus:
raise InvalidParameter('KCHCPUINF0001E')
def get_host_max_vcpus(self):
if ARCH == 'power':
max_vcpus = self.cores_available * self.threads_per_core
if max_vcpus > MAX_PPC_VCPUS:
max_vcpus = MAX_PPC_VCPUS
else:
max_vcpus = self.conn.get().getMaxVcpus('kvm')
return max_vcpus