summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniil Baturin <daniil@vyos.io>2024-03-09 20:06:40 +0100
committerGitHub <noreply@github.com>2024-03-09 20:06:40 +0100
commite8a6d3ddb6eb36299dbced2e119ba0699e062350 (patch)
treeaed5859834208a758f251bb729d20ae3caf9eb80
parentda759a46efaaa937edae2440a7743d325ebf99b1 (diff)
parent0920121eed97889ff33ef84f04a3730242ccb04b (diff)
downloadvyos-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.in20
-rw-r--r--op-mode-definitions/dhcp.xml.in30
-rw-r--r--python/vyos/kea.py24
-rwxr-xr-xsrc/op_mode/clear_dhcp_lease.py88
-rwxr-xr-xsrc/op_mode/dhcp.py23
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