2
0

add labels and conntrack stats to prometheus output

This commit is contained in:
Stefan Bühler 2023-11-15 17:29:59 +01:00
parent 5c5a309934
commit dede33a5d0
2 changed files with 76 additions and 29 deletions

View File

@ -1,7 +1,9 @@
from __future__ import annotations from __future__ import annotations
import argparse
import dataclasses
import ipaddress import ipaddress
import sys import typing
import trio import trio
@ -11,18 +13,39 @@ import capport.utils.nft_set
from . import cptypes from . import cptypes
def print_metric(name: str, mtype: str, value, *, now: int | None = None, help: str | None = None): @dataclasses.dataclass(kw_only=True, slots=True)
# no labels in our names for now, always print help and type class MetricPrinter:
if help: args: CliArguments
print(f"# HELP {name} {help}") now: int | None = None
print(f"# TYPE {name} {mtype}") _known_names: set[str] = dataclasses.field(default_factory=set, init=None)
if now: _now_str: str = dataclasses.field(init=False)
print(f"{name} {value} {now}") _label_str: str = dataclasses.field(init=False)
else:
print(f"{name} {value}") def __post_init__(self) -> None:
if not self.now is None:
self._now_str = f" {self.now}"
else:
self._now_str = ""
labels = []
if self.args.instance:
labels.append(f"instance=\"{self.args.instance}\"")
labels.append(f"interface=\"{self.args.interface}\"")
self._label_str = "{" + ",".join(labels) + "}"
def print_metric(self, *, name: str, mtype: str, value: typing.Any, help: str | None = None):
if not name in self._known_names:
self._known_names.add(name)
if help:
print(f"# HELP {name} {help}")
print(f"# TYPE {name} {mtype}")
print(f"{name}{self._label_str} {value}{self._now_str}")
def print_gauge(self, name: str, value: int | float, help: str | None = None):
self.print_metric(name=name, mtype="gauge", value=value, help=help)
async def amain(client_ifname: str): async def amain(args: CliArguments):
printer = MetricPrinter(args=args)
ns = capport.utils.nft_set.NftSet() ns = capport.utils.nft_set.NftSet()
captive_allowed_entries: set[cptypes.MacAddress] = {entry["mac"] for entry in ns.list()} captive_allowed_entries: set[cptypes.MacAddress] = {entry["mac"] for entry in ns.list()}
seen_allowed_entries: set[cptypes.MacAddress] = set() seen_allowed_entries: set[cptypes.MacAddress] = set()
@ -33,7 +56,7 @@ async def amain(client_ifname: str):
unique_ipv6 = set() unique_ipv6 = set()
async with capport.utils.ipneigh.connect() as ipn: async with capport.utils.ipneigh.connect() as ipn:
ipn.ip.strict_check = True ipn.ip.strict_check = True
async for (mac, addr) in ipn.dump_neighbors(client_ifname): async for (mac, addr) in ipn.dump_neighbors(args.interface):
if mac in captive_allowed_entries: if mac in captive_allowed_entries:
seen_allowed_entries.add(mac) seen_allowed_entries.add(mac)
unique_clients.add(mac) unique_clients.add(mac)
@ -43,50 +66,74 @@ async def amain(client_ifname: str):
else: else:
total_ipv6 += 1 total_ipv6 += 1
unique_ipv6.add(mac) unique_ipv6.add(mac)
print_metric( printer.print_gauge(
"capport_allowed_macs", "capport_allowed_macs",
"gauge",
len(captive_allowed_entries), len(captive_allowed_entries),
help="Number of allowed client mac addresses", help="Number of allowed client mac addresses",
) )
print_metric( printer.print_gauge(
"capport_allowed_neigh_macs", "capport_allowed_neigh_macs",
"gauge",
len(seen_allowed_entries), len(seen_allowed_entries),
help="Number of allowed client mac addresses seen in neighbor cache", help="Number of allowed client mac addresses seen in neighbor cache",
) )
print_metric( printer.print_gauge(
"capport_unique", "capport_unique",
"gauge",
len(unique_clients), len(unique_clients),
help="Number of clients (mac addresses) in client network seen in neighbor cache", help="Number of clients (mac addresses) in client network seen in neighbor cache",
) )
print_metric( printer.print_gauge(
"capport_unique_ipv4", "capport_unique_ipv4",
"gauge",
len(unique_ipv4), len(unique_ipv4),
help="Number of IPv4 clients (unique per mac) in client network seen in neighbor cache", help="Number of IPv4 clients (unique per mac) in client network seen in neighbor cache",
) )
print_metric( printer.print_gauge(
"capport_unique_ipv6", "capport_unique_ipv6",
"gauge",
len(unique_ipv6), len(unique_ipv6),
help="Number of IPv6 clients (unique per mac) in client network seen in neighbor cache", help="Number of IPv6 clients (unique per mac) in client network seen in neighbor cache",
) )
print_metric( printer.print_gauge(
"capport_total_ipv4", "capport_total_ipv4",
"gauge",
total_ipv4, total_ipv4,
help="Number of IPv4 addresses seen in neighbor cache", help="Number of IPv4 addresses seen in neighbor cache",
) )
print_metric( printer.print_gauge(
"capport_total_ipv6", "capport_total_ipv6",
"gauge",
total_ipv6, total_ipv6,
help="Number of IPv6 addresses seen in neighbor cache", help="Number of IPv6 addresses seen in neighbor cache",
) )
with open('/proc/sys/net/netfilter/nf_conntrack_count') as f:
printer.print_gauge(
"nf_conntrack_count",
int(f.readline()),
help="Conntrack count",
)
with open('/proc/sys/net/netfilter/nf_conntrack_max') as f:
printer.print_gauge(
"nf_conntrack_max",
int(f.readline()),
help="Conntrack max",
)
@dataclasses.dataclass
class CliArguments:
interface: str
instance: str | None
def __init__(self):
parser = argparse.ArgumentParser()
parser.add_argument(
"--interface",
required=True,
help="Name of network interface clients are connected to (to look for IP neighbors)",
)
parser.add_argument("--instance", help="Name of instance (to tag metrics with)")
args = parser.parse_args()
self.interface = args.interface
self.instance = args.instance
def main(): def main():
assert len(sys.argv) == 2, "Need name of client interface as argument" args = CliArguments()
trio.run(amain, sys.argv[1]) trio.run(amain, args)

View File

@ -16,7 +16,7 @@ fi
targetname="/var/lib/prometheus/node-exporter/capport-${instance}.prom" targetname="/var/lib/prometheus/node-exporter/capport-${instance}.prom"
tmpname="${targetname}.$$" tmpname="${targetname}.$$"
if ./stats.sh "${ifname}" > "${tmpname}"; then if ./stats.sh --instance "${instance}" --interface "${ifname}" > "${tmpname}"; then
mv "${tmpname}" "${targetname}" mv "${tmpname}" "${targetname}"
else else
rm "${tmpname}" rm "${tmpname}"