Ha, show dhcpv6 server static-mappings
doesn’t report hostname nor ipv6-address field (showing N/A instead) : dhcpv6-server: T5992: Fix op-mode Kea DHCP lease output by nvandamme · Pull Request #4221 · vyos/vyos-1x
A partial solution can leverage python vyos API for kea using:
from typing import Any, NotRequired, TypedDict
from vyos.kea import (
kea_get_leases,
kea_get_active_config,
kea6_parse_options,
kea6_options,
)
import pprint
from vyos.utils.dict import dict_search_args
from vyos.configquery import ConfigTreeQuery
# Get the current configuration
config6 = ConfigTreeQuery()
# Get system domain-name
domain_name = config6.value(f"system domain-name")
class KeaOption(
TypedDict(
"KeaOption",
{
"name": str,
"code": int,
"data": str,
"always-send": bool,
"csv-format": bool,
"never-send": bool,
"space": str,
},
)
):
pass
def parse_options(options: list[KeaOption]):
result = {}
for option in options:
name = option.get("name", None)
if not name:
name = option.get("code", None)
if not name:
continue
if option["name"] in kea6_options:
name = kea6_options[option["name"]]
if "," in option["data"]:
result[name] = [x.strip() for x in option["data"].split(",")]
else:
result[name] = option["data"]
return result
def get_domains(
options: dict[str | int, Any], alt_domains: list[str] | str | None = None
) -> list[str]:
domains: list[str] = []
if 'domain-search' in options:
if isinstance(options['domain-search'], str):
domains.append(options['domain-search'])
else:
domains.extend(options['domain-search'])
if "domain-name" in options:
domains.append(options["domain-name"])
if domain_name and domain_name not in domains:
domains.append(domain_name)
if alt_domains is not None:
if isinstance(alt_domains, str):
if ',' in alt_domains:
alt_domains = [x.strip() for x in alt_domains.split(",")]
elif ' ' in alt_domains:
alt_domains = [x.strip() for x in alt_domains.split(" ")]
else:
alt_domains = [alt_domains]
for alt in alt_domains:
if alt not in domains:
domains.append(alt)
return domains
# DCHPv4
## Get active configuration
config4 = kea_get_active_config("4")
## Get shared-networks
shared_networks4 = dict_search_args(config4, "arguments", f"Dhcp4", "shared-networks")
leases4 = kea_get_leases("4") or []
## Extract shared-networks and subnets properties
if shared_networks4:
print("DCHPv4 Shared Networks:")
for network in shared_networks4:
options = parse_options(network["option-data"])
domains = get_domains(options)
print(f"\t{network['name']} options {options} domain {domains}")
for subnet in network[f"subnet4"]:
sub_options = parse_options(subnet["option-data"])
sub_domains = get_domains(sub_options, domains)
print(f"\t\t{subnet['subnet']} options {sub_options} domains {sub_domains}")
sub_pools = subnet.get("pools", [])
print(f"\t\t\tPools:")
for pool in sub_pools:
pool_options = parse_options(pool["option-data"])
pool_domains = get_domains(pool_options, sub_domains)
print(
f"\t\t\t\t{pool['pool']} options {pool_options} domains {pool_domains}"
)
reservations = subnet.get("reservations", [])
if len(reservations) > 0:
print(f"\t\t\tReservations:")
for reservation in reservations:
mac = reservation.get("hw-address", "-")
duid = reservation.get("duid", "-")
hostname = reservation.get("hostname", "-")
ip_addresses = reservation.get("ip-addresses", [])
ip_address = reservation.get("ip-address", None)
reservation_options = parse_options(reservation["option-data"])
for ip in ip_addresses:
print(
f"\t\t\t\t{ip} {hostname} {mac} {duid} options {reservation_options}"
)
if ip_address:
print(
f"\t\t\t\t{ip_address} {hostname} {mac} {duid} options {reservation_options}"
)
if len(leases4) > 0:
has_leases = False
for lease in leases4:
if lease["subnet-id"] == subnet["id"]:
if not has_leases:
print(f"\t\t\tLeases:")
has_leases = True
hostname = lease.get("hostname", None)
if not hostname:
hostname = "-"
if hostname[-1] == ".":
hostname = hostname[:-1]
print(f"\t\t\t\t{lease['ip-address']}\t{hostname}")
# DCHPv6
## Get active configuration
config6 = kea_get_active_config("6")
## Get shared-networks
shared_networks6 = dict_search_args(config6, "arguments", f"Dhcp6", "shared-networks")
leases6 = kea_get_leases("6") or []
## Extract shared-networks and subnets properties
if shared_networks6:
print("DCHPv6 Shared Networks:")
for network in shared_networks6:
options = parse_options(network["option-data"])
domains = get_domains(options)
print(f"\t{network['name']} options {options} domain {domains}")
for subnet in network[f"subnet6"]:
sub_options = parse_options(subnet["option-data"])
sub_domains = get_domains(sub_options, domains)
print(f"\t\t{subnet['subnet']} options {sub_options} domains {sub_domains}")
sub_pools = subnet.get("pools", [])
print(f"\t\t\tPools:")
for pool in sub_pools:
pool_options = parse_options(pool["option-data"])
pool_domains = get_domains(pool_options, sub_domains)
print(
f"\t\t\t\t{pool['pool']} options {pool_options} domains {pool_domains}"
)
reservations = subnet.get("reservations", [])
if len(reservations) > 0:
print(f"\t\t\tReservations:")
for reservation in reservations:
mac = reservation.get("hw-address", "-")
duid = reservation.get("duid", "-")
hostname = reservation.get("hostname", "-")
ip_addresses = reservation.get("ip-addresses", [])
reservation_options = parse_options(reservation["option-data"])
reservation_domains = get_domains(reservation_options, pool_domains)
for ip in ip_addresses:
print(
f"\t\t\t\t{ip} {hostname} {mac} {duid} options {reservation_options} domains {reservation_domains}"
)
if len(leases6) > 0:
has_leases = False
for lease in leases6:
if lease["subnet-id"] == subnet["id"]:
if not has_leases:
print(f"\t\t\tLeases:")
has_leases = True
hostname = lease.get("hostname", None)
if not hostname:
hostname = "-"
if hostname[-1] == ".":
hostname = hostname[:-1]
print(f"\t\t\t\t{lease['ip-address']}\t{hostname}")