diff options
author | Daniil Baturin <daniil@vyos.io> | 2024-03-09 20:06:40 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-09 20:06:40 +0100 |
commit | e8a6d3ddb6eb36299dbced2e119ba0699e062350 (patch) | |
tree | aed5859834208a758f251bb729d20ae3caf9eb80 | |
parent | da759a46efaaa937edae2440a7743d325ebf99b1 (diff) | |
parent | 0920121eed97889ff33ef84f04a3730242ccb04b (diff) | |
download | vyos-1x-e8a6d3ddb6eb36299dbced2e119ba0699e062350.tar.gz vyos-1x-e8a6d3ddb6eb36299dbced2e119ba0699e062350.zip |
Merge pull request #3106 from sarthurdev/T6102
dhcp: T6102: Fix clear DHCP lease op-mode
-rw-r--r-- | op-mode-definitions/clear-dhcp-server-lease.xml.in | 20 | ||||
-rw-r--r-- | op-mode-definitions/dhcp.xml.in | 30 | ||||
-rw-r--r-- | python/vyos/kea.py | 24 | ||||
-rwxr-xr-x | src/op_mode/clear_dhcp_lease.py | 88 | ||||
-rwxr-xr-x | src/op_mode/dhcp.py | 23 |
5 files changed, 70 insertions, 115 deletions
diff --git a/op-mode-definitions/clear-dhcp-server-lease.xml.in b/op-mode-definitions/clear-dhcp-server-lease.xml.in deleted file mode 100644 index aef0eb22a..000000000 --- a/op-mode-definitions/clear-dhcp-server-lease.xml.in +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> - <node name="clear"> - <children> - <node name="dhcp-server"> - <properties> - <help>Clear DHCP server lease</help> - </properties> - <children> - <tagNode name="lease"> - <properties> - <help>DHCP server lease</help> - </properties> - <command>sudo ${vyos_op_scripts_dir}/clear_dhcp_lease.py --ip $4</command> - </tagNode> - </children> - </node> - </children> - </node> -</interfaceDefinition> diff --git a/op-mode-definitions/dhcp.xml.in b/op-mode-definitions/dhcp.xml.in index 0db7471e5..3c42c8e8f 100644 --- a/op-mode-definitions/dhcp.xml.in +++ b/op-mode-definitions/dhcp.xml.in @@ -1,5 +1,35 @@ <?xml version="1.0" encoding="UTF-8"?> <interfaceDefinition> + <node name="clear"> + <children> + <node name="dhcp-server"> + <properties> + <help>Clear DHCP server lease</help> + </properties> + <children> + <tagNode name="lease"> + <properties> + <help>DHCP server lease</help> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py clear_dhcp_server_lease --family inet --address $4</command> + </tagNode> + </children> + </node> + <node name="dhcpv6-server"> + <properties> + <help>Clear DHCPv6 server lease</help> + </properties> + <children> + <tagNode name="lease"> + <properties> + <help>DHCPv6 server lease</help> + </properties> + <command>${vyos_op_scripts_dir}/dhcp.py clear_dhcp_server_lease --family inet6 --address $4</command> + </tagNode> + </children> + </node> + </children> + </node> <node name="show"> <children> <node name="dhcp"> diff --git a/python/vyos/kea.py b/python/vyos/kea.py index 2328d0b00..89ae7ca81 100644 --- a/python/vyos/kea.py +++ b/python/vyos/kea.py @@ -56,6 +56,8 @@ kea6_options = { 'captive_portal': 'v6-captive-portal' } +kea_ctrl_socket = '/run/kea/dhcp{inet}-ctrl-socket' + def kea_parse_options(config): options = [] @@ -294,7 +296,9 @@ def kea6_parse_subnet(subnet, config): return out -def _ctrl_socket_command(path, command, args=None): +def _ctrl_socket_command(inet, command, args=None): + path = kea_ctrl_socket.format(inet=inet) + if not os.path.exists(path): return None @@ -319,19 +323,25 @@ 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') + leases = _ctrl_socket_command(inet, 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' +def kea_delete_lease(inet, ip_address): + args = {'ip-address': ip_address} - config = _ctrl_socket_command(ctrl_socket, 'config-get') + result = _ctrl_socket_command(inet, f'lease{inet}-del', args) + + if result and 'result' in result: + return result['result'] == 0 + + return False + +def kea_get_active_config(inet): + config = _ctrl_socket_command(inet, 'config-get') if not config or 'result' not in config or config['result'] != 0: return None diff --git a/src/op_mode/clear_dhcp_lease.py b/src/op_mode/clear_dhcp_lease.py deleted file mode 100755 index 7d4b47104..000000000 --- a/src/op_mode/clear_dhcp_lease.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. If not, see <http://www.gnu.org/licenses/>. - -import argparse -import re - -from vyos.configquery import ConfigTreeQuery -from vyos.kea import kea_parse_leases -from vyos.utils.io import ask_yes_no -from vyos.utils.process import call -from vyos.utils.commit import commit_in_progress - -# TODO: Update to use Kea control socket command "lease4-del" - -config = ConfigTreeQuery() -base = ['service', 'dhcp-server'] -lease_file = '/config/dhcp/dhcp4-leases.csv' - - -def del_lease_ip(address): - """ - Read lease_file and write data to this file - without specific section "lease ip" - Delete section "lease x.x.x.x { x;x;x; }" - """ - with open(lease_file, encoding='utf-8') as f: - data = f.read().rstrip() - pattern = rf"^{address},[^\n]+\n" - # Delete lease for ip block - data = re.sub(pattern, '', data) - - # Write new data to original lease_file - with open(lease_file, 'w', encoding='utf-8') as f: - f.write(data) - -def is_ip_in_leases(address): - """ - Return True if address found in the lease file - """ - leases = kea_parse_leases(lease_file) - for lease in leases: - if address == lease['address']: - return True - print(f'Address "{address}" not found in "{lease_file}"') - return False - -if not config.exists(base): - print('DHCP-server not configured!') - exit(0) - -if config.exists(base + ['failover']): - print('Lease cannot be reset in failover mode!') - exit(0) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('--ip', help='IPv4 address', action='store', required=True) - - args = parser.parse_args() - address = args.ip - - if not is_ip_in_leases(address): - exit(1) - - if commit_in_progress(): - print('Cannot clear DHCP lease while a commit is in progress') - exit(1) - - if not ask_yes_no(f'This will restart DHCP server.\nContinue?'): - exit(1) - else: - del_lease_ip(address) - call('systemctl restart kea-dhcp4-server.service') diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py index 1d9ad0e76..d27e1baf7 100755 --- a/src/op_mode/dhcp.py +++ b/src/op_mode/dhcp.py @@ -31,6 +31,7 @@ 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_delete_lease from vyos.utils.process import is_systemd_service_running time_string = "%a %b %d %H:%M:%S %Z %Y" @@ -360,6 +361,28 @@ def show_server_static_mappings(raw: bool, family: ArgFamily, pool: typing.Optio else: return _get_formatted_server_static_mappings(static_mappings, family=family) +def _lease_valid(inet, address): + leases = kea_get_leases(inet) + for lease in leases: + if address == lease['ip-address']: + return True + return False + +@_verify +def clear_dhcp_server_lease(family: ArgFamily, address: str): + v = 'v6' if family == 'inet6' else '' + inet = '6' if family == 'inet6' else '4' + + if not _lease_valid(inet, address): + print(f'Lease not found on DHCP{v} server') + return None + + if not kea_delete_lease(inet, address): + print(f'Failed to clear lease for "{address}"') + return None + + print(f'Lease "{address}" has been cleared') + def _get_raw_client_leases(family='inet', interface=None): from time import mktime from datetime import datetime |