269 lines
7.8 KiB
Python
269 lines
7.8 KiB
Python
# 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 collections
|
|
import logging
|
|
|
|
from prometheus_client import Gauge
|
|
|
|
from ironic_prometheus_exporter import utils as ipe_utils
|
|
from ironic_prometheus_exporter.parsers import descriptions
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def _build_labels(node_message):
|
|
fields = ['node_name', 'node_uuid', 'instance_uuid']
|
|
if not node_message['node_name']:
|
|
fields.remove('node_name')
|
|
return {
|
|
k: node_message[k] for k in fields
|
|
}
|
|
|
|
|
|
def build_temperature_metrics(node_message):
|
|
"""Build Prometheus temperature metrics from Oslo message.
|
|
|
|
Takes Oslo notification message carrying Redfish sensor data and
|
|
produces a data structure suitable for submitting to Prometheus.
|
|
|
|
:param node_message: Oslo notification message
|
|
|
|
Examples::
|
|
|
|
.. code-block:: python
|
|
|
|
{
|
|
# metric name
|
|
'baremetal_temp_cpu_celsius':
|
|
[
|
|
# metric value
|
|
42,
|
|
# metric instance in form of Prometheus labels
|
|
{
|
|
'node_name': 'kninode',
|
|
'node_uuid', 'XXX-YYY-ZZZ',
|
|
'instance_uuid': 'ZZZ-YYY-XXX',
|
|
'entity_id': 'CPU',
|
|
'sensor_id': '1'
|
|
}
|
|
]
|
|
]
|
|
}
|
|
"""
|
|
payload = node_message
|
|
|
|
for key in ('payload', 'Temperature'):
|
|
payload = payload.get(key, {})
|
|
|
|
metrics = collections.defaultdict(list)
|
|
|
|
for sensor_id, sensor_data in payload.items():
|
|
metric = 'baremetal_temp_%s_celsius' % (
|
|
sensor_data['physical_context'].lower())
|
|
|
|
labels = _build_labels(node_message)
|
|
|
|
labels['entity_id'] = sensor_data['physical_context']
|
|
labels['sensor_id'] = sensor_data['sensor_number']
|
|
|
|
value = sensor_data['reading_celsius']
|
|
|
|
metrics[metric].append((value, labels))
|
|
|
|
return metrics
|
|
|
|
|
|
def build_power_metrics(node_message):
|
|
"""Build Prometheus power metrics from Oslo message.
|
|
|
|
Takes Oslo notification message carrying Redfish sensor data and
|
|
produces a data structure suitable for submitting to Prometheus.
|
|
|
|
:param node_message: Oslo notification message
|
|
|
|
Examples::
|
|
|
|
.. code-block:: python
|
|
|
|
{
|
|
# metric name
|
|
'baremetal_power_status':
|
|
[
|
|
# metric value (0 - OK, 1 - on fire)
|
|
0,
|
|
# metric instance in form of Prometheus labels
|
|
{
|
|
'node_name': 'kninode',
|
|
'node_uuid', 'XXX-YYY-ZZZ',
|
|
'instance_uuid': 'ZZZ-YYY-XXX',
|
|
'entity_id': 'PSU',
|
|
'sensor_id': '0:Power@ZZZ-YYY-XXX'
|
|
}
|
|
]
|
|
]
|
|
}
|
|
"""
|
|
payload = node_message
|
|
|
|
for key in ('payload', 'Power'):
|
|
payload = payload.get(key, {})
|
|
|
|
metrics = collections.defaultdict(list)
|
|
|
|
for sensor_id, sensor_data in payload.items():
|
|
metric = 'baremetal_power_status'
|
|
|
|
labels = _build_labels(node_message)
|
|
|
|
labels['entity_id'] = 'PSU'
|
|
labels['sensor_id'] = sensor_id
|
|
|
|
value = sensor_data['health'] != 'OK' and 1 or 0
|
|
|
|
metrics[metric].append((value, labels))
|
|
|
|
return metrics
|
|
|
|
|
|
def build_fan_metrics(node_message):
|
|
"""Build Prometheus fan metrics from Oslo message.
|
|
|
|
Takes Oslo notification message carrying Redfish sensor data and
|
|
produces a data structure suitable for submitting to Prometheus.
|
|
|
|
:param node_message: Oslo notification message
|
|
|
|
Examples::
|
|
|
|
.. code-block:: python
|
|
|
|
{
|
|
# metric name
|
|
'baremetal_fan_status':
|
|
[
|
|
# metric value (0 - OK, 1 - on fire)
|
|
0,
|
|
# metric instance in form of Prometheus labels
|
|
{
|
|
'node_name': 'kninode',
|
|
'node_uuid', 'XXX-YYY-ZZZ',
|
|
'instance_uuid': 'ZZZ-YYY-XXX',
|
|
'entity_id': 'CPU',
|
|
'sensor_id': '0:Power@ZZZ-YYY-XXX'
|
|
}
|
|
]
|
|
]
|
|
}
|
|
"""
|
|
payload = node_message
|
|
|
|
for key in ('payload', 'Fan'):
|
|
payload = payload.get(key, {})
|
|
|
|
metrics = collections.defaultdict(list)
|
|
|
|
for sensor_id, sensor_data in payload.items():
|
|
metric = 'baremetal_fan_status'
|
|
|
|
labels = _build_labels(node_message)
|
|
|
|
labels['entity_id'] = sensor_data['physical_context']
|
|
labels['sensor_id'] = sensor_data['identity']
|
|
|
|
value = sensor_data['health'] != 'OK' and 1 or 0
|
|
|
|
metrics[metric].append((value, labels))
|
|
|
|
return metrics
|
|
|
|
|
|
def build_drive_metrics(node_message):
|
|
"""Build Prometheus drive metrics from Oslo message.
|
|
|
|
Takes Oslo notification message carrying Redfish sensor data and
|
|
produces a data structure suitable for submitting to Prometheus.
|
|
|
|
:param node_message: Oslo notification message
|
|
|
|
Examples::
|
|
|
|
.. code-block:: python
|
|
|
|
{
|
|
# metric name
|
|
'baremetal_drive_status':
|
|
[
|
|
# metric value (0 - OK, 1 - on fire)
|
|
0,
|
|
# metric instance in form of Prometheus labels
|
|
{
|
|
'node_name': 'kninode',
|
|
'node_uuid', 'XXX-YYY-ZZZ',
|
|
'instance_uuid': 'ZZZ-YYY-XXX',
|
|
'entity_id': 'HDD',
|
|
'sensor_id': '32ADF365C6C1B7BD'
|
|
}
|
|
]
|
|
]
|
|
}
|
|
"""
|
|
payload = node_message
|
|
|
|
for key in ('payload', 'Drive'):
|
|
payload = payload.get(key, {})
|
|
|
|
metrics = collections.defaultdict(list)
|
|
|
|
for sensor_id, sensor_data in payload.items():
|
|
metric = 'baremetal_drive_status'
|
|
|
|
labels = _build_labels(node_message)
|
|
|
|
labels['entity_id'] = 'HDD'
|
|
labels['sensor_id'] = sensor_id
|
|
|
|
value = sensor_data['health'] != 'OK' and 1 or 0
|
|
|
|
metrics[metric].append((value, labels))
|
|
|
|
return metrics
|
|
|
|
|
|
def category_registry(node_message, metrics_registry):
|
|
"""Parse Redfish metrics and submit them to Prometheus
|
|
|
|
:param node_message: Oslo notification message
|
|
:param metrics_registry: Prometheus registry
|
|
"""
|
|
metrics = build_temperature_metrics(node_message)
|
|
metrics.update(build_power_metrics(node_message))
|
|
metrics.update(build_fan_metrics(node_message))
|
|
metrics.update(build_drive_metrics(node_message))
|
|
|
|
for metric, details in metrics.items():
|
|
|
|
LOG.debug('Creating metric %s', metric)
|
|
LOG.debug('Details of the metric: %s', details)
|
|
# details is a list of tuples that contains 2 elements (value, labels)
|
|
# let's get the first tuple and the dict of labels to extract the
|
|
# list of labels necessary for the Gauge
|
|
metric_labels = details[0][1]
|
|
desc = descriptions.get_metric_description('redfish', metric)
|
|
gauge = Gauge(metric, desc, labelnames=list(metric_labels),
|
|
registry=metrics_registry)
|
|
|
|
for value, labels in details:
|
|
valid_labels = ipe_utils.update_instance_uuid(labels)
|
|
gauge.labels(**valid_labels).set(value)
|