import time import json import random import requests from collections.abc import MutableMapping from dataclasses import dataclass @dataclass class Metric: prefix: str value_name: str labels: dict value: int def format_prom(self) -> str: labels = ",".join( x + "=" + '"' + str(self.labels[x]) + '"' for x in self.labels.keys() ) return f"{self.prefix}_{self.value_name}{{{labels}}} {self.value}" class Collector(object): def __init__(self): # self.finalcommander_url = "radio.animebits.moe/api/fcmm_status" self.fcmm_urls = ["https://radio.animebits.moe/api/fcmm_status"] def _fetch_json(self, url): try: r = requests.get(url) data = json.loads(r.text) return data except json.JSONDecodeError: raise CollectorException(f"Failed to parse JSON at {url}") except requests.exceptions.ConnectionError: raise CollectorException(f"Failed to reach {url}") except requests.exceptions.ReadTimeout: raise CollectorException(f"Connection timeout to {url}") except: raise CollectorException("Request failed.") def get_fcmm(self, url): data = self._fetch_json(url) if "public_key" not in data.keys() or "servers" not in data.keys(): raise CollectorException("FinalCommander JSON seems to be wrong.") return data def get_instances(self, fcmm_data): for server in fcmm_data["servers"]: try: data = self._fetch_json(f"https://{server['Address']}/stats") except CollectorException: data = {} data["fcmm_data"] = server yield data def collect(self): metrics = [] fcmm_data = [] for url in self.fcmm_urls: try: fcmm_data.append(self.get_fcmm(url)) up = 1 except CollectorException: # TODO: log here, unreachable FCMM up = 0 metrics.append(Metric("fcmm", "up", {"url": url}, up)) servers_data = [ x for y in [self.get_instances(fcmm) for fcmm in fcmm_data] for x in y ] for server_data in servers_data: baselabel = {"server": server_data["fcmm_data"]["Address"]} if ( server_data["fcmm_data"]["LastCheckResult"] and "statistics" in server_data.keys() and "database" in servers_data.keys() ): metrics.append(Metric("orbt", "up", {**baselabel}, 1)) else: metrics.append(Metric("orbt", "up", {**baselabel}, 0)) continue for name, data in server_data["statistics"].items(): if name == "http" or name == "tls": for k, v in data.items(): for subtype, value in v.items(): metrics.append( Metric( "orbt_stats", f"{name}_{k}", {**baselabel, **{k: subtype}}, value, ) ) else: for valuetype, value in data.items(): metrics.append( Metric( "orbt_stats", name, {**baselabel, **{"type": valuetype}}, value, ) ) for name, value in server_data["database"].items(): metrics.append(Metric("orbt_db", name, {**baselabel}, value)) return metrics class CollectorException(Exception): """ Base exception class raised by Collector """