From 54c08da5a77e325b024415805fc2586afa1b0e8c Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 13 Sep 2020 15:32:57 +0200 Subject: bonding: T2877: support configuration of minimum number of active links Specifies the minimum number of links that must be active before asserting carrier. It is similar to the Cisco EtherChannel min-links feature. This allows setting the minimum number of member ports that must be up (link-up state) before marking the bond device as up (carrier on). This is useful for situations where higher level services such as clustering want to ensure a minimum number of low bandwidth links are active before switchover. This option only affects 802.3ad mode. The default value is 0. This will cause carrier to be asserted (for 802.3ad mode) whenever there is an active aggregator, regardless of the number of available links in that aggregator. Note that, because an aggregator cannot be active without at least one available link, setting this option to 0 or to 1 has the exact same effect. --- python/vyos/ifconfig/bond.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'python/vyos/ifconfig/bond.py') diff --git a/python/vyos/ifconfig/bond.py b/python/vyos/ifconfig/bond.py index 64407401b..67dcd2b69 100644 --- a/python/vyos/ifconfig/bond.py +++ b/python/vyos/ifconfig/bond.py @@ -52,6 +52,10 @@ class BondIf(Interface): 'validate': lambda v: assert_list(v, ['layer2', 'layer2+3', 'layer3+4', 'encap2+3', 'encap3+4']), 'location': '/sys/class/net/{ifname}/bonding/xmit_hash_policy', }, + 'bond_min_links': { + 'validate': assert_positive, + 'location': '/sys/class/net/{ifname}/bonding/min_links', + }, 'bond_miimon': { 'validate': assert_positive, 'location': '/sys/class/net/{ifname}/bonding/miimon' @@ -130,6 +134,29 @@ class BondIf(Interface): """ self.set_interface('bond_hash_policy', mode) + def set_min_links(self, number): + """ + Specifies the minimum number of links that must be active before + asserting carrier. It is similar to the Cisco EtherChannel min-links + feature. This allows setting the minimum number of member ports that + must be up (link-up state) before marking the bond device as up + (carrier on). This is useful for situations where higher level services + such as clustering want to ensure a minimum number of low bandwidth + links are active before switchover. This option only affect 802.3ad + mode. + + The default value is 0. This will cause carrier to be asserted (for + 802.3ad mode) whenever there is an active aggregator, regardless of the + number of available links in that aggregator. Note that, because an + aggregator cannot be active without at least one available link, + setting this option to 0 or to 1 has the exact same effect. + + Example: + >>> from vyos.ifconfig import BondIf + >>> BondIf('bond0').set_min_links('0') + """ + self.set_interface('bond_min_links', number) + def set_arp_interval(self, interval): """ Specifies the ARP link monitoring frequency in milliseconds. @@ -347,6 +374,10 @@ class BondIf(Interface): value = config.get('hash_policy') if value: self.set_hash_policy(value) + # Minimum number of member interfaces + value = config.get('min_links') + if value: self.set_min_links(value) + # Some interface options can only be changed if the interface is # administratively down if self.get_admin_state() == 'down': -- cgit v1.2.3 From 98d95b677867c27064d84033dc451ba04c9a2b7b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 15 Sep 2020 18:54:09 +0200 Subject: bonding: T2515: preserve interface admin state when removing from bond Removing a member from a bond/LACP will turn the physical interface always in admin-down state. This is invalid, the interface should be placed into the state configured on the VyOS CLI. Smoketest on bond interfaces is extended to check this behavior. --- python/vyos/ifconfig/bond.py | 22 ++++++++----- smoketest/scripts/cli/test_interfaces_bonding.py | 24 ++++++++++++++ src/conf_mode/interfaces-bonding.py | 42 +++++++++++++----------- 3 files changed, 59 insertions(+), 29 deletions(-) (limited to 'python/vyos/ifconfig/bond.py') diff --git a/python/vyos/ifconfig/bond.py b/python/vyos/ifconfig/bond.py index 67dcd2b69..c33cf30bf 100644 --- a/python/vyos/ifconfig/bond.py +++ b/python/vyos/ifconfig/bond.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors +# Copyright 2019-2020 VyOS maintainers and contributors # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -381,9 +381,14 @@ class BondIf(Interface): # Some interface options can only be changed if the interface is # administratively down if self.get_admin_state() == 'down': - # Delete bond member port(s) + # Remove ALL bond member interfaces for interface in self.get_slaves(): self.del_port(interface) + # Removing an interface from a bond will always place the underlaying + # physical interface in admin-down state! If physical interface is + # not disabled, re-enable it. + if not vyos_dict_search(f'member.interface_remove.{interface}.disable', config): + Interface(interface).set_admin_state('up') # Bonding policy/mode value = config.get('mode') @@ -391,13 +396,12 @@ class BondIf(Interface): # Add (enslave) interfaces to bond value = vyos_dict_search('member.interface', config) - if value: - for interface in value: - # if we've come here we already verified the interface - # does not have an addresses configured so just flush - # any remaining ones - Interface(interface).flush_addrs() - self.add_port(interface) + for interface in (value or []): + # if we've come here we already verified the interface + # does not have an addresses configured so just flush + # any remaining ones + Interface(interface).flush_addrs() + self.add_port(interface) # Primary device interface - must be set after 'mode' value = config.get('primary') diff --git a/smoketest/scripts/cli/test_interfaces_bonding.py b/smoketest/scripts/cli/test_interfaces_bonding.py index e3d3b25ee..b165883b9 100755 --- a/smoketest/scripts/cli/test_interfaces_bonding.py +++ b/smoketest/scripts/cli/test_interfaces_bonding.py @@ -20,6 +20,7 @@ import unittest from base_interfaces_test import BasicInterfaceTest from vyos.ifconfig import Section +from vyos.ifconfig.interface import Interface from vyos.configsession import ConfigSessionError from vyos.util import read_file @@ -57,5 +58,28 @@ class BondingInterfaceTest(BasicInterfaceTest.BaseTest): slaves = read_file(f'/sys/class/net/{interface}/bonding/slaves').split() self.assertListEqual(slaves, self._members) + def test_remove_member(self): + """ T2515: when removing a bond member the interface must be admin-up again """ + + # configure member interfaces + for interface in self._interfaces: + for option in self._options.get(interface, []): + self.session.set(self._base_path + [interface] + option.split()) + + self.session.commit() + + # remove single bond member port + for interface in self._interfaces: + remove_member = self._members[0] + self.session.delete(self._base_path + [interface, 'member', 'interface', remove_member]) + + self.session.commit() + + # removed member port must be admin-up + for interface in self._interfaces: + remove_member = self._members[0] + state = Interface(remove_member).get_admin_state() + self.assertEqual('up', state) + if __name__ == '__main__': unittest.main() diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index 16e6e4f6e..a9679b47c 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -29,6 +29,7 @@ from vyos.configverify import verify_source_interface from vyos.configverify import verify_vlan_config from vyos.configverify import verify_vrf from vyos.ifconfig import BondIf +from vyos.ifconfig import Section from vyos.validate import is_member from vyos.validate import has_address_configured from vyos import ConfigError @@ -69,31 +70,33 @@ def get_config(config=None): # into a dictionary - we will use this to add additional information # later on for wach member if 'member' in bond and 'interface' in bond['member']: - # first convert it to a list if only one member is given - if isinstance(bond['member']['interface'], str): - bond['member']['interface'] = [bond['member']['interface']] - - tmp={} - for interface in bond['member']['interface']: - tmp.update({interface: {}}) - - bond['member']['interface'] = tmp + # convert list if member interfaces to a dictionary + bond['member']['interface'] = dict.fromkeys( + bond['member']['interface'], {}) if 'mode' in bond: bond['mode'] = get_bond_mode(bond['mode']) tmp = leaf_node_changed(conf, ['mode']) - if tmp: - bond.update({'shutdown_required': ''}) + if tmp: bond.update({'shutdown_required': {}}) # determine which members have been removed - tmp = leaf_node_changed(conf, ['member', 'interface']) - if tmp: - bond.update({'shutdown_required': ''}) - if 'member' in bond: - bond['member'].update({'interface_remove': tmp }) - else: - bond.update({'member': {'interface_remove': tmp }}) + interfaces_removed = leaf_node_changed(conf, ['member', 'interface']) + if interfaces_removed: + bond.update({'shutdown_required': {}}) + if 'member' not in bond: + bond.update({'member': {}}) + + tmp = {} + for interface in interfaces_removed: + section = Section.section(interface) # this will be 'ethernet' for 'eth0' + if conf.exists(['insterfaces', section, interface, 'disable']): + tmp.update({interface : {'disable': ''}}) + else: + tmp.update({interface : {}}) + + # also present the interfaces to be removed from the bond as dictionary + bond['member'].update({'interface_remove': tmp}) if 'member' in bond and 'interface' in bond['member']: for interface, interface_config in bond['member']['interface'].items(): @@ -109,8 +112,7 @@ def get_config(config=None): # bond members must not have an assigned address tmp = has_address_configured(conf, interface) - if tmp: - interface_config.update({'has_address' : ''}) + if tmp: interface_config.update({'has_address' : ''}) return bond -- cgit v1.2.3 From d1c9ee33f25e45cea0d01f9685f99c960ed4d7f8 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 19 Sep 2020 21:08:40 +0200 Subject: ifconfig: T2653: convert VLAN interfaces do discrete class Instead of using an Adapter pattern to make interfaces VLAN-aware, create a derived class named VLANIf to represent a VLAN. This change was necessary to eliminate mixed code in Interfaces class which was VLAN - free, but recently gained some VLAN specific code for set_admin_state(). In addition this "autoresolves" the issue in T2894 as a bond vlan interface will no longer change the lower interface. --- python/vyos/ifconfig/bond.py | 3 - python/vyos/ifconfig/control.py | 6 +- python/vyos/ifconfig/dummy.py | 2 - python/vyos/ifconfig/ethernet.py | 2 - python/vyos/ifconfig/geneve.py | 2 - python/vyos/ifconfig/input.py | 2 - python/vyos/ifconfig/interface.py | 190 +++++++++++++++++++++++++++--------- python/vyos/ifconfig/l2tpv3.py | 3 - python/vyos/ifconfig/loopback.py | 2 - python/vyos/ifconfig/macvlan.py | 4 - python/vyos/ifconfig/operational.py | 5 +- python/vyos/ifconfig/tunnel.py | 1 - python/vyos/ifconfig/vlan.py | 142 --------------------------- python/vyos/ifconfig/vrrp.py | 4 +- python/vyos/ifconfig/vti.py | 2 - python/vyos/ifconfig/vtun.py | 2 - python/vyos/ifconfig/vxlan.py | 1 - python/vyos/ifconfig/wireguard.py | 2 +- python/vyos/ifconfig/wireless.py | 5 - 19 files changed, 153 insertions(+), 227 deletions(-) delete mode 100644 python/vyos/ifconfig/vlan.py (limited to 'python/vyos/ifconfig/bond.py') diff --git a/python/vyos/ifconfig/bond.py b/python/vyos/ifconfig/bond.py index c33cf30bf..9108fc180 100644 --- a/python/vyos/ifconfig/bond.py +++ b/python/vyos/ifconfig/bond.py @@ -16,15 +16,12 @@ import os from vyos.ifconfig.interface import Interface -from vyos.ifconfig.vlan import VLAN - from vyos.util import cmd from vyos.util import vyos_dict_search from vyos.validate import assert_list from vyos.validate import assert_positive @Interface.register -@VLAN.enable class BondIf(Interface): """ The Linux bonding driver provides a method for aggregating multiple network diff --git a/python/vyos/ifconfig/control.py b/python/vyos/ifconfig/control.py index a6fc8ac6c..43136f361 100644 --- a/python/vyos/ifconfig/control.py +++ b/python/vyos/ifconfig/control.py @@ -13,8 +13,8 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . - import os + from inspect import signature from inspect import _empty @@ -30,9 +30,9 @@ class Control(Section): _signature = {} def __init__(self, **kargs): - # some commands (such as operation comands - show interfaces, etc.) + # some commands (such as operation comands - show interfaces, etc.) # need to query the interface statistics. If the interface - # code is used and the debugging is enabled, the screen output + # code is used and the debugging is enabled, the screen output # will include both the command but also the debugging for that command # to prevent this, debugging can be explicitely disabled diff --git a/python/vyos/ifconfig/dummy.py b/python/vyos/ifconfig/dummy.py index 43614cd1c..19ef9d304 100644 --- a/python/vyos/ifconfig/dummy.py +++ b/python/vyos/ifconfig/dummy.py @@ -13,10 +13,8 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . - from vyos.ifconfig.interface import Interface - @Interface.register class DummyIf(Interface): """ diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index d4014a4dc..1d48941f9 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -17,13 +17,11 @@ import os import re from vyos.ifconfig.interface import Interface -from vyos.ifconfig.vlan import VLAN from vyos.validate import assert_list from vyos.util import run from vyos.util import vyos_dict_search @Interface.register -@VLAN.enable class EthernetIf(Interface): """ Abstraction of a Linux Ethernet Interface diff --git a/python/vyos/ifconfig/geneve.py b/python/vyos/ifconfig/geneve.py index dd0658668..0a13043cc 100644 --- a/python/vyos/ifconfig/geneve.py +++ b/python/vyos/ifconfig/geneve.py @@ -14,10 +14,8 @@ # License along with this library. If not, see . from copy import deepcopy - from vyos.ifconfig.interface import Interface - @Interface.register class GeneveIf(Interface): """ diff --git a/python/vyos/ifconfig/input.py b/python/vyos/ifconfig/input.py index bfab36335..a6e566d87 100644 --- a/python/vyos/ifconfig/input.py +++ b/python/vyos/ifconfig/input.py @@ -13,10 +13,8 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . - from vyos.ifconfig.interface import Interface - @Interface.register class InputIf(Interface): default = { diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index ffe69f61b..0774235b6 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -86,10 +86,6 @@ class Interface(Control): 'shellcmd': 'ip -json link show dev {ifname}', 'format': lambda j: 'up' if 'UP' in jmespath.search('[*].flags | [0]', json.loads(j)) else 'down', }, - 'vlan_protocol': { - 'shellcmd': 'ip -json -details link show dev {ifname}', - 'format': lambda j: jmespath.search('[*].linkinfo.info_data.protocol | [0]', json.loads(j)), - }, } _command_set = { @@ -562,17 +558,6 @@ class Interface(Control): """ self.set_interface('alias', ifalias) - def get_vlan_protocol(self): - """ - Retrieve VLAN protocol in use, this can be 802.1Q, 802.1ad or None - - Example: - >>> from vyos.ifconfig import Interface - >>> Interface('eth0.10').get_vlan_protocol() - '802.1Q' - """ - return self.get_interface('vlan_protocol') - def get_admin_state(self): """ Get interface administrative state. Function will return 'up' or 'down' @@ -594,17 +579,6 @@ class Interface(Control): >>> Interface('eth0').get_admin_state() 'down' """ - # A VLAN interface can only be placed in admin up state when - # the lower interface is up, too - if self.get_vlan_protocol(): - lower_interface = glob(f'/sys/class/net/{self.ifname}/lower*/flags')[0] - with open(lower_interface, 'r') as f: - flags = f.read() - # If parent is not up - bail out as we can not bring up the VLAN. - # Flags are defined in kernel source include/uapi/linux/if.h - if not int(flags, 16) & 1: - return None - if state == 'up': self._admin_state_down_cnt -= 1 if self._admin_state_down_cnt < 1: @@ -1031,33 +1005,161 @@ class Interface(Control): self.add_to_bridge(bridge) # remove no longer required 802.1ad (Q-in-Q VLANs) + ifname = config['ifname'] for vif_s_id in config.get('vif_s_remove', {}): - self.del_vlan(vif_s_id) + vif_s_ifname = f'{ifname}.{vif_s_id}' + VLANIf(vif_s_ifname).remove() # create/update 802.1ad (Q-in-Q VLANs) - ifname = config['ifname'] - 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) - vif_s['ifname'] = f'{ifname}.{vif_s_id}' - s_vlan.update(vif_s) + for vif_s_id, vif_s_config in config.get('vif_s', {}).items(): + tmp = deepcopy(VLANIf.get_config()) + tmp['ethertype'] = get_ethertype(vif_s_config.get('ethertype', '0x88A8')) + 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 + s_vlan = VLANIf(vif_s_ifname, **tmp) + s_vlan.update(vif_s_config) # 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) + for vif_c_id in vif_s_config.get('vif_c_remove', {}): + vif_c_ifname = f'{vif_s_ifname}.{vif_c_id}' + VLANIf(vif_c_ifname).remove() # 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) - vif_c['ifname'] = f'{ifname}.{vif_s_id}.{vif_c_id}' - c_vlan.update(vif_c) + for vif_c_id, vif_c_config in vif_s_config.get('vif_c', {}).items(): + tmp = deepcopy(VLANIf.get_config()) + tmp['source_interface'] = vif_s_ifname + 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) # remove no longer required 802.1q VLAN interfaces for vif_id in config.get('vif_remove', {}): - self.del_vlan(vif_id) + vif_ifname = f'{ifname}.{vif_id}' + VLANIf(vif_ifname).remove() # create/update 802.1q VLAN interfaces - for vif_id, vif in config.get('vif', {}).items(): - vlan = self.add_vlan(vif_id) - vif['ifname'] = f'{ifname}.{vif_id}' - vlan.update(vif) + for vif_id, vif_config in config.get('vif', {}).items(): + tmp = deepcopy(VLANIf.get_config()) + tmp['source_interface'] = ifname + tmp['vlan_id'] = vif_id + + vif_ifname = f'{ifname}.{vif_id}' + vif_config['ifname'] = vif_ifname + vlan = VLANIf(vif_ifname, **tmp) + vlan.update(vif_config) + + +class VLANIf(Interface): + """ Specific class which abstracts 802.1q and 802.1ad (Q-in-Q) VLAN interfaces """ + default = { + 'type': 'vlan', + 'source_interface': '', + 'vlan_id': '', + 'ethertype': '', + 'ingress_qos': '', + 'egress_qos': '', + } + + options = Interface.options + \ + ['source_interface', 'vlan_id', 'ethertype', 'ingress_qos', 'egress_qos'] + + def remove(self): + """ + 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 + >>> i = Interface('eth0') + >>> i.remove() + """ + # Do we have sub interfaces (VLANs)? As interfaces need to be deleted + # "in order" starting from Q-in-Q we delete them first. + for upper in glob(f'/sys/class/net/{self.ifname}/upper*'): + # an upper interface could be named: upper_bond0.1000.1100, thus + # we need top drop the upper_ prefix + vif_c = os.path.basename(upper) + vif_c = vif_c.replace('upper_', '') + VLANIf(vif_c).remove() + + super().remove() + + def _create(self): + # bail out early if interface already exists + if os.path.exists(f'/sys/class/net/{self.ifname}'): + return + + cmd = 'ip link add link {source_interface} name {ifname} type vlan id {vlan_id}' + if self.config['ethertype']: + cmd += ' proto {ethertype}' + if self.config['ingress_qos']: + cmd += ' ingress-qos-map {ingress_qos}' + if self.config['egress_qos']: + cmd += ' egress-qos-map {egress_qos}' + + self._cmd(cmd.format(**self.config)) + + # interface is always A/D down. It needs to be enabled explicitly + self.set_admin_state('down') + + @staticmethod + def get_config(): + """ + MACsec interfaces require a configuration when they are added using + iproute2. This static method will provide the configuration dictionary + used by this class. + + Example: + >> dict = MACsecIf().get_config() + """ + config = deepcopy(__class__.default) + del config['type'] + return config + + def set_admin_state(self, state): + """ + Set interface administrative state to be 'up' or 'down' + + Example: + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').set_admin_state('down') + >>> Interface('eth0').get_admin_state() + 'down' + """ + # A VLAN interface can only be placed in admin up state when + # the lower interface is up, too + lower_interface = glob(f'/sys/class/net/{self.ifname}/lower*/flags')[0] + with open(lower_interface, 'r') as f: + flags = f.read() + # If parent is not up - bail out as we can not bring up the VLAN. + # Flags are defined in kernel source include/uapi/linux/if.h + if not int(flags, 16) & 1: + return None + + return super().set_admin_state(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. """ + + # call base class first + super().update(config) + + # Enable/Disable of an interface must always be done at the end of the + # derived class to make use of the ref-counting set_admin_state() + # function. We will only enable the interface if 'up' was called as + # often as 'down'. This is required by some interface implementations + # as certain parameters can only be changed when the interface is + # in admin-down state. This ensures the link does not flap during + # reconfiguration. + state = 'down' if 'disable' in config else 'up' + self.set_admin_state(state) diff --git a/python/vyos/ifconfig/l2tpv3.py b/python/vyos/ifconfig/l2tpv3.py index 34147eb38..33740921e 100644 --- a/python/vyos/ifconfig/l2tpv3.py +++ b/python/vyos/ifconfig/l2tpv3.py @@ -13,12 +13,9 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . - import os - from vyos.ifconfig.interface import Interface - @Interface.register class L2TPv3If(Interface): """ diff --git a/python/vyos/ifconfig/loopback.py b/python/vyos/ifconfig/loopback.py index c70e1773f..0e632d826 100644 --- a/python/vyos/ifconfig/loopback.py +++ b/python/vyos/ifconfig/loopback.py @@ -13,10 +13,8 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . - from vyos.ifconfig.interface import Interface - @Interface.register class LoopbackIf(Interface): """ diff --git a/python/vyos/ifconfig/macvlan.py b/python/vyos/ifconfig/macvlan.py index b068ce873..9c1d09c1c 100644 --- a/python/vyos/ifconfig/macvlan.py +++ b/python/vyos/ifconfig/macvlan.py @@ -14,13 +14,9 @@ # License along with this library. If not, see . from copy import deepcopy - from vyos.ifconfig.interface import Interface -from vyos.ifconfig.vlan import VLAN - @Interface.register -@VLAN.enable class MACVLANIf(Interface): """ Abstraction of a Linux MACvlan interface diff --git a/python/vyos/ifconfig/operational.py b/python/vyos/ifconfig/operational.py index d585c1873..33e8614f0 100644 --- a/python/vyos/ifconfig/operational.py +++ b/python/vyos/ifconfig/operational.py @@ -14,20 +14,19 @@ # License along with this library. If not, see . import os + from time import time from datetime import datetime from functools import reduce - from tabulate import tabulate from vyos.ifconfig import Control - class Operational(Control): """ A class able to load Interface statistics """ - + cache_magic = 'XYZZYX' _stat_names = { diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py index 85c22b5b4..964ffe383 100644 --- a/python/vyos/ifconfig/tunnel.py +++ b/python/vyos/ifconfig/tunnel.py @@ -16,7 +16,6 @@ # https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels/ # https://community.hetzner.com/tutorials/linux-setup-gre-tunnel - from copy import deepcopy from vyos.ifconfig.interface import Interface diff --git a/python/vyos/ifconfig/vlan.py b/python/vyos/ifconfig/vlan.py deleted file mode 100644 index d68e8f6cd..000000000 --- a/python/vyos/ifconfig/vlan.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright 2019 VyOS maintainers and contributors -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. If not, see . - - -import os -import re - -from vyos.ifconfig.interface import Interface - - -# This is an internal implementation class -class VLAN: - """ - This class handels the creation and removal of a VLAN interface. It serves - as base class for BondIf and EthernetIf. - """ - - _novlan_remove = lambda : None - - @classmethod - def enable (cls,adaptee): - adaptee._novlan_remove = adaptee.remove - adaptee.remove = cls.remove - adaptee.add_vlan = cls.add_vlan - adaptee.del_vlan = cls.del_vlan - adaptee.definition['vlan'] = True - return adaptee - - def remove(self): - """ - 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 - >>> i = Interface('eth0') - >>> i.remove() - """ - ifname = self.config['ifname'] - - # Do we have sub interfaces (VLANs)? We apply a regex matching - # subinterfaces (indicated by a .) of a parent interface. - # - # As interfaces need to be deleted "in order" starting from Q-in-Q - # we delete them first. - vlan_ifs = [f for f in os.listdir(r'/sys/class/net') - if re.match(ifname + r'(?:\.\d+)(?:\.\d+)', f)] - - for vlan in vlan_ifs: - Interface(vlan).remove() - - # After deleting all Q-in-Q interfaces delete other VLAN interfaces - # which probably acted as parent to Q-in-Q or have been regular 802.1q - # interface. - vlan_ifs = [f for f in os.listdir(r'/sys/class/net') - if re.match(ifname + r'(?:\.\d+)', f)] - - for vlan in vlan_ifs: - # self.__class__ is already VLAN.enabled - self.__class__(vlan)._novlan_remove() - - # All subinterfaces are now removed, continue on the physical interface - self._novlan_remove() - - def add_vlan(self, vlan_id, ethertype='', ingress_qos='', egress_qos=''): - """ - 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 VLANIf is returned once the interface has been - created. - - @param ethertype: If specified, create 802.1ad or 802.1q Q-in-Q VLAN - interface - @param ingress_qos: Defines a mapping of VLAN header prio field to the - Linux internal packet priority on incoming frames. - @param ingress_qos: Defines a mapping of Linux internal packet priority - to VLAN header prio field but for outgoing frames. - - Example: - >>> from vyos.ifconfig import MACVLANIf - >>> i = MACVLANIf('eth0') - >>> i.add_vlan(10) - """ - vlan_ifname = self.config['ifname'] + '.' + str(vlan_id) - if os.path.exists(f'/sys/class/net/{vlan_ifname}'): - return self.__class__(vlan_ifname) - - if ethertype: - self._ethertype = ethertype - ethertype = 'proto {}'.format(ethertype) - - # Optional ingress QOS mapping - opt_i = '' - if ingress_qos: - opt_i = 'ingress-qos-map ' + ingress_qos - # Optional egress QOS mapping - opt_e = '' - if egress_qos: - opt_e = 'egress-qos-map ' + egress_qos - - # create interface in the system - cmd = 'ip link add link {ifname} name {ifname}.{vlan} type vlan {proto} id {vlan} {opt_e} {opt_i}' \ - .format(ifname=self.ifname, vlan=vlan_id, proto=ethertype, opt_e=opt_e, opt_i=opt_i) - 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 self.__class__(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. - - Example: - >>> from vyos.ifconfig import MACVLANIf - >>> i = MACVLANIf('eth0.10') - >>> i.del_vlan() - """ - ifname = self.config['ifname'] - self.__class__(f'{ifname}.{vlan_id}')._novlan_remove() diff --git a/python/vyos/ifconfig/vrrp.py b/python/vyos/ifconfig/vrrp.py index 01a7cc7ab..d3e9d5df2 100644 --- a/python/vyos/ifconfig/vrrp.py +++ b/python/vyos/ifconfig/vrrp.py @@ -16,15 +16,13 @@ import os import json import signal + from time import time from time import sleep - from tabulate import tabulate -from vyos import airbag from vyos import util - class VRRPError(Exception): pass diff --git a/python/vyos/ifconfig/vti.py b/python/vyos/ifconfig/vti.py index 56ebe01d1..d0745898c 100644 --- a/python/vyos/ifconfig/vti.py +++ b/python/vyos/ifconfig/vti.py @@ -13,10 +13,8 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . - from vyos.ifconfig.interface import Interface - @Interface.register class VTIIf(Interface): default = { diff --git a/python/vyos/ifconfig/vtun.py b/python/vyos/ifconfig/vtun.py index 60c178b9a..b25e32d63 100644 --- a/python/vyos/ifconfig/vtun.py +++ b/python/vyos/ifconfig/vtun.py @@ -13,10 +13,8 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . - from vyos.ifconfig.interface import Interface - @Interface.register class VTunIf(Interface): default = { diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py index 18a500336..dba62b61a 100644 --- a/python/vyos/ifconfig/vxlan.py +++ b/python/vyos/ifconfig/vxlan.py @@ -18,7 +18,6 @@ from copy import deepcopy from vyos import ConfigError from vyos.ifconfig.interface import Interface - @Interface.register class VXLANIf(Interface): """ diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py index fad4ef282..d8e89229d 100644 --- a/python/vyos/ifconfig/wireguard.py +++ b/python/vyos/ifconfig/wireguard.py @@ -13,9 +13,9 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . - import os import time + from datetime import timedelta from hurry.filesize import size diff --git a/python/vyos/ifconfig/wireless.py b/python/vyos/ifconfig/wireless.py index a50346ffa..053566a1e 100644 --- a/python/vyos/ifconfig/wireless.py +++ b/python/vyos/ifconfig/wireless.py @@ -13,14 +13,9 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . -import os - from vyos.ifconfig.interface import Interface -from vyos.ifconfig.vlan import VLAN - @Interface.register -@VLAN.enable class WiFiIf(Interface): """ Handle WIFI/WLAN interfaces. -- cgit v1.2.3