Files
infix/src/statd/python/yanger/infix_dhcp_server.py
T
Joachim Wiberg 6c194007a1 statd: fix double timezone in DHCP lease expiry timestamps
The isoformat() method already includes the timezone offset when the
datetime object has timezone info.  Appending "+00:00" created an
invalid double-timezone suffix like "2025-11-30T13:13:49+00:00+00:00"
which failed YANG validation.

Error was:
  admin@bpi-26-60-00:/> show dhcp-server
  Error running sysrepocfg: Command '['sysrepocfg', '-f', 'json', '-X',
  '-d', 'operational', '-x', '/infix-dhcp-server:dhcp-server']' returned
  non-zero exit status 1.
  No interface data retrieved.

With syslog showing:
  statd[4291]: libyang[0]: Invalid union value
  "2025-11-30T13:13:49+00:00+00:00" - no matching subtype found

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-30 13:36:44 +01:00

98 lines
2.7 KiB
Python
Executable File

#!/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")))