diff options
Diffstat (limited to 'src')
| -rwxr-xr-x | src/system/sync-dhcp-lease-to-hosts.py | 112 | 
1 files changed, 112 insertions, 0 deletions
| diff --git a/src/system/sync-dhcp-lease-to-hosts.py b/src/system/sync-dhcp-lease-to-hosts.py new file mode 100755 index 000000000..5c8b18faf --- /dev/null +++ b/src/system/sync-dhcp-lease-to-hosts.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2025 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. + +import argparse +import logging + +import vyos.opmode +import vyos.hostsd_client + +from vyos.configquery import ConfigTreeQuery + +from vyos.kea import kea_get_active_config +from vyos.kea import kea_get_dhcp_pools +from vyos.kea import kea_get_server_leases + +# Configure logging +logger = logging.getLogger(__name__) +# set stream as output +logs_handler = logging.StreamHandler() +logger.addHandler(logs_handler) + + +def _get_all_server_leases(inet_suffix='4') -> list: +    mappings = [] +    try: +        active_config = kea_get_active_config(inet_suffix) +    except Exception: +        raise vyos.opmode.DataUnavailable('Cannot fetch DHCP server configuration') + +    try: +        pools = kea_get_dhcp_pools(active_config, inet_suffix) +        mappings = kea_get_server_leases( +            active_config, inet_suffix, pools, state=[], origin=None +        ) +    except Exception: +        raise vyos.opmode.DataUnavailable('Cannot fetch DHCP server leases') + +    return mappings + + +if __name__ == '__main__': +    # Parse command arguments +    parser = argparse.ArgumentParser() +    group = parser.add_mutually_exclusive_group(required=True) +    group.add_argument('--inet', action='store_true', help='Use IPv4 DHCP leases') +    group.add_argument('--inet6', action='store_true', help='Use IPv6 DHCP leases') +    args = parser.parse_args() + +    inet_suffix = '4' if args.inet else '6' +    service_suffix = '' if args.inet else 'v6' + +    if inet_suffix == '6': +        raise vyos.opmode.UnsupportedOperation( +            'Syncing IPv6 DHCP leases are not supported yet' +        ) + +    # Load configuration +    config = ConfigTreeQuery() + +    # Check if DHCP server is configured +    # Using warning instead of error since this check may fail during first-time +    # DHCP server setup when the service is not yet configured in the config tree. +    # This happens when called from systemd's ExecStartPost the first time. +    if not config.exists(f'service dhcp{service_suffix}-server'): +        logger.warning(f'DHCP{service_suffix} server is not configured') + +    # Check if hostfile-update is enabled +    if not config.exists(f'service dhcp{service_suffix}-server hostfile-update'): +        logger.debug( +            f'Hostfile update is disabled for DHCP{service_suffix} server, skipping hosts update' +        ) +        exit(0) + +    lease_data = _get_all_server_leases(inet_suffix) + +    try: +        hc = vyos.hostsd_client.Client() + +        for mapping in lease_data: +            ip_addr = mapping.get('ip') +            mac_addr = mapping.get('mac') +            name = mapping.get('hostname') +            name = name if name else f'host-{mac_addr.replace(":", "-")}' +            domain = mapping.get('domain') +            fqdn = f'{name}.{domain}' if domain else name +            hc.add_hosts( +                { +                    f'dhcp-server-{ip_addr}': { +                        fqdn: {'address': [ip_addr], 'aliases': []} +                    } +                } +            ) + +        hc.apply() + +        logger.debug('Hosts store updated successfully') + +    except vyos.hostsd_client.VyOSHostsdError as e: +        raise vyos.opmode.InternalError(str(e)) | 
