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
This commit is contained in:
Edwin Zhai 2015-01-24 17:59:31 +08:00 committed by Zhai, Edwin
parent 4d92fca455
commit 8f78d80b5b
12 changed files with 457 additions and 111 deletions

View File

@ -48,12 +48,18 @@ IPMICMD = {"sdr_dump": "sdr dump",
"sdr_info": "sdr info", "sdr_info": "sdr info",
"sensor_dump": "sdr -v"} "sensor_dump": "sdr -v"}
IPMIRAWCMD = {"get_device_id": "raw 0x06 0x01", IPMIRAWCMD = {"get_device_id": "raw 0x06 0x01",
"get_nm_version": "raw 0x2e 0xca 0x57 0x01 0x00",
"init_sensor_agent": "raw 0x0a 0x2c 0x01", "init_sensor_agent": "raw 0x0a 0x2c 0x01",
"init_complete": "raw 0x0a 0x2c 0x00", "init_complete": "raw 0x0a 0x2c 0x00",
"init_sensor_agent_status": "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_power_all": "raw 0x2e 0xc8 0x57 0x01 0x00 0x01 0x00 0x00",
"read_temperature_all": "read_inlet_temperature":
"raw 0x2e 0xc8 0x57 0x01 0x00 0x02 0x00 0x00"} "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'] MANUFACTURER_ID_INTEL = ['57', '01', '00']
INTEL_PREFIX = '5701000d01' 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['Last_digit_firmware_build_number'] = 1
NM_GET_DEVICE_ID_TEMPLATE['Image_flags'] = 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): def _hex(list=None):
"""Format the return value in list into hex.""" """Format the return value in list into hex."""
@ -130,11 +154,14 @@ class NodeManager(object):
def __init__(self): def __init__(self):
if not (self._instance and self._inited): if not (self._instance and self._inited):
self.nm_support = False # As singleton, only the 1st NM pollster would trigger its
self.channel_slave = '' # initialization. nm_version indicate init result, and is shared
# across all pollsters
self._inited = True 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 @staticmethod
def _parse_slave_and_channel(file_path): def _parse_slave_and_channel(file_path):
@ -193,27 +220,69 @@ class NodeManager(object):
""" """
return self.channel_slave + ' ' + IPMIRAWCMD["get_device_id"] 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) @ipmitool.execute_ipmi_cmd(NM_STATISTICS_TEMPLATE)
def _read_power_all(self): def _read_power_all(self):
"""Get the power consumption of the whole platform.""" """Get the power consumption of the whole platform."""
return self.channel_slave + ' ' + IPMIRAWCMD['read_power_all'] return self.channel_slave + ' ' + IPMIRAWCMD['read_power_all']
@ipmitool.execute_ipmi_cmd(NM_STATISTICS_TEMPLATE) @ipmitool.execute_ipmi_cmd(NM_STATISTICS_TEMPLATE)
def _read_temperature_all(self): def _read_inlet_temperature(self):
"""Get the temperature info of the whole platform.""" """Get the inlet temperature info of the whole platform."""
return self.channel_slave + ' ' + IPMIRAWCMD['read_temperature_all'] 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): def read_power_all(self):
if self.nm_support: return self._read_power_all() if self.nm_version > 0 else {}
return self._read_power_all()
return {} def read_inlet_temperature(self):
return self._read_inlet_temperature() if self.nm_version > 0 else {}
def read_temperature_all(self): def read_outlet_temperature(self):
if self.nm_support: return self._read_outlet_temperature() if self.nm_version >= 5 else {}
return self._read_temperature_all()
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): def init_node_manager(self):
if self._init_sensor_agent_process()['ret'] == ['01']: if self._init_sensor_agent_process()['ret'] == ['01']:
@ -237,26 +306,26 @@ class NodeManager(object):
# String of channel and slave_address # String of channel and slave_address
self.channel_slave = '-b ' + channel + ' -t ' + 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 """Intel Node Manager capability checking
This function is used to detect if compute node support Intel This function is used to detect if compute node support Intel Node
Node Manager or not and parse out the slave address and channel Manager(return version number) or not(return -1) and parse out the
number of node manager. slave address and channel number of node manager.
""" """
self.manufacturer_id = self.get_device_id()['Manufacturer_ID'] self.manufacturer_id = self.get_device_id()['Manufacturer_ID']
if MANUFACTURER_ID_INTEL != self.manufacturer_id: if MANUFACTURER_ID_INTEL != self.manufacturer_id:
# If the manufacturer is not Intel, just set False and return. # If the manufacturer is not Intel, just set False and return.
return False return 0
self.discover_slave_channel() self.discover_slave_channel()
support = self._node_manager_get_device_id()['Implemented_firmware'] support = self._node_manager_get_device_id()['Implemented_firmware']
# According to Intel Node Manager spec, return value of GET_DEVICE_ID, # According to Intel Node Manager spec, return value of GET_DEVICE_ID,
# bits 3 to 0 shows if Intel NM implemented or not. # bits 3 to 0 shows if Intel NM implemented or not.
if int(support[0], 16) & 0xf != 0: if int(support[0], 16) & 0xf == 0:
return True return 0
else:
return False return _hex(self._node_manager_get_version()['NM_Version'])
def check_node_manager(self): def check_node_manager(self):
"""Intel Node Manager init and check """Intel Node Manager init and check
@ -267,7 +336,7 @@ class NodeManager(object):
""" """
try: try:
self.init_node_manager() self.init_node_manager()
has_nm = self.node_manager_support() nm_version = self.node_manager_version()
except (nmexcept.NodeManagerException, nmexcept.IPMIException): except (nmexcept.NodeManagerException, nmexcept.IPMIException):
return False return 0
return has_nm return nm_version

View File

@ -97,7 +97,7 @@ def _parse_output(output, template):
if "translate" in template: if "translate" in template:
ret = _translate_output(output) ret = _translate_output(output)
else: else:
output_list = output.strip().split(' ') output_list = output.strip().replace('\n', '').split(' ')
if sum(template.values()) != len(output_list): if sum(template.values()) != len(output_list):
raise ipmiexcept.IPMIException(_("ipmitool output " raise ipmiexcept.IPMIException(_("ipmitool output "
"length mismatch")) "length mismatch"))

View File

@ -42,21 +42,25 @@ class _Base(plugin_base.PollsterBase):
self.polling_failures = 0 self.polling_failures = 0
# Do not load this extension if no NM support # 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() raise plugin_base.ExtensionLoadError()
@property @property
def default_discovery(self): def default_discovery(self):
return 'local_node' return 'local_node'
def get_value(self, stats):
"""Get value from statistics."""
return node_manager._hex(stats["Current_value"])
@abc.abstractmethod @abc.abstractmethod
def read_data(self): def read_data(self, cache):
"""Return data sample for IPMI.""" """Return data sample for IPMI."""
def get_samples(self, manager, cache, resources): def get_samples(self, manager, cache, resources):
# Only one resource for Node Manager pollster # Only one resource for Node Manager pollster
try: try:
stats = self.read_data() stats = self.read_data(cache)
except nmexcept.IPMIException: except nmexcept.IPMIException:
self.polling_failures += 1 self.polling_failures += 1
LOG.warning(_('Polling %(name)s faild for %(cnt)s times!') LOG.warning(_('Polling %(name)s faild for %(cnt)s times!')
@ -76,7 +80,7 @@ class _Base(plugin_base.PollsterBase):
} }
if stats: if stats:
data = node_manager._hex(stats["Current_value"]) data = self.get_value(stats)
yield sample.Sample( yield sample.Sample(
name=self.NAME, name=self.NAME,
@ -90,13 +94,22 @@ class _Base(plugin_base.PollsterBase):
resource_metadata=metadata) resource_metadata=metadata)
class TemperaturePollster(_Base): class InletTemperaturePollster(_Base):
NAME = "hardware.ipmi.node.temperature" NAME = "hardware.ipmi.node.inlet_temperature"
TYPE = sample.TYPE_GAUGE TYPE = sample.TYPE_GAUGE
UNIT = "C" UNIT = "C"
def read_data(self): def read_data(self, cache):
return self.nodemanager.read_temperature_all() 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): class PowerPollster(_Base):
@ -104,5 +117,63 @@ class PowerPollster(_Base):
TYPE = sample.TYPE_GAUGE TYPE = sample.TYPE_GAUGE
UNIT = "W" UNIT = "W"
def read_data(self): def read_data(self, cache):
return self.nodemanager.read_power_all() 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"])

View File

@ -59,8 +59,8 @@ class TestManager(base.BaseTestCase):
def test_load_normal_plugins(self): def test_load_normal_plugins(self):
mgr = manager.AgentManager(namespaces=['ipmi'], mgr = manager.AgentManager(namespaces=['ipmi'],
pollster_list=['hardware.ipmi.node.*']) pollster_list=['hardware.ipmi.node.*'])
# 2 pollsters for Node Manager # 8 pollsters for Node Manager
self.assertEqual(2, len(mgr.extensions)) self.assertEqual(8, len(mgr.extensions))
# Skip loading pollster upon ExtensionLoadError # Skip loading pollster upon ExtensionLoadError
@mock.patch('ceilometer.ipmi.pollsters.node._Base.__init__', @mock.patch('ceilometer.ipmi.pollsters.node._Base.__init__',
@ -75,7 +75,9 @@ class TestManager(base.BaseTestCase):
self.assertEqual(0, len(mgr.extensions)) self.assertEqual(0, len(mgr.extensions))
err_msg = 'Skip loading extension for hardware.ipmi.node.%s' 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] calls = [mock.call(err_msg % n) for n in pollster_names]
LOG.error.assert_has_calls(calls=calls, LOG.error.assert_has_calls(calls=calls,
any_order=True) any_order=True)

View File

@ -31,6 +31,14 @@ def init_sensor_agent(parameter=''):
return (' 00\n', '') 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=''): def sdr_dump(data_file=''):
if data_file == '': if data_file == '':
raise ValueError("No file specified for ipmitool sdr dump") 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.device_id_cmd: test_data.device_id,
test_data.nm_device_id_cmd: test_data.nm_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_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.sdr_info_cmd: test_data.sdr_info,
test_data.read_sensor_temperature_cmd: test_data.sensor_temperature, test_data.read_sensor_temperature_cmd: test_data.sensor_temperature,
test_data.read_sensor_voltage_cmd: test_data.sensor_voltage, 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) return funcs[cmd_str](par_str)
def execute_with_nm(*cmd, **kwargs): def execute_with_nm_v3(*cmd, **kwargs):
"""test version of execute on Node Manager platform.""" """test version of execute on Node Manager V3.0 platform."""
funcs = {test_data.sensor_status_cmd: get_sensor_status_init, funcs = {test_data.sensor_status_cmd: get_sensor_status_init,
test_data.init_sensor_cmd: init_sensor_agent, 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) return _execute(funcs, *cmd, **kwargs)

View File

@ -332,13 +332,21 @@ read_sensor_fan_cmd = 'ipmitoolsdr-vtypeFan'
device_id_cmd = 'ipmitoolraw0x060x01' device_id_cmd = 'ipmitoolraw0x060x01'
nm_device_id_cmd = 'ipmitool-b0x6-t0x2craw0x060x01' nm_device_id_cmd = 'ipmitool-b0x6-t0x2craw0x060x01'
nm_version_cmd = 'ipmitool-b0x6-t0x2craw0x2e0xca0x570x010x00'
get_power_cmd = 'ipmitool-b0x6-t0x2craw0x2e0xc80x570x010x000x010x000x00' 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', '') 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_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) # start from byte 3, get cur- 57 00(87), min- 03 00(3)
# max- 37 02(567), avg- 5c 00(92) # 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' power_data = (' 57 01 00 57 00 03 00 37 02 5c 00 cc 37 f4 53 ce\n'
@ -346,9 +354,27 @@ 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) # start from byte 3, get cur- 17 00(23), min- 16 00(22)
# max- 18 00(24), avg- 17 00(23) # 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' 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', '') ' 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 = ('', '') sdr_info = ('', '')
sensor_temperature = (sensor_temperature_data, '') sensor_temperature = (sensor_temperature_data, '')

View File

@ -12,7 +12,9 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import abc
import mock import mock
import six
from ceilometer.ipmi.platform import intel_node_manager as node_manager from ceilometer.ipmi.platform import intel_node_manager as node_manager
from ceilometer.tests.ipmi.platform import fake_utils from ceilometer.tests.ipmi.platform import fake_utils
@ -21,19 +23,83 @@ from ceilometer import utils
from oslotest import base 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): def setUp(self):
super(TestNodeManager, self).setUp() super(_Base, self).setUp()
self.init_test_engine()
utils.execute = mock.Mock(side_effect=fake_utils.execute_with_nm)
self.nm = node_manager.NodeManager() self.nm = node_manager.NodeManager()
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
# reset inited to force an initialization of singleton for next test # reset inited to force an initialization of singleton for next test
node_manager.NodeManager()._inited = False 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): def test_read_power_all(self):
power = self.nm.read_power_all() power = self.nm.read_power_all()
@ -43,15 +109,16 @@ class TestNodeManager(base.BaseTestCase):
min_val = node_manager._hex(power["Minimum_value"]) min_val = node_manager._hex(power["Minimum_value"])
cur_val = node_manager._hex(power["Current_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 # see ipmi_test_data.py for raw data
self.assertEqual(87, cur_val) self.assertEqual(87, cur_val)
self.assertEqual(3, min_val) self.assertEqual(3, min_val)
self.assertEqual(567, max_val) self.assertEqual(567, max_val)
self.assertEqual(92, avg_val) self.assertEqual(92, avg_val)
def test_read_temperature_all(self): def test_read_inlet_temperature(self):
temperature = self.nm.read_temperature_all() temperature = self.nm.read_inlet_temperature()
avg_val = node_manager._hex(temperature["Average_value"]) avg_val = node_manager._hex(temperature["Average_value"])
max_val = node_manager._hex(temperature["Maximum_value"]) max_val = node_manager._hex(temperature["Maximum_value"])
@ -64,30 +131,38 @@ class TestNodeManager(base.BaseTestCase):
self.assertEqual(24, max_val) self.assertEqual(24, max_val)
self.assertEqual(23, avg_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): def test_read_cups_utilization(self):
super(TestNonNodeManager, self).setUp() 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) 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): def test_read_power_all(self):
# no NM support
self.assertEqual(0, self.nm.nm_version)
power = self.nm.read_power_all() power = self.nm.read_power_all()
self.assertFalse(self.nm.nm_support)
# Non-Node Manager platform return empty data # Non-Node Manager platform return empty data
self.assertEqual({}, power) self.assertEqual({}, power)
def test_read_temperature_all(self): def test_read_inlet_temperature(self):
temperature = self.nm.read_temperature_all() temperature = self.nm.read_inlet_temperature()
# Non-Node Manager platform return empty data # Non-Node Manager platform return empty data
self.assertEqual({}, temperature) self.assertEqual({}, temperature)

View File

@ -25,7 +25,7 @@ class TestIPMISensor(base.BaseTestCase):
def setUp(self): def setUp(self):
super(TestIPMISensor, self).setUp() 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() self.ipmi = ipmi_sensor.IPMISensor()
@classmethod @classmethod

View File

@ -25,13 +25,13 @@ from ceilometer.tests import base
@six.add_metaclass(abc.ABCMeta) @six.add_metaclass(abc.ABCMeta)
class TestPollsterBase(base.BaseTestCase): class TestPollsterBase(base.BaseTestCase):
@abc.abstractmethod
def fake_data(self): def fake_data(self):
"""Fake data used for test.""" """Fake data used for test."""
return None
@abc.abstractmethod
def fake_sensor_data(self, sensor_type): def fake_sensor_data(self, sensor_type):
"""Fake sensor data used for test.""" """Fake sensor data used for test."""
return None
@abc.abstractmethod @abc.abstractmethod
def make_pollster(self): def make_pollster(self):
@ -39,8 +39,12 @@ class TestPollsterBase(base.BaseTestCase):
def _test_get_samples(self): def _test_get_samples(self):
nm = mock.Mock() 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_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 nm.read_sensor_any.side_effect = self.fake_sensor_data
# We should mock the pollster first before initialize the Manager # We should mock the pollster first before initialize the Manager
# so that we don't trigger the sudo in pollsters' __init__(). # so that we don't trigger the sudo in pollsters' __init__().

View File

@ -29,10 +29,6 @@ class TestPowerPollster(base.TestPollsterBase):
# data after parsing Intel Node Manager output # data after parsing Intel Node Manager output
return {"Current_value": ['13', '00']} return {"Current_value": ['13', '00']}
def fake_sensor_data(self, sensor_type):
# No use for this test
return None
def make_pollster(self): def make_pollster(self):
return node.PowerPollster() return node.PowerPollster()
@ -44,18 +40,14 @@ class TestPowerPollster(base.TestPollsterBase):
self._verify_metering(1, 19, CONF.host) self._verify_metering(1, 19, CONF.host)
class TestTemperaturePollster(base.TestPollsterBase): class TestInletTemperaturePollster(base.TestPollsterBase):
def fake_data(self): def fake_data(self):
# data after parsing Intel Node Manager output # data after parsing Intel Node Manager output
return {"Current_value": ['23', '00']} return {"Current_value": ['23', '00']}
def fake_sensor_data(self, sensor_type):
# No use for this test
return None
def make_pollster(self): def make_pollster(self):
return node.TemperaturePollster() return node.InletTemperaturePollster()
@mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock()) @mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock())
def test_get_samples(self): def test_get_samples(self):
@ -63,3 +55,108 @@ class TestTemperaturePollster(base.TestPollsterBase):
# only one sample, and value is 35(0x23 as current_value) # only one sample, and value is 35(0x23 as current_value)
self._verify_metering(1, 35, CONF.host) 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)

View File

@ -49,10 +49,6 @@ class TestTemperatureSensorPollster(base.TestPollsterBase):
def fake_sensor_data(self, sensor_type): def fake_sensor_data(self, sensor_type):
return TEMPERATURE_SENSOR_DATA return TEMPERATURE_SENSOR_DATA
def fake_data(self):
# No use for Sensor test
return None
def make_pollster(self): def make_pollster(self):
return sensor.TemperatureSensorPollster() return sensor.TemperatureSensorPollster()
@ -68,10 +64,6 @@ class TestMissingSensorData(base.TestPollsterBase):
def fake_sensor_data(self, sensor_type): def fake_sensor_data(self, sensor_type):
return MISSING_SENSOR_DATA return MISSING_SENSOR_DATA
def fake_data(self):
# No use for Sensor test
return None
def make_pollster(self): def make_pollster(self):
return sensor.TemperatureSensorPollster() return sensor.TemperatureSensorPollster()
@ -86,10 +78,6 @@ class TestMalformedSensorData(base.TestPollsterBase):
def fake_sensor_data(self, sensor_type): def fake_sensor_data(self, sensor_type):
return MALFORMED_SENSOR_DATA return MALFORMED_SENSOR_DATA
def fake_data(self):
# No use for Sensor test
return None
def make_pollster(self): def make_pollster(self):
return sensor.TemperatureSensorPollster() return sensor.TemperatureSensorPollster()
@ -104,10 +92,6 @@ class TestMissingSensorId(base.TestPollsterBase):
def fake_sensor_data(self, sensor_type): def fake_sensor_data(self, sensor_type):
return MISSING_ID_SENSOR_DATA return MISSING_ID_SENSOR_DATA
def fake_data(self):
# No use for Sensor test
return None
def make_pollster(self): def make_pollster(self):
return sensor.TemperatureSensorPollster() return sensor.TemperatureSensorPollster()
@ -122,10 +106,6 @@ class TestFanSensorPollster(base.TestPollsterBase):
def fake_sensor_data(self, sensor_type): def fake_sensor_data(self, sensor_type):
return FAN_SENSOR_DATA return FAN_SENSOR_DATA
def fake_data(self):
# No use for Sensor test
return None
def make_pollster(self): def make_pollster(self):
return sensor.FanSensorPollster() return sensor.FanSensorPollster()
@ -141,10 +121,6 @@ class TestCurrentSensorPollster(base.TestPollsterBase):
def fake_sensor_data(self, sensor_type): def fake_sensor_data(self, sensor_type):
return CURRENT_SENSOR_DATA return CURRENT_SENSOR_DATA
def fake_data(self):
# No use for Sensor test
return None
def make_pollster(self): def make_pollster(self):
return sensor.CurrentSensorPollster() return sensor.CurrentSensorPollster()
@ -160,10 +136,6 @@ class TestVoltageSensorPollster(base.TestPollsterBase):
def fake_sensor_data(self, sensor_type): def fake_sensor_data(self, sensor_type):
return VOLTAGE_SENSOR_DATA return VOLTAGE_SENSOR_DATA
def fake_data(self):
# No use for Sensor test
return None
def make_pollster(self): def make_pollster(self):
return sensor.VoltageSensorPollster() return sensor.VoltageSensorPollster()

View File

@ -151,7 +151,13 @@ ceilometer.poll.compute =
ceilometer.poll.ipmi = ceilometer.poll.ipmi =
hardware.ipmi.node.power = ceilometer.ipmi.pollsters.node:PowerPollster 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.temperature = ceilometer.ipmi.pollsters.sensor:TemperatureSensorPollster
hardware.ipmi.voltage = ceilometer.ipmi.pollsters.sensor:VoltageSensorPollster hardware.ipmi.voltage = ceilometer.ipmi.pollsters.sensor:VoltageSensorPollster
hardware.ipmi.current = ceilometer.ipmi.pollsters.sensor:CurrentSensorPollster hardware.ipmi.current = ceilometer.ipmi.pollsters.sensor:CurrentSensorPollster