This commit is contained in:
w1kl4s 2022-07-16 18:27:05 +02:00
parent b2105df1ac
commit a306198c5a
Signed by: w1kl4s
GPG key ID: 7C5B542C41DCE7AE
5 changed files with 73 additions and 45 deletions

View file

@ -1,2 +1,2 @@
[bdist_wheel] [bdist_wheel]
universal=0 universal = 0

View file

@ -1,4 +1,5 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
# To use a consistent encoding # To use a consistent encoding
from codecs import open from codecs import open
from os import path from os import path
@ -11,35 +12,23 @@ with open(path.join(here, "README.md"), encoding="utf-8") as f:
setup( setup(
name="radio_exporter", name="radio_exporter",
version="0.1.0",
version='0.1.0',
description="", description="",
long_description=long_description, long_description=long_description,
url="https://git.gammaspectra.live/S.O.N.G/radio_exporter", url="https://git.gammaspectra.live/S.O.N.G/radio_exporter",
author="w1kl4s", author="w1kl4s",
author_email="w1kl4s@protonmail.com", author_email="w1kl4s@protonmail.com",
license="BSD 2-Clause", license="BSD 2-Clause",
classifiers=[ classifiers=[
"Development Status :: 4 - Beta", "Development Status :: 4 - Beta",
"Intended Audience :: System Administrators", "Intended Audience :: System Administrators",
"Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.8",
], ],
python_requires=">=3.6, <4", python_requires=">=3.6, <4",
keywords="monitoring metrics exporter radio", keywords="monitoring metrics exporter radio",
packages=["src"], packages=["src"],
install_requires=[ install_requires=["requests"],
"requests" entry_points={"console_scripts": ["radio_exporter=src.main:start"]},
],
entry_points={
"console_scripts": ["radio_exporter=src.main:start"]
}
) )

View file

@ -5,26 +5,32 @@ import requests
from collections.abc import MutableMapping from collections.abc import MutableMapping
from dataclasses import dataclass from dataclasses import dataclass
@dataclass @dataclass
class Metric: class Metric:
prefix: str prefix: str
value_name: str value_name: str
labels: dict labels: dict
value: int value: int
def format_prom(self) -> str: def format_prom(self) -> str:
labels = ','.join(x + '=' + '"' + str(self.labels[x]) + '"' for x in self.labels.keys()) labels = ",".join(
x + "=" + '"' + str(self.labels[x]) + '"' for x in self.labels.keys()
)
return f"{self.prefix}_{self.value_name}{{{labels}}} {self.value}" return f"{self.prefix}_{self.value_name}{{{labels}}} {self.value}"
class Collector(object): class Collector(object):
def __init__(self): def __init__(self):
#self.finalcommander_url = "radio.animebits.moe/api/fcmm_status" # self.finalcommander_url = "radio.animebits.moe/api/fcmm_status"
self.fcmm_urls = ["https://radio.animebits.moe/api/fcmm_status"] self.fcmm_urls = ["https://radio.animebits.moe/api/fcmm_status"]
def _fetch_json(self, url): def _fetch_json(self, url):
try: try:
r = requests.get(url) r = requests.get(url)
data = json.loads(r.text) data = json.loads(r.text)
return data return data
except json.JSONDecodeError: except json.JSONDecodeError:
raise CollectorException(f"Failed to parse JSON at {url}") raise CollectorException(f"Failed to parse JSON at {url}")
except requests.exceptions.ConnectionError: except requests.exceptions.ConnectionError:
@ -36,11 +42,11 @@ class Collector(object):
def get_fcmm(self, url): def get_fcmm(self, url):
data = self._fetch_json(url) data = self._fetch_json(url)
if "public_key" not in data.keys() or "servers" not in data.keys(): if "public_key" not in data.keys() or "servers" not in data.keys():
raise CollectorException("FinalCommander JSON seems to be wrong.") raise CollectorException("FinalCommander JSON seems to be wrong.")
return data return data
def get_instances(self, fcmm_data): def get_instances(self, fcmm_data):
for server in fcmm_data["servers"]: for server in fcmm_data["servers"]:
@ -64,7 +70,9 @@ class Collector(object):
up = 0 up = 0
metrics.append(Metric("fcmm", "up", {"url": url}, up)) 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] 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: for server_data in servers_data:
baselabel = {"server": server_data["fcmm_data"]["Address"]} baselabel = {"server": server_data["fcmm_data"]["Address"]}
@ -80,21 +88,34 @@ class Collector(object):
for name, data in server_data["statistics"].items(): for name, data in server_data["statistics"].items():
if name == "http" or name == "tls": if name == "http" or name == "tls":
for k,v in data.items(): for k, v in data.items():
for subtype,value in v.items(): for subtype, value in v.items():
metrics.append(Metric("orbt_stats", f"{name}_{k}", {**baselabel, **{k: subtype}}, value)) metrics.append(
Metric(
"orbt_stats",
f"{name}_{k}",
{**baselabel, **{k: subtype}},
value,
)
)
else: else:
for valuetype, value in data.items(): for valuetype, value in data.items():
metrics.append(Metric("orbt_stats", name, {**baselabel, **{"type": valuetype}}, value)) metrics.append(
Metric(
"orbt_stats",
name,
{**baselabel, **{"type": valuetype}},
value,
)
)
for name, value in server_data["database"].items(): for name, value in server_data["database"].items():
metrics.append(Metric("orbt_db", name, {**baselabel}, value)) metrics.append(Metric("orbt_db", name, {**baselabel}, value))
return metrics return metrics
class CollectorException(Exception): class CollectorException(Exception):
""" """
Base exception class raised by Collector Base exception class raised by Collector
""" """

View file

@ -1,14 +1,15 @@
import socket import socket
import signal import signal
from ipaddress import ip_address from ipaddress import ip_address
from http.server import HTTPServer,BaseHTTPRequestHandler from http.server import HTTPServer, BaseHTTPRequestHandler
from selectors import DefaultSelector, EVENT_READ from selectors import DefaultSelector, EVENT_READ
from src import Collector from src import Collector
class PromServer(HTTPServer):
class PromServer(HTTPServer):
def __init__(self, address): def __init__(self, address):
if ip_address(address[0]).version == 6: if ip_address(address[0]).version == 6:
self.address_family = socket.AF_INET6 self.address_family = socket.AF_INET6
super().__init__(address, self.RequestHandler) super().__init__(address, self.RequestHandler)
@ -19,14 +20,14 @@ class PromServer(HTTPServer):
metrics = [x.format_prom() for x in self.server.collector.collect()] metrics = [x.format_prom() for x in self.server.collector.collect()]
output = '\n'.join(metrics) + '\n' output = "\n".join(metrics) + "\n"
self.send_response(200) self.send_response(200)
self.end_headers() self.end_headers()
self.wfile.write(output.encode()) self.wfile.write(output.encode())
def signal_handler(self, signum, frame): def signal_handler(self, signum, frame):
print('Signal handler called with signal', signum) print("Signal handler called with signal", signum)
self.interrupt_write.send(b'\0') self.interrupt_write.send(b"\0")
def serve_forever(self): def serve_forever(self):
sel = DefaultSelector() sel = DefaultSelector()
@ -41,7 +42,7 @@ class PromServer(HTTPServer):
self.handle_request() self.handle_request()
def start(self): def start(self):
self.collector = Collector.Collector() self.collector = Collector.Collector()
self.interrupt_read, self.interrupt_write = socket.socketpair() self.interrupt_read, self.interrupt_write = socket.socketpair()
signal.signal(signal.SIGINT, self.signal_handler) signal.signal(signal.SIGINT, self.signal_handler)
self.serve_forever() self.serve_forever()

View file

@ -3,14 +3,31 @@ import argparse
from src import PrometheusServer from src import PrometheusServer
def start(): def start():
parser = argparse.ArgumentParser(description="Run radio exporter") parser = argparse.ArgumentParser(description="Run radio exporter")
parser.add_argument("-p", "--port", metavar="port", action='store', type=int, default=8888, help='Port for http server to listen on') parser.add_argument(
parser.add_argument("-a", "--address", metavar="address", action='store', default='0.0.0.0', help='Address for http server to listen on') "-p",
"--port",
metavar="port",
action="store",
type=int,
default=8888,
help="Port for http server to listen on",
)
parser.add_argument(
"-a",
"--address",
metavar="address",
action="store",
default="0.0.0.0",
help="Address for http server to listen on",
)
args = parser.parse_args() args = parser.parse_args()
server = PrometheusServer.PromServer((args.address, args.port)) server = PrometheusServer.PromServer((args.address, args.port))
server.start() server.start()
if __name__ == '__main__':
if __name__ == "__main__":
start() start()