diff options
| author | Christian Breunig <christian@breunig.cc> | 2024-02-13 17:36:39 +0100 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-13 17:36:39 +0100 | 
| commit | bae2b7c506223d59cc136123444b22e10bea6c0b (patch) | |
| tree | eb8762be0fb824daa96f385f265f99907c03dff0 /src | |
| parent | 83bf14e34a986ba35904f3889341310f85ae88c4 (diff) | |
| parent | dbfaa47b0036339e28ae8fd57c8007612e1b38f3 (diff) | |
| download | vyos-1x-bae2b7c506223d59cc136123444b22e10bea6c0b.tar.gz vyos-1x-bae2b7c506223d59cc136123444b22e10bea6c0b.zip | |
Merge pull request #2998 from sarthurdev/T5992
dhcpv6-server: T5992: Fix op-mode DHCP lease output + updates
Diffstat (limited to 'src')
| -rwxr-xr-x | src/op_mode/dhcp.py | 93 | 
1 files changed, 79 insertions, 14 deletions
| diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py index a64acec31..1d9ad0e76 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,8 @@ 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'] +mapping_sort_valid = ['mac', 'ip', 'pool', 'duid']  ArgFamily = typing.Literal['inet', 'inet6']  ArgState = typing.Literal['all', 'active', 'free', 'expired', 'released', 'abandoned', 'reset', 'backup'] @@ -77,8 +78,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 +89,37 @@ 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'] + +            if lease['type'] == 'IA_PD': +                prefix_len = lease['prefix-len'] +                data_lease['ip'] += f'/{prefix_len}'          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 +181,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 @@ -241,6 +250,47 @@ def _get_formatted_pool_statistics(pool_data, family='inet'):      output = tabulate(data_entries, headers, numalign='left')      return output +def _get_raw_server_static_mappings(family='inet', pool=None, sorted=None): +    if pool is None: +        pool = _get_dhcp_pools(family=family) +    else: +        pool = [pool] + +    v = 'v6' if family == 'inet6' else '' +    mappings = [] +    for p in pool: +        pool_config = config.get_config_dict(['service', f'dhcp{v}-server', 'shared-network-name', p], +                                             get_first_key=True) +        if 'subnet' in pool_config: +            for subnet, subnet_config in pool_config['subnet'].items(): +                if 'static-mapping' in subnet_config: +                    for name, mapping_config in subnet_config['static-mapping'].items(): +                        mapping = {'pool': p, 'subnet': subnet, 'name': name} +                        mapping.update(mapping_config) +                        mappings.append(mapping) + +    if sorted: +        if sorted == 'ip': +            data.sort(key = lambda x:ip_address(x['ip-address'])) +        else: +            data.sort(key = lambda x:x[sorted]) +    return mappings + +def _get_formatted_server_static_mappings(raw_data, family='inet'): +    data_entries = [] +    for entry in raw_data: +        pool = entry.get('pool') +        subnet = entry.get('subnet') +        name = entry.get('name') +        ip_addr = entry.get('ip-address', 'N/A') +        mac_addr = entry.get('mac', 'N/A') +        duid = entry.get('duid', 'N/A') +        description = entry.get('description', 'N/A') +        data_entries.append([pool, subnet, name, ip_addr, mac_addr, duid, description]) + +    headers = ['Pool', 'Subnet', 'Name', 'IP Address', 'MAC Address', 'DUID', 'Description'] +    output = tabulate(data_entries, headers, numalign='left') +    return output  def _verify(func):      """Decorator checks if DHCP(v6) config exists""" @@ -294,6 +344,21 @@ def show_server_leases(raw: bool, family: ArgFamily, pool: typing.Optional[str],      else:          return _get_formatted_server_leases(lease_data, family=family) +@_verify +def show_server_static_mappings(raw: bool, family: ArgFamily, pool: typing.Optional[str], +                                sorted: typing.Optional[str]): +    v = 'v6' if family == 'inet6' else '' +    if pool and pool not in _get_dhcp_pools(family=family): +        raise vyos.opmode.IncorrectValue(f'DHCP{v} pool "{pool}" does not exist!') + +    if sorted and sorted not in mapping_sort_valid: +        raise vyos.opmode.IncorrectValue(f'DHCP{v} sort "{sorted}" is invalid!') + +    static_mappings = _get_raw_server_static_mappings(family=family, pool=pool, sorted=sorted) +    if raw: +        return static_mappings +    else: +        return _get_formatted_server_static_mappings(static_mappings, family=family)  def _get_raw_client_leases(family='inet', interface=None):      from time import mktime | 
