summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2022-02-20 10:36:33 +0100
committerChristian Poessinger <christian@poessinger.com>2022-02-20 18:49:55 +0100
commit3a1a7c40a13ee9f5561823a79876d88d3f5bf053 (patch)
treed0e38c4532080a375f40a17970994e2c2ac8b088
parent29ba813fb65b8b292105cdae4f8f71fcce6350a1 (diff)
downloadvyos-1x-3a1a7c40a13ee9f5561823a79876d88d3f5bf053.tar.gz
vyos-1x-3a1a7c40a13ee9f5561823a79876d88d3f5bf053.zip
interface: T4203: prevent DHCP client restart if not necessary
In the past whenever a change happened to any interface and it was configured as a DHCP client, VyOS always had a breif outage as DHCP released the old lease and re-aquired a new one - bad! This commit changes the behavior that DHCP client is only restarted if any one of the possible options one can set for DHCP client under the "dhcp-options" node is altered.
-rw-r--r--python/vyos/configdict.py38
-rwxr-xr-xpython/vyos/ifconfig/interface.py26
2 files changed, 51 insertions, 13 deletions
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index e7f515ea9..efeb6dc1f 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -423,6 +423,15 @@ def get_interface_dict(config, base, ifname=''):
bond = is_member(config, ifname, 'bonding')
if bond: dict.update({'is_bond_member' : bond})
+ # Check if any DHCP options changed which require a client restat
+ for leaf_node in ['client-id', 'default-route-distance', 'host-name',
+ 'no-default-route', 'vendor-class-id']:
+ dhcp = leaf_node_changed(config, ['dhcp-options', leaf_node])
+ if dhcp:
+ dict.update({'dhcp_options_old' : dhcp})
+ # one option is suffiecient to set 'dhcp_options_old' key
+ break
+
# Some interfaces come with a source_interface which must also not be part
# of any other bond or bridge interface as it is exclusivly assigned as the
# Kernels "lower" interface to this new "virtual/upper" interface.
@@ -470,6 +479,15 @@ def get_interface_dict(config, base, ifname=''):
bridge = is_member(config, f'{ifname}.{vif}', 'bridge')
if bridge: dict['vif'][vif].update({'is_bridge_member' : bridge})
+ # Check if any DHCP options changed which require a client restat
+ for leaf_node in ['client-id', 'default-route-distance', 'host-name',
+ 'no-default-route', 'vendor-class-id']:
+ dhcp = leaf_node_changed(config, ['vif', vif, 'dhcp-options', leaf_node])
+ if dhcp:
+ dict['vif'][vif].update({'dhcp_options_old' : dhcp})
+ # one option is suffiecient to set 'dhcp_options_old' key
+ break
+
for vif_s, vif_s_config in dict.get('vif_s', {}).items():
default_vif_s_values = defaults(base + ['vif-s'])
# XXX: T2665: we only wan't the vif-s defaults - do not care about vif-c
@@ -495,6 +513,15 @@ def get_interface_dict(config, base, ifname=''):
bridge = is_member(config, f'{ifname}.{vif_s}', 'bridge')
if bridge: dict['vif_s'][vif_s].update({'is_bridge_member' : bridge})
+ # Check if any DHCP options changed which require a client restat
+ for leaf_node in ['client-id', 'default-route-distance', 'host-name',
+ 'no-default-route', 'vendor-class-id']:
+ dhcp = leaf_node_changed(config, ['vif-s', vif_s, 'dhcp-options', leaf_node])
+ if dhcp:
+ dict['vif_s'][vif_s].update({'dhcp_options_old' : dhcp})
+ # one option is suffiecient to set 'dhcp_options_old' key
+ break
+
for vif_c, vif_c_config in vif_s_config.get('vif_c', {}).items():
default_vif_c_values = defaults(base + ['vif-s', 'vif-c'])
@@ -521,6 +548,16 @@ def get_interface_dict(config, base, ifname=''):
if bridge: dict['vif_s'][vif_s]['vif_c'][vif_c].update(
{'is_bridge_member' : bridge})
+ # Check if any DHCP options changed which require a client restat
+ for leaf_node in ['client-id', 'default-route-distance', 'host-name',
+ 'no-default-route', 'vendor-class-id']:
+ dhcp = leaf_node_changed(config, ['vif-s', vif_s, 'vif-c', vif_c,
+ 'dhcp-options', leaf_node])
+ if dhcp:
+ dict['vif_s'][vif_s]['vif_c'][vif_c].update({'dhcp_options_old' : dhcp})
+ # one option is suffiecient to set 'dhcp_options_old' key
+ break
+
# Check vif, vif-s/vif-c VLAN interfaces for removal
dict = get_removed_vlans(config, dict)
return dict
@@ -545,7 +582,6 @@ def get_vlan_ids(interface):
return vlan_ids
-
def get_accel_dict(config, base, chap_secrets):
"""
Common utility function to retrieve and mangle the Accel-PPP configuration
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 91c7f0c33..cf1887bf6 100755
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -1228,12 +1228,11 @@ class Interface(Control):
options_file = f'{config_base}_{ifname}.options'
pid_file = f'{config_base}_{ifname}.pid'
lease_file = f'{config_base}_{ifname}.leases'
-
- # Stop client with old config files to get the right IF_METRIC.
systemd_service = f'dhclient@{ifname}.service'
- if is_systemd_service_active(systemd_service):
- self._cmd(f'systemctl stop {systemd_service}')
+ # 'up' check is mandatory b/c even if the interface is A/D, as soon as
+ # the DHCP client is started the interface will be placed in u/u state.
+ # This is not what we intended to do when disabling an interface.
if enable and 'disable' not in self._config:
if dict_search('dhcp_options.host_name', self._config) == None:
# read configured system hostname.
@@ -1244,16 +1243,19 @@ class Interface(Control):
tmp = {'dhcp_options' : { 'host_name' : hostname}}
self._config = dict_merge(tmp, self._config)
- render(options_file, 'dhcp-client/daemon-options.tmpl',
- self._config)
- render(config_file, 'dhcp-client/ipv4.tmpl',
- self._config)
+ render(options_file, 'dhcp-client/daemon-options.tmpl', self._config)
+ render(config_file, 'dhcp-client/ipv4.tmpl', self._config)
- # 'up' check is mandatory b/c even if the interface is A/D, as soon as
- # the DHCP client is started the interface will be placed in u/u state.
- # This is not what we intended to do when disabling an interface.
- return self._cmd(f'systemctl restart {systemd_service}')
+ # When the DHCP client is restarted a brief outage will occur, as
+ # the old lease is released a new one is acquired (T4203). We will
+ # only restart DHCP client if it's option changed, or if it's not
+ # running, but it should be running (e.g. on system startup)
+ if 'dhcp_options_old' in self._config or not is_systemd_service_active(systemd_service):
+ return self._cmd(f'systemctl restart {systemd_service}')
+ return None
else:
+ if is_systemd_service_active(systemd_service):
+ self._cmd(f'systemctl stop {systemd_service}')
# cleanup old config files
for file in [config_file, options_file, pid_file, lease_file]:
if os.path.isfile(file):