Adding support to view indiv. cpu-core info

Closes-Bug: #1639340

This commit adds the relevant changes to the get_cpu function, keeping it backwards compatible with the old method.

Change-Id: I3c3a792e88e9a041236eca7283ebfdf1026910d8
This commit is contained in:
Sharpz7 2024-04-02 22:23:51 +00:00
parent df7eccd7f1
commit c79d74eaf7
3 changed files with 145 additions and 25 deletions

View File

@ -806,6 +806,16 @@ class NetworkInterface(encoding.SerializableComparable):
self.client_id = client_id
class CPUInfo(encoding.SerializableComparable):
serializable_fields = ('cpus')
def __init__(self, cpus=None):
self.cpus = cpus or []
def cpus(self):
return self.cpus
class CPU(encoding.SerializableComparable):
serializable_fields = ('model_name', 'frequency', 'count', 'architecture',
'flags', 'socket_count')
@ -1470,35 +1480,74 @@ class GenericHardwareManager(HardwareManager):
return network_interfaces_list
def get_cpus(self):
lines = il_utils.execute('lscpu')[0]
@staticmethod
def create_cpu_info_dict(lines):
cpu_info = {k.strip().lower(): v.strip() for k, v in
(line.split(':', 1)
for line in lines.split('\n')
if line.strip())}
# Current CPU frequency can be different from maximum one on modern
# processors
freq = cpu_info.get('cpu max mhz', cpu_info.get('cpu mhz'))
for line in lines.split('\n')
if line.strip())}
flags = []
out = il_utils.try_execute('grep', '-Em1', '^flags', '/proc/cpuinfo')
if out:
try:
# Example output (much longer for a real system):
# flags : fpu vme de pse
flags = out[0].strip().split(':', 1)[1].strip().split()
except (IndexError, ValueError):
LOG.warning('Malformed CPU flags information: %s', out)
else:
LOG.warning('Failed to get CPU flags')
return cpu_info
return CPU(model_name=cpu_info.get('model name'),
frequency=freq,
# this includes hyperthreading cores
count=int(cpu_info.get('cpu(s)')),
architecture=cpu_info.get('architecture'),
flags=flags,
socket_count=int(cpu_info.get('socket(s)', 0)))
def get_cpus(self):
cpu_info_dicts = []
cpus = []
# Try the new method, if it fails fall back to the old one
try:
cores = il_utils.execute('cat /proc/cpuinfo')[0]
cores = cores.replace("\t", "")
cores = cores.split("\n\n")
for lines in cores:
cpu_info = self.create_cpu_info_dict(lines)
if cpu_info is not None:
cpu_info_dicts.append(cpu_info)
if len(cpu_info_dicts) == 0:
raise Exception
except Exception:
lines = il_utils.execute('lscpu')[0]
cpu_info = self.create_cpu_info_dict(lines)
cpu_info_dicts.append(cpu_info)
for cpu_info in cpu_info_dicts:
# Current CPU frequency can be different from maximum one on modern
# processors
freq = cpu_info.get('cpu max mhz', cpu_info.get('cpu mhz'))
flags = []
out = il_utils.try_execute(
'grep', '-Em1', '^flags', '/proc/cpuinfo'
)
if out:
try:
# Example output (much longer for a real system):
# flags : fpu vme de pse
flags = out[0].strip().split(':', 1)[1].strip().split()
except (IndexError, ValueError):
LOG.warning('Malformed CPU flags information: %s', out)
else:
LOG.warning('Failed to get CPU flags')
cpu = CPU(
model_name=cpu_info.get('model name'),
frequency=freq,
# this includes hyperthreading cores
count=int(cpu_info.get('cpu(s)', 0)),
architecture=cpu_info.get('architecture'),
flags=flags,
socket_count=int(cpu_info.get('socket(s)', 0))
)
cpus.append(cpu)
if len(cpus) == 1:
return cpus[0]
return CPUInfo(cpus=cpus)
def get_memory(self):
# psutil returns a long, so we force it to an int

View File

@ -338,6 +338,64 @@ L3 cache: 15360K
NUMA node0 CPU(s): 0-11
"""
PROC_CPUINFO_OUTPUT = """
processor : 0
vendor_id : AuthenticAMD
cpu family : 23
model : 49
model name : AMD EPYC 7282 16-Core Processor
stepping : 0
microcode : 0x8301055
cpu MHz : 2794.748
cache size : 512 KB
physical id : 0
siblings : 6
core id : 0
cpu cores : 6
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 16
wp : yes
flags : fpu vme de pse
bugs : sysret_ss_attrs
bogomips : 5589.49
TLB size : 1024 4K pages
clflush size : 64
cache_alignment : 64
address sizes : 40 bits physical, 48 bits virtual
power management:
processor : 1
vendor_id : AuthenticAMD
cpu family : 23
model : 49
model name : AMD EPYC 7282 16-Core Processor
stepping : 0
microcode : 0x8301055
cpu MHz : 2794.748
cache size : 512 KB
physical id : 0
siblings : 6
core id : 1
cpu cores : 6
apicid : 1
initial apicid : 1
fpu : yes
fpu_exception : yes
cpuid level : 16
wp : yes
flags : fpu vme de pse
bogomips : 5589.49
TLB size : 1024 4K pages
clflush size : 64
cache_alignment : 64
address sizes : 40 bits physical, 48 bits virtual
power management:
"""
# NOTE(dtanstur): flags list stripped down for sanity reasons
CPUINFO_FLAGS_OUTPUT = """
flags : fpu vme de pse

View File

@ -889,6 +889,19 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual('x86_64', cpus.architecture)
self.assertEqual(['fpu', 'vme', 'de', 'pse'], cpus.flags)
@mock.patch.object(il_utils, 'execute', autospec=True)
def test_get_cpus_multi(self, mocked_execute):
mocked_execute.side_effect = [(hws.PROC_CPUINFO_OUTPUT, ''),
(hws.CPUINFO_FLAGS_OUTPUT, ''),
(hws.CPUINFO_FLAGS_OUTPUT, '')]
cpus = self.hardware.get_cpus()
clock_speeds = ["2794.748", "2794.748"]
for i, cpu in enumerate(cpus.cpus):
self.assertEqual('AMD EPYC 7282 16-Core Processor',
cpu.model_name)
self.assertEqual(clock_speeds[i], cpu.frequency)
@mock.patch.object(il_utils, 'execute', autospec=True)
def test_get_cpus_no_flags(self, mocked_execute):
mocked_execute.side_effect = [(hws.LSCPU_OUTPUT, ''),