pinecrypt-gateway-exporter/exporter.py

89 lines
3.0 KiB
Python
Raw Normal View History

2021-05-31 10:21:25 +00:00
#!/usr/bin/env python
2021-06-14 19:38:46 +00:00
import aiohttp
import asyncio
2021-05-31 10:21:25 +00:00
import os
2021-06-14 19:38:46 +00:00
import re
from collections import Counter
from sanic import Sanic, response, exceptions
2021-05-31 10:21:25 +00:00
app = Sanic("exporter")
2021-06-14 19:38:46 +00:00
PREFIX = "pinecrypt_gateway_"
PROMETHEUS_BEARER_TOKEN = os.getenv("PROMETHEUS_BEARER_TOKEN")
if not PROMETHEUS_BEARER_TOKEN:
raise ValueError("No PROMETHEUS_BEARER_TOKEN specified")
2021-05-31 10:21:25 +00:00
2021-06-14 19:38:46 +00:00
async def wrap(i):
metrics_seen = set()
async for name, tp, value, labels in i:
if name not in metrics_seen:
2021-06-14 19:38:46 +00:00
yield "# TYPE %s %s" % (PREFIX + name, tp)
metrics_seen.add(name)
yield "%s%s %d" % (
2021-06-14 19:38:46 +00:00
PREFIX + name,
("{%s}" % ",".join(["%s=\"%s\"" % j for j in labels.items()]) if labels else ""),
value)
async def exporter_stats():
# Export number of open file handles
yield "exporter_file_descriptors", "gauge", len(os.listdir("/proc/self/fd")), {}
async def openvpn_stats(port, service):
last_seen = Counter()
# Export OpenVPN status
reader, writer = await asyncio.open_connection("127.0.0.1", port)
writer.write(b"status 2\n")
await writer.drain()
while True:
line = await reader.readline()
values = line.decode("ascii").strip().split(",")
header = values[0]
if header == "END":
break
elif header == "CLIENT_LIST":
_, cn, remote, _, _, rxbytes, txbytes, _, since, _, cid, pid, cipher = values
labels = {"cn": cn, "service": service}
yield "client_connected_since", "gauge", int(since), labels
labels["cipher"] = cipher
yield "client_rx_bytes", "counter", int(rxbytes), labels
yield "client_tx_bytes", "counter", int(txbytes), labels
elif header == "ROUTING_TABLE":
_, addr, cn, remote, _, last_ref = values
last_seen[cn] = max(last_seen[cn], int(last_ref))
writer.close()
await writer.wait_closed()
for key, value in last_seen.items():
yield "client_last_seen", "gauge", value, {"cn": key, "service": service}
2021-05-31 10:21:25 +00:00
@app.route("/metrics")
async def view_export(request):
if request.token != PROMETHEUS_BEARER_TOKEN:
raise exceptions.Forbidden("Invalid bearer token")
2021-05-31 10:21:25 +00:00
async def streaming_fn(response):
for i in exporter_stats(), openvpn_stats(7505, "openvpn-udp"), openvpn_stats(7506, "openvpn-tcp"):
async for line in wrap(i):
await response.write(line + "\n")
2021-06-14 19:38:46 +00:00
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=5)) as session:
for port in (4001, 5001, 8001, 9001):
async with session.get("http://127.0.0.1:%d/metrics" % port) as upstream_response:
async for line in upstream_response.content:
if not re.match("(# (HELP|TYPE) )?(pinecrypt|goredns)_", line.decode("ascii")):
continue
await response.write(line)
return response.stream(streaming_fn, content_type="text/plain")
2021-05-31 10:21:25 +00:00
app.run(port=3001)