prometheus stats cli tool
This commit is contained in:
parent
c753f490ac
commit
f1b36bd171
@ -34,3 +34,4 @@ where = src
|
|||||||
[options.entry_points]
|
[options.entry_points]
|
||||||
console_scripts =
|
console_scripts =
|
||||||
capport-control = capport.control.run:main
|
capport-control = capport.control.run:main
|
||||||
|
capport-stats = capport.stats:main
|
||||||
|
61
src/capport/stats.py
Normal file
61
src/capport/stats.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import ipaddress
|
||||||
|
import sys
|
||||||
|
import typing
|
||||||
|
import time
|
||||||
|
|
||||||
|
import trio
|
||||||
|
|
||||||
|
import capport.utils.ipneigh
|
||||||
|
import capport.utils.nft_set
|
||||||
|
from . import cptypes
|
||||||
|
|
||||||
|
|
||||||
|
def print_metric(name: str, mtype: str, value, *, now: typing.Optional[int]=None, help: typing.Optional[str]=None):
|
||||||
|
# no labels in our names for now, always print help and type
|
||||||
|
if help:
|
||||||
|
print(f'# HELP {name} {help}')
|
||||||
|
print(f'# TYPE {name} {mtype}')
|
||||||
|
if now:
|
||||||
|
print(f'{name} {value} {now}')
|
||||||
|
else:
|
||||||
|
print(f'{name} {value}')
|
||||||
|
|
||||||
|
|
||||||
|
async def amain(client_ifname: str):
|
||||||
|
ns = capport.utils.nft_set.NftSet()
|
||||||
|
captive_allowed_entries: typing.Set[cptypes.MacAddress] = {
|
||||||
|
entry['mac']
|
||||||
|
for entry in ns.list()
|
||||||
|
}
|
||||||
|
seen_allowed_entries: typing.Set[cptypes.MacAddress] = set()
|
||||||
|
total_ipv4 = 0
|
||||||
|
total_ipv6 = 0
|
||||||
|
unique_clients = set()
|
||||||
|
unique_ipv4 = set()
|
||||||
|
unique_ipv6 = set()
|
||||||
|
async with capport.utils.ipneigh.connect() as ipn:
|
||||||
|
ipn.ip.strict_check = True
|
||||||
|
async for (mac, addr) in ipn.dump_neighbors(client_ifname):
|
||||||
|
if mac in captive_allowed_entries:
|
||||||
|
seen_allowed_entries.add(mac)
|
||||||
|
unique_clients.add(mac)
|
||||||
|
if isinstance(addr, ipaddress.IPv4Address):
|
||||||
|
total_ipv4 += 1
|
||||||
|
unique_ipv4.add(mac)
|
||||||
|
else:
|
||||||
|
total_ipv6 += 1
|
||||||
|
unique_ipv6.add(mac)
|
||||||
|
print_metric('capport_allowed_macs', 'gauge', len(captive_allowed_entries), help='Number of allowed client mac addresses')
|
||||||
|
print_metric('capport_allowed_neigh_macs', 'gauge', len(seen_allowed_entries), help='Number of allowed client mac addresses seen in neighbor cache')
|
||||||
|
print_metric('capport_unique', 'gauge', len(unique_clients), help='Number of clients (mac addresses) in client network seen in neighbor cache')
|
||||||
|
print_metric('capport_unique_ipv4', 'gauge', len(unique_ipv4), help='Number of IPv4 clients (unique per mac) in client network seen in neighbor cache')
|
||||||
|
print_metric('capport_unique_ipv6', 'gauge', len(unique_ipv6), help='Number of IPv4 clients (unique per mac) in client network seen in neighbor cache')
|
||||||
|
print_metric('capport_total_ipv4', 'gauge', total_ipv4, help='Number of IPv4 addresses seen in neighbor cache')
|
||||||
|
print_metric('capport_total_ipv6', 'gauge', total_ipv6, help='Number of IPv6 addresses seen in neighbor cache')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
assert len(sys.argv) == 2, "Need name of client interface as argument"
|
||||||
|
trio.run(amain, sys.argv[1])
|
@ -2,10 +2,14 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import errno
|
import errno
|
||||||
|
import ipaddress
|
||||||
|
import socket
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
import pr2modules.iproute.linux
|
import pr2modules.iproute.linux
|
||||||
import pr2modules.netlink.exceptions
|
import pr2modules.netlink.exceptions
|
||||||
|
import pr2modules.netlink.rtnl
|
||||||
|
import pr2modules.netlink.rtnl.ndmsg
|
||||||
from capport import cptypes
|
from capport import cptypes
|
||||||
|
|
||||||
|
|
||||||
@ -18,6 +22,7 @@ async def connect():
|
|||||||
class NeighborController:
|
class NeighborController:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.ip = pr2modules.iproute.linux.IPRoute()
|
self.ip = pr2modules.iproute.linux.IPRoute()
|
||||||
|
self.ip.bind()
|
||||||
|
|
||||||
async def get_neighbor(
|
async def get_neighbor(
|
||||||
self,
|
self,
|
||||||
@ -61,3 +66,19 @@ class NeighborController:
|
|||||||
if e.code == errno.ENOENT:
|
if e.code == errno.ENOENT:
|
||||||
return None
|
return None
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
async def dump_neighbors(self, interface: str) -> typing.Generator[typing.Tuple[cptypes.MacAddress, cptypes.IPAddress]]:
|
||||||
|
ifindex = socket.if_nametoindex(interface)
|
||||||
|
unicast_num = pr2modules.netlink.rtnl.rt_type['unicast']
|
||||||
|
# ip.neigh doesn't support AF_UNSPEC (as it is 0 and evaluates to `False` and gets forced to AF_INET)
|
||||||
|
for family in (socket.AF_INET, socket.AF_INET6):
|
||||||
|
for neigh in self.ip.neigh('dump', ifindex=ifindex, family=family):
|
||||||
|
if neigh['ndm_type'] != unicast_num:
|
||||||
|
continue
|
||||||
|
mac = neigh.get_attr(neigh.name2nla('lladdr'))
|
||||||
|
if not mac:
|
||||||
|
continue
|
||||||
|
dst = ipaddress.ip_address(neigh.get_attr(neigh.name2nla('dst')))
|
||||||
|
if dst.is_link_local:
|
||||||
|
continue
|
||||||
|
yield (cptypes.MacAddress.parse(mac), dst)
|
||||||
|
24
stats-to-prometheus-collector.sh
Executable file
24
stats-to-prometheus-collector.sh
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
base=$(dirname "$(readlink -f "$0")")
|
||||||
|
cd "${base}"
|
||||||
|
|
||||||
|
instance=$1
|
||||||
|
ifname=$2
|
||||||
|
|
||||||
|
if [ -z "${instance}" -o -z "${ifname}" ]; then
|
||||||
|
echo >&2 "Syntax: $0 instancename clientifname"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
targetname="/var/lib/prometheus/node-exporter/capport-${instance}.prom"
|
||||||
|
tmpname="${targetname}.$$"
|
||||||
|
|
||||||
|
if ./stats.sh "${ifname}" > "${tmpname}"; then
|
||||||
|
mv "${tmpname}" "${targetname}"
|
||||||
|
else
|
||||||
|
rm "${tmpname}"
|
||||||
|
exit 1
|
||||||
|
fi
|
Loading…
Reference in New Issue
Block a user