Chris Dent 8323cfe92a Handle poorly formed individual sensor readings
ipmitool returns a series of multiple readings per run. Some of
these readings may be of a form that parser code cannot handle. The
desired behavior in this case is for the single reading to be
dropped and for parsing to continue. This change ensures that
behavior by continuing through the loop instead of allowing
exceptions to cause the loop to exit.

Tests have been added which verify the correct behavior.

Note that though there was an opportunity to log each skipped
reading this would lead to a very large number of log messages as
many or even most samples from ipmitool are not parsed.

Change-Id: I6a3193d5a6e12c69ca5c548e5fb58d8bc9646348
Closes-Bug: #1381600
2014-10-15 17:22:41 +01:00

107 lines
2.9 KiB
Python

# Copyright 2014 Intel
#
# 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.
from oslo.config import cfg
from oslo.utils import timeutils
from ceilometer.ipmi.notifications import ironic as parser
from ceilometer.ipmi.platform import ipmi_sensor
from ceilometer import plugin
from ceilometer import sample
CONF = cfg.CONF
CONF.import_opt('host', 'ceilometer.service')
class InvalidSensorData(ValueError):
pass
class SensorPollster(plugin.PollsterBase):
METRIC = None
def __init__(self):
self.ipmi = ipmi_sensor.IPMISensor()
@property
def default_discovery(self):
return None
def _get_sensor_types(self, data, sensor_type):
try:
return (sensor_type_data for _, sensor_type_data
in data[sensor_type].items())
except KeyError:
return []
def get_samples(self, manager, cache, resources):
stats = self.ipmi.read_sensor_any(self.METRIC)
sensor_type_data = self._get_sensor_types(stats, self.METRIC)
for sensor_data in sensor_type_data:
# Continue if sensor_data is not parseable.
try:
sensor_reading = sensor_data['Sensor Reading']
sensor_id = sensor_data['Sensor ID']
except KeyError:
continue
if not parser.validate_reading(sensor_reading):
continue
try:
volume, unit = parser.parse_reading(sensor_reading)
except parser.InvalidSensorData:
continue
resource_id = '%(host)s-%(sensor-id)s' % {
'host': CONF.host,
'sensor-id': parser.transform_id(sensor_id)
}
metadata = {
'node': CONF.host
}
yield sample.Sample(
name='hardware.ipmi.%s' % self.METRIC.lower(),
type=sample.TYPE_GAUGE,
unit=unit,
volume=volume,
user_id=None,
project_id=None,
resource_id=resource_id,
timestamp=timeutils.utcnow().isoformat(),
resource_metadata=metadata)
class TemperatureSensorPollster(SensorPollster):
METRIC = 'Temperature'
class CurrentSensorPollster(SensorPollster):
METRIC = 'Current'
class FanSensorPollster(SensorPollster):
METRIC = 'Fan'
class VoltageSensorPollster(SensorPollster):
METRIC = 'Voltage'