diff options
author | Christian Poessinger <christian@poessinger.com> | 2019-09-03 18:58:27 +0200 |
---|---|---|
committer | Christian Poessinger <christian@poessinger.com> | 2019-09-04 11:25:31 +0200 |
commit | 1fc4c201d7771ac4164e9480d55a654b7674d098 (patch) | |
tree | 62d08c1c8843bc6031278cfa824c74f98e757b78 | |
parent | 029085250c4e2dea8a98f3031cf6b8037ffdd534 (diff) | |
download | vyos-1x-1fc4c201d7771ac4164e9480d55a654b7674d098.tar.gz vyos-1x-1fc4c201d7771ac4164e9480d55a654b7674d098.zip |
bonding: T1614: T1557: add vif/vif-s VLAN interface support
Support for vif-c interfaces is still missing
-rw-r--r-- | python/vyos/ifconfig.py | 47 | ||||
-rwxr-xr-x | src/conf_mode/interface-bonding.py | 101 |
2 files changed, 136 insertions, 12 deletions
diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 1d03e4e74..bb2d23d0d 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -87,7 +87,9 @@ class Interface: def remove(self): """ - Remove system interface + Remove interface from operating system. Removing the interface + deconfigures all assigned IP addresses and clear possible DHCP(v6) + client processes. Example: >>> from vyos.ifconfig import Interface @@ -964,10 +966,53 @@ class BridgeIf(Interface): .format(self._ifname, interface), priority) + class EthernetIf(Interface): def __init__(self, ifname, type=None): super().__init__(ifname, type) + def add_vlan(self, vlan_id, ethertype=''): + """ + A virtual LAN (VLAN) is any broadcast domain that is partitioned and + isolated in a computer network at the data link layer (OSI layer 2). + Use this function to create a new VLAN interface on a given physical + interface. + + This function creates both 802.1q and 802.1ad (Q-in-Q) interfaces. Proto + parameter is used to indicate VLAN type. + + A new object of type EthernetIf is returned once the interface has been + created. + """ + vlan_ifname = self._ifname + '.' + str(vlan_id) + if not os.path.exists('/sys/class/net/{}'.format(vlan_ifname)): + self._vlan_id = int(vlan_id) + + if ethertype: + self._ethertype = ethertype + ethertype='proto {}'.format(ethertype) + + # create interface in the system + cmd = 'ip link add link {intf} name {intf}.{vlan} type vlan {proto} id {vlan}'.format(intf=self._ifname, vlan=self._vlan_id, proto=ethertype) + self._cmd(cmd) + + # return new object mapping to the newly created interface + # we can now work on this object for e.g. IP address setting + # or interface description and so on + return EthernetIf(vlan_ifname) + + + def del_vlan(self, vlan_id): + """ + Remove VLAN interface from operating system. Removing the interface + deconfigures all assigned IP addresses and clear possible DHCP(v6) + client processes. + """ + vlan_ifname = self._ifname + '.' + str(vlan_id) + tmp = EthernetIf(vlan_ifname) + tmp.remove() + + class BondIf(EthernetIf): """ The Linux bonding driver provides a method for aggregating multiple network diff --git a/src/conf_mode/interface-bonding.py b/src/conf_mode/interface-bonding.py index a623ca524..9e7be19f5 100755 --- a/src/conf_mode/interface-bonding.py +++ b/src/conf_mode/interface-bonding.py @@ -21,7 +21,8 @@ import os from copy import deepcopy from sys import exit from netifaces import interfaces -from vyos.ifconfig import BondIf + +from vyos.ifconfig import BondIf, EthernetIf from vyos.config import Config from vyos import ConfigError @@ -49,7 +50,9 @@ default_config_data = { 'mtu': 1500, 'primary': '', 'vif_s': [], - 'vif': [] + 'vif_s_remove': [], + 'vif': [], + 'vif_remove': [] } def diff(first, second): @@ -74,6 +77,15 @@ def get_bond_mode(mode): else: raise ConfigError('invalid bond mode "{}"'.format(mode)) +def get_ethertype(ethertype_val): + if ethertype_val == '0x88A8': + return '802.1ad' + elif ethertype_val == '0x8100': + return '802.1q' + else: + raise ConfigError('invalid ethertype "{}"'.format(ethertype_val)) + + def vlan_to_dict(conf): """ Common used function which will extract VLAN related information from config @@ -82,7 +94,7 @@ def vlan_to_dict(conf): Function call's itself recursively if a vif-s/vif-c pair is detected. """ vlan = { - 'id': conf.get_level().split(' ')[-1], # get the '100' in 'interfaces bonding bond0 vif-s 100' + 'id': conf.get_level().split()[-1], # get the '100' in 'interfaces bonding bond0 vif-s 100' 'address': [], 'address_remove': [], 'description': '', @@ -133,9 +145,14 @@ def vlan_to_dict(conf): if conf.exists('disable'): vlan['disable'] = True - # ethertype (only on vif-s nodes) - if conf.exists('ethertype'): - vlan['ethertype'] = conf.return_value('ethertype') + # ethertype is mandatory on vif-s nodes and only exists here! + # check if this is a vif-s node at all: + if conf.get_level().split()[-2] == 'vif-s': + # ethertype uses a default of 0x88A8 + tmp = '0x88A8' + if conf.exists('ethertype'): + tmp = conf.return_value('ethertype') + vlan['ethertype'] = get_ethertype(tmp) # Media Access Control (MAC) address if conf.exists('mac'): @@ -158,6 +175,39 @@ def vlan_to_dict(conf): return vlan + +def apply_vlan_config(vlan, config): + """ + Generic function to apply a VLAN configuration from a dictionary + to a VLAN interface + """ + + if type(vlan) != type(EthernetIf("lo")): + raise TypeError() + + # Configure interface address(es) + for addr in config['address_remove']: + vlan.del_addr(addr) + for addr in config['address']: + vlan.add_addr(addr) + + # update interface description used e.g. within SNMP + vlan.ifalias = config['description'] + # ignore link state changes + vlan.link_detect = config['disable_link_detect'] + # Maximum Transmission Unit (MTU) + vlan.mtu = config['mtu'] + # Change VLAN interface MAC address + if config['mac']: + vlan.mac = config['mac'] + + # enable/disable VLAN interface + if config['disable']: + vlan.state = 'down' + else: + vlan.state = 'up' + + def get_config(): # initialize kernel module if not loaded if not os.path.isfile('/sys/class/net/bonding_masters'): @@ -271,6 +321,12 @@ def get_config(): # re-set configuration level and retrieve vif-s interfaces conf.set_level(cfg_base) + # Determine vif-s interfaces (currently effective) - to determine which + # vif-s interface is no longer present and needs to be removed + eff_intf = conf.list_effective_nodes('vif-s') + act_intf = conf.list_nodes('vif-s') + bond['vif_s_remove'] = diff(eff_intf, act_intf) + if conf.exists('vif-s'): for vif_s in conf.list_nodes('vif-s'): # set config level to vif-s interface @@ -279,6 +335,12 @@ def get_config(): # re-set configuration level and retrieve vif-s interfaces conf.set_level(cfg_base) + # Determine vif interfaces (currently effective) - to determine which + # vif interface is no longer present and needs to be removed + eff_intf = conf.list_effective_nodes('vif') + act_intf = conf.list_nodes('vif') + bond['vif_remove'] = diff(eff_intf, act_intf) + if conf.exists('vif'): for vif in conf.list_nodes('vif'): # set config level to vif interface @@ -287,6 +349,7 @@ def get_config(): return bond + def verify(bond): if len (bond['arp_mon_tgt']) > 16: raise ConfigError('The maximum number of targets that can be specified is 16') @@ -300,9 +363,11 @@ def verify(bond): return None + def generate(bond): return None + def apply(bond): b = BondIf(bond['intf']) @@ -378,13 +443,27 @@ def apply(bond): # Primary device interface b.primary = bond['primary'] - # - # VLAN config goes here - # - # Add (enslave) interfaces to bond for intf in bond['member']: - b.add_port(intf) + b.add_port(intf) + + # remove no longer required service VLAN interfaces (vif-s) + for vif_s in bond['vif_s_remove']: + b.del_vlan(vif_s) + + # create service VLAN interfaces (vif-s) + for vif_s in bond['vif_s']: + vlan = b.add_vlan(vif_s['id'], ethertype=vif_s['ethertype']) + apply_vlan_config(vlan, vif_s) + + # remove no longer required VLAN interfaces (vif) + for vif in bond['vif_remove']: + b.del_vlan(vif) + + # create VLAN interfaces (vif) + for vif in bond['vif']: + vlan = b.add_vlan(vif['id']) + apply_vlan_config(vlan, vif) # As the bond interface is always disabled first when changing # parameters we will only re-enable the interface if it is not |