'Custom Python HP ILO Node Exporter not changing hostname by request

Im trying to edit this project in Python to have HP ILO exporter for Prometheus, so far I read a few articles here on stackoverflow and tried to implement some functionalities, eventually I came up to partialy working script but the hostname is not changing after first request, is there a way to dump collector? I have tried it with try&except but it just does not work. The goal is to use curl like this

curl localhost:9116/metrics?hostname=ip

And what will happen if there will be 10 requests at the same time with different hostname? Should it create somekind of a queue?

Can someone help me? Thanks

Original Project : https://github.com/JackWindows/ilo-exporter

My code :

#!/usr/bin/env python
import collections
import os
import time
import flask
import redfish
import waitress
from flask import Flask
from prometheus_client import make_wsgi_app
from prometheus_client.core import GaugeMetricFamily, REGISTRY
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from flask import request
from time import sleep
from flask import Flask, Response, request
import traceback
from werkzeug.wsgi import ClosingIterator

class AfterResponse:
    def __init__(self, app=None):
        self.callbacks = []
        if app:
            self.init_app(app)

    def __call__(self, callback):
        self.callbacks.append(callback)
        return callback

    def init_app(self, app):
        # install extension
        app.after_response = self

        # install middleware
        app.wsgi_app = AfterResponseMiddleware(app.wsgi_app, self)

    def flush(self):
        for fn in self.callbacks:
            try:
                fn()
            except Exception:
                traceback.print_exc()

class AfterResponseMiddleware:
    def __init__(self, application, after_response_ext):
        self.application = application
        self.after_response_ext = after_response_ext

    def __call__(self, environ, after_response):
        iterator = self.application(environ, after_response)
        try:
            return ClosingIterator(iterator, [self.after_response_ext.flush])
        except Exception:
            traceback.print_exc()
            return iterator



class ILOCollector(object):
    def __init__(self, hostname: str, port: int = 443, user: str = 'admin', password: str = 'password') -> None:
        self.ilo = redfish.LegacyRestClient(base_url=hostname, username=user, password=password)
        self.ilo.login()

        system = self.ilo.get('/redfish/v1/Systems/1/').obj
        self.label_names = ('hostname', 'product_name', 'sn')
        self.label_values = (hostname, system.Model, system.SerialNumber.strip())

    def collect(self):
        embedded_media = self.ilo.get('/redfish/v1/Managers/1/EmbeddedMedia/').obj
        smart_storage = self.ilo.get('/redfish/v1/Systems/1/SmartStorage/').obj
        thermal = self.ilo.get('/redfish/v1/Chassis/1/Thermal/').obj
        power = self.ilo.get('/redfish/v1/Chassis/1/Power/').obj

        g = GaugeMetricFamily('hpilo_health',
                              'iLO health status, -1: Unknown, 0: OK, 1: Degraded, 2: Failed.',
                              labels=self.label_names + ('component',))

        def status_to_code(status: str) -> int:
            status = status.lower()
            ret = -1
            if status == 'ok':
                ret = 0
            elif status == 'warning':
                ret = 1
            elif status == 'failed':
                ret = 2
            return ret

        g.add_metric(self.label_values + ('embedded_media',), status_to_code(embedded_media.Controller.Status.Health))
        g.add_metric(self.label_values + ('smart_storage',), status_to_code(smart_storage.Status.Health))
        for fan in thermal.Fans:
            g.add_metric(self.label_values + (fan.FanName,), status_to_code(fan.Status.Health))
        yield g

        g = GaugeMetricFamily('hpilo_fan_speed', 'Fan speed in percentage.',
                              labels=self.label_names + ('fan',), unit='percentage')
        for fan in thermal.Fans:
            g.add_metric(self.label_values + (fan.FanName,), fan.CurrentReading)
        yield g

        sensors_by_unit = collections.defaultdict(list)
        for sensor in thermal.Temperatures:
            if sensor.Status.State.lower() != 'enabled':
                continue
            reading = sensor.CurrentReading
            unit = sensor.Units
            sensors_by_unit[unit].append((sensor.Name, reading))
        for unit in sensors_by_unit:
            g = GaugeMetricFamily('hpilo_temperature', 'Temperature sensors reading.',
                                  labels=self.label_names + ('sensor',), unit=unit.lower())
            for sensor_name, sensor_reading in sensors_by_unit[unit]:
                g.add_metric(self.label_values + (sensor_name,), sensor_reading)
            yield g

        g = GaugeMetricFamily('hpilo_power_current', 'Current power consumption in Watts.', labels=self.label_names,
                              unit='watts')
        g.add_metric(self.label_values, power.PowerConsumedWatts)
        yield g

        label_values = self.label_values + (str(power.PowerMetrics.IntervalInMin),)
        g = GaugeMetricFamily('hpilo_power_average', 'Average power consumption in Watts.',
                              labels=self.label_names + ('IntervalInMin',), unit='watts')
        g.add_metric(label_values, power.PowerMetrics.AverageConsumedWatts)
        yield g
        g = GaugeMetricFamily('hpilo_power_min', 'Min power consumption in Watts.',
                              labels=self.label_names + ('IntervalInMin',), unit='watts')
        g.add_metric(label_values, power.PowerMetrics.MinConsumedWatts)
        yield g
        g = GaugeMetricFamily('hpilo_power_max', 'Max power consumption in Watts.',
                              labels=self.label_names + ('IntervalInMin',), unit='watts')
        g.add_metric(label_values, power.PowerMetrics.MaxConsumedWatts)
        yield g


# Create Flask app
app = Flask('iLO Exporter')

@app.route('/')
def root():
    return '''<html>
<head><title>iLO Exporter</title></head>
<body>
<h1>iLO Exporter</h1>
<p><a href='/metrics'>Metrics</a></p>
</body>
</html>'''

AfterResponse(app)

@app.after_response
def say_hi():
    print("hi")
    
@app.route("/metrics")
def home():
    try:
        REGISTRY.unregister(collector)
    except:
        print("An exception occurred")
        pass
    port = int(os.getenv('ILO_PORT', 443))
    user = os.getenv('ILO_USER', 'admin')
    password = os.getenv('ILO_PASSWORD', 'password')
    hostname = request.args.get('hostname')
    app.wsgi_app = DispatcherMiddleware(app.wsgi_app, {
    '/metrics': make_wsgi_app()
    })
    collector = ILOCollector(hostname, port, user, password)
    REGISTRY.register(collector)
    
    



if __name__ == '__main__':
    exporter_port = int(os.getenv('LISTEN_PORT', 9116))
    waitress.serve(app, host='0.0.0.0', port=exporter_port)
    

        
        
        


        
    





Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source