summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/vyos/frrender.py50
-rw-r--r--python/vyos/ifconfig/ethernet.py173
-rw-r--r--python/vyos/ifconfig/interface.py12
-rw-r--r--python/vyos/utils/network.py4
4 files changed, 165 insertions, 74 deletions
diff --git a/python/vyos/frrender.py b/python/vyos/frrender.py
index badc5d59f..544983b2c 100644
--- a/python/vyos/frrender.py
+++ b/python/vyos/frrender.py
@@ -218,11 +218,11 @@ def get_frrender_dict(conf, argv=None) -> dict:
# values present on the CLI - that's why we have if conf.exists()
eigrp_cli_path = ['protocols', 'eigrp']
if conf.exists(eigrp_cli_path):
- isis = conf.get_config_dict(eigrp_cli_path, key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True,
- with_recursive_defaults=True)
- dict.update({'eigrp' : isis})
+ eigrp = conf.get_config_dict(eigrp_cli_path, key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True,
+ with_recursive_defaults=True)
+ dict.update({'eigrp' : eigrp})
elif conf.exists_effective(eigrp_cli_path):
dict.update({'eigrp' : {'deleted' : ''}})
@@ -360,17 +360,24 @@ def get_frrender_dict(conf, argv=None) -> dict:
static = conf.get_config_dict(static_cli_path, key_mangling=('-', '_'),
get_first_key=True,
no_tag_node_value_mangle=True)
-
- # T3680 - get a list of all interfaces currently configured to use DHCP
- tmp = get_dhcp_interfaces(conf)
- if tmp: static.update({'dhcp' : tmp})
- tmp = get_pppoe_interfaces(conf)
- if tmp: static.update({'pppoe' : tmp})
-
dict.update({'static' : static})
elif conf.exists_effective(static_cli_path):
dict.update({'static' : {'deleted' : ''}})
+ # T3680 - get a list of all interfaces currently configured to use DHCP
+ tmp = get_dhcp_interfaces(conf)
+ if tmp:
+ if 'static' in dict:
+ dict['static'].update({'dhcp' : tmp})
+ else:
+ dict.update({'static' : {'dhcp' : tmp}})
+ tmp = get_pppoe_interfaces(conf)
+ if tmp:
+ if 'static' in dict:
+ dict['static'].update({'pppoe' : tmp})
+ else:
+ dict.update({'static' : {'pppoe' : tmp}})
+
# keep a re-usable list of dependent VRFs
dependent_vrfs_default = {}
if 'bgp' in dict:
@@ -534,17 +541,32 @@ def get_frrender_dict(conf, argv=None) -> dict:
dict.update({'vrf' : vrf})
+ if os.path.exists(frr_debug_enable):
+ import pprint
+ pprint.pprint(dict)
+
return dict
class FRRender:
+ cached_config_dict = {}
def __init__(self):
self._frr_conf = '/run/frr/config/vyos.frr.conf'
def generate(self, config_dict) -> None:
+ """
+ Generate FRR configuration file
+ Returns False if no changes to configuration were made, otherwise True
+ """
if not isinstance(config_dict, dict):
tmp = type(config_dict)
raise ValueError(f'Config must be of type "dict" and not "{tmp}"!')
+
+ if self.cached_config_dict == config_dict:
+ debug('FRR: NO CHANGES DETECTED')
+ return False
+ self.cached_config_dict = config_dict
+
def inline_helper(config_dict) -> str:
output = '!\n'
if 'babel' in config_dict and 'deleted' not in config_dict['babel']:
@@ -639,7 +661,7 @@ class FRRender:
debug(output)
write_file(self._frr_conf, output)
debug('FRR: RENDERING CONFIG COMPLETE')
- return None
+ return True
def apply(self, count_max=5):
count = 0
@@ -649,7 +671,7 @@ class FRRender:
debug(f'FRR: reloading configuration - tries: {count} | Python class ID: {id(self)}')
cmdline = '/usr/lib/frr/frr-reload.py --reload'
if os.path.exists(frr_debug_enable):
- cmdline += ' --debug'
+ cmdline += ' --debug --stdout'
rc, emsg = rc_cmd(f'{cmdline} {self._frr_conf}')
if rc != 0:
sleep(2)
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index 50dd0f396..d0c03dbe0 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -26,11 +26,13 @@ from vyos.utils.file import read_file
from vyos.utils.process import run
from vyos.utils.assertion import assert_list
+
@Interface.register
class EthernetIf(Interface):
"""
Abstraction of a Linux Ethernet Interface
"""
+
iftype = 'ethernet'
definition = {
**Interface.definition,
@@ -41,7 +43,7 @@ class EthernetIf(Interface):
'broadcast': True,
'bridgeable': True,
'eternal': '(lan|eth|eno|ens|enp|enx)[0-9]+$',
- }
+ },
}
@staticmethod
@@ -49,32 +51,35 @@ class EthernetIf(Interface):
run(f'ethtool --features {ifname} {option} {value}')
return False
- _command_set = {**Interface._command_set, **{
- 'gro': {
- 'validate': lambda v: assert_list(v, ['on', 'off']),
- 'possible': lambda i, v: EthernetIf.feature(i, 'gro', v),
- },
- 'gso': {
- 'validate': lambda v: assert_list(v, ['on', 'off']),
- 'possible': lambda i, v: EthernetIf.feature(i, 'gso', v),
- },
- 'hw-tc-offload': {
- 'validate': lambda v: assert_list(v, ['on', 'off']),
- 'possible': lambda i, v: EthernetIf.feature(i, 'hw-tc-offload', v),
- },
- 'lro': {
- 'validate': lambda v: assert_list(v, ['on', 'off']),
- 'possible': lambda i, v: EthernetIf.feature(i, 'lro', v),
- },
- 'sg': {
- 'validate': lambda v: assert_list(v, ['on', 'off']),
- 'possible': lambda i, v: EthernetIf.feature(i, 'sg', v),
- },
- 'tso': {
- 'validate': lambda v: assert_list(v, ['on', 'off']),
- 'possible': lambda i, v: EthernetIf.feature(i, 'tso', v),
+ _command_set = {
+ **Interface._command_set,
+ **{
+ 'gro': {
+ 'validate': lambda v: assert_list(v, ['on', 'off']),
+ 'possible': lambda i, v: EthernetIf.feature(i, 'gro', v),
+ },
+ 'gso': {
+ 'validate': lambda v: assert_list(v, ['on', 'off']),
+ 'possible': lambda i, v: EthernetIf.feature(i, 'gso', v),
+ },
+ 'hw-tc-offload': {
+ 'validate': lambda v: assert_list(v, ['on', 'off']),
+ 'possible': lambda i, v: EthernetIf.feature(i, 'hw-tc-offload', v),
+ },
+ 'lro': {
+ 'validate': lambda v: assert_list(v, ['on', 'off']),
+ 'possible': lambda i, v: EthernetIf.feature(i, 'lro', v),
+ },
+ 'sg': {
+ 'validate': lambda v: assert_list(v, ['on', 'off']),
+ 'possible': lambda i, v: EthernetIf.feature(i, 'sg', v),
+ },
+ 'tso': {
+ 'validate': lambda v: assert_list(v, ['on', 'off']),
+ 'possible': lambda i, v: EthernetIf.feature(i, 'tso', v),
+ },
},
- }}
+ }
@staticmethod
def get_bond_member_allowed_options() -> list:
@@ -106,7 +111,7 @@ class EthernetIf(Interface):
'ring_buffer.rx',
'ring_buffer.tx',
'speed',
- 'hw_id'
+ 'hw_id',
]
return bond_allowed_sections
@@ -130,7 +135,11 @@ class EthernetIf(Interface):
self.set_admin_state('down')
# Remove all VLAN subinterfaces - filter with the VLAN dot
- for vlan in [x for x in Section.interfaces(self.iftype) if x.startswith(f'{self.ifname}.')]:
+ for vlan in [
+ x
+ for x in Section.interfaces(self.iftype)
+ if x.startswith(f'{self.ifname}.')
+ ]:
Interface(vlan).remove()
super().remove()
@@ -149,10 +158,12 @@ class EthernetIf(Interface):
ifname = self.config['ifname']
if enable not in ['on', 'off']:
- raise ValueError("Value out of range")
+ raise ValueError('Value out of range')
if not self.ethtool.check_flow_control():
- self._debug_msg(f'NIC driver does not support changing flow control settings!')
+ self._debug_msg(
+ 'NIC driver does not support changing flow control settings!'
+ )
return False
current = self.ethtool.get_flow_control()
@@ -180,12 +191,24 @@ class EthernetIf(Interface):
"""
ifname = self.config['ifname']
- if speed not in ['auto', '10', '100', '1000', '2500', '5000', '10000',
- '25000', '40000', '50000', '100000', '400000']:
- raise ValueError("Value out of range (speed)")
+ if speed not in [
+ 'auto',
+ '10',
+ '100',
+ '1000',
+ '2500',
+ '5000',
+ '10000',
+ '25000',
+ '40000',
+ '50000',
+ '100000',
+ '400000',
+ ]:
+ raise ValueError('Value out of range (speed)')
if duplex not in ['auto', 'full', 'half']:
- raise ValueError("Value out of range (duplex)")
+ raise ValueError('Value out of range (duplex)')
if not self.ethtool.check_speed_duplex(speed, duplex):
Warning(f'changing speed/duplex setting on "{ifname}" is unsupported!')
@@ -224,7 +247,9 @@ class EthernetIf(Interface):
# 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!')
+ print(
+ 'Warning: could not set speed/duplex settings: operation not permitted!'
+ )
def set_gro(self, state):
"""
@@ -243,7 +268,9 @@ class EthernetIf(Interface):
if not fixed:
return self.set_interface('gro', 'on' if state else 'off')
else:
- print('Adapter does not support changing generic-receive-offload settings!')
+ print(
+ 'Adapter does not support changing generic-receive-offload settings!'
+ )
return False
def set_gso(self, state):
@@ -262,7 +289,9 @@ class EthernetIf(Interface):
if not fixed:
return self.set_interface('gso', 'on' if state else 'off')
else:
- print('Adapter does not support changing generic-segmentation-offload settings!')
+ print(
+ 'Adapter does not support changing generic-segmentation-offload settings!'
+ )
return False
def set_hw_tc_offload(self, state):
@@ -300,7 +329,9 @@ class EthernetIf(Interface):
if not fixed:
return self.set_interface('lro', 'on' if state else 'off')
else:
- print('Adapter does not support changing large-receive-offload settings!')
+ print(
+ 'Adapter does not support changing large-receive-offload settings!'
+ )
return False
def set_rps(self, state):
@@ -332,13 +363,15 @@ class EthernetIf(Interface):
for i in range(0, cpu_count, 32):
# Extract the next 32-bit chunk
chunk = (rps_cpus >> i) & 0xFFFFFFFF
- hex_chunks.append(f"{chunk:08x}")
+ hex_chunks.append(f'{chunk:08x}')
# Join the chunks with commas
- rps_cpus = ",".join(hex_chunks)
+ rps_cpus = ','.join(hex_chunks)
for i in range(queues):
- self._write_sysfs(f'/sys/class/net/{self.ifname}/queues/rx-{i}/rps_cpus', rps_cpus)
+ self._write_sysfs(
+ f'/sys/class/net/{self.ifname}/queues/rx-{i}/rps_cpus', rps_cpus
+ )
# send bitmask representation as hex string without leading '0x'
return True
@@ -348,10 +381,13 @@ class EthernetIf(Interface):
queues = len(glob(f'/sys/class/net/{self.ifname}/queues/rx-*'))
if state:
global_rfs_flow = 32768
- rfs_flow = int(global_rfs_flow/queues)
+ rfs_flow = int(global_rfs_flow / queues)
for i in range(0, queues):
- self._write_sysfs(f'/sys/class/net/{self.ifname}/queues/rx-{i}/rps_flow_cnt', rfs_flow)
+ self._write_sysfs(
+ f'/sys/class/net/{self.ifname}/queues/rx-{i}/rps_flow_cnt',
+ rfs_flow,
+ )
return True
@@ -392,7 +428,9 @@ class EthernetIf(Interface):
if not fixed:
return self.set_interface('tso', 'on' if state else 'off')
else:
- print('Adapter does not support changing tcp-segmentation-offload settings!')
+ print(
+ 'Adapter does not support changing tcp-segmentation-offload settings!'
+ )
return False
def set_ring_buffer(self, rx_tx, size):
@@ -417,39 +455,64 @@ class EthernetIf(Interface):
print(f'could not set "{rx_tx}" ring-buffer for {ifname}')
return output
+ def set_switchdev(self, enable):
+ ifname = self.config['ifname']
+ addr, code = self._popen(
+ f"ethtool -i {ifname} | grep bus-info | awk '{{print $2}}'"
+ )
+ if code != 0:
+ print(f'could not resolve PCIe address of {ifname}')
+ return
+
+ enabled = False
+ state, code = self._popen(
+ f"/sbin/devlink dev eswitch show pci/{addr} | awk '{{print $3}}'"
+ )
+ if code == 0 and state == 'switchdev':
+ enabled = True
+
+ if enable and not enabled:
+ output, code = self._popen(
+ f'/sbin/devlink dev eswitch set pci/{addr} mode switchdev'
+ )
+ if code != 0:
+ print(f'{ifname} does not support switchdev mode')
+ elif not enable and enabled:
+ self._cmd(f'/sbin/devlink dev eswitch set pci/{addr} mode legacy')
+
def update(self, config):
- """ General helper function which works on a dictionary retrived by
+ """General helper function which works on a dictionary retrived by
get_config_dict(). It's main intention is to consolidate the scattered
interface setup code and provide a single point of entry when workin
- on any interface. """
+ on any interface."""
# disable ethernet flow control (pause frames)
value = 'off' if 'disable_flow_control' in config else 'on'
self.set_flow_control(value)
# GRO (generic receive offload)
- self.set_gro(dict_search('offload.gro', config) != None)
+ self.set_gro(dict_search('offload.gro', config) is not None)
# GSO (generic segmentation offload)
- self.set_gso(dict_search('offload.gso', config) != None)
+ self.set_gso(dict_search('offload.gso', config) is not None)
# GSO (generic segmentation offload)
- self.set_hw_tc_offload(dict_search('offload.hw_tc_offload', config) != None)
+ self.set_hw_tc_offload(dict_search('offload.hw_tc_offload', config) is not None)
# LRO (large receive offload)
- self.set_lro(dict_search('offload.lro', config) != None)
+ self.set_lro(dict_search('offload.lro', config) is not None)
# RPS - Receive Packet Steering
- self.set_rps(dict_search('offload.rps', config) != None)
+ self.set_rps(dict_search('offload.rps', config) is not None)
# RFS - Receive Flow Steering
- self.set_rfs(dict_search('offload.rfs', config) != None)
+ self.set_rfs(dict_search('offload.rfs', config) is not None)
# scatter-gather option
- self.set_sg(dict_search('offload.sg', config) != None)
+ self.set_sg(dict_search('offload.sg', config) is not None)
# TSO (TCP segmentation offloading)
- self.set_tso(dict_search('offload.tso', config) != None)
+ self.set_tso(dict_search('offload.tso', config) is not None)
# Set physical interface speed and duplex
if 'speed_duplex_changed' in config:
@@ -463,6 +526,8 @@ class EthernetIf(Interface):
for rx_tx, size in config['ring_buffer'].items():
self.set_ring_buffer(rx_tx, size)
+ self.set_switchdev('switchdev' in config)
+
# call base class last
super().update(config)
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index cad1685a9..de821ab60 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -1423,11 +1423,13 @@ class Interface(Control):
tmp = get_interface_address(self.ifname)
if tmp and 'addr_info' in tmp:
for address_dict in tmp['addr_info']:
- # Only remove dynamic assigned addresses
- if 'dynamic' not in address_dict:
- continue
- address = address_dict['local']
- self.del_addr(address)
+ if address_dict['family'] == 'inet':
+ # Only remove dynamic assigned addresses
+ if 'dynamic' not in address_dict:
+ continue
+ address = address_dict['local']
+ prefixlen = address_dict['prefixlen']
+ self.del_addr(f'{address}/{prefixlen}')
# cleanup old config files
for file in [dhclient_config_file, systemd_override_file, dhclient_lease_file]:
diff --git a/python/vyos/utils/network.py b/python/vyos/utils/network.py
index 8fce08de0..dc0c0a6d6 100644
--- a/python/vyos/utils/network.py
+++ b/python/vyos/utils/network.py
@@ -69,7 +69,9 @@ def get_vrf_members(vrf: str) -> list:
answer = json.loads(output)
for data in answer:
if 'ifname' in data:
- interfaces.append(data.get('ifname'))
+ # Skip PIM interfaces which appears in VRF
+ if 'pim' not in data.get('ifname'):
+ interfaces.append(data.get('ifname'))
except:
pass
return interfaces