diff options
-rw-r--r-- | python/vyos/ifconfig/bond.py | 3 | ||||
-rw-r--r-- | python/vyos/ifconfig/control.py | 6 | ||||
-rw-r--r-- | python/vyos/ifconfig/dummy.py | 2 | ||||
-rw-r--r-- | python/vyos/ifconfig/ethernet.py | 2 | ||||
-rw-r--r-- | python/vyos/ifconfig/geneve.py | 2 | ||||
-rw-r--r-- | python/vyos/ifconfig/input.py | 2 | ||||
-rw-r--r-- | python/vyos/ifconfig/interface.py | 190 | ||||
-rw-r--r-- | python/vyos/ifconfig/l2tpv3.py | 3 | ||||
-rw-r--r-- | python/vyos/ifconfig/loopback.py | 2 | ||||
-rw-r--r-- | python/vyos/ifconfig/macvlan.py | 4 | ||||
-rw-r--r-- | python/vyos/ifconfig/operational.py | 5 | ||||
-rw-r--r-- | python/vyos/ifconfig/tunnel.py | 1 | ||||
-rw-r--r-- | python/vyos/ifconfig/vlan.py | 142 | ||||
-rw-r--r-- | python/vyos/ifconfig/vrrp.py | 4 | ||||
-rw-r--r-- | python/vyos/ifconfig/vti.py | 2 | ||||
-rw-r--r-- | python/vyos/ifconfig/vtun.py | 2 | ||||
-rw-r--r-- | python/vyos/ifconfig/vxlan.py | 1 | ||||
-rw-r--r-- | python/vyos/ifconfig/wireguard.py | 2 | ||||
-rw-r--r-- | python/vyos/ifconfig/wireless.py | 5 |
19 files changed, 153 insertions, 227 deletions
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 <http://www.gnu.org/licenses/>. - 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 <http://www.gnu.org/licenses/>. - 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 <http://www.gnu.org/licenses/>. 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 <http://www.gnu.org/licenses/>. - 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 <http://www.gnu.org/licenses/>. - 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 <http://www.gnu.org/licenses/>. - 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 <http://www.gnu.org/licenses/>. 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 <http://www.gnu.org/licenses/>. 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 <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 -# 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 <http://www.gnu.org/licenses/>. - - -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 <http://www.gnu.org/licenses/>. - 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 <http://www.gnu.org/licenses/>. - 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 <http://www.gnu.org/licenses/>. - 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 <http://www.gnu.org/licenses/>. -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. |