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:
parent
4d92fca455
commit
8f78d80b5b
@ -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
|
||||
|
@ -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"))
|
||||
|
@ -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"])
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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,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)
|
||||
# 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', '')
|
||||
|
||||
# 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 = ('', '')
|
||||
|
||||
sensor_temperature = (sensor_temperature_data, '')
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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__().
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user