Add IPMI support
Adds IPMI engine to get sensor data or system power/thermal information on IPMI capable node, improves overall efficiency and maximizes overall usage for data center. Implements bp ipmi-support Change-Id: I77c881fdaf39c69cfa990468110dbbfa1f8df8dd Signed-off-by: Zhai Edwin <edwin.zhai@intel.com>
This commit is contained in:
parent
a31b137539
commit
1081ac1b6e
0
ceilometer/ipmi/__init__.py
Normal file
0
ceilometer/ipmi/__init__.py
Normal file
0
ceilometer/ipmi/platform/__init__.py
Normal file
0
ceilometer/ipmi/platform/__init__.py
Normal file
24
ceilometer/ipmi/platform/exception.py
Normal file
24
ceilometer/ipmi/platform/exception.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Copyright 2014 Intel Corporation.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Author: Zhai Edwin <edwin.zhai@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
class NodeManagerException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class IPMIException(Exception):
|
||||||
|
pass
|
274
ceilometer/ipmi/platform/intel_node_manager.py
Normal file
274
ceilometer/ipmi/platform/intel_node_manager.py
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
# Copyright 2014 Intel Corporation.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Author: Zhai Edwin <edwin.zhai@intel.com>
|
||||||
|
# Author: Gao Fengqian <fengqian.gao@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""Node manager engine to collect power and temperature of compute node.
|
||||||
|
|
||||||
|
Intel Node Manager Technology enables the datacenter IT to monitor and control
|
||||||
|
actual server power, thermal and compute utlization behavior through industry
|
||||||
|
defined standard IPMI. This file provides Node Manager engine to get simple
|
||||||
|
system power and temperature data based on ipmitool.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import binascii
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
|
||||||
|
from ceilometer.ipmi.platform import exception as nmexcept
|
||||||
|
from ceilometer.ipmi.platform import ipmitool
|
||||||
|
from ceilometer.openstack.common.gettextutils import _
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
try:
|
||||||
|
import collections as ordereddict
|
||||||
|
except ImportError:
|
||||||
|
import ordereddict
|
||||||
|
|
||||||
|
node_manager_init_retry = cfg.IntOpt('node_manager_init_retry',
|
||||||
|
default=3,
|
||||||
|
help='Number of retries upon Intel Node '
|
||||||
|
'Manager initialization failure')
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
CONF.register_opt(node_manager_init_retry, group='ipmi')
|
||||||
|
|
||||||
|
IPMICMD = {"sdr_dump": "sdr dump",
|
||||||
|
"sdr_info": "sdr info",
|
||||||
|
"sensor_dump": "sdr -v"}
|
||||||
|
IPMIRAWCMD = {"get_device_id": "raw 0x06 0x01",
|
||||||
|
"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"}
|
||||||
|
|
||||||
|
MANUFACTURER_ID_INTEL = ['57', '01', '00']
|
||||||
|
INTEL_PREFIX = '5701000d01'
|
||||||
|
|
||||||
|
# The template dict are made according to the spec. It contains the expected
|
||||||
|
# length of each item. And it can be used to parse the output of IPMI command.
|
||||||
|
|
||||||
|
ONE_RETURN_TEMPLATE = {"ret": 1}
|
||||||
|
|
||||||
|
BMC_INFO_TEMPLATE = ordereddict.OrderedDict()
|
||||||
|
BMC_INFO_TEMPLATE['Device_ID'] = 1
|
||||||
|
BMC_INFO_TEMPLATE['Device_Revision'] = 1
|
||||||
|
BMC_INFO_TEMPLATE['Firmware_Revision_1'] = 1
|
||||||
|
BMC_INFO_TEMPLATE['Firmware_Revision_2'] = 1
|
||||||
|
BMC_INFO_TEMPLATE['IPMI_Version'] = 1
|
||||||
|
BMC_INFO_TEMPLATE['Additional_Device_support'] = 1
|
||||||
|
BMC_INFO_TEMPLATE['Manufacturer_ID'] = 3
|
||||||
|
BMC_INFO_TEMPLATE['Product_ID'] = 2
|
||||||
|
BMC_INFO_TEMPLATE['Auxiliary_Firmware_Revision'] = 4
|
||||||
|
|
||||||
|
NM_STATISTICS_TEMPLATE = ordereddict.OrderedDict()
|
||||||
|
NM_STATISTICS_TEMPLATE['Manufacturer_ID'] = 3
|
||||||
|
NM_STATISTICS_TEMPLATE['Current_value'] = 2
|
||||||
|
NM_STATISTICS_TEMPLATE['Minimum_value'] = 2
|
||||||
|
NM_STATISTICS_TEMPLATE['Maximum_value'] = 2
|
||||||
|
NM_STATISTICS_TEMPLATE['Average_value'] = 2
|
||||||
|
NM_STATISTICS_TEMPLATE['Time_stamp'] = 4
|
||||||
|
NM_STATISTICS_TEMPLATE['Report_period'] = 4
|
||||||
|
NM_STATISTICS_TEMPLATE["DomainID_PolicyState"] = 1
|
||||||
|
|
||||||
|
NM_GET_DEVICE_ID_TEMPLATE = ordereddict.OrderedDict()
|
||||||
|
NM_GET_DEVICE_ID_TEMPLATE['Device_ID'] = 1
|
||||||
|
NM_GET_DEVICE_ID_TEMPLATE['Device_revision'] = 1
|
||||||
|
NM_GET_DEVICE_ID_TEMPLATE['Firmware_revision_1'] = 1
|
||||||
|
NM_GET_DEVICE_ID_TEMPLATE['Firmware_Revision_2'] = 1
|
||||||
|
NM_GET_DEVICE_ID_TEMPLATE['IPMI_Version'] = 1
|
||||||
|
NM_GET_DEVICE_ID_TEMPLATE['Additinal_Device_support'] = 1
|
||||||
|
NM_GET_DEVICE_ID_TEMPLATE['Manufacturer_ID'] = 3
|
||||||
|
NM_GET_DEVICE_ID_TEMPLATE['Product_ID_min_version'] = 1
|
||||||
|
NM_GET_DEVICE_ID_TEMPLATE['Product_ID_major_version'] = 1
|
||||||
|
NM_GET_DEVICE_ID_TEMPLATE['Implemented_firmware'] = 1
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
def _hex(list=[]):
|
||||||
|
"""Format the return value in list into hex."""
|
||||||
|
if list:
|
||||||
|
list.reverse()
|
||||||
|
return int(''.join(list), 16)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
class NodeManager(object):
|
||||||
|
"""The python implementation of Intel Node Manager engine using ipmitool
|
||||||
|
|
||||||
|
The class implements the engine to read power and temperature of
|
||||||
|
compute node. It uses ipmitool to execute the IPMI command and parse
|
||||||
|
the output into dict.
|
||||||
|
"""
|
||||||
|
_inited = False
|
||||||
|
_instance = None
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
"""Singleton to avoid duplicated initialization."""
|
||||||
|
if not cls._instance:
|
||||||
|
cls._instance = super(NodeManager, cls).__new__(cls, *args,
|
||||||
|
**kwargs)
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if not (self._instance and self._inited):
|
||||||
|
self.nm_support = False
|
||||||
|
self.channel_slave = ''
|
||||||
|
self._inited = True
|
||||||
|
|
||||||
|
self.nm_support = self.check_node_manager()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _parse_slave_and_channel(file_path):
|
||||||
|
"""Parse the dumped file to get slave address and channel number.
|
||||||
|
|
||||||
|
:param file_path: file path of dumped SDR file.
|
||||||
|
:return: slave address and channel number of target device.
|
||||||
|
"""
|
||||||
|
ret = None
|
||||||
|
prefix = INTEL_PREFIX
|
||||||
|
# According to Intel Node Manager spec, section 4.5, for Intel NM
|
||||||
|
# discovery OEM SDR records are type C0h. It contains manufacture ID
|
||||||
|
# and OEM data in the record body.
|
||||||
|
# 0-2 bytes are OEM ID, byte 3 is 0Dh and byte 4 is 01h. Byte 5, 6
|
||||||
|
# is Intel NM device slave address and channel number/sensor owner LUN.
|
||||||
|
with open(file_path, 'rb') as bin_fp:
|
||||||
|
for line in bin_fp.readlines():
|
||||||
|
if line:
|
||||||
|
data_str = binascii.hexlify(line)
|
||||||
|
if prefix in data_str:
|
||||||
|
oem_id_index = data_str.index(prefix)
|
||||||
|
ret = data_str[oem_id_index + len(prefix):
|
||||||
|
oem_id_index + len(prefix) + 4]
|
||||||
|
# Byte 5 is slave address. [7:4] from byte 6 is channel
|
||||||
|
# number, so just pick ret[2] here.
|
||||||
|
ret = (ret[0:2], ret[2])
|
||||||
|
break
|
||||||
|
return ret
|
||||||
|
|
||||||
|
@ipmitool.execute_ipmi_cmd(BMC_INFO_TEMPLATE)
|
||||||
|
def get_device_id(self):
|
||||||
|
"""IPMI command GET_DEVICE_ID."""
|
||||||
|
return IPMIRAWCMD["get_device_id"]
|
||||||
|
|
||||||
|
@ipmitool.execute_ipmi_cmd(ONE_RETURN_TEMPLATE)
|
||||||
|
def _init_sensor_agent(self):
|
||||||
|
"""Run initialization agent."""
|
||||||
|
return IPMIRAWCMD["init_sensor_agent"]
|
||||||
|
|
||||||
|
@ipmitool.execute_ipmi_cmd(ONE_RETURN_TEMPLATE)
|
||||||
|
def _init_sensor_agent_process(self):
|
||||||
|
"""Check the status of initialization agent."""
|
||||||
|
return IPMIRAWCMD["init_sensor_agent_status"]
|
||||||
|
|
||||||
|
@ipmitool.execute_ipmi_cmd()
|
||||||
|
def _dump_sdr_file(self, data_file=""):
|
||||||
|
"""Dump SDR into a file."""
|
||||||
|
return IPMICMD["sdr_dump"] + " " + data_file
|
||||||
|
|
||||||
|
@ipmitool.execute_ipmi_cmd(NM_GET_DEVICE_ID_TEMPLATE)
|
||||||
|
def _node_manager_get_device_id(self):
|
||||||
|
"""GET_DEVICE_ID command in Intel Node Manager
|
||||||
|
|
||||||
|
Different from IPMI command GET_DEVICE_ID, it contains more information
|
||||||
|
of Intel Node Manager.
|
||||||
|
"""
|
||||||
|
return self.channel_slave + ' ' + IPMIRAWCMD["get_device_id"]
|
||||||
|
|
||||||
|
@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_power_all(self):
|
||||||
|
if self.nm_support:
|
||||||
|
return self._read_power_all()
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def read_temperature_all(self):
|
||||||
|
if self.nm_support:
|
||||||
|
return self._read_temperature_all()
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def init_node_manager(self):
|
||||||
|
if self._init_sensor_agent_process()['ret'] == ['01']:
|
||||||
|
return
|
||||||
|
# Run sensor initialization agent
|
||||||
|
for i in range(CONF.ipmi.node_manager_init_retry):
|
||||||
|
self._init_sensor_agent()
|
||||||
|
time.sleep(1)
|
||||||
|
if self._init_sensor_agent_process()['ret'] == ['01']:
|
||||||
|
return
|
||||||
|
|
||||||
|
raise nmexcept.NodeManagerException(_('Node Manager init failed'))
|
||||||
|
|
||||||
|
def discover_slave_channel(self):
|
||||||
|
"""Discover target slave address and channel number."""
|
||||||
|
file_path = tempfile.mkstemp()[1]
|
||||||
|
self._dump_sdr_file(data_file=file_path)
|
||||||
|
ret = self._parse_slave_and_channel(file_path)
|
||||||
|
slave_address = ''.join(['0x', ret[0]])
|
||||||
|
channel = ''.join(['0x', ret[1]])
|
||||||
|
# String of channel and slave_address
|
||||||
|
self.channel_slave = '-b ' + channel + ' -t ' + slave_address
|
||||||
|
|
||||||
|
def node_manager_support(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.
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
def check_node_manager(self):
|
||||||
|
"""Intel Node Manager init and check
|
||||||
|
|
||||||
|
This function is used to initialize Intel Node Manager and check the
|
||||||
|
capability without throwing exception. It's safe to call it on
|
||||||
|
non-NodeManager platform.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.init_node_manager()
|
||||||
|
has_nm = self.node_manager_support()
|
||||||
|
except (nmexcept.NodeManagerException, nmexcept.IPMIException):
|
||||||
|
return False
|
||||||
|
return has_nm
|
115
ceilometer/ipmi/platform/ipmi_sensor.py
Normal file
115
ceilometer/ipmi/platform/ipmi_sensor.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# Copyright 2014 Intel Corporation.
|
||||||
|
#
|
||||||
|
# Author: Zhai Edwin <edwin.zhai@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""IPMI sensor to collect various sensor data of compute node"""
|
||||||
|
|
||||||
|
from ceilometer.ipmi.platform import exception as ipmiexcept
|
||||||
|
from ceilometer.ipmi.platform import ipmitool
|
||||||
|
from ceilometer.openstack.common.gettextutils import _
|
||||||
|
|
||||||
|
IPMICMD = {"sdr_dump": "sdr dump",
|
||||||
|
"sdr_info": "sdr info",
|
||||||
|
"sensor_dump": "sdr -v",
|
||||||
|
"sensor_dump_temperature": "sdr -v type Temperature",
|
||||||
|
"sensor_dump_current": "sdr -v type Current",
|
||||||
|
"sensor_dump_fan": "sdr -v type Fan",
|
||||||
|
"sensor_dump_voltage": "sdr -v type Voltage"}
|
||||||
|
|
||||||
|
# Requires translation of output into dict
|
||||||
|
DICT_TRANSLATE_TEMPLATE = {"translate": 1}
|
||||||
|
|
||||||
|
|
||||||
|
class IPMISensor(object):
|
||||||
|
"""The python implementation of IPMI sensor using ipmitool
|
||||||
|
|
||||||
|
The class implements the IPMI sensor to get various sensor data of
|
||||||
|
compute node. It uses ipmitool to execute the IPMI command and parse
|
||||||
|
the output into dict.
|
||||||
|
"""
|
||||||
|
_inited = False
|
||||||
|
_instance = None
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
"""Singleton to avoid duplicated initialization."""
|
||||||
|
if not cls._instance:
|
||||||
|
cls._instance = super(IPMISensor, cls).__new__(cls, *args,
|
||||||
|
**kwargs)
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if not (self._instance and self._inited):
|
||||||
|
self.ipmi_support = False
|
||||||
|
self._inited = True
|
||||||
|
|
||||||
|
self.ipmi_support = self.check_ipmi()
|
||||||
|
|
||||||
|
@ipmitool.execute_ipmi_cmd()
|
||||||
|
def _get_sdr_info(self):
|
||||||
|
"""Get the SDR info."""
|
||||||
|
return IPMICMD['sdr_info']
|
||||||
|
|
||||||
|
@ipmitool.execute_ipmi_cmd(DICT_TRANSLATE_TEMPLATE)
|
||||||
|
def _read_sensor_all(self):
|
||||||
|
"""Get the sensor data for type."""
|
||||||
|
return IPMICMD['sensor_dump']
|
||||||
|
|
||||||
|
@ipmitool.execute_ipmi_cmd(DICT_TRANSLATE_TEMPLATE)
|
||||||
|
def _read_sensor_temperature(self):
|
||||||
|
"""Get the sensor data for Temperature."""
|
||||||
|
return IPMICMD['sensor_dump_temperature']
|
||||||
|
|
||||||
|
@ipmitool.execute_ipmi_cmd(DICT_TRANSLATE_TEMPLATE)
|
||||||
|
def _read_sensor_voltage(self):
|
||||||
|
"""Get the sensor data for Voltage."""
|
||||||
|
return IPMICMD['sensor_dump_voltage']
|
||||||
|
|
||||||
|
@ipmitool.execute_ipmi_cmd(DICT_TRANSLATE_TEMPLATE)
|
||||||
|
def _read_sensor_current(self):
|
||||||
|
"""Get the sensor data for Current."""
|
||||||
|
return IPMICMD['sensor_dump_current']
|
||||||
|
|
||||||
|
@ipmitool.execute_ipmi_cmd(DICT_TRANSLATE_TEMPLATE)
|
||||||
|
def _read_sensor_fan(self):
|
||||||
|
"""Get the sensor data for Fan."""
|
||||||
|
return IPMICMD['sensor_dump_fan']
|
||||||
|
|
||||||
|
def read_sensor_any(self, sensor_type=''):
|
||||||
|
"""Get the sensor data for type."""
|
||||||
|
if not self.ipmi_support:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
mapping = {'': self._read_sensor_all,
|
||||||
|
'Temperature': self._read_sensor_temperature,
|
||||||
|
'Fan': self._read_sensor_fan,
|
||||||
|
'Voltage': self._read_sensor_voltage,
|
||||||
|
'Current': self._read_sensor_current}
|
||||||
|
|
||||||
|
try:
|
||||||
|
return mapping[sensor_type]()
|
||||||
|
except KeyError:
|
||||||
|
raise ipmiexcept.IPMIException(_('Wrong sensor type'))
|
||||||
|
|
||||||
|
def check_ipmi(self):
|
||||||
|
"""IPMI capability checking
|
||||||
|
|
||||||
|
This function is used to detect if compute node is IPMI capable
|
||||||
|
platform. Just run a simple IPMI command to get SDR info for check.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self._get_sdr_info()
|
||||||
|
except ipmiexcept.IPMIException:
|
||||||
|
return False
|
||||||
|
return True
|
132
ceilometer/ipmi/platform/ipmitool.py
Normal file
132
ceilometer/ipmi/platform/ipmitool.py
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
# Copyright 2014 Intel Corp.
|
||||||
|
#
|
||||||
|
# Author: Zhai Edwin <edwin.zhai@intel.com>
|
||||||
|
# Author: whaom <whaom@cn.ibm.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""Utils to run ipmitool for data collection"""
|
||||||
|
|
||||||
|
from ceilometer.ipmi.platform import exception as ipmiexcept
|
||||||
|
from ceilometer.openstack.common.gettextutils import _
|
||||||
|
from ceilometer.openstack.common import processutils
|
||||||
|
from ceilometer import utils
|
||||||
|
|
||||||
|
|
||||||
|
# Following 2 functions are copied from ironic project to handle ipmitool's
|
||||||
|
# sensor data output. Need code clean and sharing in future.
|
||||||
|
# Check ironic/drivers/modules/ipmitool.py
|
||||||
|
|
||||||
|
|
||||||
|
def _get_sensor_type(sensor_data_dict):
|
||||||
|
# Have only three sensor type name IDs: 'Sensor Type (Analog)'
|
||||||
|
# 'Sensor Type (Discrete)' and 'Sensor Type (Threshold)'
|
||||||
|
|
||||||
|
for key in ('Sensor Type (Analog)', 'Sensor Type (Discrete)',
|
||||||
|
'Sensor Type (Threshold)'):
|
||||||
|
try:
|
||||||
|
return sensor_data_dict[key].split(' ', 1)[0]
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
raise ipmiexcept.IPMIException(_("parse IPMI sensor data failed,"
|
||||||
|
"unknown sensor type"))
|
||||||
|
|
||||||
|
|
||||||
|
def _process_sensor(sensor_data):
|
||||||
|
sensor_data_fields = sensor_data.split('\n')
|
||||||
|
sensor_data_dict = {}
|
||||||
|
for field in sensor_data_fields:
|
||||||
|
if not field:
|
||||||
|
continue
|
||||||
|
kv_value = field.split(':')
|
||||||
|
if len(kv_value) != 2:
|
||||||
|
continue
|
||||||
|
sensor_data_dict[kv_value[0].strip()] = kv_value[1].strip()
|
||||||
|
|
||||||
|
return sensor_data_dict
|
||||||
|
|
||||||
|
|
||||||
|
def _translate_output(output):
|
||||||
|
"""Translate the return value into JSON dict
|
||||||
|
|
||||||
|
:param output: output of the execution of IPMI command(sensor reading)
|
||||||
|
"""
|
||||||
|
sensors_data_dict = {}
|
||||||
|
|
||||||
|
sensors_data_array = output.split('\n\n')
|
||||||
|
for sensor_data in sensors_data_array:
|
||||||
|
sensor_data_dict = _process_sensor(sensor_data)
|
||||||
|
if not sensor_data_dict:
|
||||||
|
continue
|
||||||
|
|
||||||
|
sensor_type = _get_sensor_type(sensor_data_dict)
|
||||||
|
|
||||||
|
# ignore the sensors which have no current 'Sensor Reading' data
|
||||||
|
sensor_id = sensor_data_dict['Sensor ID']
|
||||||
|
if 'Sensor Reading' in sensor_data_dict:
|
||||||
|
sensors_data_dict.setdefault(sensor_type,
|
||||||
|
{})[sensor_id] = sensor_data_dict
|
||||||
|
|
||||||
|
# get nothing, no valid sensor data
|
||||||
|
if not sensors_data_dict:
|
||||||
|
raise ipmiexcept.IPMIException(_("parse IPMI sensor data failed,"
|
||||||
|
"No data retrieved from given input"))
|
||||||
|
return sensors_data_dict
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_output(output, template):
|
||||||
|
"""Parse the return value of IPMI command into dict
|
||||||
|
|
||||||
|
:param output: output of the execution of IPMI command
|
||||||
|
:param template: a dict that contains the expected items of
|
||||||
|
IPMI command and its length.
|
||||||
|
"""
|
||||||
|
ret = {}
|
||||||
|
index = 0
|
||||||
|
if not (output and template):
|
||||||
|
return ret
|
||||||
|
|
||||||
|
if "translate" in template:
|
||||||
|
ret = _translate_output(output)
|
||||||
|
else:
|
||||||
|
output_list = output.strip().split(' ')
|
||||||
|
if sum(template.values()) != len(output_list):
|
||||||
|
raise ipmiexcept.IPMIException(_("ipmitool output "
|
||||||
|
"length mismatch"))
|
||||||
|
for item in template.items():
|
||||||
|
index_end = index + item[1]
|
||||||
|
update_value = output_list[index: index_end]
|
||||||
|
ret[item[0]] = update_value
|
||||||
|
index = index_end
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def execute_ipmi_cmd(template={}):
|
||||||
|
"""Decorator for the execution of IPMI command.
|
||||||
|
|
||||||
|
It parses the output of IPMI command into dictionary.
|
||||||
|
"""
|
||||||
|
def _execute_ipmi_cmd(f):
|
||||||
|
def _execute(self, **kwargs):
|
||||||
|
args = ['ipmitool']
|
||||||
|
command = f(self, **kwargs)
|
||||||
|
args.extend(command.split(" "))
|
||||||
|
try:
|
||||||
|
(out, __) = utils.execute(*args, run_as_root=True)
|
||||||
|
except processutils.ProcessExecutionError:
|
||||||
|
raise ipmiexcept.IPMIException(_("running ipmitool failure"))
|
||||||
|
return _parse_output(out, template)
|
||||||
|
return _execute
|
||||||
|
|
||||||
|
return _execute_ipmi_cmd
|
285
ceilometer/openstack/common/processutils.py
Normal file
285
ceilometer/openstack/common/processutils.py
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
# Copyright 2011 OpenStack Foundation.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
System-level utilities and helper functions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import logging
|
||||||
|
import multiprocessing
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import shlex
|
||||||
|
import signal
|
||||||
|
|
||||||
|
from eventlet.green import subprocess
|
||||||
|
from eventlet import greenthread
|
||||||
|
import six
|
||||||
|
|
||||||
|
from ceilometer.openstack.common.gettextutils import _
|
||||||
|
from ceilometer.openstack.common import strutils
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidArgumentError(Exception):
|
||||||
|
def __init__(self, message=None):
|
||||||
|
super(InvalidArgumentError, self).__init__(message)
|
||||||
|
|
||||||
|
|
||||||
|
class UnknownArgumentError(Exception):
|
||||||
|
def __init__(self, message=None):
|
||||||
|
super(UnknownArgumentError, self).__init__(message)
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessExecutionError(Exception):
|
||||||
|
def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None,
|
||||||
|
description=None):
|
||||||
|
self.exit_code = exit_code
|
||||||
|
self.stderr = stderr
|
||||||
|
self.stdout = stdout
|
||||||
|
self.cmd = cmd
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
if description is None:
|
||||||
|
description = _("Unexpected error while running command.")
|
||||||
|
if exit_code is None:
|
||||||
|
exit_code = '-'
|
||||||
|
message = _('%(description)s\n'
|
||||||
|
'Command: %(cmd)s\n'
|
||||||
|
'Exit code: %(exit_code)s\n'
|
||||||
|
'Stdout: %(stdout)r\n'
|
||||||
|
'Stderr: %(stderr)r') % {'description': description,
|
||||||
|
'cmd': cmd,
|
||||||
|
'exit_code': exit_code,
|
||||||
|
'stdout': stdout,
|
||||||
|
'stderr': stderr}
|
||||||
|
super(ProcessExecutionError, self).__init__(message)
|
||||||
|
|
||||||
|
|
||||||
|
class NoRootWrapSpecified(Exception):
|
||||||
|
def __init__(self, message=None):
|
||||||
|
super(NoRootWrapSpecified, self).__init__(message)
|
||||||
|
|
||||||
|
|
||||||
|
def _subprocess_setup():
|
||||||
|
# Python installs a SIGPIPE handler by default. This is usually not what
|
||||||
|
# non-Python subprocesses expect.
|
||||||
|
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||||
|
|
||||||
|
|
||||||
|
def execute(*cmd, **kwargs):
|
||||||
|
"""Helper method to shell out and execute a command through subprocess.
|
||||||
|
|
||||||
|
Allows optional retry.
|
||||||
|
|
||||||
|
:param cmd: Passed to subprocess.Popen.
|
||||||
|
:type cmd: string
|
||||||
|
:param process_input: Send to opened process.
|
||||||
|
:type process_input: string
|
||||||
|
:param env_variables: Environment variables and their values that
|
||||||
|
will be set for the process.
|
||||||
|
:type env_variables: dict
|
||||||
|
:param check_exit_code: Single bool, int, or list of allowed exit
|
||||||
|
codes. Defaults to [0]. Raise
|
||||||
|
:class:`ProcessExecutionError` unless
|
||||||
|
program exits with one of these code.
|
||||||
|
:type check_exit_code: boolean, int, or [int]
|
||||||
|
:param delay_on_retry: True | False. Defaults to True. If set to True,
|
||||||
|
wait a short amount of time before retrying.
|
||||||
|
:type delay_on_retry: boolean
|
||||||
|
:param attempts: How many times to retry cmd.
|
||||||
|
:type attempts: int
|
||||||
|
:param run_as_root: True | False. Defaults to False. If set to True,
|
||||||
|
the command is prefixed by the command specified
|
||||||
|
in the root_helper kwarg.
|
||||||
|
:type run_as_root: boolean
|
||||||
|
:param root_helper: command to prefix to commands called with
|
||||||
|
run_as_root=True
|
||||||
|
:type root_helper: string
|
||||||
|
:param shell: whether or not there should be a shell used to
|
||||||
|
execute this command. Defaults to false.
|
||||||
|
:type shell: boolean
|
||||||
|
:param loglevel: log level for execute commands.
|
||||||
|
:type loglevel: int. (Should be logging.DEBUG or logging.INFO)
|
||||||
|
:returns: (stdout, stderr) from process execution
|
||||||
|
:raises: :class:`UnknownArgumentError` on
|
||||||
|
receiving unknown arguments
|
||||||
|
:raises: :class:`ProcessExecutionError`
|
||||||
|
"""
|
||||||
|
|
||||||
|
process_input = kwargs.pop('process_input', None)
|
||||||
|
env_variables = kwargs.pop('env_variables', None)
|
||||||
|
check_exit_code = kwargs.pop('check_exit_code', [0])
|
||||||
|
ignore_exit_code = False
|
||||||
|
delay_on_retry = kwargs.pop('delay_on_retry', True)
|
||||||
|
attempts = kwargs.pop('attempts', 1)
|
||||||
|
run_as_root = kwargs.pop('run_as_root', False)
|
||||||
|
root_helper = kwargs.pop('root_helper', '')
|
||||||
|
shell = kwargs.pop('shell', False)
|
||||||
|
loglevel = kwargs.pop('loglevel', logging.DEBUG)
|
||||||
|
|
||||||
|
if isinstance(check_exit_code, bool):
|
||||||
|
ignore_exit_code = not check_exit_code
|
||||||
|
check_exit_code = [0]
|
||||||
|
elif isinstance(check_exit_code, int):
|
||||||
|
check_exit_code = [check_exit_code]
|
||||||
|
|
||||||
|
if kwargs:
|
||||||
|
raise UnknownArgumentError(_('Got unknown keyword args: %r') % kwargs)
|
||||||
|
|
||||||
|
if run_as_root and hasattr(os, 'geteuid') and os.geteuid() != 0:
|
||||||
|
if not root_helper:
|
||||||
|
raise NoRootWrapSpecified(
|
||||||
|
message=_('Command requested root, but did not '
|
||||||
|
'specify a root helper.'))
|
||||||
|
cmd = shlex.split(root_helper) + list(cmd)
|
||||||
|
|
||||||
|
cmd = map(str, cmd)
|
||||||
|
sanitized_cmd = strutils.mask_password(' '.join(cmd))
|
||||||
|
|
||||||
|
while attempts > 0:
|
||||||
|
attempts -= 1
|
||||||
|
try:
|
||||||
|
LOG.log(loglevel, _('Running cmd (subprocess): %s'), sanitized_cmd)
|
||||||
|
_PIPE = subprocess.PIPE # pylint: disable=E1101
|
||||||
|
|
||||||
|
if os.name == 'nt':
|
||||||
|
preexec_fn = None
|
||||||
|
close_fds = False
|
||||||
|
else:
|
||||||
|
preexec_fn = _subprocess_setup
|
||||||
|
close_fds = True
|
||||||
|
|
||||||
|
obj = subprocess.Popen(cmd,
|
||||||
|
stdin=_PIPE,
|
||||||
|
stdout=_PIPE,
|
||||||
|
stderr=_PIPE,
|
||||||
|
close_fds=close_fds,
|
||||||
|
preexec_fn=preexec_fn,
|
||||||
|
shell=shell,
|
||||||
|
env=env_variables)
|
||||||
|
result = None
|
||||||
|
for _i in six.moves.range(20):
|
||||||
|
# NOTE(russellb) 20 is an arbitrary number of retries to
|
||||||
|
# prevent any chance of looping forever here.
|
||||||
|
try:
|
||||||
|
if process_input is not None:
|
||||||
|
result = obj.communicate(process_input)
|
||||||
|
else:
|
||||||
|
result = obj.communicate()
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno in (errno.EAGAIN, errno.EINTR):
|
||||||
|
continue
|
||||||
|
raise
|
||||||
|
break
|
||||||
|
obj.stdin.close() # pylint: disable=E1101
|
||||||
|
_returncode = obj.returncode # pylint: disable=E1101
|
||||||
|
LOG.log(loglevel, 'Result was %s' % _returncode)
|
||||||
|
if not ignore_exit_code and _returncode not in check_exit_code:
|
||||||
|
(stdout, stderr) = result
|
||||||
|
sanitized_stdout = strutils.mask_password(stdout)
|
||||||
|
sanitized_stderr = strutils.mask_password(stderr)
|
||||||
|
raise ProcessExecutionError(exit_code=_returncode,
|
||||||
|
stdout=sanitized_stdout,
|
||||||
|
stderr=sanitized_stderr,
|
||||||
|
cmd=sanitized_cmd)
|
||||||
|
return result
|
||||||
|
except ProcessExecutionError:
|
||||||
|
if not attempts:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
LOG.log(loglevel, _('%r failed. Retrying.'), sanitized_cmd)
|
||||||
|
if delay_on_retry:
|
||||||
|
greenthread.sleep(random.randint(20, 200) / 100.0)
|
||||||
|
finally:
|
||||||
|
# NOTE(termie): this appears to be necessary to let the subprocess
|
||||||
|
# call clean something up in between calls, without
|
||||||
|
# it two execute calls in a row hangs the second one
|
||||||
|
greenthread.sleep(0)
|
||||||
|
|
||||||
|
|
||||||
|
def trycmd(*args, **kwargs):
|
||||||
|
"""A wrapper around execute() to more easily handle warnings and errors.
|
||||||
|
|
||||||
|
Returns an (out, err) tuple of strings containing the output of
|
||||||
|
the command's stdout and stderr. If 'err' is not empty then the
|
||||||
|
command can be considered to have failed.
|
||||||
|
|
||||||
|
:discard_warnings True | False. Defaults to False. If set to True,
|
||||||
|
then for succeeding commands, stderr is cleared
|
||||||
|
|
||||||
|
"""
|
||||||
|
discard_warnings = kwargs.pop('discard_warnings', False)
|
||||||
|
|
||||||
|
try:
|
||||||
|
out, err = execute(*args, **kwargs)
|
||||||
|
failed = False
|
||||||
|
except ProcessExecutionError as exn:
|
||||||
|
out, err = '', six.text_type(exn)
|
||||||
|
failed = True
|
||||||
|
|
||||||
|
if not failed and discard_warnings and err:
|
||||||
|
# Handle commands that output to stderr but otherwise succeed
|
||||||
|
err = ''
|
||||||
|
|
||||||
|
return out, err
|
||||||
|
|
||||||
|
|
||||||
|
def ssh_execute(ssh, cmd, process_input=None,
|
||||||
|
addl_env=None, check_exit_code=True):
|
||||||
|
LOG.debug('Running cmd (SSH): %s', cmd)
|
||||||
|
if addl_env:
|
||||||
|
raise InvalidArgumentError(_('Environment not supported over SSH'))
|
||||||
|
|
||||||
|
if process_input:
|
||||||
|
# This is (probably) fixable if we need it...
|
||||||
|
raise InvalidArgumentError(_('process_input not supported over SSH'))
|
||||||
|
|
||||||
|
stdin_stream, stdout_stream, stderr_stream = ssh.exec_command(cmd)
|
||||||
|
channel = stdout_stream.channel
|
||||||
|
|
||||||
|
# NOTE(justinsb): This seems suspicious...
|
||||||
|
# ...other SSH clients have buffering issues with this approach
|
||||||
|
stdout = stdout_stream.read()
|
||||||
|
stderr = stderr_stream.read()
|
||||||
|
stdin_stream.close()
|
||||||
|
|
||||||
|
exit_status = channel.recv_exit_status()
|
||||||
|
|
||||||
|
# exit_status == -1 if no exit code was returned
|
||||||
|
if exit_status != -1:
|
||||||
|
LOG.debug('Result was %s' % exit_status)
|
||||||
|
if check_exit_code and exit_status != 0:
|
||||||
|
raise ProcessExecutionError(exit_code=exit_status,
|
||||||
|
stdout=stdout,
|
||||||
|
stderr=stderr,
|
||||||
|
cmd=cmd)
|
||||||
|
|
||||||
|
return (stdout, stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def get_worker_count():
|
||||||
|
"""Utility to get the default worker count.
|
||||||
|
|
||||||
|
@return: The number of CPUs if that can be determined, else a default
|
||||||
|
worker count of 1 is returned.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return multiprocessing.cpu_count()
|
||||||
|
except NotImplementedError:
|
||||||
|
return 1
|
0
ceilometer/tests/ipmi/__init__.py
Normal file
0
ceilometer/tests/ipmi/__init__.py
Normal file
0
ceilometer/tests/ipmi/platform/__init__.py
Normal file
0
ceilometer/tests/ipmi/platform/__init__.py
Normal file
98
ceilometer/tests/ipmi/platform/fake_utils.py
Normal file
98
ceilometer/tests/ipmi/platform/fake_utils.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# Copyright 2014 Intel Corp.
|
||||||
|
#
|
||||||
|
# Author: Zhai Edwin <edwin.zhai@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import binascii
|
||||||
|
|
||||||
|
from ceilometer.ipmi.platform import exception as nmexcept
|
||||||
|
from ceilometer.ipmi.platform import intel_node_manager as node_manager
|
||||||
|
from ceilometer.tests.ipmi.platform import ipmitool_test_data as test_data
|
||||||
|
|
||||||
|
|
||||||
|
def get_sensor_status_init(parameter=''):
|
||||||
|
return (' 01\n', '')
|
||||||
|
|
||||||
|
|
||||||
|
def get_sensor_status_uninit(parameter=''):
|
||||||
|
return (' 00\n', '')
|
||||||
|
|
||||||
|
|
||||||
|
def init_sensor_agent(parameter=''):
|
||||||
|
return (' 00\n', '')
|
||||||
|
|
||||||
|
|
||||||
|
def sdr_dump(data_file=''):
|
||||||
|
if data_file == '':
|
||||||
|
raise ValueError("No file specified for ipmitool sdr dump")
|
||||||
|
fake_slave_address = '2c'
|
||||||
|
fake_channel = '60'
|
||||||
|
hexstr = node_manager.INTEL_PREFIX + fake_slave_address + fake_channel
|
||||||
|
data = binascii.unhexlify(hexstr)
|
||||||
|
with open(data_file, 'wb') as bin_fp:
|
||||||
|
bin_fp.write(data)
|
||||||
|
|
||||||
|
return ('', '')
|
||||||
|
|
||||||
|
|
||||||
|
def _execute(funcs, *cmd, **kwargs):
|
||||||
|
|
||||||
|
datas = {
|
||||||
|
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.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,
|
||||||
|
test_data.read_sensor_current_cmd: test_data.sensor_current,
|
||||||
|
test_data.read_sensor_fan_cmd: test_data.sensor_fan,
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd[1] == 'sdr' and cmd[2] == 'dump':
|
||||||
|
# ipmitool sdr dump /tmp/XXXX
|
||||||
|
cmd_str = "".join(cmd[:3])
|
||||||
|
par_str = cmd[3]
|
||||||
|
else:
|
||||||
|
cmd_str = "".join(cmd)
|
||||||
|
par_str = ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
return datas[cmd_str]
|
||||||
|
except KeyError:
|
||||||
|
return funcs[cmd_str](par_str)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_with_nm(*cmd, **kwargs):
|
||||||
|
"""test version of execute on Node Manager 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}
|
||||||
|
|
||||||
|
return _execute(funcs, *cmd, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_without_nm(*cmd, **kwargs):
|
||||||
|
"""test version of execute on Non-Node Manager platform."""
|
||||||
|
|
||||||
|
funcs = {test_data.sensor_status_cmd: get_sensor_status_uninit,
|
||||||
|
test_data.init_sensor_cmd: init_sensor_agent,
|
||||||
|
test_data.sdr_dump_cmd: sdr_dump}
|
||||||
|
|
||||||
|
return _execute(funcs, *cmd, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_without_ipmi(*cmd, **kwargs):
|
||||||
|
raise nmexcept.IPMIException
|
359
ceilometer/tests/ipmi/platform/ipmitool_test_data.py
Normal file
359
ceilometer/tests/ipmi/platform/ipmitool_test_data.py
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
# Copyright 2014 Intel Corp.
|
||||||
|
#
|
||||||
|
# Author: Zhai Edwin <edwin.zhai@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""Sample data for test_intel_node_manager and test_ipmi_sensor.
|
||||||
|
|
||||||
|
This data is provided as a sample of the data expected from the ipmitool
|
||||||
|
binary, which produce Node Manager/IPMI raw data
|
||||||
|
"""
|
||||||
|
|
||||||
|
sensor_temperature_data = """Sensor ID : SSB Therm Trip (0xd)
|
||||||
|
Entity ID : 7.1 (System Board)
|
||||||
|
Sensor Type (Discrete): Temperature
|
||||||
|
Assertions Enabled : Digital State
|
||||||
|
[State Asserted]
|
||||||
|
Deassertions Enabled : Digital State
|
||||||
|
[State Asserted]
|
||||||
|
|
||||||
|
Sensor ID : BB P1 VR Temp (0x20)
|
||||||
|
Entity ID : 7.1 (System Board)
|
||||||
|
Sensor Type (Analog) : Temperature
|
||||||
|
Sensor Reading : 25 (+/- 0) degrees C
|
||||||
|
Status : ok
|
||||||
|
Nominal Reading : 58.000
|
||||||
|
Normal Minimum : 10.000
|
||||||
|
Normal Maximum : 105.000
|
||||||
|
Upper critical : 115.000
|
||||||
|
Upper non-critical : 110.000
|
||||||
|
Lower critical : 0.000
|
||||||
|
Lower non-critical : 5.000
|
||||||
|
Positive Hysteresis : 2.000
|
||||||
|
Negative Hysteresis : 2.000
|
||||||
|
Minimum sensor range : Unspecified
|
||||||
|
Maximum sensor range : Unspecified
|
||||||
|
Event Message Control : Per-threshold
|
||||||
|
Readable Thresholds : lcr lnc unc ucr
|
||||||
|
Settable Thresholds : lcr lnc unc ucr
|
||||||
|
Threshold Read Mask : lcr lnc unc ucr
|
||||||
|
Assertion Events :
|
||||||
|
Assertions Enabled : lnc- lcr- unc+ ucr+
|
||||||
|
Deassertions Enabled : lnc- lcr- unc+ ucr+
|
||||||
|
|
||||||
|
Sensor ID : Front Panel Temp (0x21)
|
||||||
|
Entity ID : 12.1 (Front Panel Board)
|
||||||
|
Sensor Type (Analog) : Temperature
|
||||||
|
Sensor Reading : 23 (+/- 0) degrees C
|
||||||
|
Status : ok
|
||||||
|
Nominal Reading : 28.000
|
||||||
|
Normal Minimum : 10.000
|
||||||
|
Normal Maximum : 45.000
|
||||||
|
Upper critical : 55.000
|
||||||
|
Upper non-critical : 50.000
|
||||||
|
Lower critical : 0.000
|
||||||
|
Lower non-critical : 5.000
|
||||||
|
Positive Hysteresis : 2.000
|
||||||
|
Negative Hysteresis : 2.000
|
||||||
|
Minimum sensor range : Unspecified
|
||||||
|
Maximum sensor range : Unspecified
|
||||||
|
Event Message Control : Per-threshold
|
||||||
|
Readable Thresholds : lcr lnc unc ucr
|
||||||
|
Settable Thresholds : lcr lnc unc ucr
|
||||||
|
Threshold Read Mask : lcr lnc unc ucr
|
||||||
|
Assertion Events :
|
||||||
|
Assertions Enabled : lnc- lcr- unc+ ucr+
|
||||||
|
Deassertions Enabled : lnc- lcr- unc+ ucr+
|
||||||
|
|
||||||
|
Sensor ID : SSB Temp (0x22)
|
||||||
|
Entity ID : 7.1 (System Board)
|
||||||
|
Sensor Type (Analog) : Temperature
|
||||||
|
Sensor Reading : 43 (+/- 0) degrees C
|
||||||
|
Status : ok
|
||||||
|
Nominal Reading : 52.000
|
||||||
|
Normal Minimum : 10.000
|
||||||
|
Normal Maximum : 93.000
|
||||||
|
Upper critical : 103.000
|
||||||
|
Upper non-critical : 98.000
|
||||||
|
Lower critical : 0.000
|
||||||
|
Lower non-critical : 5.000
|
||||||
|
Positive Hysteresis : 2.000
|
||||||
|
Negative Hysteresis : 2.000
|
||||||
|
Minimum sensor range : Unspecified
|
||||||
|
Maximum sensor range : Unspecified
|
||||||
|
Event Message Control : Per-threshold
|
||||||
|
Readable Thresholds : lcr lnc unc ucr
|
||||||
|
Settable Thresholds : lcr lnc unc ucr
|
||||||
|
Threshold Read Mask : lcr lnc unc ucr
|
||||||
|
Assertion Events :
|
||||||
|
Assertions Enabled : lnc- lcr- unc+ ucr+
|
||||||
|
Deassertions Enabled : lnc- lcr- unc+ ucr+
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
sensor_voltage_data = """Sensor ID : VR Watchdog (0xb)
|
||||||
|
Entity ID : 7.1 (System Board)
|
||||||
|
Sensor Type (Discrete): Voltage
|
||||||
|
Assertions Enabled : Digital State
|
||||||
|
[State Asserted]
|
||||||
|
Deassertions Enabled : Digital State
|
||||||
|
[State Asserted]
|
||||||
|
|
||||||
|
Sensor ID : BB +12.0V (0xd0)
|
||||||
|
Entity ID : 7.1 (System Board)
|
||||||
|
Sensor Type (Analog) : Voltage
|
||||||
|
Sensor Reading : 11.831 (+/- 0) Volts
|
||||||
|
Status : ok
|
||||||
|
Nominal Reading : 11.935
|
||||||
|
Normal Minimum : 11.363
|
||||||
|
Normal Maximum : 12.559
|
||||||
|
Upper critical : 13.391
|
||||||
|
Upper non-critical : 13.027
|
||||||
|
Lower critical : 10.635
|
||||||
|
Lower non-critical : 10.947
|
||||||
|
Positive Hysteresis : 0.052
|
||||||
|
Negative Hysteresis : 0.052
|
||||||
|
Minimum sensor range : Unspecified
|
||||||
|
Maximum sensor range : Unspecified
|
||||||
|
Event Message Control : Per-threshold
|
||||||
|
Readable Thresholds : lcr lnc unc ucr
|
||||||
|
Settable Thresholds : lcr lnc unc ucr
|
||||||
|
Threshold Read Mask : lcr lnc unc ucr
|
||||||
|
Assertion Events :
|
||||||
|
Assertions Enabled : lnc- lcr- unc+ ucr+
|
||||||
|
Deassertions Enabled : lnc- lcr- unc+ ucr+
|
||||||
|
|
||||||
|
Sensor ID : BB +1.35 P1LV AB (0xe4)
|
||||||
|
Entity ID : 7.1 (System Board)
|
||||||
|
Sensor Type (Analog) : Voltage
|
||||||
|
Sensor Reading : Disabled
|
||||||
|
Status : Disabled
|
||||||
|
Nominal Reading : 1.342
|
||||||
|
Normal Minimum : 1.275
|
||||||
|
Normal Maximum : 1.409
|
||||||
|
Upper critical : 1.488
|
||||||
|
Upper non-critical : 1.445
|
||||||
|
Lower critical : 1.201
|
||||||
|
Lower non-critical : 1.244
|
||||||
|
Positive Hysteresis : 0.006
|
||||||
|
Negative Hysteresis : 0.006
|
||||||
|
Minimum sensor range : Unspecified
|
||||||
|
Maximum sensor range : Unspecified
|
||||||
|
Event Message Control : Per-threshold
|
||||||
|
Readable Thresholds : lcr lnc unc ucr
|
||||||
|
Settable Thresholds : lcr lnc unc ucr
|
||||||
|
Threshold Read Mask : lcr lnc unc ucr
|
||||||
|
Event Status : Unavailable
|
||||||
|
Assertions Enabled : lnc- lcr- unc+ ucr+
|
||||||
|
Deassertions Enabled : lnc- lcr- unc+ ucr+
|
||||||
|
|
||||||
|
Sensor ID : BB +5.0V (0xd1)
|
||||||
|
Entity ID : 7.1 (System Board)
|
||||||
|
Sensor Type (Analog) : Voltage
|
||||||
|
Sensor Reading : 4.959 (+/- 0) Volts
|
||||||
|
Status : ok
|
||||||
|
Nominal Reading : 4.981
|
||||||
|
Normal Minimum : 4.742
|
||||||
|
Normal Maximum : 5.241
|
||||||
|
Upper critical : 5.566
|
||||||
|
Upper non-critical : 5.415
|
||||||
|
Lower critical : 4.416
|
||||||
|
Lower non-critical : 4.546
|
||||||
|
Positive Hysteresis : 0.022
|
||||||
|
Negative Hysteresis : 0.022
|
||||||
|
Minimum sensor range : Unspecified
|
||||||
|
Maximum sensor range : Unspecified
|
||||||
|
Event Message Control : Per-threshold
|
||||||
|
Readable Thresholds : lcr lnc unc ucr
|
||||||
|
Settable Thresholds : lcr lnc unc ucr
|
||||||
|
Threshold Read Mask : lcr lnc unc ucr
|
||||||
|
Assertion Events :
|
||||||
|
Assertions Enabled : lnc- lcr- unc+ ucr+
|
||||||
|
Deassertions Enabled : lnc- lcr- unc+ ucr+
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
sensor_current_data = """Sensor ID : PS1 Curr Out % (0x58)
|
||||||
|
Entity ID : 10.1 (Power Supply)
|
||||||
|
Sensor Type (Analog) : Current
|
||||||
|
Sensor Reading : 11 (+/- 0) unspecified
|
||||||
|
Status : ok
|
||||||
|
Nominal Reading : 50.000
|
||||||
|
Normal Minimum : 0.000
|
||||||
|
Normal Maximum : 100.000
|
||||||
|
Upper critical : 118.000
|
||||||
|
Upper non-critical : 100.000
|
||||||
|
Positive Hysteresis : Unspecified
|
||||||
|
Negative Hysteresis : Unspecified
|
||||||
|
Minimum sensor range : Unspecified
|
||||||
|
Maximum sensor range : Unspecified
|
||||||
|
Event Message Control : Per-threshold
|
||||||
|
Readable Thresholds : unc ucr
|
||||||
|
Settable Thresholds : unc ucr
|
||||||
|
Threshold Read Mask : unc ucr
|
||||||
|
Assertion Events :
|
||||||
|
Assertions Enabled : unc+ ucr+
|
||||||
|
Deassertions Enabled : unc+ ucr+
|
||||||
|
|
||||||
|
Sensor ID : PS2 Curr Out % (0x59)
|
||||||
|
Entity ID : 10.2 (Power Supply)
|
||||||
|
Sensor Type (Analog) : Current
|
||||||
|
Sensor Reading : 0 (+/- 0) unspecified
|
||||||
|
Status : ok
|
||||||
|
Nominal Reading : 50.000
|
||||||
|
Normal Minimum : 0.000
|
||||||
|
Normal Maximum : 100.000
|
||||||
|
Upper critical : 118.000
|
||||||
|
Upper non-critical : 100.000
|
||||||
|
Positive Hysteresis : Unspecified
|
||||||
|
Negative Hysteresis : Unspecified
|
||||||
|
Minimum sensor range : Unspecified
|
||||||
|
Maximum sensor range : Unspecified
|
||||||
|
Event Message Control : Per-threshold
|
||||||
|
Readable Thresholds : unc ucr
|
||||||
|
Settable Thresholds : unc ucr
|
||||||
|
Threshold Read Mask : unc ucr
|
||||||
|
Assertion Events :
|
||||||
|
Assertions Enabled : unc+ ucr+
|
||||||
|
Deassertions Enabled : unc+ ucr+
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
sensor_fan_data = """Sensor ID : System Fan 1 (0x30)
|
||||||
|
Entity ID : 29.1 (Fan Device)
|
||||||
|
Sensor Type (Analog) : Fan
|
||||||
|
Sensor Reading : 4704 (+/- 0) RPM
|
||||||
|
Status : ok
|
||||||
|
Nominal Reading : 7497.000
|
||||||
|
Normal Minimum : 2499.000
|
||||||
|
Normal Maximum : 12495.000
|
||||||
|
Lower critical : 1715.000
|
||||||
|
Lower non-critical : 1960.000
|
||||||
|
Positive Hysteresis : 49.000
|
||||||
|
Negative Hysteresis : 49.000
|
||||||
|
Minimum sensor range : Unspecified
|
||||||
|
Maximum sensor range : Unspecified
|
||||||
|
Event Message Control : Per-threshold
|
||||||
|
Readable Thresholds : lcr lnc
|
||||||
|
Settable Thresholds : lcr lnc
|
||||||
|
Threshold Read Mask : lcr lnc
|
||||||
|
Assertion Events :
|
||||||
|
Assertions Enabled : lnc- lcr-
|
||||||
|
Deassertions Enabled : lnc- lcr-
|
||||||
|
|
||||||
|
Sensor ID : System Fan 2 (0x32)
|
||||||
|
Entity ID : 29.2 (Fan Device)
|
||||||
|
Sensor Type (Analog) : Fan
|
||||||
|
Sensor Reading : 4704 (+/- 0) RPM
|
||||||
|
Status : ok
|
||||||
|
Nominal Reading : 7497.000
|
||||||
|
Normal Minimum : 2499.000
|
||||||
|
Normal Maximum : 12495.000
|
||||||
|
Lower critical : 1715.000
|
||||||
|
Lower non-critical : 1960.000
|
||||||
|
Positive Hysteresis : 49.000
|
||||||
|
Negative Hysteresis : 49.000
|
||||||
|
Minimum sensor range : Unspecified
|
||||||
|
Maximum sensor range : Unspecified
|
||||||
|
Event Message Control : Per-threshold
|
||||||
|
Readable Thresholds : lcr lnc
|
||||||
|
Settable Thresholds : lcr lnc
|
||||||
|
Threshold Read Mask : lcr lnc
|
||||||
|
Assertion Events :
|
||||||
|
Assertions Enabled : lnc- lcr-
|
||||||
|
Deassertions Enabled : lnc- lcr-
|
||||||
|
|
||||||
|
Sensor ID : System Fan 3 (0x34)
|
||||||
|
Entity ID : 29.3 (Fan Device)
|
||||||
|
Sensor Type (Analog) : Fan
|
||||||
|
Sensor Reading : 4704 (+/- 0) RPM
|
||||||
|
Status : ok
|
||||||
|
Nominal Reading : 7497.000
|
||||||
|
Normal Minimum : 2499.000
|
||||||
|
Normal Maximum : 12495.000
|
||||||
|
Lower critical : 1715.000
|
||||||
|
Lower non-critical : 1960.000
|
||||||
|
Positive Hysteresis : 49.000
|
||||||
|
Negative Hysteresis : 49.000
|
||||||
|
Minimum sensor range : Unspecified
|
||||||
|
Maximum sensor range : Unspecified
|
||||||
|
Event Message Control : Per-threshold
|
||||||
|
Readable Thresholds : lcr lnc
|
||||||
|
Settable Thresholds : lcr lnc
|
||||||
|
Threshold Read Mask : lcr lnc
|
||||||
|
Assertion Events :
|
||||||
|
Assertions Enabled : lnc- lcr-
|
||||||
|
Deassertions Enabled : lnc- lcr-
|
||||||
|
|
||||||
|
Sensor ID : System Fan 4 (0x36)
|
||||||
|
Entity ID : 29.4 (Fan Device)
|
||||||
|
Sensor Type (Analog) : Fan
|
||||||
|
Sensor Reading : 4606 (+/- 0) RPM
|
||||||
|
Status : ok
|
||||||
|
Nominal Reading : 7497.000
|
||||||
|
Normal Minimum : 2499.000
|
||||||
|
Normal Maximum : 12495.000
|
||||||
|
Lower critical : 1715.000
|
||||||
|
Lower non-critical : 1960.000
|
||||||
|
Positive Hysteresis : 49.000
|
||||||
|
Negative Hysteresis : 49.000
|
||||||
|
Minimum sensor range : Unspecified
|
||||||
|
Maximum sensor range : Unspecified
|
||||||
|
Event Message Control : Per-threshold
|
||||||
|
Readable Thresholds : lcr lnc
|
||||||
|
Settable Thresholds : lcr lnc
|
||||||
|
Threshold Read Mask : lcr lnc
|
||||||
|
Assertion Events :
|
||||||
|
Assertions Enabled : lnc- lcr-
|
||||||
|
Deassertions Enabled : lnc- lcr-
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
sensor_status_cmd = 'ipmitoolraw0x0a0x2c0x00'
|
||||||
|
init_sensor_cmd = 'ipmitoolraw0x0a0x2c0x01'
|
||||||
|
sdr_dump_cmd = 'ipmitoolsdrdump'
|
||||||
|
sdr_info_cmd = 'ipmitoolsdrinfo'
|
||||||
|
|
||||||
|
read_sensor_all_cmd = 'ipmitoolsdr-v'
|
||||||
|
read_sensor_temperature_cmd = 'ipmitoolsdr-vtypeTemperature'
|
||||||
|
read_sensor_voltage_cmd = 'ipmitoolsdr-vtypeVoltage'
|
||||||
|
read_sensor_current_cmd = 'ipmitoolsdr-vtypeCurrent'
|
||||||
|
read_sensor_fan_cmd = 'ipmitoolsdr-vtypeFan'
|
||||||
|
|
||||||
|
device_id_cmd = 'ipmitoolraw0x060x01'
|
||||||
|
nm_device_id_cmd = 'ipmitool-b0x6-t0x2craw0x060x01'
|
||||||
|
get_power_cmd = 'ipmitool-b0x6-t0x2craw0x2e0xc80x570x010x000x010x000x00'
|
||||||
|
get_temperature_cmd = 'ipmitool-b0x6-t0x2craw0x2e0xc80x570x010x000x020x000x00'
|
||||||
|
|
||||||
|
|
||||||
|
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', '')
|
||||||
|
|
||||||
|
# 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'
|
||||||
|
' 9b 12 01 50\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', '')
|
||||||
|
|
||||||
|
sdr_info = ('', '')
|
||||||
|
|
||||||
|
sensor_temperature = (sensor_temperature_data, '')
|
||||||
|
sensor_voltage = (sensor_voltage_data, '')
|
||||||
|
sensor_current = (sensor_current_data, '')
|
||||||
|
sensor_fan = (sensor_fan_data, '')
|
84
ceilometer/tests/ipmi/platform/test_intel_node_manager.py
Normal file
84
ceilometer/tests/ipmi/platform/test_intel_node_manager.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# Copyright 2014 Intel Corp.
|
||||||
|
#
|
||||||
|
# Author: Zhai Edwin <edwin.zhai@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from ceilometer.ipmi.platform import intel_node_manager as node_manager
|
||||||
|
from ceilometer.tests.ipmi.platform import fake_utils
|
||||||
|
from ceilometer import utils
|
||||||
|
|
||||||
|
from oslotest import base
|
||||||
|
|
||||||
|
|
||||||
|
class TestNodeManager(base.BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestNodeManager, self).setUp()
|
||||||
|
|
||||||
|
utils.execute = mock.Mock(side_effect=fake_utils.execute_with_nm)
|
||||||
|
self.nm = node_manager.NodeManager()
|
||||||
|
|
||||||
|
def test_read_power_all(self):
|
||||||
|
power = self.nm.read_power_all()
|
||||||
|
|
||||||
|
avg_val = node_manager._hex(power["Average_value"])
|
||||||
|
max_val = node_manager._hex(power["Maximum_value"])
|
||||||
|
min_val = node_manager._hex(power["Minimum_value"])
|
||||||
|
cur_val = node_manager._hex(power["Current_value"])
|
||||||
|
|
||||||
|
self.assertTrue(self.nm.nm_support)
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
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"])
|
||||||
|
|
||||||
|
self.assertTrue(self.nm.nm_support)
|
||||||
|
# see ipmi_test_data.py for raw data
|
||||||
|
self.assertEqual(23, cur_val)
|
||||||
|
self.assertEqual(22, min_val)
|
||||||
|
self.assertEqual(24, max_val)
|
||||||
|
self.assertEqual(23, avg_val)
|
||||||
|
|
||||||
|
|
||||||
|
class TestNonNodeManager(base.BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestNonNodeManager, self).setUp()
|
||||||
|
|
||||||
|
utils.execute = mock.Mock(side_effect=fake_utils.execute_without_nm)
|
||||||
|
self.nm = node_manager.NodeManager()
|
||||||
|
self.nm.nm_support = False
|
||||||
|
|
||||||
|
def test_read_power_all(self):
|
||||||
|
power = self.nm.read_power_all()
|
||||||
|
|
||||||
|
# Non-Node Manager platform return empty data
|
||||||
|
self.assertTrue(power == {})
|
||||||
|
|
||||||
|
def test_read_temperature_all(self):
|
||||||
|
temperature = self.nm.read_temperature_all()
|
||||||
|
|
||||||
|
# Non-Node Manager platform return empty data
|
||||||
|
self.assertTrue(temperature == {})
|
118
ceilometer/tests/ipmi/platform/test_ipmi_sensor.py
Normal file
118
ceilometer/tests/ipmi/platform/test_ipmi_sensor.py
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
# Copyright 2014 Intel Corp.
|
||||||
|
#
|
||||||
|
# Author: Zhai Edwin <edwin.zhai@intel.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from ceilometer.ipmi.platform import ipmi_sensor
|
||||||
|
from ceilometer.tests.ipmi.platform import fake_utils
|
||||||
|
from ceilometer import utils
|
||||||
|
|
||||||
|
from oslotest import base
|
||||||
|
|
||||||
|
|
||||||
|
class TestIPMISensor(base.BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestIPMISensor, self).setUp()
|
||||||
|
|
||||||
|
utils.execute = mock.Mock(side_effect=fake_utils.execute_with_nm)
|
||||||
|
self.ipmi = ipmi_sensor.IPMISensor()
|
||||||
|
|
||||||
|
def test_read_sensor_temperature(self):
|
||||||
|
sensors = self.ipmi.read_sensor_any('Temperature')
|
||||||
|
|
||||||
|
# only temperature data returned.
|
||||||
|
self.assertTrue('Temperature' in sensors)
|
||||||
|
self.assertEqual(1, len(sensors))
|
||||||
|
|
||||||
|
# 4 sensor data in total, ignore 1 without 'Sensor Reading'.
|
||||||
|
# Check ceilometer/tests/ipmi/platform/ipmi_test_data.py
|
||||||
|
self.assertEqual(3, len(sensors['Temperature']))
|
||||||
|
sensor = sensors['Temperature']['BB P1 VR Temp (0x20)']
|
||||||
|
self.assertEqual('25 (+/- 0) degrees C', sensor['Sensor Reading'])
|
||||||
|
|
||||||
|
def test_read_sensor_voltage(self):
|
||||||
|
sensors = self.ipmi.read_sensor_any('Voltage')
|
||||||
|
|
||||||
|
# only voltage data returned.
|
||||||
|
self.assertTrue('Voltage' in sensors)
|
||||||
|
self.assertEqual(1, len(sensors))
|
||||||
|
|
||||||
|
# 4 sensor data in total, ignore 1 without 'Sensor Reading'.
|
||||||
|
# Check ceilometer/tests/ipmi/platform/ipmi_test_data.py
|
||||||
|
self.assertEqual(3, len(sensors['Voltage']))
|
||||||
|
sensor = sensors['Voltage']['BB +5.0V (0xd1)']
|
||||||
|
self.assertEqual('4.959 (+/- 0) Volts', sensor['Sensor Reading'])
|
||||||
|
|
||||||
|
def test_read_sensor_current(self):
|
||||||
|
sensors = self.ipmi.read_sensor_any('Current')
|
||||||
|
|
||||||
|
# only Current data returned.
|
||||||
|
self.assertTrue('Current' in sensors)
|
||||||
|
self.assertEqual(1, len(sensors))
|
||||||
|
|
||||||
|
# 2 sensor data in total.
|
||||||
|
# Check ceilometer/tests/ipmi/platform/ipmi_test_data.py
|
||||||
|
self.assertEqual(2, len(sensors['Current']))
|
||||||
|
sensor = sensors['Current']['PS1 Curr Out % (0x58)']
|
||||||
|
self.assertEqual('11 (+/- 0) unspecified', sensor['Sensor Reading'])
|
||||||
|
|
||||||
|
def test_read_sensor_fan(self):
|
||||||
|
sensors = self.ipmi.read_sensor_any('Fan')
|
||||||
|
|
||||||
|
# only Fan data returned.
|
||||||
|
self.assertTrue('Fan' in sensors)
|
||||||
|
self.assertEqual(1, len(sensors))
|
||||||
|
|
||||||
|
# 2 sensor data in total.
|
||||||
|
# Check ceilometer/tests/ipmi/platform/ipmi_test_data.py
|
||||||
|
self.assertEqual(4, len(sensors['Fan']))
|
||||||
|
sensor = sensors['Fan']['System Fan 2 (0x32)']
|
||||||
|
self.assertEqual('4704 (+/- 0) RPM', sensor['Sensor Reading'])
|
||||||
|
|
||||||
|
|
||||||
|
class TestNonIPMISensor(base.BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestNonIPMISensor, self).setUp()
|
||||||
|
|
||||||
|
utils.execute = mock.Mock(side_effect=fake_utils.execute_without_ipmi)
|
||||||
|
self.ipmi = ipmi_sensor.IPMISensor()
|
||||||
|
self.ipmi.ipmi_support = False
|
||||||
|
|
||||||
|
def test_read_sensor_temperature(self):
|
||||||
|
sensors = self.ipmi.read_sensor_any('Temperature')
|
||||||
|
|
||||||
|
# Non-IPMI platform return empty data
|
||||||
|
self.assertTrue(sensors == {})
|
||||||
|
|
||||||
|
def test_read_sensor_voltage(self):
|
||||||
|
sensors = self.ipmi.read_sensor_any('Voltage')
|
||||||
|
|
||||||
|
# Non-IPMI platform return empty data
|
||||||
|
self.assertTrue(sensors == {})
|
||||||
|
|
||||||
|
def test_read_sensor_current(self):
|
||||||
|
sensors = self.ipmi.read_sensor_any('Current')
|
||||||
|
|
||||||
|
# Non-IPMI platform return empty data
|
||||||
|
self.assertTrue(sensors == {})
|
||||||
|
|
||||||
|
def test_read_sensor_fan(self):
|
||||||
|
sensors = self.ipmi.read_sensor_any('Fan')
|
||||||
|
|
||||||
|
# Non-IPMI platform return empty data
|
||||||
|
self.assertTrue(sensors == {})
|
@ -27,11 +27,32 @@ import hashlib
|
|||||||
import multiprocessing
|
import multiprocessing
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
|
from ceilometer.openstack.common import processutils
|
||||||
|
from oslo.config import cfg
|
||||||
from oslo.utils import timeutils
|
from oslo.utils import timeutils
|
||||||
from oslo.utils import units
|
from oslo.utils import units
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
|
||||||
|
rootwrap_conf = cfg.StrOpt('rootwrap_config',
|
||||||
|
default="/etc/ceilometer/rootwrap.conf",
|
||||||
|
help='Path to the rootwrap configuration file to'
|
||||||
|
'use for running commands as root')
|
||||||
|
CONF = cfg.CONF
|
||||||
|
CONF.register_opt(rootwrap_conf)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_root_helper():
|
||||||
|
return 'sudo ceilometer-rootwrap %s' % CONF.rootwrap_config
|
||||||
|
|
||||||
|
|
||||||
|
def execute(*cmd, **kwargs):
|
||||||
|
"""Convenience wrapper around oslo's execute() method."""
|
||||||
|
if 'run_as_root' in kwargs and 'root_helper' not in kwargs:
|
||||||
|
kwargs['root_helper'] = _get_root_helper()
|
||||||
|
return processutils.execute(*cmd, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def recursive_keypairs(d, separator=':'):
|
def recursive_keypairs(d, separator=':'):
|
||||||
"""Generator that produces sequence of keypairs for nested dictionaries."""
|
"""Generator that produces sequence of keypairs for nested dictionaries."""
|
||||||
for name, value in sorted(six.iteritems(d)):
|
for name, value in sorted(six.iteritems(d)):
|
||||||
|
27
etc/ceilometer/rootwrap.conf
Normal file
27
etc/ceilometer/rootwrap.conf
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Configuration for ceilometer-rootwrap
|
||||||
|
# This file should be owned by (and only-writeable by) the root user
|
||||||
|
|
||||||
|
[DEFAULT]
|
||||||
|
# List of directories to load filter definitions from (separated by ',').
|
||||||
|
# These directories MUST all be only writeable by root !
|
||||||
|
filters_path=/etc/ceilometer/rootwrap.d,/usr/share/ceilometer/rootwrap
|
||||||
|
|
||||||
|
# List of directories to search executables in, in case filters do not
|
||||||
|
# explicitely specify a full path (separated by ',')
|
||||||
|
# If not specified, defaults to system PATH environment variable.
|
||||||
|
# These directories MUST all be only writeable by root !
|
||||||
|
exec_dirs=/sbin,/usr/sbin,/bin,/usr/bin
|
||||||
|
|
||||||
|
# Enable logging to syslog
|
||||||
|
# Default value is False
|
||||||
|
use_syslog=False
|
||||||
|
|
||||||
|
# Which syslog facility to use.
|
||||||
|
# Valid values include auth, authpriv, syslog, user0, user1...
|
||||||
|
# Default value is 'syslog'
|
||||||
|
syslog_log_facility=syslog
|
||||||
|
|
||||||
|
# Which messages to log.
|
||||||
|
# INFO means log all usage
|
||||||
|
# ERROR means only log unsuccessful attempts
|
||||||
|
syslog_log_level=ERROR
|
7
etc/ceilometer/rootwrap.d/ipmi.filters
Normal file
7
etc/ceilometer/rootwrap.d/ipmi.filters
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# ceilometer-rootwrap command filters for IPMI capable nodes
|
||||||
|
# This file should be owned by (and only-writeable by) the root user
|
||||||
|
|
||||||
|
[Filters]
|
||||||
|
# ceilometer/ipmi/nodemanager/node_manager.py: 'ipmitool'
|
||||||
|
ipmitool: CommandFilter, ipmitool, root
|
||||||
|
|
@ -10,6 +10,7 @@ module=log
|
|||||||
module=log_handler
|
module=log_handler
|
||||||
module=loopingcall
|
module=loopingcall
|
||||||
module=policy
|
module=policy
|
||||||
|
module=processutils
|
||||||
module=service
|
module=service
|
||||||
module=threadgroup
|
module=threadgroup
|
||||||
|
|
||||||
|
@ -16,8 +16,10 @@ lockfile>=0.8
|
|||||||
lxml>=2.3
|
lxml>=2.3
|
||||||
msgpack-python>=0.4.0
|
msgpack-python>=0.4.0
|
||||||
netaddr>=0.7.6
|
netaddr>=0.7.6
|
||||||
|
ordereddict
|
||||||
oslo.db>=0.4.0
|
oslo.db>=0.4.0
|
||||||
oslo.config>=1.4.0.0a3
|
oslo.config>=1.4.0.0a3
|
||||||
|
oslo.rootwrap>=1.3.0.0a1
|
||||||
oslo.vmware>=0.5 # Apache-2.0
|
oslo.vmware>=0.5 # Apache-2.0
|
||||||
PasteDeploy>=1.5.0
|
PasteDeploy>=1.5.0
|
||||||
pbr>=0.6,!=0.7,<1.0
|
pbr>=0.6,!=0.7,<1.0
|
||||||
|
@ -263,6 +263,7 @@ console_scripts =
|
|||||||
ceilometer-send-sample = ceilometer.cli:send_sample
|
ceilometer-send-sample = ceilometer.cli:send_sample
|
||||||
ceilometer-dbsync = ceilometer.cmd.storage:dbsync
|
ceilometer-dbsync = ceilometer.cmd.storage:dbsync
|
||||||
ceilometer-expirer = ceilometer.cmd.storage:expirer
|
ceilometer-expirer = ceilometer.cmd.storage:expirer
|
||||||
|
ceilometer-rootwrap = oslo.rootwrap.cmd:main
|
||||||
ceilometer-collector = ceilometer.cmd.collector:main
|
ceilometer-collector = ceilometer.cmd.collector:main
|
||||||
ceilometer-alarm-evaluator = ceilometer.cmd.alarm:evaluator
|
ceilometer-alarm-evaluator = ceilometer.cmd.alarm:evaluator
|
||||||
ceilometer-alarm-notifier = ceilometer.cmd.alarm:notifier
|
ceilometer-alarm-notifier = ceilometer.cmd.alarm:notifier
|
||||||
|
Loading…
x
Reference in New Issue
Block a user