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:
Edwin Zhai 2014-09-06 00:01:45 +08:00
parent a31b137539
commit 1081ac1b6e
19 changed files with 1548 additions and 0 deletions

View File

View File

View 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

View 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

View 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

View 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

View 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

View File

View 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

View 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, '')

View 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 == {})

View 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 == {})

View File

@ -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)):

View 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

View 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

View File

@ -10,6 +10,7 @@ module=log
module=log_handler
module=loopingcall
module=policy
module=processutils
module=service
module=threadgroup

View File

@ -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

View File

@ -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