knot-prom-exporter/knot-prom.py
2022-07-03 17:28:17 +02:00

109 lines
3.9 KiB
Python

#!/usr/bin/env python3
#coding: utf-8
import subprocess
from prometheus_client import start_http_server, Gauge
from time import sleep
def get_stats():
'''
Get the knot stats, return them in a dict
'''
# Getting info
results = subprocess.run(["knotc", "stats"], capture_output=True).stdout
results = results.decode('utf-8')
results = results.split("\n")
results.remove("")
stats = {}
for result in results:
result = result.split(" = ")
statname = result[0]
statname = statname.replace("-", "_")
statname = statname.replace(".", "_")
statname = statname.replace("[", "_")
statname = statname.replace("]", "")
stats[statname] = int(result[1])
return stats
def gen_metrics():
'''
Generate all the prometheus metrics
Set labels. We only have single-labels for now. Please don't try multi-labels.
Return them as a list
'''
metrics = [
Gauge('server_zone_count', 'Number of zones', ['node']),
Gauge('mod_rrl', 'Queries that were truncated or dropped because of response rate limiting', ['type', 'node']),
Gauge('mod_stats_request_protocol', 'requests via udp or tcp in ipv4', ['protocol', 'node']),
Gauge('mod_stats_server_operation', 'Total number of incoming queries', ['type', 'node']),
Gauge('mod_stats_request_bytes', 'Bytes received responding to queries', ['type', 'node']),
Gauge('mod_stats_response_bytes', 'Bytes sent responding to queries', ['type', 'node']),
Gauge('mod_stats_edns_presence', 'DNS Extension (EDNS) present in request or response', ['type', 'node']),
Gauge('mod_stats_flag_presence', 'Flag present in request', ['flag', 'node']),
Gauge('mod_stats_response_code', 'outgoing response code', ['code', 'node']),
Gauge('mod_stats_request_edns_option', 'Extended DNS (EDNS) option in request', ['option', 'node']),
Gauge('mod_stats_response_edns_option', 'Extended DNS (EDNS) option in response', ['option', 'node']),
Gauge('mod_stats_reply_nodata', 'RFC 2308 Nodata in reply', ['type', 'node']),
Gauge('mod_stats_query_type', 'Number of queries by type', ['type', 'node']),
]
return metrics
def get_nodename():
'''
Returns the hostname.
'''
from socket import gethostname
hostname = gethostname()
return str(hostname)
def populate_metrics(stats, metrics):
'''
Take dict of stats and list of metrics (all summaries)
Populate the metrics with the stats
Return the metrics
'''
#These metrics don't use labels
nolabel_metrics = []
for statname in stats:
statvalue = stats[statname]
nodename = get_nodename()
if statname not in nolabel_metrics:
#Getting the statname, its label's value, and corresponding metric to set to it
#A metrics label value is implied by the last field in the stats name
#Example : stat mods_query_type_A corresponds will be metrics mods_query_type with label
#type set to A
statname = statname.split("_")
label = statname.pop(-1)
statname = "_".join(statname)
else:
label = "Nolabel"
for metric in metrics:
if statname == metric._name and label != "Nolabel":
labelname = metric._labelnames
for i in labelname:
if i != "node":
labelname = i
metric.labels(label, nodename).set(statvalue)
elif statname == metric._name and label == "Nolabel":
metric.set(statvalue)
return metrics
def main():
print("Initializing knot's prometheus exporter...")
metrics = gen_metrics()
start_http_server(8001)
print("Started http server on port 8001")
while True:
stats = get_stats()
metrics = populate_metrics(stats, metrics)
sleep(5)
if __name__ == '__main__':
main()