summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/vyos/base.py8
-rw-r--r--python/vyos/config.py57
-rw-r--r--python/vyos/configdict.py118
-rw-r--r--python/vyos/configverify.py6
-rw-r--r--python/vyos/ethtool.py8
-rw-r--r--python/vyos/ifconfig/ethernet.py17
-rw-r--r--python/vyos/ifconfig/geneve.py2
-rwxr-xr-xpython/vyos/ifconfig/interface.py20
-rw-r--r--python/vyos/ifconfig/pppoe.py80
-rw-r--r--python/vyos/ifconfig/vxlan.py2
10 files changed, 171 insertions, 147 deletions
diff --git a/python/vyos/base.py b/python/vyos/base.py
index fd22eaccd..78067d5b2 100644
--- a/python/vyos/base.py
+++ b/python/vyos/base.py
@@ -1,4 +1,4 @@
-# Copyright 2018-2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2018-2022 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
@@ -15,6 +15,12 @@
from textwrap import fill
+class Warning():
+ def __init__(self, message):
+ # Reformat the message and trim it to 72 characters in length
+ message = fill(message, width=72)
+ print(f'\nWARNING: {message}')
+
class DeprecationWarning():
def __init__(self, message):
# Reformat the message and trim it to 72 characters in length
diff --git a/python/vyos/config.py b/python/vyos/config.py
index a5c1ad122..287fd2ed1 100644
--- a/python/vyos/config.py
+++ b/python/vyos/config.py
@@ -142,31 +142,41 @@ class Config(object):
def exists(self, path):
"""
- Checks if a node with given path exists in the running or proposed config
+ Checks if a node or value with given path exists in the proposed config.
+
+ Args:
+ path (str): Configuration tree path
Returns:
- True if node exists, False otherwise
+ True if node or value exists in the proposed config, False otherwise
Note:
- This function cannot be used outside a configuration sessions.
+ This function should not be used outside of configuration sessions.
In operational mode scripts, use ``exists_effective``.
"""
- if not self._session_config:
+ if self._session_config is None:
return False
+
+ # Assume the path is a node path first
if self._session_config.exists(self._make_path(path)):
return True
else:
+ # If that check fails, it may mean the path has a value at the end.
# libvyosconfig exists() works only for _nodes_, not _values_
- # libvyattacfg one also worked for values, so we emulate that case here
+ # libvyattacfg also worked for values, so we emulate that case here
if isinstance(path, str):
path = re.split(r'\s+', path)
path_without_value = path[:-1]
- path_str = " ".join(path_without_value)
try:
- value = self._session_config.return_value(self._make_path(path_str))
- return (value == path[-1])
+ # return_values() is safe to use with single-value nodes,
+ # it simply returns a single-item list in that case.
+ values = self._session_config.return_values(self._make_path(path_without_value))
+
+ # If we got this far, the node does exist and has values,
+ # so we need to check if it has the value in question among its values.
+ return (path[-1] in values)
except vyos.configtree.ConfigTreeError:
- # node doesn't exist at all
+ # Even the parent node doesn't exist at all
return False
def session_changed(self):
@@ -380,7 +390,7 @@ class Config(object):
def exists_effective(self, path):
"""
- Check if a node exists in the running (effective) config
+ Checks if a node or value exists in the running (effective) config.
Args:
path (str): Configuration tree path
@@ -392,10 +402,31 @@ class Config(object):
This function is safe to use in operational mode. In configuration mode,
it ignores uncommited changes.
"""
- if self._running_config:
- return(self._running_config.exists(self._make_path(path)))
+ if self._running_config is None:
+ return False
+
+ # Assume the path is a node path first
+ if self._running_config.exists(self._make_path(path)):
+ return True
+ else:
+ # If that check fails, it may mean the path has a value at the end.
+ # libvyosconfig exists() works only for _nodes_, not _values_
+ # libvyattacfg also worked for values, so we emulate that case here
+ if isinstance(path, str):
+ path = re.split(r'\s+', path)
+ path_without_value = path[:-1]
+ try:
+ # return_values() is safe to use with single-value nodes,
+ # it simply returns a single-item list in that case.
+ values = self._running_config.return_values(self._make_path(path_without_value))
+
+ # If we got this far, the node does exist and has values,
+ # so we need to check if it has the value in question among its values.
+ return (path[-1] in values)
+ except vyos.configtree.ConfigTreeError:
+ # Even the parent node doesn't exist at all
+ return False
- return False
def return_effective_value(self, path, default=None):
"""
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 551c27b67..04ddc10e9 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -1,4 +1,4 @@
-# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2022 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
@@ -104,6 +104,11 @@ def list_diff(first, second):
second = set(second)
return [item for item in first if item not in second]
+def is_node_changed(conf, path):
+ from vyos.configdiff import get_config_diff
+ D = get_config_diff(conf, key_mangling=('-', '_'))
+ return D.is_node_changed(path)
+
def leaf_node_changed(conf, path):
"""
Check if a leaf node was altered. If it has been altered - values has been
@@ -114,7 +119,6 @@ def leaf_node_changed(conf, path):
"""
from vyos.configdiff import get_config_diff
D = get_config_diff(conf, key_mangling=('-', '_'))
- D.set_level(conf.get_level())
(new, old) = D.get_value_diff(path)
if new != old:
if isinstance(old, dict):
@@ -144,12 +148,11 @@ def node_changed(conf, path, key_mangling=None, recursive=False):
"""
from vyos.configdiff import get_config_diff, Diff
D = get_config_diff(conf, key_mangling)
- D.set_level(conf.get_level())
# get_child_nodes() will return dict_keys(), mangle this into a list with PEP448
keys = D.get_child_nodes_diff(path, expand_nodes=Diff.DELETE, recursive=recursive)['delete'].keys()
return list(keys)
-def get_removed_vlans(conf, dict):
+def get_removed_vlans(conf, path, dict):
"""
Common function to parse a dictionary retrieved via get_config_dict() and
determine any added/removed VLAN interfaces - be it 802.1q or Q-in-Q.
@@ -159,16 +162,17 @@ def get_removed_vlans(conf, dict):
# Check vif, vif-s/vif-c VLAN interfaces for removal
D = get_config_diff(conf, key_mangling=('-', '_'))
D.set_level(conf.get_level())
+
# get_child_nodes() will return dict_keys(), mangle this into a list with PEP448
- keys = D.get_child_nodes_diff(['vif'], expand_nodes=Diff.DELETE)['delete'].keys()
+ keys = D.get_child_nodes_diff(path + ['vif'], expand_nodes=Diff.DELETE)['delete'].keys()
if keys: dict['vif_remove'] = [*keys]
# get_child_nodes() will return dict_keys(), mangle this into a list with PEP448
- keys = D.get_child_nodes_diff(['vif-s'], expand_nodes=Diff.DELETE)['delete'].keys()
+ keys = D.get_child_nodes_diff(path + ['vif-s'], expand_nodes=Diff.DELETE)['delete'].keys()
if keys: dict['vif_s_remove'] = [*keys]
for vif in dict.get('vif_s', {}).keys():
- keys = D.get_child_nodes_diff(['vif-s', vif, 'vif-c'], expand_nodes=Diff.DELETE)['delete'].keys()
+ keys = D.get_child_nodes_diff(path + ['vif-s', vif, 'vif-c'], expand_nodes=Diff.DELETE)['delete'].keys()
if keys: dict['vif_s'][vif]['vif_c_remove'] = [*keys]
return dict
@@ -212,10 +216,6 @@ def is_member(conf, interface, intftype=None):
intftype = intftypes if intftype == None else [intftype]
- # set config level to root
- old_level = conf.get_level()
- conf.set_level([])
-
for iftype in intftype:
base = ['interfaces', iftype]
for intf in conf.list_nodes(base):
@@ -225,7 +225,6 @@ def is_member(conf, interface, intftype=None):
get_first_key=True, no_tag_node_value_mangle=True)
ret_val.update({intf : tmp})
- old_level = conf.set_level(old_level)
return ret_val
def is_mirror_intf(conf, interface, direction=None):
@@ -247,8 +246,6 @@ def is_mirror_intf(conf, interface, direction=None):
direction = directions if direction == None else [direction]
ret_val = None
- old_level = conf.get_level()
- conf.set_level([])
base = ['interfaces']
for dir in direction:
@@ -262,7 +259,6 @@ def is_mirror_intf(conf, interface, direction=None):
get_first_key=True)
ret_val = {intf : tmp}
- old_level = conf.set_level(old_level)
return ret_val
def has_vlan_subinterface_configured(conf, intf):
@@ -276,15 +272,11 @@ def has_vlan_subinterface_configured(conf, intf):
from vyos.ifconfig import Section
ret = False
- old_level = conf.get_level()
- conf.set_level([])
-
intfpath = ['interfaces', Section.section(intf), intf]
if ( conf.exists(intfpath + ['vif']) or
conf.exists(intfpath + ['vif-s'])):
ret = True
- conf.set_level(old_level)
return ret
def is_source_interface(conf, interface, intftype=None):
@@ -306,11 +298,6 @@ def is_source_interface(conf, interface, intftype=None):
'have a source-interface')
intftype = intftypes if intftype == None else [intftype]
-
- # set config level to root
- old_level = conf.get_level()
- conf.set_level([])
-
for it in intftype:
base = ['interfaces', it]
for intf in conf.list_nodes(base):
@@ -319,7 +306,6 @@ def is_source_interface(conf, interface, intftype=None):
ret_val = intf
break
- old_level = conf.set_level(old_level)
return ret_val
def get_dhcp_interfaces(conf, vrf=None):
@@ -330,40 +316,67 @@ def get_dhcp_interfaces(conf, vrf=None):
if not dict:
return dhcp_interfaces
- def check_dhcp(config, ifname):
+ def check_dhcp(config):
+ ifname = config['ifname']
tmp = {}
if 'address' in config and 'dhcp' in config['address']:
options = {}
- if 'dhcp_options' in config and 'default_route_distance' in config['dhcp_options']:
- options.update({'distance' : config['dhcp_options']['default_route_distance']})
+ if dict_search('dhcp_options.default_route_distance', config) != None:
+ options.update({'dhcp_options' : config['dhcp_options']})
if 'vrf' in config:
if vrf is config['vrf']: tmp.update({ifname : options})
else: tmp.update({ifname : options})
+
return tmp
for section, interface in dict.items():
for ifname in interface:
+ # always reset config level, as get_interface_dict() will alter it
+ conf.set_level([])
# we already have a dict representation of the config from get_config_dict(),
# but with the extended information from get_interface_dict() we also
# get the DHCP client default-route-distance default option if not specified.
- ifconfig = get_interface_dict(conf, ['interfaces', section], ifname)
+ _, ifconfig = get_interface_dict(conf, ['interfaces', section], ifname)
- tmp = check_dhcp(ifconfig, ifname)
+ tmp = check_dhcp(ifconfig)
dhcp_interfaces.update(tmp)
# check per VLAN interfaces
for vif, vif_config in ifconfig.get('vif', {}).items():
- tmp = check_dhcp(vif_config, f'{ifname}.{vif}')
+ tmp = check_dhcp(vif_config)
dhcp_interfaces.update(tmp)
# check QinQ VLAN interfaces
- for vif_s, vif_s_config in ifconfig.get('vif-s', {}).items():
- tmp = check_dhcp(vif_s_config, f'{ifname}.{vif_s}')
+ for vif_s, vif_s_config in ifconfig.get('vif_s', {}).items():
+ tmp = check_dhcp(vif_s_config)
dhcp_interfaces.update(tmp)
- for vif_c, vif_c_config in vif_s_config.get('vif-c', {}).items():
- tmp = check_dhcp(vif_c_config, f'{ifname}.{vif_s}.{vif_c}')
+ for vif_c, vif_c_config in vif_s_config.get('vif_c', {}).items():
+ tmp = check_dhcp(vif_c_config)
dhcp_interfaces.update(tmp)
return dhcp_interfaces
+def get_pppoe_interfaces(conf, vrf=None):
+ """ Common helper functions to retrieve all interfaces from current CLI
+ sessions that have DHCP configured. """
+ pppoe_interfaces = {}
+ for ifname in conf.list_nodes(['interfaces', 'pppoe']):
+ # always reset config level, as get_interface_dict() will alter it
+ conf.set_level([])
+ # we already have a dict representation of the config from get_config_dict(),
+ # but with the extended information from get_interface_dict() we also
+ # get the DHCP client default-route-distance default option if not specified.
+ ifconfig = get_interface_dict(conf, ['interfaces', 'pppoe'], ifname)
+
+ options = {}
+ if 'default_route_distance' in ifconfig:
+ options.update({'default_route_distance' : ifconfig['default_route_distance']})
+ if 'no_default_route' in ifconfig:
+ options.update({'no_default_route' : {}})
+ if 'vrf' in ifconfig:
+ if vrf is ifconfig['vrf']: pppoe_interfaces.update({ifname : options})
+ else: pppoe_interfaces.update({ifname : options})
+
+ return pppoe_interfaces
+
def get_interface_dict(config, base, ifname=''):
"""
Common utility function to retrieve and mangle the interfaces configuration
@@ -373,7 +386,6 @@ def get_interface_dict(config, base, ifname=''):
Return a dictionary with the necessary interface config keys.
"""
-
if not ifname:
from vyos import ConfigError
# determine tagNode instance
@@ -390,9 +402,8 @@ def get_interface_dict(config, base, ifname=''):
for vif in ['vif', 'vif_s']:
if vif in default_values: del default_values[vif]
- # setup config level which is extracted in get_removed_vlans()
- config.set_level(base + [ifname])
- dict = config.get_config_dict([], key_mangling=('-', '_'), get_first_key=True,
+ dict = config.get_config_dict(base + [ifname], key_mangling=('-', '_'),
+ get_first_key=True,
no_tag_node_value_mangle=True)
# Check if interface has been removed. We must use exists() as
@@ -400,8 +411,8 @@ def get_interface_dict(config, base, ifname=''):
# node like the following exists.
# +macsec macsec1 {
# +}
- if not config.exists([]):
- dict.update({'deleted' : ''})
+ if not config.exists(base + [ifname]):
+ dict.update({'deleted' : {}})
# Add interface instance name into dictionary
dict.update({'ifname': ifname})
@@ -428,7 +439,7 @@ def get_interface_dict(config, base, ifname=''):
# XXX: T2665: blend in proper DHCPv6-PD default values
dict = T2665_set_dhcpv6pd_defaults(dict)
- address = leaf_node_changed(config, ['address'])
+ address = leaf_node_changed(config, base + [ifname, 'address'])
if address: dict.update({'address_old' : address})
# Check if we are a member of a bridge device
@@ -459,10 +470,10 @@ def get_interface_dict(config, base, ifname=''):
tmp = is_member(config, dict['source_interface'], 'bonding')
if tmp: dict.update({'source_interface_is_bond_member' : tmp})
- mac = leaf_node_changed(config, ['mac'])
+ mac = leaf_node_changed(config, base + [ifname, 'mac'])
if mac: dict.update({'mac_old' : mac})
- eui64 = leaf_node_changed(config, ['ipv6', 'address', 'eui64'])
+ eui64 = leaf_node_changed(config, base + [ifname, 'ipv6', 'address', 'eui64'])
if eui64:
tmp = dict_search('ipv6.address', dict)
if not tmp:
@@ -474,6 +485,9 @@ def get_interface_dict(config, base, ifname=''):
# identical for all types of VLAN interfaces as they all include the same
# XML definitions which hold the defaults.
for vif, vif_config in dict.get('vif', {}).items():
+ # Add subinterface name to dictionary
+ dict['vif'][vif].update({'ifname' : f'{ifname}.{vif}'})
+
default_vif_values = defaults(base + ['vif'])
# XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely
# remove the default values from the dict.
@@ -483,7 +497,7 @@ def get_interface_dict(config, base, ifname=''):
# Only add defaults if interface is not about to be deleted - this is
# to keep a cleaner config dict.
if 'deleted' not in dict:
- address = leaf_node_changed(config, ['vif', vif, 'address'])
+ address = leaf_node_changed(config, base + [ifname, 'vif', vif, 'address'])
if address: dict['vif'][vif].update({'address_old' : address})
dict['vif'][vif] = dict_merge(default_vif_values, dict['vif'][vif])
@@ -505,6 +519,9 @@ def get_interface_dict(config, base, ifname=''):
if dhcp: dict['vif'][vif].update({'dhcp_options_changed' : ''})
for vif_s, vif_s_config in dict.get('vif_s', {}).items():
+ # Add subinterface name to dictionary
+ dict['vif_s'][vif_s].update({'ifname' : f'{ifname}.{vif_s}'})
+
default_vif_s_values = defaults(base + ['vif-s'])
# XXX: T2665: we only wan't the vif-s defaults - do not care about vif-c
if 'vif_c' in default_vif_s_values: del default_vif_s_values['vif_c']
@@ -517,7 +534,7 @@ def get_interface_dict(config, base, ifname=''):
# Only add defaults if interface is not about to be deleted - this is
# to keep a cleaner config dict.
if 'deleted' not in dict:
- address = leaf_node_changed(config, ['vif-s', vif_s, 'address'])
+ address = leaf_node_changed(config, base + [ifname, 'vif-s', vif_s, 'address'])
if address: dict['vif_s'][vif_s].update({'address_old' : address})
dict['vif_s'][vif_s] = dict_merge(default_vif_s_values,
@@ -541,6 +558,9 @@ def get_interface_dict(config, base, ifname=''):
if dhcp: dict['vif_s'][vif_s].update({'dhcp_options_changed' : ''})
for vif_c, vif_c_config in vif_s_config.get('vif_c', {}).items():
+ # Add subinterface name to dictionary
+ dict['vif_s'][vif_s]['vif_c'][vif_c].update({'ifname' : f'{ifname}.{vif_s}.{vif_c}'})
+
default_vif_c_values = defaults(base + ['vif-s', 'vif-c'])
# XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely
@@ -551,7 +571,7 @@ def get_interface_dict(config, base, ifname=''):
# Only add defaults if interface is not about to be deleted - this is
# to keep a cleaner config dict.
if 'deleted' not in dict:
- address = leaf_node_changed(config, ['vif-s', vif_s, 'vif-c', vif_c, 'address'])
+ address = leaf_node_changed(config, base + [ifname, 'vif-s', vif_s, 'vif-c', vif_c, 'address'])
if address: dict['vif_s'][vif_s]['vif_c'][vif_c].update(
{'address_old' : address})
@@ -578,8 +598,8 @@ def get_interface_dict(config, base, ifname=''):
if dhcp: dict['vif_s'][vif_s]['vif_c'][vif_c].update({'dhcp_options_changed' : ''})
# Check vif, vif-s/vif-c VLAN interfaces for removal
- dict = get_removed_vlans(config, dict)
- return dict
+ dict = get_removed_vlans(config, base + [ifname], dict)
+ return ifname, dict
def get_vlan_ids(interface):
"""
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index 1062d51ee..438485d98 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -1,4 +1,4 @@
-# Copyright 2020-2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2020-2022 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
@@ -205,10 +205,10 @@ def verify_mirror_redirect(config):
raise ConfigError(f'Requested redirect interface "{redirect_ifname}" '\
'does not exist!')
- if dict_search('traffic_policy.in', config) != None:
+ if ('mirror' in config or 'redirect' in config) and dict_search('traffic_policy.in', config) is not None:
# XXX: support combination of limiting and redirect/mirror - this is an
# artificial limitation
- raise ConfigError('Can not use ingress policy tigether with mirror or redirect!')
+ raise ConfigError('Can not use ingress policy together with mirror or redirect!')
def verify_authentication(config):
"""
diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py
index 672ee2cc9..2b6012a73 100644
--- a/python/vyos/ethtool.py
+++ b/python/vyos/ethtool.py
@@ -1,4 +1,4 @@
-# Copyright 2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2021-2022 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
@@ -18,8 +18,10 @@ import re
from vyos.util import popen
-# These drivers do not support using ethtool to change the speed, duplex, or flow control settings
-_drivers_without_speed_duplex_flow = ['vmxnet3', 'virtio_net', 'xen_netfront', 'iavf', 'ice', 'i40e']
+# These drivers do not support using ethtool to change the speed, duplex, or
+# flow control settings
+_drivers_without_speed_duplex_flow = ['vmxnet3', 'virtio_net', 'xen_netfront',
+ 'iavf', 'ice', 'i40e', 'hv_netvsc']
class Ethtool:
"""
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index 9d54dc78e..1280fc238 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -170,11 +170,18 @@ class EthernetIf(Interface):
return
cmd = f'ethtool --change {ifname}'
- if speed == 'auto' or duplex == 'auto':
- cmd += ' autoneg on'
- else:
- cmd += f' speed {speed} duplex {duplex} autoneg off'
- return self._cmd(cmd)
+ try:
+ if speed == 'auto' or duplex == 'auto':
+ cmd += ' autoneg on'
+ else:
+ cmd += f' speed {speed} duplex {duplex} autoneg off'
+ return self._cmd(cmd)
+ except PermissionError:
+ # Some NICs do not tell that they don't suppport settings speed/duplex,
+ # but they do not actually support it either.
+ # In that case it's probably better to ignore the error
+ # than end up with a broken config.
+ print('Warning: could not set speed/duplex settings: operation not permitted!')
def set_gro(self, state):
"""
diff --git a/python/vyos/ifconfig/geneve.py b/python/vyos/ifconfig/geneve.py
index 7cb3968df..276c34cd7 100644
--- a/python/vyos/ifconfig/geneve.py
+++ b/python/vyos/ifconfig/geneve.py
@@ -42,7 +42,7 @@ class GeneveIf(Interface):
# arguments used by iproute2. For more information please refer to:
# - https://man7.org/linux/man-pages/man8/ip-link.8.html
mapping = {
- 'parameters.ip.dont_fragment': 'df set',
+ 'parameters.ip.df' : 'df',
'parameters.ip.tos' : 'tos',
'parameters.ip.ttl' : 'ttl',
'parameters.ipv6.flowlabel' : 'flowlabel',
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 6b0f08fd4..22441d1d2 100755
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -736,8 +736,9 @@ class Interface(Control):
if all_rp_filter == 1: global_setting = 'strict'
elif all_rp_filter == 2: global_setting = 'loose'
- print(f'WARNING: Global source-validation is set to "{global_setting}\n"' \
- 'this overrides per interface setting!')
+ from vyos.base import Warning
+ Warning(f'Global source-validation is set to "{global_setting} '\
+ f'this overrides per interface setting!')
tmp = self.get_interface('rp_filter')
if int(tmp) == value:
@@ -1243,8 +1244,8 @@ 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.j2', self._config)
+ render(config_file, 'dhcp-client/ipv4.j2', self._config)
# 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
@@ -1274,8 +1275,7 @@ class Interface(Control):
systemd_service = f'dhcp6c@{ifname}.service'
if enable and 'disable' not in self._config:
- render(config_file, 'dhcp-client/ipv6.tmpl',
- self._config)
+ render(config_file, 'dhcp-client/ipv6.j2', self._config)
# We must ignore any return codes. This is required to enable
# DHCPv6-PD for interfaces which are yet not up and running.
@@ -1587,12 +1587,10 @@ class Interface(Control):
tmp['source_interface'] = ifname
tmp['vlan_id'] = vif_s_id
- vif_s_ifname = f'{ifname}.{vif_s_id}'
- vif_s_config['ifname'] = vif_s_ifname
-
# It is not possible to change the VLAN encapsulation protocol
# "on-the-fly". For this "quirk" we need to actively delete and
# re-create the VIF-S interface.
+ vif_s_ifname = f'{ifname}.{vif_s_id}'
if self.exists(vif_s_ifname):
cur_cfg = get_interface_config(vif_s_ifname)
protocol = dict_search('linkinfo.info_data.protocol', cur_cfg).lower()
@@ -1614,7 +1612,6 @@ class Interface(Control):
tmp['vlan_id'] = vif_c_id
vif_c_ifname = f'{vif_s_ifname}.{vif_c_id}'
- vif_c_config['ifname'] = vif_c_ifname
c_vlan = VLANIf(vif_c_ifname, **tmp)
c_vlan.update(vif_c_config)
@@ -1625,10 +1622,7 @@ class Interface(Control):
# create/update 802.1q VLAN interfaces
for vif_id, vif_config in config.get('vif', {}).items():
-
vif_ifname = f'{ifname}.{vif_id}'
- vif_config['ifname'] = vif_ifname
-
tmp = deepcopy(VLANIf.get_config())
tmp['source_interface'] = ifname
tmp['vlan_id'] = vif_id
diff --git a/python/vyos/ifconfig/pppoe.py b/python/vyos/ifconfig/pppoe.py
index 1d13264bf..63ffc8069 100644
--- a/python/vyos/ifconfig/pppoe.py
+++ b/python/vyos/ifconfig/pppoe.py
@@ -27,12 +27,13 @@ class PPPoEIf(Interface):
},
}
- def _remove_routes(self, vrf=''):
+ def _remove_routes(self, vrf=None):
# Always delete default routes when interface is removed
+ vrf_cmd = ''
if vrf:
- vrf = f'-c "vrf {vrf}"'
- self._cmd(f'vtysh -c "conf t" {vrf} -c "no ip route 0.0.0.0/0 {self.ifname} tag 210"')
- self._cmd(f'vtysh -c "conf t" {vrf} -c "no ipv6 route ::/0 {self.ifname} tag 210"')
+ vrf_cmd = f'-c "vrf {vrf}"'
+ self._cmd(f'vtysh -c "conf t" {vrf_cmd} -c "no ip route 0.0.0.0/0 {self.ifname} tag 210"')
+ self._cmd(f'vtysh -c "conf t" {vrf_cmd} -c "no ipv6 route ::/0 {self.ifname} tag 210"')
def remove(self):
"""
@@ -44,11 +45,11 @@ class PPPoEIf(Interface):
>>> i = Interface('pppoe0')
>>> i.remove()
"""
-
+ vrf = None
tmp = get_interface_config(self.ifname)
- vrf = ''
if 'master' in tmp:
- self._remove_routes(tmp['master'])
+ vrf = tmp['master']
+ self._remove_routes(vrf)
# remove bond master which places members in disabled state
super().remove()
@@ -84,10 +85,12 @@ class PPPoEIf(Interface):
self._config = config
# remove old routes from an e.g. old VRF assignment
- vrf = ''
- if 'vrf_old' in config:
- vrf = config['vrf_old']
- self._remove_routes(vrf)
+ if 'shutdown_required':
+ vrf = None
+ tmp = get_interface_config(self.ifname)
+ if 'master' in tmp:
+ vrf = tmp['master']
+ self._remove_routes(vrf)
# DHCPv6 PD handling is a bit different on PPPoE interfaces, as we do
# not require an 'address dhcpv6' CLI option as with other interfaces
@@ -98,54 +101,15 @@ class PPPoEIf(Interface):
super().update(config)
- if 'default_route' not in config or config['default_route'] == 'none':
- return
-
- #
- # Set default routes pointing to pppoe interface
- #
- vrf = ''
- sed_opt = '^ip route'
-
- install_v4 = True
- install_v6 = True
-
# generate proper configuration string when VRFs are in use
+ vrf = ''
if 'vrf' in config:
tmp = config['vrf']
vrf = f'-c "vrf {tmp}"'
- sed_opt = f'vrf {tmp}'
-
- if config['default_route'] == 'auto':
- # only add route if there is no default route present
- tmp = self._cmd(f'vtysh -c "show running-config staticd no-header" | sed -n "/{sed_opt}/,/!/p"')
- for line in tmp.splitlines():
- line = line.lstrip()
- if line.startswith('ip route 0.0.0.0/0'):
- install_v4 = False
- continue
-
- if 'ipv6' in config and line.startswith('ipv6 route ::/0'):
- install_v6 = False
- continue
-
- elif config['default_route'] == 'force':
- # Force means that all static routes are replaced with the ones from this interface
- tmp = self._cmd(f'vtysh -c "show running-config staticd no-header" | sed -n "/{sed_opt}/,/!/p"')
- for line in tmp.splitlines():
- if self.ifname in line:
- # It makes no sense to remove a route with our interface and the later re-add it.
- # This will only make traffic disappear - which is a no-no!
- continue
-
- line = line.lstrip()
- if line.startswith('ip route 0.0.0.0/0'):
- self._cmd(f'vtysh -c "conf t" {vrf} -c "no {line}"')
-
- if 'ipv6' in config and line.startswith('ipv6 route ::/0'):
- self._cmd(f'vtysh -c "conf t" {vrf} -c "no {line}"')
-
- if install_v4:
- self._cmd(f'vtysh -c "conf t" {vrf} -c "ip route 0.0.0.0/0 {self.ifname} tag 210"')
- if install_v6 and 'ipv6' in config:
- self._cmd(f'vtysh -c "conf t" {vrf} -c "ipv6 route ::/0 {self.ifname} tag 210"')
+
+ if 'no_default_route' not in config:
+ # Set default route(s) pointing to PPPoE interface
+ distance = config['default_route_distance']
+ self._cmd(f'vtysh -c "conf t" {vrf} -c "ip route 0.0.0.0/0 {self.ifname} tag 210 {distance}"')
+ if 'ipv6' in config:
+ self._cmd(f'vtysh -c "conf t" {vrf} -c "ipv6 route ::/0 {self.ifname} tag 210 {distance}"')
diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py
index 516a19f24..5baff10a9 100644
--- a/python/vyos/ifconfig/vxlan.py
+++ b/python/vyos/ifconfig/vxlan.py
@@ -57,7 +57,7 @@ class VXLANIf(Interface):
'group' : 'group',
'external' : 'external',
'gpe' : 'gpe',
- 'parameters.ip.dont_fragment': 'df set',
+ 'parameters.ip.df' : 'df',
'parameters.ip.tos' : 'tos',
'parameters.ip.ttl' : 'ttl',
'parameters.ipv6.flowlabel' : 'flowlabel',