diff --git a/mu5001tool/__main__.py b/mu5001tool/__main__.py index 87f4769..2522cfd 100644 --- a/mu5001tool/__main__.py +++ b/mu5001tool/__main__.py @@ -1,4 +1,5 @@ from . import Mu5001Tool +from .prometheus_exporter import prometheus_exporter import argparse from pprint import pprint @@ -27,6 +28,12 @@ def run_status(m): sp_status = subparsers.add_parser("status", help="General modem status information") sp_status.set_defaults(func=run_status) +def run_prometheus_exporter(m): + prometheus_exporter(m) + +sp_prometheus_exporter = subparsers.add_parser("prometheus-exporter", help="Serve metrics as prometheus exporter") +sp_prometheus_exporter.set_defaults(func=run_prometheus_exporter) + def main(): args = parser.parse_args() diff --git a/mu5001tool/prometheus_exporter.py b/mu5001tool/prometheus_exporter.py new file mode 100644 index 0000000..b03de09 --- /dev/null +++ b/mu5001tool/prometheus_exporter.py @@ -0,0 +1,155 @@ +from . import Mu5001Tool +from http.server import BaseHTTPRequestHandler, HTTPServer, HTTPStatus +import socket +import traceback + +class HTTPServerV6(HTTPServer): + address_family = socket.AF_INET6 + +def make_prometheus_exporter_request_handler(m): + class PrometheusExporterRequestHandler(BaseHTTPRequestHandler): + def do_GET(self, head_only=False): + if self.path == "/": + self.make_response( + "Prometheus Exporter for MU5001", + head_only=head_only + ) + elif self.path == "/metrics": + try: + self.make_response( + self.export(), + head_only=head_only + ) + except Exception as e: + traceback.print_exc() + + self.send_error(HTTPStatus.INTERNAL_SERVER_ERROR, "Failed to fetch metrics") + + else: + self.send_error(HTTPStatus.NOT_FOUND, "File not found") + + def do_HEAD(self): + self.do_GET(head_only=True) + + def make_response(self, content, head_only=False): + encoded = content.encode("utf-8") + self.send_response(HTTPStatus.OK) + self.send_header("Content-Type", "text/plain; charset=utf-8") + self.send_header("Conten-Length", str(len(encoded))) + self.end_headers() + if not head_only: + self.wfile.write(encoded) + + def export(self): + cmds_as_metric_value = [ + "adjfsdfj", + "battery_charging", + "battery_pers", + "battery_temp", + "battery_value", + "battery_vol_percent", + "data_volume_alert_percent", + "data_volume_limit_size", + "data_volume_limit_switch", + "dhcp_wan_status", + "lte_ca_pcell_band", + "lte_ca_pcell_bandwidth", + "lte_ca_scell_band", + "lte_ca_scell_bandwidth", + "lte_rsrp", + "lte_rssi", + "lte_snr", + "mdm_mcc", + "monthly_rx_bytes", + "monthly_time", + "monthly_tx_bytes", + "nr5g_action_channel", + "pin_status", + "ppp_dial_conn_fail_counter", + "realtime_rx_bytes", + "realtime_rx_thrpt", + "realtime_time", + "realtime_tx_bytes", + "realtime_tx_thrpt", + "rmcc", + "rmnc", + "signalbar", + "sms_unread_num", + "wan_active_channel", + "wifi_5g_enable", + "wifi_access_sta_num", + "wifi_chip1_ssid1_access_sta_num", + "wifi_chip1_ssid2_access_sta_num", + "wifi_chip2_ssid1_access_sta_num", + "wifi_chip2_ssid2_access_sta_num", + "wifi_onoff_state", + ] + + cmds_as_metric_label = [ + "battery_charg_type", + "data_volume_limit_unit", + "dial_mode", + "mode_main_state", + "modem_main_state", + "network_provider", + "network_type", + "opms_wan_auto_mode", + "opms_wan_mode", + "ppp_status", + "roam_setting_option", + "simcard_roam", + "spn_name_data", + "wan_connect_status", + "wan_lte_ca", + "wifi_chip1_ssid1_auth_mode", + "wifi_chip1_ssid1_ssid", + "wifi_chip2_ssid1_auth_mode", + "wifi_chip2_ssid1_ssid", + "cell_id", + "lte_pci", + "nr5g_action_band", + "nr5g_pci", + "wan_active_band", + ] + + data = m.get_cmd_process({ + "multi_data": 1, + "cmd": ",".join(cmds_as_metric_value + cmds_as_metric_label), + }) + + out = [] + + for cmd in cmds_as_metric_value: + d = data.get(cmd) + + if d is None: + continue + + try: + v = int(d) + except ValueError as e: + try: + v = float(d) + except ValueError as e: + raise ValueError(f"cmd: {cmd}: {e}") + + out.append(f"mu5001_{cmd} {v}") + + for cmd in cmds_as_metric_label: + d = data.get(cmd) + + if d is None or d == "": + continue + + out.append(f"mu5001_{cmd}{{{cmd}=\"{d}\"}} 1") + + out.sort() + + return "\n".join(out) + + return PrometheusExporterRequestHandler + +def prometheus_exporter(m): + with HTTPServerV6(("::1", 9242), make_prometheus_exporter_request_handler(m)) as httpd: + print("Starting prometheus exporter on http://[{}]:{}".format(*httpd.socket.getsockname()[:2])) + httpd.serve_forever()