summaryrefslogtreecommitdiff
path: root/src/op_mode/dhcp.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/op_mode/dhcp.py')
-rwxr-xr-xsrc/op_mode/dhcp.py75
1 files changed, 39 insertions, 36 deletions
diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py
index 77f38992b..a64acec31 100755
--- a/src/op_mode/dhcp.py
+++ b/src/op_mode/dhcp.py
@@ -21,7 +21,6 @@ import typing
from datetime import datetime
from glob import glob
from ipaddress import ip_address
-from isc_dhcp_leases import IscDhcpLeases
from tabulate import tabulate
import vyos.opmode
@@ -29,9 +28,9 @@ import vyos.opmode
from vyos.base import Warning
from vyos.configquery import ConfigTreeQuery
-from vyos.utils.dict import dict_search
-from vyos.utils.file import read_file
-from vyos.utils.process import cmd
+from vyos.kea import kea_get_active_config
+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"
@@ -43,6 +42,7 @@ sort_valid_inet6 = ['end', 'iaid_duid', 'ip', 'last_communication', 'pool', 'rem
ArgFamily = typing.Literal['inet', 'inet6']
ArgState = typing.Literal['all', 'active', 'free', 'expired', 'released', 'abandoned', 'reset', 'backup']
+ArgOrigin = typing.Literal['local', 'remote']
def _utc_to_local(utc_dt):
return datetime.fromtimestamp((datetime.fromtimestamp(utc_dt) - datetime(1970, 1, 1)).total_seconds())
@@ -71,42 +71,47 @@ def _find_list_of_dict_index(lst, key='ip', value='') -> int:
return idx
-def _get_raw_server_leases(family='inet', pool=None, sorted=None, state=[]) -> list:
+def _get_raw_server_leases(family='inet', pool=None, sorted=None, state=[], origin=None) -> list:
"""
Get DHCP server leases
:return list
"""
- lease_file = '/config/dhcpdv6.leases' if family == 'inet6' else '/config/dhcpd.leases'
- data = []
- leases = IscDhcpLeases(lease_file).get()
+ inet_suffix = '6' if family == 'inet6' else '4'
+ lease_file = f'/config/dhcp/dhcp{inet_suffix}-leases.csv'
+ leases = kea_parse_leases(lease_file)
if pool is None:
pool = _get_dhcp_pools(family=family)
else:
pool = [pool]
+ active_config = kea_get_active_config(inet_suffix)
+
+ data = []
for lease in leases:
data_lease = {}
- data_lease['ip'] = lease.ip
- data_lease['state'] = lease.binding_state
- data_lease['pool'] = lease.sets.get('shared-networkname', '')
- data_lease['end'] = lease.end.timestamp() if lease.end else None
+ data_lease['ip'] = lease['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['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.ethernet
- data_lease['start'] = lease.start.timestamp()
- data_lease['hostname'] = lease.hostname
+ data_lease['mac'] = lease['hwaddr']
+ data_lease['start'] = lease['start_timestamp'].timestamp()
+ data_lease['hostname'] = lease['hostname']
if family == 'inet6':
- data_lease['last_communication'] = lease.last_communication.timestamp()
- data_lease['iaid_duid'] = _format_hex_string(lease.host_identifier_string)
- lease_types_long = {'na': 'non-temporary', 'ta': 'temporary', 'pd': 'prefix delegation'}
- data_lease['type'] = lease_types_long[lease.type]
+ 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['remaining'] = '-'
- if lease.end:
- data_lease['remaining'] = lease.end - datetime.utcnow()
+ if lease['expire']:
+ data_lease['remaining'] = lease['expire_timestamp'] - datetime.utcnow()
if data_lease['remaining'].days >= 0:
# substraction gives us a timedelta object which can't be formatted with strftime
@@ -115,7 +120,7 @@ def _get_raw_server_leases(family='inet', pool=None, sorted=None, state=[]) -> l
# Do not add old leases
if data_lease['remaining'] != '' and data_lease['pool'] in pool and data_lease['state'] != 'free':
- if not state or data_lease['state'] in state:
+ if not state or state == 'all' or data_lease['state'] in state:
data.append(data_lease)
# deduplicate
@@ -150,10 +155,11 @@ def _get_formatted_server_leases(raw_data, family='inet'):
remain = lease.get('remaining')
pool = lease.get('pool')
hostname = lease.get('hostname')
- data_entries.append([ipaddr, hw_addr, state, start, end, remain, pool, hostname])
+ origin = lease.get('origin')
+ data_entries.append([ipaddr, hw_addr, state, start, end, remain, pool, hostname, origin])
headers = ['IP Address', 'MAC address', 'State', 'Lease start', 'Lease expiration', 'Remaining', 'Pool',
- 'Hostname']
+ 'Hostname', 'Origin']
if family == 'inet6':
for lease in raw_data:
@@ -188,14 +194,11 @@ def _get_pool_size(pool, family='inet'):
size = 0
subnets = config.list_nodes(f'{base} subnet')
for subnet in subnets:
- if family == 'inet6':
- ranges = config.list_nodes(f'{base} subnet {subnet} address-range start')
- else:
- ranges = config.list_nodes(f'{base} subnet {subnet} range')
+ ranges = config.list_nodes(f'{base} subnet {subnet} range')
for range in ranges:
if family == 'inet6':
- start = config.list_nodes(f'{base} subnet {subnet} address-range start')[0]
- stop = config.value(f'{base} subnet {subnet} address-range start {start} stop')
+ start = config.value(f'{base} subnet {subnet} range {range} start')
+ stop = config.value(f'{base} subnet {subnet} range {range} stop')
else:
start = config.value(f'{base} subnet {subnet} range {range} start')
stop = config.value(f'{base} subnet {subnet} range {range} stop')
@@ -267,12 +270,12 @@ def show_pool_statistics(raw: bool, family: ArgFamily, pool: typing.Optional[str
@_verify
def show_server_leases(raw: bool, family: ArgFamily, pool: typing.Optional[str],
- sorted: typing.Optional[str], state: typing.Optional[ArgState]):
+ sorted: typing.Optional[str], state: typing.Optional[ArgState],
+ origin: typing.Optional[ArgOrigin] ):
# if dhcp server is down, inactive leases may still be shown as active, so warn the user.
- v = '6' if family == 'inet6' else ''
- service_name = 'DHCPv6' if family == 'inet6' else 'DHCP'
- if not is_systemd_service_running(f'isc-dhcp-server{v}.service'):
- Warning(f'{service_name} server is configured but not started. Data may be stale.')
+ v = '6' if family == 'inet6' else '4'
+ if not is_systemd_service_running(f'kea-dhcp{v}-server.service'):
+ Warning('DHCP server is configured but not started. Data may be stale.')
v = 'v6' if family == 'inet6' else ''
if pool and pool not in _get_dhcp_pools(family=family):
@@ -285,7 +288,7 @@ def show_server_leases(raw: bool, family: ArgFamily, pool: typing.Optional[str],
if sorted and sorted not in sort_valid:
raise vyos.opmode.IncorrectValue(f'DHCP{v} sort "{sorted}" is invalid!')
- lease_data = _get_raw_server_leases(family=family, pool=pool, sorted=sorted, state=state)
+ lease_data = _get_raw_server_leases(family=family, pool=pool, sorted=sorted, state=state, origin=origin)
if raw:
return lease_data
else: