From c44d24eae276f77c700e961a22c0cd5582416dff Mon Sep 17 00:00:00 2001
From: sarthurdev <965089+sarthurdev@users.noreply.github.com>
Date: Tue, 13 Feb 2024 00:19:30 +0100
Subject: dhcpv6-server: T5992: Fix op-mode Kea DHCP lease output
Due to Kea's lease file cleanup, the CSV file content is inconsistent. This commit makes changes to use the Kea control socket to fetch current lease information.
---
op-mode-definitions/dhcp.xml.in | 2 +-
python/vyos/kea.py | 35 ++++++++++-------------------------
src/op_mode/dhcp.py | 32 ++++++++++++++++++--------------
3 files changed, 29 insertions(+), 40 deletions(-)
diff --git a/op-mode-definitions/dhcp.xml.in b/op-mode-definitions/dhcp.xml.in
index ceb321f3e..ab305580b 100644
--- a/op-mode-definitions/dhcp.xml.in
+++ b/op-mode-definitions/dhcp.xml.in
@@ -130,7 +130,7 @@
Show DHCPv6 server leases sorted by the specified key
- end iaid_duid ip last_communication pool remaining state type
+ end duid ip last_communication pool remaining state type
${vyos_op_scripts_dir}/dhcp.py show_server_leases --family inet6 --sort $6
diff --git a/python/vyos/kea.py b/python/vyos/kea.py
index 720bebec3..0a629fcbc 100644
--- a/python/vyos/kea.py
+++ b/python/vyos/kea.py
@@ -17,8 +17,6 @@ import json
import os
import socket
-from datetime import datetime
-
from vyos.template import is_ipv6
from vyos.template import isc_static_route
from vyos.template import netmask_from_cidr
@@ -290,29 +288,6 @@ def kea6_parse_subnet(subnet, config):
return out
-def kea_parse_leases(lease_path):
- contents = read_file(lease_path)
- lines = contents.split("\n")
- output = []
-
- if len(lines) < 2:
- return output
-
- headers = lines[0].split(",")
-
- for line in lines[1:]:
- line_out = dict(zip(headers, line.split(",")))
-
- lifetime = int(line_out['valid_lifetime'])
- expiry = int(line_out['expire'])
-
- line_out['start_timestamp'] = datetime.utcfromtimestamp(expiry - lifetime)
- line_out['expire_timestamp'] = datetime.utcfromtimestamp(expiry) if expiry else None
-
- output.append(line_out)
-
- return output
-
def _ctrl_socket_command(path, command, args=None):
if not os.path.exists(path):
return None
@@ -337,6 +312,16 @@ def _ctrl_socket_command(path, command, args=None):
return json.loads(result.decode('utf-8'))
+def kea_get_leases(inet):
+ ctrl_socket = f'/run/kea/dhcp{inet}-ctrl-socket'
+
+ leases = _ctrl_socket_command(ctrl_socket, f'lease{inet}-get-all')
+
+ if not leases or 'result' not in leases or leases['result'] != 0:
+ return []
+
+ return leases['arguments']['leases']
+
def kea_get_active_config(inet):
ctrl_socket = f'/run/kea/dhcp{inet}-ctrl-socket'
diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py
index a64acec31..d47765414 100755
--- a/src/op_mode/dhcp.py
+++ b/src/op_mode/dhcp.py
@@ -29,8 +29,8 @@ from vyos.base import Warning
from vyos.configquery import ConfigTreeQuery
from vyos.kea import kea_get_active_config
+from vyos.kea import kea_get_leases
from vyos.kea import kea_get_pool_from_subnet_id
-from vyos.kea import kea_parse_leases
from vyos.utils.process import is_systemd_service_running
time_string = "%a %b %d %H:%M:%S %Z %Y"
@@ -38,7 +38,7 @@ time_string = "%a %b %d %H:%M:%S %Z %Y"
config = ConfigTreeQuery()
lease_valid_states = ['all', 'active', 'free', 'expired', 'released', 'abandoned', 'reset', 'backup']
sort_valid_inet = ['end', 'mac', 'hostname', 'ip', 'pool', 'remaining', 'start', 'state']
-sort_valid_inet6 = ['end', 'iaid_duid', 'ip', 'last_communication', 'pool', 'remaining', 'state', 'type']
+sort_valid_inet6 = ['end', 'duid', 'ip', 'last_communication', 'pool', 'remaining', 'state', 'type']
ArgFamily = typing.Literal['inet', 'inet6']
ArgState = typing.Literal['all', 'active', 'free', 'expired', 'released', 'abandoned', 'reset', 'backup']
@@ -77,8 +77,7 @@ def _get_raw_server_leases(family='inet', pool=None, sorted=None, state=[], orig
:return list
"""
inet_suffix = '6' if family == 'inet6' else '4'
- lease_file = f'/config/dhcp/dhcp{inet_suffix}-leases.csv'
- leases = kea_parse_leases(lease_file)
+ leases = kea_get_leases(inet_suffix)
if pool is None:
pool = _get_dhcp_pools(family=family)
@@ -89,28 +88,33 @@ def _get_raw_server_leases(family='inet', pool=None, sorted=None, state=[], orig
data = []
for lease in leases:
+ lifetime = lease['valid-lft']
+ expiry = (lease['cltt'] + lifetime)
+
+ lease['start_timestamp'] = datetime.utcfromtimestamp(expiry - lifetime)
+ lease['expire_timestamp'] = datetime.utcfromtimestamp(expiry) if expiry else None
+
data_lease = {}
- data_lease['ip'] = lease['address']
- lease_state_long = {'0': 'active', '1': 'rejected', '2': 'expired'}
+ data_lease['ip'] = lease['ip-address']
+ lease_state_long = {0: 'active', 1: 'rejected', 2: 'expired'}
data_lease['state'] = lease_state_long[lease['state']]
- data_lease['pool'] = kea_get_pool_from_subnet_id(active_config, inet_suffix, lease['subnet_id']) if active_config else '-'
+ data_lease['pool'] = kea_get_pool_from_subnet_id(active_config, inet_suffix, lease['subnet-id']) if active_config else '-'
data_lease['end'] = lease['expire_timestamp'].timestamp() if lease['expire_timestamp'] else None
data_lease['origin'] = 'local' # TODO: Determine remote in HA
if family == 'inet':
- data_lease['mac'] = lease['hwaddr']
+ data_lease['mac'] = lease['hw-address']
data_lease['start'] = lease['start_timestamp'].timestamp()
data_lease['hostname'] = lease['hostname']
if family == 'inet6':
data_lease['last_communication'] = lease['start_timestamp'].timestamp()
- data_lease['iaid_duid'] = _format_hex_string(lease['duid'])
- lease_types_long = {'0': 'non-temporary', '1': 'temporary', '2': 'prefix delegation'}
- data_lease['type'] = lease_types_long[lease['lease_type']]
+ data_lease['duid'] = _format_hex_string(lease['duid'])
+ data_lease['type'] = lease['type']
data_lease['remaining'] = '-'
- if lease['expire']:
+ if lease['valid-lft'] > 0:
data_lease['remaining'] = lease['expire_timestamp'] - datetime.utcnow()
if data_lease['remaining'].days >= 0:
@@ -172,11 +176,11 @@ def _get_formatted_server_leases(raw_data, family='inet'):
remain = lease.get('remaining')
lease_type = lease.get('type')
pool = lease.get('pool')
- host_identifier = lease.get('iaid_duid')
+ host_identifier = lease.get('duid')
data_entries.append([ipaddr, state, start, end, remain, lease_type, pool, host_identifier])
headers = ['IPv6 address', 'State', 'Last communication', 'Lease expiration', 'Remaining', 'Type', 'Pool',
- 'IAID_DUID']
+ 'DUID']
output = tabulate(data_entries, headers, numalign='left')
return output
--
cgit v1.2.3