summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface-definitions/interfaces-ethernet.xml.in2
-rw-r--r--python/vyos/configdict.py29
-rw-r--r--python/vyos/configverify.py53
-rw-r--r--python/vyos/ifconfig/ethernet.py101
-rw-r--r--python/vyos/ifconfig/interface.py106
-rw-r--r--python/vyos/ifconfig_vlan.py24
-rwxr-xr-xsrc/conf_mode/interfaces-ethernet.py329
7 files changed, 371 insertions, 273 deletions
diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in
index 1e32a15f8..e8f3f09f1 100644
--- a/interface-definitions/interfaces-ethernet.xml.in
+++ b/interface-definitions/interfaces-ethernet.xml.in
@@ -56,6 +56,7 @@
</constraint>
<constraintErrorMessage>duplex must be auto, half or full</constraintErrorMessage>
</properties>
+ <defaultValue>auto</defaultValue>
</leafNode>
#include <include/interface-hw-id.xml.i>
<node name="ip">
@@ -265,6 +266,7 @@
</constraint>
<constraintErrorMessage>Speed must be auto, 10, 100, 1000, 2500, 5000, 10000, 25000, 40000, 50000 or 100000</constraintErrorMessage>
</properties>
+ <defaultValue>auto</defaultValue>
</leafNode>
#include <include/vif-s.xml.i>
#include <include/vif.xml.i>
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 0dc7578d8..682caed8f 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -102,12 +102,35 @@ def dict_merge(source, destination):
return tmp
def list_diff(first, second):
- """
- Diff two dictionaries and return only unique items
- """
+ """ Diff two dictionaries and return only unique items """
second = set(second)
return [item for item in first if item not in second]
+def T2665_default_dict_cleanup(dict):
+ """ Cleanup default keys for tag nodes https://phabricator.vyos.net/T2665. """
+ # Cleanup
+ for vif in ['vif', 'vif_s']:
+ if vif in dict.keys():
+ for key in ['ip', 'mtu']:
+ if key in dict[vif].keys():
+ del dict[vif][key]
+
+ # cleanup VIF-S defaults
+ if 'vif_c' in dict[vif].keys():
+ for key in ['ip', 'mtu']:
+ if key in dict[vif]['vif_c'].keys():
+ del dict[vif]['vif_c'][key]
+ # If there is no vif-c defined and we just cleaned the default
+ # keys - we can clean the entire vif-c dict as it's useless
+ if not dict[vif]['vif_c']:
+ del dict[vif]['vif_c']
+
+ # If there is no real vif/vif-s defined and we just cleaned the default
+ # keys - we can clean the entire vif dict as it's useless
+ if not dict[vif]:
+ del dict[vif]
+
+ return dict
def get_ethertype(ethertype_val):
if ethertype_val == '0x88A8':
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index 32129a048..36b10c956 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -41,14 +41,14 @@ def verify_vrf(config):
def verify_address(config):
"""
- Common helper function used by interface implementations to
- perform recurring validation of IP address assignmenr
- when interface also is part of a bridge.
+ Common helper function used by interface implementations to perform
+ recurring validation of IP address assignment when interface is part
+ of a bridge or bond.
"""
if {'is_bridge_member', 'address'} <= set(config):
raise ConfigError(
- f'Cannot assign address to interface "{ifname}" as it is a '
- f'member of bridge "{is_bridge_member}"!'.format(**config))
+ 'Cannot assign address to interface "{ifname}" as it is a '
+ 'member of bridge "{is_bridge_member}"!'.format(**config))
def verify_bridge_delete(config):
@@ -62,6 +62,15 @@ def verify_bridge_delete(config):
'Interface "{ifname}" cannot be deleted as it is a '
'member of bridge "{is_bridge_member}"!'.format(**config))
+def verify_interface_exists(config):
+ """
+ Common helper function used by interface implementations to perform
+ recurring validation if an interface actually exists.
+ """
+ from netifaces import interfaces
+ if not config['ifname'] in interfaces():
+ raise ConfigError(f'Interface "{ifname}" does not exist!'
+ .format(**config))
def verify_source_interface(config):
"""
@@ -76,3 +85,37 @@ def verify_source_interface(config):
if not config['source_interface'] in interfaces():
raise ConfigError(f'Source interface {source_interface} does not '
f'exist'.format(**config))
+
+def verify_dhcpv6(config):
+ """
+ Common helper function used by interface implementations to perform
+ recurring validation of DHCPv6 options which are mutually exclusive.
+ """
+ if {'parameters_only', 'temporary'} <= set(config.get('dhcpv6_options', {})):
+ raise ConfigError('DHCPv6 temporary and parameters-only options '
+ 'are mutually exclusive!')
+
+def verify_vlan_config(config):
+ """
+ Common helper function used by interface implementations to perform
+ recurring validation of interface VLANs
+ """
+ # 802.1q VLANs
+ for vlan in config.get('vif', {}).keys():
+ vlan = config['vif'][vlan]
+ verify_dhcpv6(vlan)
+ verify_address(vlan)
+ verify_vrf(vlan)
+
+ # 802.1ad (Q-in-Q) VLANs
+ for vlan in config.get('vif_s', {}).keys():
+ vlan = config['vif_s'][vlan]
+ verify_dhcpv6(vlan)
+ verify_address(vlan)
+ verify_vrf(vlan)
+
+ for vlan in config.get('vif_s', {}).get('vif_c', {}).keys():
+ vlan = config['vif_c'][vlan]
+ verify_dhcpv6(vlan)
+ verify_address(vlan)
+ verify_vrf(vlan)
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index 5b18926c9..8a50a8699 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -15,13 +15,14 @@
import os
import re
+import jmespath
+from vyos.configdict import get_ethertype
from vyos.ifconfig.interface import Interface
from vyos.ifconfig.vlan import VLAN
from vyos.validate import assert_list
from vyos.util import run
-
@Interface.register
@VLAN.enable
class EthernetIf(Interface):
@@ -252,3 +253,101 @@ class EthernetIf(Interface):
>>> i.set_udp_offload('on')
"""
return self.set_interface('ufo', state)
+
+
+ def update(self, config):
+ """ 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. """
+
+ # now call the regular function from within our base class
+ super().update(config)
+
+ # disable ethernet flow control (pause frames)
+ value = 'off' if 'disable_flow_control' in config.keys() else 'on'
+ self.set_flow_control(value)
+
+ # GRO (generic receive offload)
+ tmp = jmespath.search('offload_options.generic_receive', config)
+ value = tmp if (tmp != None) else 'off'
+ self.set_gro(value)
+
+ # GSO (generic segmentation offload)
+ tmp = jmespath.search('offload_options.generic_segmentation', config)
+ value = tmp if (tmp != None) else 'off'
+ self.set_gso(value)
+
+ # scatter-gather option
+ tmp = jmespath.search('offload_options.scatter_gather', config)
+ value = tmp if (tmp != None) else 'off'
+ self.set_sg(value)
+
+ # TSO (TCP segmentation offloading)
+ tmp = jmespath.search('offload_options.udp_fragmentation', config)
+ value = tmp if (tmp != None) else 'off'
+ self.set_tso(value)
+
+ # UDP fragmentation offloading
+ tmp = jmespath.search('offload_options.udp_fragmentation', config)
+ value = tmp if (tmp != None) else 'off'
+ self.set_ufo(value)
+
+ # Set physical interface speed and duplex
+ if {'speed', 'duplex'} <= set(config):
+ speed = config.get('speed')
+ duplex = config.get('duplex')
+ self.set_speed_duplex(speed, duplex)
+
+ # Delete old IPv6 EUI64 addresses before changing MAC
+
+ # Change interface MAC address - re-set to real hardware address (hw-id)
+ # if custom mac is removed. Skip if bond member.
+ if 'is_bond_member' not in config:
+ mac = config.get('hw_id')
+ if 'mac' in config:
+ mac = config.get('mac')
+ if mac:
+ self.set_mac(mac)
+
+ # Add IPv6 EUI-based addresses
+ tmp = jmespath.search('ipv6.address.eui64', config)
+ if tmp:
+ # XXX: T2636 workaround: convert string to a list with one element
+ if isinstance(tmp, str):
+ tmp = [tmp]
+ for addr in tmp:
+ self.add_ipv6_eui64_address(addr)
+
+ # re-add ourselves to any bridge we might have fallen out of
+ if 'is_bridge_member' in config:
+ bridge = config.get('is_bridge_member')
+ self.add_to_bridge(bridge)
+
+ # remove no longer required 802.1ad (Q-in-Q VLANs)
+ for vif_s_id in config.get('vif_s_remove', {}):
+ self.del_vlan(vif_s_id)
+
+ # create/update 802.1ad (Q-in-Q VLANs)
+ for vif_s_id, vif_s in config.get('vif_s', {}).items():
+ tmp=get_ethertype(vif_s.get('ethertype', '0x88A8'))
+ s_vlan = self.add_vlan(vif_s_id, ethertype=tmp)
+ s_vlan.update(vif_s)
+
+ # remove no longer required client VLAN (vif-c)
+ for vif_c_id in vif_s.get('vif_c_remove', {}):
+ s_vlan.del_vlan(vif_c_id)
+
+ # create/update client VLAN (vif-c) interface
+ for vif_c_id, vif_c in vif_s.get('vif_c', {}).items():
+ c_vlan = s_vlan.add_vlan(vif_c_id)
+ c_vlan.update(vif_c)
+
+ # remove no longer required 802.1q VLAN interfaces
+ for vif_id in config.get('vif_remove', {}):
+ self.del_vlan(vif_id)
+
+ # create/update 802.1q VLAN interfaces
+ for vif_id, vif in config.get('vif', {}).items():
+ vlan = self.add_vlan(vif_id)
+ vlan.update(vif)
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 8d7b247fc..689faa22b 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -16,6 +16,7 @@
import os
import re
import json
+import jmespath
from copy import deepcopy
from ipaddress import IPv4Network
@@ -322,11 +323,11 @@ class Interface(Control):
self.set_admin_state('down')
self.set_interface('mac', mac)
-
+
# Turn an interface to the 'up' state if it was changed to 'down' by this fucntion
if prev_state == 'up':
self.set_admin_state('up')
-
+
def set_vrf(self, vrf=''):
"""
Add/Remove interface from given VRF instance.
@@ -773,14 +774,17 @@ class Interface(Control):
on any interface. """
# Update interface description
- self.set_alias(config.get('description', None))
+ self.set_alias(config.get('description', ''))
+
+ # Ignore link state changes
+ value = '2' if 'disable_link_detect' in config else '1'
+ self.set_link_detect(value)
# Configure assigned interface IP addresses. No longer
# configured addresses will be removed first
new_addr = config.get('address', [])
- # XXX workaround for T2636, convert IP address string to a list
- # with one element
+ # XXX: T2636 workaround: convert string to a list with one element
if isinstance(new_addr, str):
new_addr = [new_addr]
@@ -800,6 +804,96 @@ class Interface(Control):
# Bind interface instance into VRF
self.set_vrf(config.get('vrf', ''))
+ # DHCP options
+ if 'dhcp_options' in config:
+ dhcp_options = config.get('dhcp_options')
+ if 'client_id' in dhcp_options:
+ self.dhcp.v4.options['client_id'] = dhcp_options.get('client_id')
+
+ if 'host_name' in dhcp_options:
+ self.dhcp.v4.options['hostname'] = dhcp_options.get('host_name')
+
+ if 'vendor_class_id' in dhcp_options:
+ self.dhcp.v4.options['vendor_class_id'] = dhcp_options.get('vendor_class_id')
+
+ # DHCPv6 options
+ if 'dhcpv6_options' in config:
+ dhcpv6_options = config.get('dhcpv6_options')
+ if 'parameters_only' in dhcpv6_options:
+ self.dhcp.v6.options['dhcpv6_prm_only'] = True
+
+ if 'temporary' in dhcpv6_options:
+ self.dhcp.v6.options['dhcpv6_temporary'] = True
+
+ if 'prefix_delegation' in dhcpv6_options:
+ prefix_delegation = dhcpv6_options.get('prefix_delegation')
+ if 'length' in prefix_delegation:
+ self.dhcp.v6.options['dhcpv6_pd_length'] = prefix_delegation.get('length')
+
+ if 'interface' in prefix_delegation:
+ self.dhcp.v6.options['dhcpv6_pd_interfaces'] = prefix_delegation.get('interface')
+
+ # Configure ARP cache timeout in milliseconds - has default value
+ tmp = jmespath.search('ip.arp_cache_timeout', config)
+ value = tmp if (tmp != None) else '30'
+ self.set_arp_cache_tmo(value)
+
+ # Configure ARP filter configuration
+ tmp = jmespath.search('ip.disable_arp_filter', config)
+ value = '0' if (tmp != None) else '1'
+ self.set_arp_filter(value)
+
+ # Configure ARP accept
+ tmp = jmespath.search('ip.enable_arp_accept', config)
+ value = '1' if (tmp != None) else '0'
+ self.set_arp_accept(value)
+
+ # Configure ARP announce
+ tmp = jmespath.search('ip.enable_arp_announce', config)
+ value = '1' if (tmp != None) else '0'
+ self.set_arp_announce(value)
+
+ # Configure ARP ignore
+ tmp = jmespath.search('ip.enable_arp_ignore', config)
+ value = '1' if (tmp != None) else '0'
+ self.set_arp_ignore(value)
+
+ # Enable proxy-arp on this interface
+ tmp = jmespath.search('ip.enable_proxy_arp', config)
+ value = '1' if (tmp != None) else '0'
+ self.set_proxy_arp(value)
+
+ # Enable private VLAN proxy ARP on this interface
+ tmp = jmespath.search('ip.proxy_arp_pvlan', config)
+ value = '1' if (tmp != None) else '0'
+ self.set_proxy_arp_pvlan(value)
+
+ # IPv6 forwarding
+ tmp = jmespath.search('ipv6.disable_forwarding', config)
+ value = '0' if (tmp != None) else '1'
+ self.set_ipv6_forwarding(value)
+
+ # IPv6 router advertisements
+ tmp = jmespath.search('ipv6.address.autoconf', config)
+ value = '2' if (tmp != None) else '1'
+ if 'dhcpv6' in new_addr:
+ value = '2'
+ self.set_ipv6_accept_ra(value)
+
+ # IPv6 address autoconfiguration
+ tmp = jmespath.search('ipv6.address.autoconf', config)
+ value = '1' if (tmp != None) else '0'
+ self.set_ipv6_autoconf(value)
+
+ # IPv6 Duplicate Address Detection (DAD) tries
+ tmp = jmespath.search('ipv6.dup_addr_detect_transmits', config)
+ value = tmp if (tmp != None) else '1'
+ self.set_ipv6_dad_messages(value)
+
+ # MTU - Maximum Transfer Unit
+ if 'mtu' in config:
+ self.set_mtu(config.get('mtu'))
+
# Interface administrative state
- state = 'down' if 'disable' in config.keys() else 'up'
+ state = 'down' if 'disable' in config else 'up'
self.set_admin_state(state)
diff --git a/python/vyos/ifconfig_vlan.py b/python/vyos/ifconfig_vlan.py
index 442cb0db8..ecb6796fa 100644
--- a/python/vyos/ifconfig_vlan.py
+++ b/python/vyos/ifconfig_vlan.py
@@ -16,6 +16,30 @@
from netifaces import interfaces
from vyos import ConfigError
+def get_removed_vlans(conf, 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.
+ """
+ from vyos.configdiff import get_config_diff, Diff
+
+ # 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()
+ 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()
+ 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()
+ dict['vif_s'][vif]['vif_c_remove'] = [*keys]
+
+ return dict
+
def apply_all_vlans(intf, intfconfig):
"""
Function applies all VLANs to the passed interface.
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py
index 8b895c4d2..60aafae32 100755
--- a/src/conf_mode/interfaces-ethernet.py
+++ b/src/conf_mode/interfaces-ethernet.py
@@ -17,295 +17,108 @@
import os
from sys import exit
-from copy import deepcopy
-from netifaces import interfaces
+from vyos.config import Config
+from vyos.configdict import dict_merge
+from vyos.configdict import T2665_default_dict_cleanup
+from vyos.configverify import verify_interface_exists
+from vyos.configverify import verify_dhcpv6
+from vyos.configverify import verify_address
+from vyos.configverify import verify_vrf
+from vyos.configverify import verify_vlan_config
from vyos.ifconfig import EthernetIf
-from vyos.ifconfig_vlan import apply_all_vlans, verify_vlan_config
-from vyos.configdict import list_diff, intf_to_dict, add_to_dict, interface_default_data
+from vyos.ifconfig_vlan import get_removed_vlans
from vyos.validate import is_member
-from vyos.config import Config
+from vyos.xml import defaults
from vyos import ConfigError
-
from vyos import airbag
airbag.enable()
-default_config_data = {
- **interface_default_data,
- 'deleted': False,
- 'duplex': 'auto',
- 'flow_control': 'on',
- 'hw_id': '',
- 'ip_arp_cache_tmo': 30,
- 'ip_proxy_arp_pvlan': 0,
- 'is_bond_member': False,
- 'intf': '',
- 'offload_gro': 'off',
- 'offload_gso': 'off',
- 'offload_sg': 'off',
- 'offload_tso': 'off',
- 'offload_ufo': 'off',
- 'speed': 'auto',
- 'vif_s': {},
- 'vif_s_remove': [],
- 'vif': {},
- 'vif_remove': [],
- 'vrf': ''
-}
-
def get_config():
+ """ Retrive CLI config as dictionary. Dictionary can never be empty,
+ as at least the interface name will be added or a deleted flag """
+ conf = Config()
+
# determine tagNode instance
if 'VYOS_TAGNODE_VALUE' not in os.environ:
raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified')
- ifname = os.environ['VYOS_TAGNODE_VALUE']
- conf = Config()
-
- # check if ethernet interface has been removed
- cfg_base = ['interfaces', 'ethernet', ifname]
- if not conf.exists(cfg_base):
- eth = deepcopy(default_config_data)
- eth['intf'] = ifname
- eth['deleted'] = True
- # we can not bail out early as ethernet interface can not be removed
- # Kernel will complain with: RTNETLINK answers: Operation not supported.
- # Thus we need to remove individual settings
- return eth
-
- # set new configuration level
- conf.set_level(cfg_base)
-
- eth, disabled = intf_to_dict(conf, default_config_data)
-
- # disable ethernet flow control (pause frames)
- if conf.exists('disable-flow-control'):
- eth['flow_control'] = 'off'
-
- # retrieve real hardware address
- if conf.exists('hw-id'):
- eth['hw_id'] = conf.return_value('hw-id')
-
- # interface duplex
- if conf.exists('duplex'):
- eth['duplex'] = conf.return_value('duplex')
+ # retrieve interface default values
+ base = ['interfaces', 'ethernet']
+ default_values = defaults(base)
- # ARP cache entry timeout in seconds
- if conf.exists('ip arp-cache-timeout'):
- eth['ip_arp_cache_tmo'] = int(conf.return_value('ip arp-cache-timeout'))
-
- # Enable private VLAN proxy ARP on this interface
- if conf.exists('ip proxy-arp-pvlan'):
- eth['ip_proxy_arp_pvlan'] = 1
-
- # check if we are a member of any bond
- eth['is_bond_member'] = is_member(conf, eth['intf'], 'bonding')
-
- # GRO (generic receive offload)
- if conf.exists('offload-options generic-receive'):
- eth['offload_gro'] = conf.return_value('offload-options generic-receive')
-
- # GSO (generic segmentation offload)
- if conf.exists('offload-options generic-segmentation'):
- eth['offload_gso'] = conf.return_value('offload-options generic-segmentation')
-
- # scatter-gather option
- if conf.exists('offload-options scatter-gather'):
- eth['offload_sg'] = conf.return_value('offload-options scatter-gather')
-
- # TSO (TCP segmentation offloading)
- if conf.exists('offload-options tcp-segmentation'):
- eth['offload_tso'] = conf.return_value('offload-options tcp-segmentation')
-
- # UDP fragmentation offloading
- if conf.exists('offload-options udp-fragmentation'):
- eth['offload_ufo'] = conf.return_value('offload-options udp-fragmentation')
-
- # interface speed
- if conf.exists('speed'):
- eth['speed'] = conf.return_value('speed')
-
- # remove default IPv6 link-local address if member of a bond
- if eth['is_bond_member'] and 'fe80::/64' in eth['ipv6_eui64_prefix']:
- eth['ipv6_eui64_prefix'].remove('fe80::/64')
- eth['ipv6_eui64_prefix_remove'].append('fe80::/64')
-
- add_to_dict(conf, disabled, eth, 'vif', 'vif')
- add_to_dict(conf, disabled, eth, 'vif-s', 'vif_s')
-
- return eth
-
-
-def verify(eth):
- if eth['deleted']:
+ ifname = os.environ['VYOS_TAGNODE_VALUE']
+ base = base + [ifname]
+ # setup config level which is extracted in get_removed_vlans()
+ conf.set_level(base)
+ ethernet = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True)
+
+ # Check if interface has been removed
+ if ethernet == {}:
+ ethernet.update({'deleted' : ''})
+
+ # We have gathered the dict representation of the CLI, but there are
+ # default options which we need to update into the dictionary
+ # retrived.
+ ethernet = dict_merge(default_values, ethernet)
+
+ # Add interface instance name into dictionary
+ ethernet.update({'ifname': ifname})
+
+ # Check if we are a member of a bridge device
+ bridge = is_member(conf, ifname, 'bridge')
+ if bridge:
+ tmp = {'is_bridge_member' : bridge}
+ ethernet.update(tmp)
+
+ # Check if we are a member of a bond device
+ bond = is_member(conf, ifname, 'bonding')
+ if bond:
+ tmp = {'is_bond_member' : bond}
+ ethernet.update(tmp)
+
+ ethernet = T2665_default_dict_cleanup( ethernet )
+ # Check vif, vif-s/vif-c VLAN interfaces for removal
+ ethernet = get_removed_vlans( conf, ethernet )
+ return ethernet
+
+def verify(ethernet):
+ if 'deleted' in ethernet.keys():
return None
- if eth['intf'] not in interfaces():
- raise ConfigError(f"Interface ethernet {eth['intf']} does not exist")
+ verify_interface_exists(ethernet)
- if eth['speed'] == 'auto':
- if eth['duplex'] != 'auto':
+ if ethernet.get('speed', None) == 'auto':
+ if ethernet.get('duplex', None) != 'auto':
raise ConfigError('If speed is hardcoded, duplex must be hardcoded, too')
- if eth['duplex'] == 'auto':
- if eth['speed'] != 'auto':
+ if ethernet.get('duplex', None) == 'auto':
+ if ethernet.get('speed', None) != 'auto':
raise ConfigError('If duplex is hardcoded, speed must be hardcoded, too')
- if eth['dhcpv6_prm_only'] and eth['dhcpv6_temporary']:
- raise ConfigError('DHCPv6 temporary and parameters-only options are mutually exclusive!')
+ verify_dhcpv6(ethernet)
+ verify_address(ethernet)
+ verify_vrf(ethernet)
- memberof = eth['is_bridge_member'] if eth['is_bridge_member'] else eth['is_bond_member']
-
- if ( memberof
- and ( eth['address']
- or eth['ipv6_eui64_prefix']
- or eth['ipv6_autoconf'] ) ):
- raise ConfigError((
- f'Cannot assign address to interface "{eth["intf"]}" '
- f'as it is a member of "{memberof}"!'))
-
- if eth['vrf']:
- if eth['vrf'] not in interfaces():
- raise ConfigError(f'VRF "{eth["vrf"]}" does not exist')
-
- if memberof:
- raise ConfigError((
- f'Interface "{eth["intf"]}" cannot be member of VRF "{eth["vrf"]}" '
- f'and "{memberof}" at the same time!'))
-
- if eth['mac'] and eth['is_bond_member']:
- print('WARNING: "mac {0}" command will be ignored because {1} is a part of {2}'\
- .format(eth['mac'], eth['intf'], eth['is_bond_member']))
+ if {'is_bond_member', 'mac'} <= set(ethernet):
+ print(f'WARNING: changing mac address "{mac}" will be ignored as "{ifname}" '
+ f'is a member of bond "{is_bond_member}"'.format(**ethernet))
# use common function to verify VLAN configuration
- verify_vlan_config(eth)
+ verify_vlan_config(ethernet)
return None
-def generate(eth):
+def generate(ethernet):
return None
-def apply(eth):
- e = EthernetIf(eth['intf'])
- if eth['deleted']:
- # apply all vlans to interface (they need removing too)
- apply_all_vlans(e, eth)
-
+def apply(ethernet):
+ e = EthernetIf(ethernet['ifname'])
+ if 'deleted' in ethernet.keys():
# delete interface
e.remove()
else:
- # update interface description used e.g. within SNMP
- e.set_alias(eth['description'])
-
- if eth['dhcp_client_id']:
- e.dhcp.v4.options['client_id'] = eth['dhcp_client_id']
-
- if eth['dhcp_hostname']:
- e.dhcp.v4.options['hostname'] = eth['dhcp_hostname']
-
- if eth['dhcp_vendor_class_id']:
- e.dhcp.v4.options['vendor_class_id'] = eth['dhcp_vendor_class_id']
-
- if eth['dhcpv6_prm_only']:
- e.dhcp.v6.options['dhcpv6_prm_only'] = True
-
- if eth['dhcpv6_temporary']:
- e.dhcp.v6.options['dhcpv6_temporary'] = True
-
- if eth['dhcpv6_pd_length']:
- e.dhcp.v6.options['dhcpv6_pd_length'] = eth['dhcpv6_pd_length']
-
- if eth['dhcpv6_pd_interfaces']:
- e.dhcp.v6.options['dhcpv6_pd_interfaces'] = eth['dhcpv6_pd_interfaces']
-
- # ignore link state changes
- e.set_link_detect(eth['disable_link_detect'])
- # disable ethernet flow control (pause frames)
- e.set_flow_control(eth['flow_control'])
- # configure ARP cache timeout in milliseconds
- e.set_arp_cache_tmo(eth['ip_arp_cache_tmo'])
- # configure ARP filter configuration
- e.set_arp_filter(eth['ip_disable_arp_filter'])
- # configure ARP accept
- e.set_arp_accept(eth['ip_enable_arp_accept'])
- # configure ARP announce
- e.set_arp_announce(eth['ip_enable_arp_announce'])
- # configure ARP ignore
- e.set_arp_ignore(eth['ip_enable_arp_ignore'])
- # Enable proxy-arp on this interface
- e.set_proxy_arp(eth['ip_proxy_arp'])
- # Enable private VLAN proxy ARP on this interface
- e.set_proxy_arp_pvlan(eth['ip_proxy_arp_pvlan'])
- # IPv6 accept RA
- e.set_ipv6_accept_ra(eth['ipv6_accept_ra'])
- # IPv6 address autoconfiguration
- e.set_ipv6_autoconf(eth['ipv6_autoconf'])
- # IPv6 forwarding
- e.set_ipv6_forwarding(eth['ipv6_forwarding'])
- # IPv6 Duplicate Address Detection (DAD) tries
- e.set_ipv6_dad_messages(eth['ipv6_dup_addr_detect'])
-
- # Delete old IPv6 EUI64 addresses before changing MAC
- for addr in eth['ipv6_eui64_prefix_remove']:
- e.del_ipv6_eui64_address(addr)
-
- # Change interface MAC address - re-set to real hardware address (hw-id)
- # if custom mac is removed. Skip if bond member.
- if not eth['is_bond_member']:
- if eth['mac']:
- e.set_mac(eth['mac'])
- elif eth['hw_id']:
- e.set_mac(eth['hw_id'])
-
- # Add IPv6 EUI-based addresses
- for addr in eth['ipv6_eui64_prefix']:
- e.add_ipv6_eui64_address(addr)
-
- # Maximum Transmission Unit (MTU)
- e.set_mtu(eth['mtu'])
-
- # GRO (generic receive offload)
- e.set_gro(eth['offload_gro'])
-
- # GSO (generic segmentation offload)
- e.set_gso(eth['offload_gso'])
-
- # scatter-gather option
- e.set_sg(eth['offload_sg'])
-
- # TSO (TCP segmentation offloading)
- e.set_tso(eth['offload_tso'])
-
- # UDP fragmentation offloading
- e.set_ufo(eth['offload_ufo'])
-
- # Set physical interface speed and duplex
- e.set_speed_duplex(eth['speed'], eth['duplex'])
-
- # Enable/Disable interface
- if eth['disable']:
- e.set_admin_state('down')
- else:
- e.set_admin_state('up')
-
- # Configure interface address(es)
- # - not longer required addresses get removed first
- # - newly addresses will be added second
- for addr in eth['address_remove']:
- e.del_addr(addr)
- for addr in eth['address']:
- e.add_addr(addr)
-
- # assign/remove VRF (ONLY when not a member of a bridge or bond,
- # otherwise 'nomaster' removes it from it)
- if not ( eth['is_bridge_member'] or eth['is_bond_member'] ):
- e.set_vrf(eth['vrf'])
-
- # re-add ourselves to any bridge we might have fallen out of
- if eth['is_bridge_member']:
- e.add_to_bridge(eth['is_bridge_member'])
-
- # apply all vlans to interface
- apply_all_vlans(e, eth)
+ e.update(ethernet)
if __name__ == '__main__':