#!/usr/bin/env python3 """ Collect operational data for infix-dhcp-server.yang from dnsmasq """ from datetime import datetime, timezone import json import dbus def leases(leases_file): """Populate DHCP leases table""" table = [] try: with open(leases_file, 'r', encoding='utf-8') as fd: for line in fd: tokens = line.strip().split(" ") if len(tokens) != 5: continue # Handle infinite lease time as specified in RFC 2131 if tokens[0] == "0": expires = "never" else: dt = datetime.fromtimestamp(int(tokens[0]), tz=timezone.utc) expires = dt.isoformat() row = { "expires": expires, "address": tokens[2], "phys-address": tokens[1], } if tokens[3] != '*': row["hostname"] = tokens[3] else: row["hostname"] = "" if tokens[4] != '*': row["client-id"] = tokens[4] else: row["client-id"] = "" table.append(row) except (IOError, OSError, ValueError): pass return table def statistics(): """Fetch DHCP server metrics over D-Bus""" try: bus = dbus.SystemBus() obj = bus.get_object("uk.org.thekelleys.dnsmasq", "/uk/org/thekelleys/dnsmasq") srv = dbus.Interface(obj, "uk.org.thekelleys.dnsmasq") metrics = srv.GetMetrics() except dbus.exceptions.DBusException: metrics = { "dhcp_offer": 0, "dhcp_ack": 0, "dhcp_nak": 0, "dhcp_decline": 0, "dhcp_discover": 0, "dhcp_request": 0, "dhcp_release": 0, "dhcp_inform": 0 } return { "out-offers": metrics["dhcp_offer"], "out-acks": metrics["dhcp_ack"], "out-naks": metrics["dhcp_nak"], "in-declines": metrics["dhcp_decline"], "in-discovers": metrics["dhcp_discover"], "in-requests": metrics["dhcp_request"], "in-releases": metrics["dhcp_release"], "in-informs": metrics["dhcp_inform"] } def operational(leases_file="/var/lib/misc/dnsmasq.leases"): """Return operational status for DHCP server""" return { "infix-dhcp-server:dhcp-server": { "statistics": statistics(), "leases": { "lease": leases(leases_file) } } } if __name__ == "__main__": print(json.dumps(operational(leases_file="mock.leases")))