|
| 1 | +from netifaces import ifaddresses, AF_INET6 |
| 2 | +from subprocess import Popen, PIPE, DEVNULL |
| 3 | +import json |
| 4 | +import os |
| 5 | + |
| 6 | +class Metric(object): |
| 7 | + |
| 8 | + def __init__(self, sensor_id, sensor_key, value, label): |
| 9 | + self._value = self.parse_value(value) |
| 10 | + self._sensor_id = sensor_id |
| 11 | + self._sensor_key = sensor_key |
| 12 | + self._label = label |
| 13 | + |
| 14 | + @classmethod |
| 15 | + def parse_value(cls, value): |
| 16 | + parse = getattr(cls, "parse") |
| 17 | + return parse(value) |
| 18 | + |
| 19 | + # For debug purposes |
| 20 | + def __repr__(self): |
| 21 | + return "%s, %s: %s %s [%s]" % ( |
| 22 | + self._sensor_id, |
| 23 | + self._sensor_key, |
| 24 | + self._value, |
| 25 | + getattr(self, "unit", "?"), |
| 26 | + self._label) |
| 27 | + |
| 28 | +class Temperature(Metric): |
| 29 | + parse = float |
| 30 | + unit = "°C" |
| 31 | + |
| 32 | +class FanRpm(Metric): |
| 33 | + parse = float |
| 34 | + unit = "RPM" |
| 35 | + |
| 36 | +class FanFault(Metric): |
| 37 | + parse = float |
| 38 | + |
| 39 | +class Voltage(Metric): |
| 40 | + parse = float |
| 41 | + unit = "V" |
| 42 | + |
| 43 | +class Power(Metric): |
| 44 | + parse = float |
| 45 | + unit = "W" |
| 46 | + |
| 47 | +class Current(Metric): |
| 48 | + parse = float |
| 49 | + unit = "A" |
| 50 | + |
| 51 | +def get_metric_value(metrics, name): |
| 52 | + label, sensor_id, sensor_key = name.split("_") |
| 53 | + for metric in metrics: |
| 54 | + if metric._label == label and metric._sensor_id == sensor_id and metric._sensor_key == sensor_key: |
| 55 | + return metric._value |
| 56 | + return None |
| 57 | + |
| 58 | +def get_link_local_interface(): |
| 59 | + cdc_ether_path = "/sys/bus/usb/drivers/cdc_ether" |
| 60 | + for ether in os.listdir(cdc_ether_path): |
| 61 | + concrete_ether = os.path.join(cdc_ether_path, ether) |
| 62 | + if os.path.isdir(concrete_ether): |
| 63 | + concrete_ether_net = os.path.join(concrete_ether, 'net') |
| 64 | + if os.path.exists(concrete_ether_net): |
| 65 | + return os.listdir(concrete_ether_net)[0] |
| 66 | + |
| 67 | +def get_link_local_address(link_local_interface): |
| 68 | + for addr in ifaddresses(link_local_interface)[AF_INET6]: |
| 69 | + address = addr['addr'].split('%')[0] |
| 70 | + # according to rfc4291 this ipv6 address is used for link local connection |
| 71 | + if address.startswith('fe80:'): |
| 72 | + # first address is taken for BMC and second for this host |
| 73 | + return address[:-1] + '1' |
| 74 | + return None |
| 75 | + |
| 76 | +def get_psu_metrics(): |
| 77 | + link_local_interface = get_link_local_interface() |
| 78 | + link_local_address = get_link_local_address(link_local_interface) |
| 79 | + |
| 80 | + http_address = "http://[%s%%%s]:8080" % (link_local_address, link_local_interface) |
| 81 | + args = "/api/sys/bmc/sensors/%20-A%20-u%20" |
| 82 | + cmd = "curl " + http_address + args |
| 83 | + output = Popen(cmd.split(), stdout=PIPE, stderr=DEVNULL).stdout.read() |
| 84 | + output = json.loads(output.decode())["Information"]["Description"][0].strip() |
| 85 | + sections = output.split("\n\n") |
| 86 | + |
| 87 | + metrics = [] |
| 88 | + # iterating through drivers and their sensors |
| 89 | + for section in sections: |
| 90 | + fields = section.split("\n") |
| 91 | + |
| 92 | + label = None |
| 93 | + # iterating through sensors and their inputs |
| 94 | + for field in fields[1:]: # skipping driver name |
| 95 | + # parsing input sensor |
| 96 | + if field.startswith(" "): |
| 97 | + field = field.replace(" ", "") |
| 98 | + # split sensor into name and value |
| 99 | + field_key, field_value = field.split(": ") |
| 100 | + if "_" in field_key: |
| 101 | + sensor_id, sensor_key = field_key.split("_", 1) |
| 102 | + if sensor_key == "input": |
| 103 | + if sensor_id.startswith("temp"): |
| 104 | + metrics.append( |
| 105 | + Temperature(sensor_id, sensor_key, field_value, label=label)) |
| 106 | + elif sensor_id.startswith("in"): |
| 107 | + metrics.append( |
| 108 | + Voltage(sensor_id, sensor_key, field_value, label=label)) |
| 109 | + elif sensor_id.startswith("power"): |
| 110 | + metrics.append( |
| 111 | + Power(sensor_id, sensor_key, field_value, label=label)) |
| 112 | + elif sensor_id.startswith("curr"): |
| 113 | + metrics.append( |
| 114 | + Current(sensor_id, sensor_key, field_value, label=label)) |
| 115 | + elif sensor_id.startswith("fan"): |
| 116 | + metrics.append( |
| 117 | + FanRpm(sensor_id, sensor_key, field_value, label=label)) |
| 118 | + elif sensor_key == "fault": |
| 119 | + if sensor_id.startswith("fan"): |
| 120 | + metrics.append( |
| 121 | + FanFault(sensor_id, sensor_key, field_value, label=label)) |
| 122 | + elif field.startswith("ERROR"): |
| 123 | + syslog.syslog(syslog.LOG_INFO, field) |
| 124 | + else: |
| 125 | + label = field[:-1] # strip off trailing ":" character |
| 126 | + |
| 127 | + return metrics |
0 commit comments