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 struct
|
||||
|
||||
from ceilometer.openstack.common import processutils
|
||||
from oslo.config import cfg
|
||||
from oslo.utils import timeutils
|
||||
from oslo.utils import units
|
||||
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=':'):
|
||||
"""Generator that produces sequence of keypairs for nested dictionaries."""
|
||||
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=loopingcall
|
||||
module=policy
|
||||
module=processutils
|
||||
module=service
|
||||
module=threadgroup
|
||||
|
||||
|
@ -16,8 +16,10 @@ lockfile>=0.8
|
||||
lxml>=2.3
|
||||
msgpack-python>=0.4.0
|
||||
netaddr>=0.7.6
|
||||
ordereddict
|
||||
oslo.db>=0.4.0
|
||||
oslo.config>=1.4.0.0a3
|
||||
oslo.rootwrap>=1.3.0.0a1
|
||||
oslo.vmware>=0.5 # Apache-2.0
|
||||
PasteDeploy>=1.5.0
|
||||
pbr>=0.6,!=0.7,<1.0
|
||||
|
@ -263,6 +263,7 @@ console_scripts =
|
||||
ceilometer-send-sample = ceilometer.cli:send_sample
|
||||
ceilometer-dbsync = ceilometer.cmd.storage:dbsync
|
||||
ceilometer-expirer = ceilometer.cmd.storage:expirer
|
||||
ceilometer-rootwrap = oslo.rootwrap.cmd:main
|
||||
ceilometer-collector = ceilometer.cmd.collector:main
|
||||
ceilometer-alarm-evaluator = ceilometer.cmd.alarm:evaluator
|
||||
ceilometer-alarm-notifier = ceilometer.cmd.alarm:notifier
|
||||
|
Loading…
x
Reference in New Issue
Block a user