From 6670da4ed1b76a272b16f66c524cb48e1177b2bd Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Wed, 20 Apr 2016 13:58:14 +0200 Subject: [PATCH] Returns CPU flags in the CPU inventory These flags will be processed in a new ironic-inspector plugin to support setting capabilities like cpu_vt (virtualization enabled). Change-Id: I5fe9310c316841eabdd2d5e2ef2ae30afa03d29a Partial-Bug: #1571580 --- doc/source/index.rst | 4 +- ironic_python_agent/hardware.py | 23 +++++++-- .../tests/unit/test_hardware.py | 47 ++++++++++++++++++- .../tests/unit/test_ironic_api_client.py | 6 ++- .../notes/cpu-flags-e3cec7e5cba069ef.yaml | 3 ++ 5 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 releasenotes/notes/cpu-flags-e3cec7e5cba069ef.yaml diff --git a/doc/source/index.rst b/doc/source/index.rst index 4f1397c2b..f1032ff74 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -87,8 +87,8 @@ The inventory is a dictionary (JSON object), containing at least the following fields: ``cpu`` - CPU information: ``model_name``, ``frequency``, ``count`` and - ``architecture``. + CPU information: ``model_name``, ``frequency``, ``count``, + ``architecture`` and ``flags``. ``memory`` RAM information: ``total`` (total size in bytes), ``physical_mb`` diff --git a/ironic_python_agent/hardware.py b/ironic_python_agent/hardware.py index 3e74d350d..f049095af 100644 --- a/ironic_python_agent/hardware.py +++ b/ironic_python_agent/hardware.py @@ -189,13 +189,16 @@ class NetworkInterface(encoding.SerializableComparable): class CPU(encoding.SerializableComparable): - serializable_fields = ('model_name', 'frequency', 'count', 'architecture') + serializable_fields = ('model_name', 'frequency', 'count', 'architecture', + 'flags') - def __init__(self, model_name, frequency, count, architecture): + def __init__(self, model_name, frequency, count, architecture, + flags=None): self.model_name = model_name self.frequency = frequency self.count = count self.architecture = architecture + self.flags = flags or [] class Memory(encoding.SerializableComparable): @@ -446,11 +449,25 @@ class GenericHardwareManager(HardwareManager): # 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 = 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(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')) + architecture=cpu_info.get('architecture'), + flags=flags) def get_memory(self): # psutil returns a long, so we force it to an int diff --git a/ironic_python_agent/tests/unit/test_hardware.py b/ironic_python_agent/tests/unit/test_hardware.py index 5c0b60814..4dd2cb647 100644 --- a/ironic_python_agent/tests/unit/test_hardware.py +++ b/ironic_python_agent/tests/unit/test_hardware.py @@ -224,6 +224,11 @@ L3 cache: 15360K NUMA node0 CPU(s): 0-11 """ +# NOTE(dtanstur): flags list stripped down for sanity reasons +CPUINFO_FLAGS_OUTPUT = """ +flags : fpu vme de pse +""" + class FakeHardwareManager(hardware.GenericHardwareManager): def __init__(self, hardware_support): @@ -437,7 +442,10 @@ class TestGenericHardwareManager(test_base.BaseTestCase): @mock.patch.object(utils, 'execute') def test_get_cpus(self, mocked_execute): - mocked_execute.return_value = LSCPU_OUTPUT, '' + mocked_execute.side_effect = [ + (LSCPU_OUTPUT, ''), + (CPUINFO_FLAGS_OUTPUT, '') + ] cpus = self.hardware.get_cpus() self.assertEqual('Intel(R) Xeon(R) CPU E5-2609 0 @ 2.40GHz', @@ -445,10 +453,14 @@ class TestGenericHardwareManager(test_base.BaseTestCase): self.assertEqual('2400.0000', cpus.frequency) self.assertEqual(4, cpus.count) self.assertEqual('x86_64', cpus.architecture) + self.assertEqual(['fpu', 'vme', 'de', 'pse'], cpus.flags) @mock.patch.object(utils, 'execute') def test_get_cpus2(self, mocked_execute): - mocked_execute.return_value = LSCPU_OUTPUT_NO_MAX_MHZ, '' + mocked_execute.side_effect = [ + (LSCPU_OUTPUT_NO_MAX_MHZ, ''), + (CPUINFO_FLAGS_OUTPUT, '') + ] cpus = self.hardware.get_cpus() self.assertEqual('Intel(R) Xeon(R) CPU E5-1650 v3 @ 3.50GHz', @@ -456,6 +468,37 @@ class TestGenericHardwareManager(test_base.BaseTestCase): self.assertEqual('1794.433', cpus.frequency) self.assertEqual(12, cpus.count) self.assertEqual('x86_64', cpus.architecture) + self.assertEqual(['fpu', 'vme', 'de', 'pse'], cpus.flags) + + @mock.patch.object(utils, 'execute') + def test_get_cpus_no_flags(self, mocked_execute): + mocked_execute.side_effect = [ + (LSCPU_OUTPUT, ''), + processutils.ProcessExecutionError() + ] + + cpus = self.hardware.get_cpus() + self.assertEqual('Intel(R) Xeon(R) CPU E5-2609 0 @ 2.40GHz', + cpus.model_name) + self.assertEqual('2400.0000', cpus.frequency) + self.assertEqual(4, cpus.count) + self.assertEqual('x86_64', cpus.architecture) + self.assertEqual([], cpus.flags) + + @mock.patch.object(utils, 'execute') + def test_get_cpus_illegal_flags(self, mocked_execute): + mocked_execute.side_effect = [ + (LSCPU_OUTPUT, ''), + ('I am not a flag', '') + ] + + cpus = self.hardware.get_cpus() + self.assertEqual('Intel(R) Xeon(R) CPU E5-2609 0 @ 2.40GHz', + cpus.model_name) + self.assertEqual('2400.0000', cpus.frequency) + self.assertEqual(4, cpus.count) + self.assertEqual('x86_64', cpus.architecture) + self.assertEqual([], cpus.flags) @mock.patch('psutil.version_info', (2, 0)) @mock.patch('psutil.phymem_usage', autospec=True) diff --git a/ironic_python_agent/tests/unit/test_ironic_api_client.py b/ironic_python_agent/tests/unit/test_ironic_api_client.py index 2e3defe38..7d79a6e7c 100644 --- a/ironic_python_agent/tests/unit/test_ironic_api_client.py +++ b/ironic_python_agent/tests/unit/test_ironic_api_client.py @@ -176,7 +176,8 @@ class TestBaseIronicPythonAgent(test_base.BaseTestCase): u'model_name': u'Awesome Jay CPU x10 9001', u'frequency': u'9001', u'count': u'10', - u'architecture': u'ARMv9' + u'architecture': u'ARMv9', + u'flags': [], }, u'disks': [ { @@ -313,7 +314,8 @@ class TestBaseIronicPythonAgent(test_base.BaseTestCase): u'model_name': u'Awesome Jay CPU x10 9001', u'frequency': u'9001', u'count': u'10', - u'architecture': u'ARMv9' + u'architecture': u'ARMv9', + u'flags': [], }, u'disks': [ { diff --git a/releasenotes/notes/cpu-flags-e3cec7e5cba069ef.yaml b/releasenotes/notes/cpu-flags-e3cec7e5cba069ef.yaml new file mode 100644 index 000000000..586616afb --- /dev/null +++ b/releasenotes/notes/cpu-flags-e3cec7e5cba069ef.yaml @@ -0,0 +1,3 @@ +--- +features: + - Return CPU flags with the CPU inventory.