From 8f78d80b5b23aade9b119ca45edbfb98e9a2350f Mon Sep 17 00:00:00 2001 From: Edwin Zhai Date: Sat, 24 Jan 2015 17:59:31 +0800 Subject: [PATCH] Add more power and thermal data Add airflow and CPU/Mem/IO utilization metric, which are critical to show node usage statistics. DocImpact Change-Id: I92cf297b0fc2436c3530536e0c76fd69058369b0 Implements: blueprint power-thermal-data --- .../ipmi/platform/intel_node_manager.py | 123 ++++++++++++++---- ceilometer/ipmi/platform/ipmitool.py | 2 +- ceilometer/ipmi/pollsters/node.py | 89 +++++++++++-- ceilometer/tests/agent/test_manager.py | 8 +- ceilometer/tests/ipmi/platform/fake_utils.py | 32 ++++- .../tests/ipmi/platform/ipmitool_test_data.py | 32 ++++- .../ipmi/platform/test_intel_node_manager.py | 117 ++++++++++++++--- .../tests/ipmi/platform/test_ipmi_sensor.py | 2 +- ceilometer/tests/ipmi/pollsters/base.py | 10 +- ceilometer/tests/ipmi/pollsters/test_node.py | 117 +++++++++++++++-- .../tests/ipmi/pollsters/test_sensor.py | 28 ---- setup.cfg | 8 +- 12 files changed, 457 insertions(+), 111 deletions(-) diff --git a/ceilometer/ipmi/platform/intel_node_manager.py b/ceilometer/ipmi/platform/intel_node_manager.py index 61a1be01..ca07849b 100644 --- a/ceilometer/ipmi/platform/intel_node_manager.py +++ b/ceilometer/ipmi/platform/intel_node_manager.py @@ -48,12 +48,18 @@ IPMICMD = {"sdr_dump": "sdr dump", "sdr_info": "sdr info", "sensor_dump": "sdr -v"} IPMIRAWCMD = {"get_device_id": "raw 0x06 0x01", + "get_nm_version": "raw 0x2e 0xca 0x57 0x01 0x00", "init_sensor_agent": "raw 0x0a 0x2c 0x01", "init_complete": "raw 0x0a 0x2c 0x00", "init_sensor_agent_status": "raw 0x0a 0x2c 0x00", "read_power_all": "raw 0x2e 0xc8 0x57 0x01 0x00 0x01 0x00 0x00", - "read_temperature_all": - "raw 0x2e 0xc8 0x57 0x01 0x00 0x02 0x00 0x00"} + "read_inlet_temperature": + "raw 0x2e 0xc8 0x57 0x01 0x00 0x02 0x00 0x00", + "read_outlet_temperature": + "raw 0x2e 0xc8 0x57 0x01 0x00 0x05 0x00 0x00", + "read_airflow": "raw 0x2e 0xc8 0x57 0x01 0x00 0x04 0x00 0x00", + "read_cups_utilization": "raw 0x2e 0x65 0x57 0x01 0x00 0x05", + "read_cups_index": "raw 0x2e 0x65 0x57 0x01 0x00 0x01"} MANUFACTURER_ID_INTEL = ['57', '01', '00'] INTEL_PREFIX = '5701000d01' @@ -99,6 +105,24 @@ NM_GET_DEVICE_ID_TEMPLATE['Firmware_build_number'] = 1 NM_GET_DEVICE_ID_TEMPLATE['Last_digit_firmware_build_number'] = 1 NM_GET_DEVICE_ID_TEMPLATE['Image_flags'] = 1 +NM_GET_VERSION_TEMPLATE = collections.OrderedDict() +NM_GET_VERSION_TEMPLATE['Manufacturer_ID'] = 3 +NM_GET_VERSION_TEMPLATE['NM_Version'] = 1 +NM_GET_VERSION_TEMPLATE['IPMI_Version'] = 1 +NM_GET_VERSION_TEMPLATE['Patch_Version'] = 1 +NM_GET_VERSION_TEMPLATE['Firmware_Revision_Major'] = 1 +NM_GET_VERSION_TEMPLATE['Firmware_Revision_Minor'] = 1 + +NM_CUPS_UTILIZATION_TEMPLATE = collections.OrderedDict() +NM_CUPS_UTILIZATION_TEMPLATE['Manufacturer_ID'] = 3 +NM_CUPS_UTILIZATION_TEMPLATE['CPU_Utilization'] = 8 +NM_CUPS_UTILIZATION_TEMPLATE['Mem_Utilization'] = 8 +NM_CUPS_UTILIZATION_TEMPLATE['IO_Utilization'] = 8 + +NM_CUPS_INDEX_TEMPLATE = collections.OrderedDict() +NM_CUPS_INDEX_TEMPLATE['Manufacturer_ID'] = 3 +NM_CUPS_INDEX_TEMPLATE['CUPS_Index'] = 2 + def _hex(list=None): """Format the return value in list into hex.""" @@ -130,11 +154,14 @@ class NodeManager(object): def __init__(self): if not (self._instance and self._inited): - self.nm_support = False - self.channel_slave = '' + # As singleton, only the 1st NM pollster would trigger its + # initialization. nm_version indicate init result, and is shared + # across all pollsters self._inited = True + self.nm_version = 0 + self.channel_slave = '' - self.nm_support = self.check_node_manager() + self.nm_version = self.check_node_manager() @staticmethod def _parse_slave_and_channel(file_path): @@ -193,27 +220,69 @@ class NodeManager(object): """ return self.channel_slave + ' ' + IPMIRAWCMD["get_device_id"] + @ipmitool.execute_ipmi_cmd(NM_GET_VERSION_TEMPLATE) + def _node_manager_get_version(self): + """GET_NODE_MANAGER_VERSION command in Intel Node Manager + + Byte 4 of the response: + 01h - Intel NM 1.0 + 02h - Intel NM 1.5 + 03h - Intel NM 2.0 + 04h - Intel NM 2.5 + 05h - Intel NM 3.0 + """ + return self.channel_slave + ' ' + IPMIRAWCMD["get_nm_version"] + @ipmitool.execute_ipmi_cmd(NM_STATISTICS_TEMPLATE) def _read_power_all(self): """Get the power consumption of the whole platform.""" return self.channel_slave + ' ' + IPMIRAWCMD['read_power_all'] @ipmitool.execute_ipmi_cmd(NM_STATISTICS_TEMPLATE) - def _read_temperature_all(self): - """Get the temperature info of the whole platform.""" - return self.channel_slave + ' ' + IPMIRAWCMD['read_temperature_all'] + def _read_inlet_temperature(self): + """Get the inlet temperature info of the whole platform.""" + return self.channel_slave + ' ' + IPMIRAWCMD['read_inlet_temperature'] + + @ipmitool.execute_ipmi_cmd(NM_STATISTICS_TEMPLATE) + def _read_outlet_temperature(self): + """Get the outlet temperature info of the whole platform.""" + return self.channel_slave + ' ' + IPMIRAWCMD['read_outlet_temperature'] + + @ipmitool.execute_ipmi_cmd(NM_STATISTICS_TEMPLATE) + def _read_airflow(self): + """Get the volumetric airflow of the whole platform.""" + return self.channel_slave + ' ' + IPMIRAWCMD['read_airflow'] + + @ipmitool.execute_ipmi_cmd(NM_CUPS_UTILIZATION_TEMPLATE) + def _read_cups_utilization(self): + """Get the average CUPS utilization of the whole platform.""" + return self.channel_slave + ' ' + IPMIRAWCMD['read_cups_utilization'] + + @ipmitool.execute_ipmi_cmd(NM_CUPS_INDEX_TEMPLATE) + def _read_cups_index(self): + """Get the CUPS Index of the whole platform.""" + return self.channel_slave + ' ' + IPMIRAWCMD['read_cups_index'] def read_power_all(self): - if self.nm_support: - return self._read_power_all() + return self._read_power_all() if self.nm_version > 0 else {} - return {} + def read_inlet_temperature(self): + return self._read_inlet_temperature() if self.nm_version > 0 else {} - def read_temperature_all(self): - if self.nm_support: - return self._read_temperature_all() + def read_outlet_temperature(self): + return self._read_outlet_temperature() if self.nm_version >= 5 else {} - return {} + def read_airflow(self): + # only available after NM 3.0 + return self._read_airflow() if self.nm_version >= 5 else {} + + def read_cups_utilization(self): + # only available after NM 3.0 + return self._read_cups_utilization() if self.nm_version >= 5 else {} + + def read_cups_index(self): + # only available after NM 3.0 + return self._read_cups_index() if self.nm_version >= 5 else {} def init_node_manager(self): if self._init_sensor_agent_process()['ret'] == ['01']: @@ -237,26 +306,26 @@ class NodeManager(object): # String of channel and slave_address self.channel_slave = '-b ' + channel + ' -t ' + slave_address - def node_manager_support(self): + def node_manager_version(self): """Intel Node Manager capability checking - This function is used to detect if compute node support Intel - Node Manager or not and parse out the slave address and channel - number of node manager. + This function is used to detect if compute node support Intel Node + Manager(return version number) or not(return -1) and parse out the + slave address and channel number of node manager. """ self.manufacturer_id = self.get_device_id()['Manufacturer_ID'] if MANUFACTURER_ID_INTEL != self.manufacturer_id: # If the manufacturer is not Intel, just set False and return. - return False + return 0 self.discover_slave_channel() support = self._node_manager_get_device_id()['Implemented_firmware'] # According to Intel Node Manager spec, return value of GET_DEVICE_ID, # bits 3 to 0 shows if Intel NM implemented or not. - if int(support[0], 16) & 0xf != 0: - return True - else: - return False + if int(support[0], 16) & 0xf == 0: + return 0 + + return _hex(self._node_manager_get_version()['NM_Version']) def check_node_manager(self): """Intel Node Manager init and check @@ -267,7 +336,7 @@ class NodeManager(object): """ try: self.init_node_manager() - has_nm = self.node_manager_support() + nm_version = self.node_manager_version() except (nmexcept.NodeManagerException, nmexcept.IPMIException): - return False - return has_nm + return 0 + return nm_version diff --git a/ceilometer/ipmi/platform/ipmitool.py b/ceilometer/ipmi/platform/ipmitool.py index 3aefddd9..7b049588 100644 --- a/ceilometer/ipmi/platform/ipmitool.py +++ b/ceilometer/ipmi/platform/ipmitool.py @@ -97,7 +97,7 @@ def _parse_output(output, template): if "translate" in template: ret = _translate_output(output) else: - output_list = output.strip().split(' ') + output_list = output.strip().replace('\n', '').split(' ') if sum(template.values()) != len(output_list): raise ipmiexcept.IPMIException(_("ipmitool output " "length mismatch")) diff --git a/ceilometer/ipmi/pollsters/node.py b/ceilometer/ipmi/pollsters/node.py index bab40a0a..cc833eba 100644 --- a/ceilometer/ipmi/pollsters/node.py +++ b/ceilometer/ipmi/pollsters/node.py @@ -42,21 +42,25 @@ class _Base(plugin_base.PollsterBase): self.polling_failures = 0 # Do not load this extension if no NM support - if not self.nodemanager.nm_support: + if self.nodemanager.nm_version == 0: raise plugin_base.ExtensionLoadError() @property def default_discovery(self): return 'local_node' + def get_value(self, stats): + """Get value from statistics.""" + return node_manager._hex(stats["Current_value"]) + @abc.abstractmethod - def read_data(self): + def read_data(self, cache): """Return data sample for IPMI.""" def get_samples(self, manager, cache, resources): # Only one resource for Node Manager pollster try: - stats = self.read_data() + stats = self.read_data(cache) except nmexcept.IPMIException: self.polling_failures += 1 LOG.warning(_('Polling %(name)s faild for %(cnt)s times!') @@ -76,7 +80,7 @@ class _Base(plugin_base.PollsterBase): } if stats: - data = node_manager._hex(stats["Current_value"]) + data = self.get_value(stats) yield sample.Sample( name=self.NAME, @@ -90,13 +94,22 @@ class _Base(plugin_base.PollsterBase): resource_metadata=metadata) -class TemperaturePollster(_Base): - NAME = "hardware.ipmi.node.temperature" +class InletTemperaturePollster(_Base): + NAME = "hardware.ipmi.node.inlet_temperature" TYPE = sample.TYPE_GAUGE UNIT = "C" - def read_data(self): - return self.nodemanager.read_temperature_all() + def read_data(self, cache): + return self.nodemanager.read_inlet_temperature() + + +class OutletTemperaturePollster(_Base): + NAME = "hardware.ipmi.node.outlet_temperature" + TYPE = sample.TYPE_GAUGE + UNIT = "C" + + def read_data(self, cache): + return self.nodemanager.read_outlet_temperature() class PowerPollster(_Base): @@ -104,5 +117,63 @@ class PowerPollster(_Base): TYPE = sample.TYPE_GAUGE UNIT = "W" - def read_data(self): + def read_data(self, cache): return self.nodemanager.read_power_all() + + +class AirflowPollster(_Base): + NAME = "hardware.ipmi.node.airflow" + TYPE = sample.TYPE_GAUGE + UNIT = "CFM" + + def read_data(self, cache): + return self.nodemanager.read_airflow() + + +class CUPSIndexPollster(_Base): + NAME = "hardware.ipmi.node.cups" + TYPE = sample.TYPE_GAUGE + UNIT = "CUPS" + + def read_data(self, cache): + return self.nodemanager.read_cups_index() + + def get_value(self, stats): + return node_manager._hex(stats["CUPS_Index"]) + + +class _CUPSUtilPollsterBase(_Base): + CACHE_KEY_CUPS = 'CUPS' + + def read_data(self, cache): + i_cache = cache.setdefault(self.CACHE_KEY_CUPS, {}) + if not i_cache: + i_cache.update(self.nodemanager.read_cups_utilization()) + return i_cache + + +class CPUUtilPollster(_CUPSUtilPollsterBase): + NAME = "hardware.ipmi.node.cpu_util" + TYPE = sample.TYPE_GAUGE + UNIT = "%" + + def get_value(self, stats): + return node_manager._hex(stats["CPU_Utilization"]) + + +class MemUtilPollster(_CUPSUtilPollsterBase): + NAME = "hardware.ipmi.node.mem_util" + TYPE = sample.TYPE_GAUGE + UNIT = "%" + + def get_value(self, stats): + return node_manager._hex(stats["Mem_Utilization"]) + + +class IOUtilPollster(_CUPSUtilPollsterBase): + NAME = "hardware.ipmi.node.io_util" + TYPE = sample.TYPE_GAUGE + UNIT = "%" + + def get_value(self, stats): + return node_manager._hex(stats["IO_Utilization"]) diff --git a/ceilometer/tests/agent/test_manager.py b/ceilometer/tests/agent/test_manager.py index 9a0c9efb..767d453f 100644 --- a/ceilometer/tests/agent/test_manager.py +++ b/ceilometer/tests/agent/test_manager.py @@ -59,8 +59,8 @@ class TestManager(base.BaseTestCase): def test_load_normal_plugins(self): mgr = manager.AgentManager(namespaces=['ipmi'], pollster_list=['hardware.ipmi.node.*']) - # 2 pollsters for Node Manager - self.assertEqual(2, len(mgr.extensions)) + # 8 pollsters for Node Manager + self.assertEqual(8, len(mgr.extensions)) # Skip loading pollster upon ExtensionLoadError @mock.patch('ceilometer.ipmi.pollsters.node._Base.__init__', @@ -75,7 +75,9 @@ class TestManager(base.BaseTestCase): self.assertEqual(0, len(mgr.extensions)) err_msg = 'Skip loading extension for hardware.ipmi.node.%s' - pollster_names = ['power', 'temperature'] + pollster_names = [ + 'power', 'inlet_temperature', 'outlet_temperature', + 'airflow', 'cups', 'cpu_util', 'mem_util', 'io_util'] calls = [mock.call(err_msg % n) for n in pollster_names] LOG.error.assert_has_calls(calls=calls, any_order=True) diff --git a/ceilometer/tests/ipmi/platform/fake_utils.py b/ceilometer/tests/ipmi/platform/fake_utils.py index ab1474c6..e134195b 100644 --- a/ceilometer/tests/ipmi/platform/fake_utils.py +++ b/ceilometer/tests/ipmi/platform/fake_utils.py @@ -31,6 +31,14 @@ def init_sensor_agent(parameter=''): return (' 00\n', '') +def get_nm_version_v2(parameter=''): + return test_data.nm_version_v2 + + +def get_nm_version_v3(parameter=''): + return test_data.nm_version_v3 + + def sdr_dump(data_file=''): if data_file == '': raise ValueError("No file specified for ipmitool sdr dump") @@ -50,7 +58,11 @@ def _execute(funcs, *cmd, **kwargs): test_data.device_id_cmd: test_data.device_id, test_data.nm_device_id_cmd: test_data.nm_device_id, test_data.get_power_cmd: test_data.power_data, - test_data.get_temperature_cmd: test_data.temperature_data, + test_data.get_inlet_temp_cmd: test_data.inlet_temperature_data, + test_data.get_outlet_temp_cmd: test_data.outlet_temperature_data, + test_data.get_airflow_cmd: test_data.airflow_data, + test_data.get_cups_index_cmd: test_data.cups_index_data, + test_data.get_cups_util_cmd: test_data.cups_util_data, test_data.sdr_info_cmd: test_data.sdr_info, test_data.read_sensor_temperature_cmd: test_data.sensor_temperature, test_data.read_sensor_voltage_cmd: test_data.sensor_voltage, @@ -72,12 +84,24 @@ def _execute(funcs, *cmd, **kwargs): return funcs[cmd_str](par_str) -def execute_with_nm(*cmd, **kwargs): - """test version of execute on Node Manager platform.""" +def execute_with_nm_v3(*cmd, **kwargs): + """test version of execute on Node Manager V3.0 platform.""" funcs = {test_data.sensor_status_cmd: get_sensor_status_init, test_data.init_sensor_cmd: init_sensor_agent, - test_data.sdr_dump_cmd: sdr_dump} + test_data.sdr_dump_cmd: sdr_dump, + test_data.nm_version_cmd: get_nm_version_v3} + + return _execute(funcs, *cmd, **kwargs) + + +def execute_with_nm_v2(*cmd, **kwargs): + """test version of execute on Node Manager V2.0 platform.""" + + funcs = {test_data.sensor_status_cmd: get_sensor_status_init, + test_data.init_sensor_cmd: init_sensor_agent, + test_data.sdr_dump_cmd: sdr_dump, + test_data.nm_version_cmd: get_nm_version_v2} return _execute(funcs, *cmd, **kwargs) diff --git a/ceilometer/tests/ipmi/platform/ipmitool_test_data.py b/ceilometer/tests/ipmi/platform/ipmitool_test_data.py index 1b6bcc33..7504aba3 100644 --- a/ceilometer/tests/ipmi/platform/ipmitool_test_data.py +++ b/ceilometer/tests/ipmi/platform/ipmitool_test_data.py @@ -332,13 +332,21 @@ read_sensor_fan_cmd = 'ipmitoolsdr-vtypeFan' device_id_cmd = 'ipmitoolraw0x060x01' nm_device_id_cmd = 'ipmitool-b0x6-t0x2craw0x060x01' +nm_version_cmd = 'ipmitool-b0x6-t0x2craw0x2e0xca0x570x010x00' get_power_cmd = 'ipmitool-b0x6-t0x2craw0x2e0xc80x570x010x000x010x000x00' -get_temperature_cmd = 'ipmitool-b0x6-t0x2craw0x2e0xc80x570x010x000x020x000x00' +get_inlet_temp_cmd = 'ipmitool-b0x6-t0x2craw0x2e0xc80x570x010x000x020x000x00' +get_outlet_temp_cmd = 'ipmitool-b0x6-t0x2craw0x2e0xc80x570x010x000x050x000x00' +get_airflow_cmd = 'ipmitool-b0x6-t0x2craw0x2e0xc80x570x010x000x040x000x00' +get_cups_index_cmd = 'ipmitool-b0x6-t0x2craw0x2e0x650x570x010x000x01' +get_cups_util_cmd = 'ipmitool-b0x6-t0x2craw0x2e0x650x570x010x000x05' device_id = (' 21 01 01 04 02 bf 57 01 00 49 00 01 07 50 0b', '') nm_device_id = (' 50 01 02 15 02 21 57 01 00 02 0b 02 09 10 01', '') +nm_version_v2 = (' 57 01 00 03 02 00 02 15', '') +nm_version_v3 = (' 57 01 00 05 03 00 03 06', '') + # start from byte 3, get cur- 57 00(87), min- 03 00(3) # max- 37 02(567), avg- 5c 00(92) power_data = (' 57 01 00 57 00 03 00 37 02 5c 00 cc 37 f4 53 ce\n' @@ -346,8 +354,26 @@ power_data = (' 57 01 00 57 00 03 00 37 02 5c 00 cc 37 f4 53 ce\n' # start from byte 3, get cur- 17 00(23), min- 16 00(22) # max- 18 00(24), avg- 17 00(23) -temperature_data = (' 57 01 00 17 00 16 00 18 00 17 00 f3 6f fe 53 85\n' - ' b7 02 00 50\n', '') +inlet_temperature_data = (' 57 01 00 17 00 16 00 18 00 17 00 f3 6f fe 53 85\n' + ' b7 02 00 50\n', '') + +# start from byte 3, get cur- 19 00(25), min- 18 00(24) +# max- 1b 00(27), avg- 19 00(25) +outlet_temperature_data = (' 57 01 00 19 00 18 00 1b 00 19 00 f3 6f fe 53 85\n' + ' b7 02 00 50\n', '') + +# start from byte 3, get cur- be 00(190), min- 96 00(150) +# max- 26 02(550), avg- cb 00(203) +airflow_data = (' 57 01 00 be 00 96 00 26 02 cb 00 e1 65 c1 54 db\n' + ' b7 02 00 50\n', '') + +# start from byte 3, cups index 2e 00 (46) +cups_index_data = (' 57 01 00 2e 00\n', '') + +# start from byte 3, get cup_util - 33 00 ...(51), mem_util - 05 00 ...(5) +# io_util - 00 00 ...(0) +cups_util_data = (' 57 01 00 33 00 00 00 00 00 00 00 05 00 00 00 00\n' + ' 00 00 00 00 00 00 00 00 00 00 00\n', '') sdr_info = ('', '') diff --git a/ceilometer/tests/ipmi/platform/test_intel_node_manager.py b/ceilometer/tests/ipmi/platform/test_intel_node_manager.py index a7cb03f2..e40fa9ea 100644 --- a/ceilometer/tests/ipmi/platform/test_intel_node_manager.py +++ b/ceilometer/tests/ipmi/platform/test_intel_node_manager.py @@ -12,7 +12,9 @@ # License for the specific language governing permissions and limitations # under the License. +import abc import mock +import six from ceilometer.ipmi.platform import intel_node_manager as node_manager from ceilometer.tests.ipmi.platform import fake_utils @@ -21,19 +23,83 @@ from ceilometer import utils from oslotest import base -class TestNodeManager(base.BaseTestCase): +@six.add_metaclass(abc.ABCMeta) +class _Base(base.BaseTestCase): + + @abc.abstractmethod + def init_test_engine(self): + """Prepare specific ipmitool as engine for different NM version.""" def setUp(self): - super(TestNodeManager, self).setUp() - - utils.execute = mock.Mock(side_effect=fake_utils.execute_with_nm) + super(_Base, self).setUp() + self.init_test_engine() self.nm = node_manager.NodeManager() @classmethod def tearDownClass(cls): # reset inited to force an initialization of singleton for next test node_manager.NodeManager()._inited = False - super(TestNodeManager, cls).tearDownClass() + super(_Base, cls).tearDownClass() + + +class TestNodeManagerV3(_Base): + + def init_test_engine(self): + utils.execute = mock.Mock(side_effect=fake_utils.execute_with_nm_v3) + + def test_read_airflow(self): + airflow = self.nm.read_airflow() + avg_val = node_manager._hex(airflow["Average_value"]) + max_val = node_manager._hex(airflow["Maximum_value"]) + min_val = node_manager._hex(airflow["Minimum_value"]) + cur_val = node_manager._hex(airflow["Current_value"]) + + # get NM 3.0 + self.assertEqual(5, self.nm.nm_version) + + # see ipmi_test_data.py for raw data + self.assertEqual(190, cur_val) + self.assertEqual(150, min_val) + self.assertEqual(550, max_val) + self.assertEqual(203, avg_val) + + def test_read_outlet_temperature(self): + temperature = self.nm.read_outlet_temperature() + avg_val = node_manager._hex(temperature["Average_value"]) + max_val = node_manager._hex(temperature["Maximum_value"]) + min_val = node_manager._hex(temperature["Minimum_value"]) + cur_val = node_manager._hex(temperature["Current_value"]) + + # get NM 3.0 + self.assertEqual(5, self.nm.nm_version) + + # see ipmi_test_data.py for raw data + self.assertEqual(25, cur_val) + self.assertEqual(24, min_val) + self.assertEqual(27, max_val) + self.assertEqual(25, avg_val) + + def test_read_cups_utilization(self): + cups_util = self.nm.read_cups_utilization() + cpu_util = node_manager._hex(cups_util["CPU_Utilization"]) + mem_util = node_manager._hex(cups_util["Mem_Utilization"]) + io_util = node_manager._hex(cups_util["IO_Utilization"]) + + # see ipmi_test_data.py for raw data + self.assertEqual(51, cpu_util) + self.assertEqual(5, mem_util) + self.assertEqual(0, io_util) + + def test_read_cups_index(self): + cups_index = self.nm.read_cups_index() + index = node_manager._hex(cups_index["CUPS_Index"]) + self.assertEqual(46, index) + + +class TestNodeManager(_Base): + + def init_test_engine(self): + utils.execute = mock.Mock(side_effect=fake_utils.execute_with_nm_v2) def test_read_power_all(self): power = self.nm.read_power_all() @@ -43,15 +109,16 @@ class TestNodeManager(base.BaseTestCase): min_val = node_manager._hex(power["Minimum_value"]) cur_val = node_manager._hex(power["Current_value"]) - self.assertTrue(self.nm.nm_support) + # get NM 2.0 + self.assertEqual(3, self.nm.nm_version) # see ipmi_test_data.py for raw data self.assertEqual(87, cur_val) self.assertEqual(3, min_val) self.assertEqual(567, max_val) self.assertEqual(92, avg_val) - def test_read_temperature_all(self): - temperature = self.nm.read_temperature_all() + def test_read_inlet_temperature(self): + temperature = self.nm.read_inlet_temperature() avg_val = node_manager._hex(temperature["Average_value"]) max_val = node_manager._hex(temperature["Maximum_value"]) @@ -64,30 +131,38 @@ class TestNodeManager(base.BaseTestCase): self.assertEqual(24, max_val) self.assertEqual(23, avg_val) + def test_read_airflow(self): + airflow = self.nm.read_airflow() + self.assertEqual({}, airflow) -class TestNonNodeManager(base.BaseTestCase): + def test_read_outlet_temperature(self): + temperature = self.nm.read_outlet_temperature() + self.assertEqual({}, temperature) - def setUp(self): - super(TestNonNodeManager, self).setUp() + def test_read_cups_utilization(self): + cups_util = self.nm.read_cups_utilization() + self.assertEqual({}, cups_util) + def test_read_cups_index(self): + cups_index = self.nm.read_cups_index() + self.assertEqual({}, cups_index) + + +class TestNonNodeManager(_Base): + + def init_test_engine(self): utils.execute = mock.Mock(side_effect=fake_utils.execute_without_nm) - self.nm = node_manager.NodeManager() - - @classmethod - def tearDownClass(cls): - # reset inited to force an initialization of singleton for next test - node_manager.NodeManager()._inited = False - super(TestNonNodeManager, cls).tearDownClass() def test_read_power_all(self): + # no NM support + self.assertEqual(0, self.nm.nm_version) power = self.nm.read_power_all() - self.assertFalse(self.nm.nm_support) # Non-Node Manager platform return empty data self.assertEqual({}, power) - def test_read_temperature_all(self): - temperature = self.nm.read_temperature_all() + def test_read_inlet_temperature(self): + temperature = self.nm.read_inlet_temperature() # Non-Node Manager platform return empty data self.assertEqual({}, temperature) diff --git a/ceilometer/tests/ipmi/platform/test_ipmi_sensor.py b/ceilometer/tests/ipmi/platform/test_ipmi_sensor.py index be2d2f8a..a6b284a1 100644 --- a/ceilometer/tests/ipmi/platform/test_ipmi_sensor.py +++ b/ceilometer/tests/ipmi/platform/test_ipmi_sensor.py @@ -25,7 +25,7 @@ class TestIPMISensor(base.BaseTestCase): def setUp(self): super(TestIPMISensor, self).setUp() - utils.execute = mock.Mock(side_effect=fake_utils.execute_with_nm) + utils.execute = mock.Mock(side_effect=fake_utils.execute_with_nm_v2) self.ipmi = ipmi_sensor.IPMISensor() @classmethod diff --git a/ceilometer/tests/ipmi/pollsters/base.py b/ceilometer/tests/ipmi/pollsters/base.py index eb14c042..6b8023d4 100644 --- a/ceilometer/tests/ipmi/pollsters/base.py +++ b/ceilometer/tests/ipmi/pollsters/base.py @@ -25,13 +25,13 @@ from ceilometer.tests import base @six.add_metaclass(abc.ABCMeta) class TestPollsterBase(base.BaseTestCase): - @abc.abstractmethod def fake_data(self): """Fake data used for test.""" + return None - @abc.abstractmethod def fake_sensor_data(self, sensor_type): """Fake sensor data used for test.""" + return None @abc.abstractmethod def make_pollster(self): @@ -39,8 +39,12 @@ class TestPollsterBase(base.BaseTestCase): def _test_get_samples(self): nm = mock.Mock() - nm.read_temperature_all.side_effect = self.fake_data + nm.read_inlet_temperature.side_effect = self.fake_data + nm.read_outlet_temperature.side_effect = self.fake_data nm.read_power_all.side_effect = self.fake_data + nm.read_airflow.side_effect = self.fake_data + nm.read_cups_index.side_effect = self.fake_data + nm.read_cups_utilization.side_effect = self.fake_data nm.read_sensor_any.side_effect = self.fake_sensor_data # We should mock the pollster first before initialize the Manager # so that we don't trigger the sudo in pollsters' __init__(). diff --git a/ceilometer/tests/ipmi/pollsters/test_node.py b/ceilometer/tests/ipmi/pollsters/test_node.py index b66bdf54..a68dfe36 100644 --- a/ceilometer/tests/ipmi/pollsters/test_node.py +++ b/ceilometer/tests/ipmi/pollsters/test_node.py @@ -29,10 +29,6 @@ class TestPowerPollster(base.TestPollsterBase): # data after parsing Intel Node Manager output return {"Current_value": ['13', '00']} - def fake_sensor_data(self, sensor_type): - # No use for this test - return None - def make_pollster(self): return node.PowerPollster() @@ -44,18 +40,14 @@ class TestPowerPollster(base.TestPollsterBase): self._verify_metering(1, 19, CONF.host) -class TestTemperaturePollster(base.TestPollsterBase): +class TestInletTemperaturePollster(base.TestPollsterBase): def fake_data(self): # data after parsing Intel Node Manager output return {"Current_value": ['23', '00']} - def fake_sensor_data(self, sensor_type): - # No use for this test - return None - def make_pollster(self): - return node.TemperaturePollster() + return node.InletTemperaturePollster() @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) def test_get_samples(self): @@ -63,3 +55,108 @@ class TestTemperaturePollster(base.TestPollsterBase): # only one sample, and value is 35(0x23 as current_value) self._verify_metering(1, 35, CONF.host) + + +class TestOutletTemperaturePollster(base.TestPollsterBase): + + def fake_data(self): + # data after parsing Intel Node Manager output + return {"Current_value": ['25', '00']} + + def make_pollster(self): + return node.OutletTemperaturePollster() + + @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) + def test_get_samples(self): + self._test_get_samples() + + # only one sample, and value is 37(0x25 as current_value) + self._verify_metering(1, 37, CONF.host) + + +class TestAirflowPollster(base.TestPollsterBase): + + def fake_data(self): + # data after parsing Intel Node Manager output + return {"Current_value": ['be', '00']} + + def make_pollster(self): + return node.AirflowPollster() + + @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) + def test_get_samples(self): + self._test_get_samples() + + # only one sample, and value is 190(0xbe as current_value) + self._verify_metering(1, 190, CONF.host) + + +class TestCUPSIndexPollster(base.TestPollsterBase): + + def fake_data(self): + # data after parsing Intel Node Manager output + return {"CUPS_Index": ['2e', '00']} + + def make_pollster(self): + return node.CUPSIndexPollster() + + @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) + def test_get_samples(self): + self._test_get_samples() + + # only one sample, and value is 190(0xbe) + self._verify_metering(1, 46, CONF.host) + + +class CPUUtilPollster(base.TestPollsterBase): + + def fake_data(self): + # data after parsing Intel Node Manager output + return {"CPU_Utilization": + ['33', '00', '00', '00', '00', '00', '00', '00']} + + def make_pollster(self): + return node.CPUUtilPollster() + + @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) + def test_get_samples(self): + self._test_get_samples() + + # only one sample, and value is 190(0xbe) + self._verify_metering(1, 51, CONF.host) + + +class MemUtilPollster(base.TestPollsterBase): + + def fake_data(self): + # data after parsing Intel Node Manager output + return {"Mem_Utilization": + ['05', '00', '00', '00', '00', '00', '00', '00']} + + def make_pollster(self): + return node.MemUtilPollster() + + @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) + def test_get_samples(self): + self._test_get_samples() + + # only one sample, and value is 5(0x05) + self._verify_metering(1, 5, CONF.host) + + +class IOUtilPollster(base.TestPollsterBase): + + def fake_data(self): + # data after parsing Intel Node Manager output + return {"IO_Utilization": + ['00', '00', '00', '00', '00', '00', '00', '00']} + + def make_pollster(self): + return node.IOUtilPollster() + + @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) + def test_get_samples(self): + self._test_get_samples() + + # only one sample, and value is 0(0x00) + self._verify_metering(1, 0, CONF.host) diff --git a/ceilometer/tests/ipmi/pollsters/test_sensor.py b/ceilometer/tests/ipmi/pollsters/test_sensor.py index 794d89dc..5405e089 100644 --- a/ceilometer/tests/ipmi/pollsters/test_sensor.py +++ b/ceilometer/tests/ipmi/pollsters/test_sensor.py @@ -49,10 +49,6 @@ class TestTemperatureSensorPollster(base.TestPollsterBase): def fake_sensor_data(self, sensor_type): return TEMPERATURE_SENSOR_DATA - def fake_data(self): - # No use for Sensor test - return None - def make_pollster(self): return sensor.TemperatureSensorPollster() @@ -68,10 +64,6 @@ class TestMissingSensorData(base.TestPollsterBase): def fake_sensor_data(self, sensor_type): return MISSING_SENSOR_DATA - def fake_data(self): - # No use for Sensor test - return None - def make_pollster(self): return sensor.TemperatureSensorPollster() @@ -86,10 +78,6 @@ class TestMalformedSensorData(base.TestPollsterBase): def fake_sensor_data(self, sensor_type): return MALFORMED_SENSOR_DATA - def fake_data(self): - # No use for Sensor test - return None - def make_pollster(self): return sensor.TemperatureSensorPollster() @@ -104,10 +92,6 @@ class TestMissingSensorId(base.TestPollsterBase): def fake_sensor_data(self, sensor_type): return MISSING_ID_SENSOR_DATA - def fake_data(self): - # No use for Sensor test - return None - def make_pollster(self): return sensor.TemperatureSensorPollster() @@ -122,10 +106,6 @@ class TestFanSensorPollster(base.TestPollsterBase): def fake_sensor_data(self, sensor_type): return FAN_SENSOR_DATA - def fake_data(self): - # No use for Sensor test - return None - def make_pollster(self): return sensor.FanSensorPollster() @@ -141,10 +121,6 @@ class TestCurrentSensorPollster(base.TestPollsterBase): def fake_sensor_data(self, sensor_type): return CURRENT_SENSOR_DATA - def fake_data(self): - # No use for Sensor test - return None - def make_pollster(self): return sensor.CurrentSensorPollster() @@ -160,10 +136,6 @@ class TestVoltageSensorPollster(base.TestPollsterBase): def fake_sensor_data(self, sensor_type): return VOLTAGE_SENSOR_DATA - def fake_data(self): - # No use for Sensor test - return None - def make_pollster(self): return sensor.VoltageSensorPollster() diff --git a/setup.cfg b/setup.cfg index d2402658..96a3cec5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -151,7 +151,13 @@ ceilometer.poll.compute = ceilometer.poll.ipmi = hardware.ipmi.node.power = ceilometer.ipmi.pollsters.node:PowerPollster - hardware.ipmi.node.temperature = ceilometer.ipmi.pollsters.node:TemperaturePollster + hardware.ipmi.node.inlet_temperature = ceilometer.ipmi.pollsters.node:InletTemperaturePollster + hardware.ipmi.node.outlet_temperature = ceilometer.ipmi.pollsters.node:OutletTemperaturePollster + hardware.ipmi.node.airflow = ceilometer.ipmi.pollsters.node:AirflowPollster + hardware.ipmi.node.cups = ceilometer.ipmi.pollsters.node:CUPSIndexPollster + hardware.ipmi.node.cpu_util = ceilometer.ipmi.pollsters.node:CPUUtilPollster + hardware.ipmi.node.mem_util = ceilometer.ipmi.pollsters.node:MemUtilPollster + hardware.ipmi.node.io_util = ceilometer.ipmi.pollsters.node:IOUtilPollster hardware.ipmi.temperature = ceilometer.ipmi.pollsters.sensor:TemperatureSensorPollster hardware.ipmi.voltage = ceilometer.ipmi.pollsters.sensor:VoltageSensorPollster hardware.ipmi.current = ceilometer.ipmi.pollsters.sensor:CurrentSensorPollster