diff options
-rw-r--r-- | interface-definitions/interfaces-bridge.xml.in | 2 | ||||
-rw-r--r-- | python/vyos/configdict.py | 22 | ||||
-rw-r--r-- | python/vyos/ifconfig/bridge.py | 28 | ||||
-rw-r--r-- | python/vyos/ifconfig/interface.py | 60 | ||||
-rw-r--r-- | python/vyos/ifconfig/stp.py | 70 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-bonding.py | 6 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-bridge.py | 23 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-openvpn.py | 3 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-tunnel.py | 3 |
9 files changed, 93 insertions, 124 deletions
diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in index 787e856d7..0a777865b 100644 --- a/interface-definitions/interfaces-bridge.xml.in +++ b/interface-definitions/interfaces-bridge.xml.in @@ -5,7 +5,7 @@ <tagNode name="bridge" owner="${vyos_conf_scripts_dir}/interfaces-bridge.py"> <properties> <help>Bridge Interface</help> - <priority>489</priority> + <priority>310</priority> <constraint> <regex>^br[0-9]+$</regex> </constraint> diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 02006465c..62df3334c 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -194,11 +194,9 @@ def is_member(conf, interface, intftype=None): interface name -> Interface is a member of this interface False -> interface type cannot have members """ - from vyos.xml import is_tag - from vyos.xml import is_leaf - ret_val = None intftypes = ['bonding', 'bridge'] + if intftype not in intftypes + [None]: raise ValueError(( f'unknown interface type "{intftype}" or it cannot ' @@ -210,19 +208,13 @@ def is_member(conf, interface, intftype=None): old_level = conf.get_level() conf.set_level([]) - for it in intftype: - base = ['interfaces', it] + for iftype in intftype: + base = ['interfaces', iftype] for intf in conf.list_nodes(base): - memberintf = base + [intf, 'member', 'interface'] - if is_tag(memberintf): - if interface in conf.list_nodes(memberintf): - ret_val = intf - break - elif is_leaf(memberintf): - if ( conf.exists(memberintf) and - interface in conf.return_values(memberintf) ): - ret_val = intf - break + member = base + [intf, 'member', 'interface', interface] + if conf.exists(member): + tmp = conf.get_config_dict(member, key_mangling=('-', '_'), get_first_key=True) + ret_val = {intf : tmp} old_level = conf.set_level(old_level) return ret_val diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py index c133a56fc..7867a7387 100644 --- a/python/vyos/ifconfig/bridge.py +++ b/python/vyos/ifconfig/bridge.py @@ -234,25 +234,33 @@ class BridgeIf(Interface): if member in interfaces(): self.del_port(member) - STPBridgeIf = STP.enable(BridgeIf) tmp = vyos_dict_search('member.interface', config) if tmp: for interface, interface_config in tmp.items(): - # 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() + # if interface does yet not exist bail out early and + # add it later + if interface not in interfaces(): + continue + + # Bridge lower "physical" interface + lower = Interface(interface) + + # If we've come that far we already verified the interface does + # not have any addresses configured by CLI so just flush any + # remaining ones + lower.flush_addrs() # enslave interface port to bridge self.add_port(interface) - tmp = STPBridgeIf(interface) # set bridge port path cost - value = interface_config.get('cost') - tmp.set_path_cost(value) + if 'cost' in interface_config: + value = interface_config.get('cost') + lower.set_path_cost(value) # set bridge port path priority - value = interface_config.get('priority') - tmp.set_path_priority(value) + if 'priority' in interface_config: + value = interface_config.get('priority') + lower.set_path_priority(value) # 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() diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 47ec94bd3..c1f9d14af 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -46,6 +46,7 @@ from vyos.validate import assert_positive from vyos.validate import assert_range from vyos.ifconfig.control import Control +from vyos.ifconfig.stp import STP from vyos.ifconfig.vrrp import VRRP from vyos.ifconfig.operational import Operational from vyos.ifconfig import Section @@ -167,6 +168,18 @@ class Interface(Control): 'validate': assert_positive, 'location': '/proc/sys/net/ipv6/conf/{ifname}/dad_transmits', }, + 'path_cost': { + # XXX: we should set a maximum + 'validate': assert_positive, + 'location': '/sys/class/net/{ifname}/brport/path_cost', + 'errormsg': '{ifname} is not a bridge port member' + }, + 'path_priority': { + # XXX: we should set a maximum + 'validate': assert_positive, + 'location': '/sys/class/net/{ifname}/brport/priority', + 'errormsg': '{ifname} is not a bridge port member' + }, 'proxy_arp': { 'validate': assert_boolean, 'location': '/proc/sys/net/ipv4/conf/{ifname}/proxy_arp', @@ -628,6 +641,28 @@ class Interface(Control): self._admin_state_down_cnt += 1 return self.set_interface('admin_state', state) + def set_path_cost(self, cost): + """ + Set interface path cost, only relevant for STP enabled interfaces + + Example: + + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').set_path_cost(4) + """ + self.set_interface('path_cost', cost) + + def set_path_priority(self, priority): + """ + Set interface path priority, only relevant for STP enabled interfaces + + Example: + + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').set_path_priority(4) + """ + self.set_interface('path_priority', priority) + def set_proxy_arp(self, enable): """ Set per interface proxy ARP configuration @@ -809,24 +844,27 @@ class Interface(Control): # flush all addresses self._cmd(f'ip addr flush dev "{self.ifname}"') - def add_to_bridge(self, br): + def add_to_bridge(self, bridge_dict): """ Adds the interface to the bridge with the passed port config. Returns False if bridge doesn't exist. """ - # check if the bridge exists (on boot it doesn't) - if br not in Section.interfaces('bridge'): - return False - + # drop all interface addresses first self.flush_addrs() - # add interface to bridge - use Section.klass to get BridgeIf class - Section.klass(br)(br, create=False).add_port(self.ifname) - # TODO: port config (STP) + for bridge, bridge_config in bridge_dict.items(): + # add interface to bridge - use Section.klass to get BridgeIf class + Section.klass(bridge)(bridge, create=True).add_port(self.ifname) - return True + # set bridge port path cost + if 'cost' in bridge_config: + self.set_path_cost(bridge_config['cost']) + + # set bridge port path priority + if 'priority' in bridge_config: + self.set_path_cost(bridge_config['priority']) def set_dhcp(self, enable): """ @@ -1047,8 +1085,8 @@ class Interface(Control): # 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) + bridge_dict = config.get('is_bridge_member') + self.add_to_bridge(bridge_dict) # remove no longer required 802.1ad (Q-in-Q VLANs) ifname = config['ifname'] diff --git a/python/vyos/ifconfig/stp.py b/python/vyos/ifconfig/stp.py deleted file mode 100644 index 5e83206c2..000000000 --- a/python/vyos/ifconfig/stp.py +++ /dev/null @@ -1,70 +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/>. - - -from vyos.ifconfig.interface import Interface - -from vyos.validate import assert_positive - - -class STP: - """ - A spanning-tree capable interface. This applies only to bridge port member - interfaces! - """ - - @classmethod - def enable (cls, adaptee): - adaptee._sysfs_set = {**adaptee._sysfs_set, **cls._sysfs_set} - adaptee.set_path_cost = cls.set_path_cost - adaptee.set_path_priority = cls.set_path_priority - return adaptee - - _sysfs_set = { - 'path_cost': { - # XXX: we should set a maximum - 'validate': assert_positive, - 'location': '/sys/class/net/{ifname}/brport/path_cost', - 'errormsg': '{ifname} is not a bridge port member' - }, - 'path_priority': { - # XXX: we should set a maximum - 'validate': assert_positive, - 'location': '/sys/class/net/{ifname}/brport/priority', - 'errormsg': '{ifname} is not a bridge port member' - }, - } - - def set_path_cost(self, cost): - """ - Set interface path cost, only relevant for STP enabled interfaces - - Example: - - >>> from vyos.ifconfig import Interface - >>> Interface('eth0').set_path_cost(4) - """ - self.set_interface('path_cost', cost) - - def set_path_priority(self, priority): - """ - Set interface path priority, only relevant for STP enabled interfaces - - Example: - - >>> from vyos.ifconfig import Interface - >>> Interface('eth0').set_path_priority(4) - """ - self.set_interface('path_priority', priority) diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index 9763620ac..ea9bd54d4 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -109,7 +109,7 @@ def get_config(config=None): # Check if member interface is already member of a bond tmp = is_member(conf, interface, 'bonding') - if tmp and tmp != bond['ifname']: + if tmp and bond['ifname'] not in tmp: interface_config.update({'is_bond_member' : tmp}) # Check if member interface is used as source-interface on another interface @@ -162,11 +162,11 @@ def verify(bond): raise ConfigError(error_msg + 'it does not exist!') if 'is_bridge_member' in interface_config: - tmp = interface_config['is_bridge_member'] + tmp = next(iter(interface_config['is_bridge_member'])) raise ConfigError(error_msg + f'it is already a member of bridge "{tmp}"!') if 'is_bond_member' in interface_config: - tmp = interface_config['is_bond_member'] + tmp = next(iter(interface_config['is_bond_member'])) raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!') if 'is_source_interface' in interface_config: diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py index 485decb17..4aeb8fc67 100755 --- a/src/conf_mode/interfaces-bridge.py +++ b/src/conf_mode/interfaces-bridge.py @@ -24,6 +24,7 @@ from vyos.configdict import get_interface_dict from vyos.configdict import node_changed from vyos.configdict import is_member from vyos.configdict import is_source_interface +from vyos.configdict import dict_merge from vyos.configverify import verify_dhcpv6 from vyos.configverify import verify_vrf from vyos.ifconfig import BridgeIf @@ -69,25 +70,26 @@ def get_config(config=None): # the default dictionary is not properly paged into the dict (see T2665) # thus we will ammend it ourself default_member_values = defaults(base + ['member', 'interface']) - for interface, interface_config in bridge['member']['interface'].items(): - interface_config.update(default_member_values) + for interface in bridge['member']['interface']: + bridge['member']['interface'][interface] = dict_merge( + default_member_values, bridge['member']['interface'][interface]) # Check if member interface is already member of another bridge tmp = is_member(conf, interface, 'bridge') - if tmp and tmp != bridge['ifname']: - interface_config.update({'is_bridge_member' : tmp}) + if tmp and bridge['ifname'] not in tmp: + bridge['member']['interface'][interface].update({'is_bridge_member' : tmp}) # Check if member interface is already member of a bond tmp = is_member(conf, interface, 'bonding') - if tmp: interface_config.update({'is_bond_member' : tmp}) + if tmp: bridge['member']['interface'][interface].update({'is_bond_member' : tmp}) # Check if member interface is used as source-interface on another interface tmp = is_source_interface(conf, interface) - if tmp: interface_config.update({'is_source_interface' : tmp}) + if tmp: bridge['member']['interface'][interface].update({'is_source_interface' : tmp}) # Bridge members must not have an assigned address tmp = has_address_configured(conf, interface) - if tmp: interface_config.update({'has_address' : ''}) + if tmp: bridge['member']['interface'][interface].update({'has_address' : ''}) return bridge @@ -105,15 +107,12 @@ def verify(bridge): if interface == 'lo': raise ConfigError('Loopback interface "lo" can not be added to a bridge') - if interface not in interfaces(): - raise ConfigError(error_msg + 'it does not exist!') - if 'is_bridge_member' in interface_config: - tmp = interface_config['is_bridge_member'] + tmp = next(iter(interface_config['is_bridge_member'])) raise ConfigError(error_msg + f'it is already a member of bridge "{tmp}"!') if 'is_bond_member' in interface_config: - tmp = interface_config['is_bond_member'] + tmp = next(iter(interface_config['is_bond_member'])) raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!') if 'is_source_interface' in interface_config: diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 518dbdc0e..f2b580c6f 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -208,7 +208,8 @@ def get_config(config=None): openvpn['auth_user_pass_file'] = f"/run/openvpn/{openvpn['intf']}.pw" # check if interface is member of a bridge - openvpn['is_bridge_member'] = is_member(conf, openvpn['intf'], 'bridge') + tmp = is_member(conf, openvpn['intf'], 'bridge') + if tmp: openvpn['is_bridge_member'] = next(iter(tmp)) # Check if interface instance has been removed if not conf.exists('interfaces openvpn ' + openvpn['intf']): diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py index f1d885b15..5561514bd 100755 --- a/src/conf_mode/interfaces-tunnel.py +++ b/src/conf_mode/interfaces-tunnel.py @@ -462,7 +462,8 @@ def get_config(config=None): options['tunnel'] = {} # check for bridges - options['bridge'] = is_member(config, ifname, 'bridge') + tmp = is_member(config, ifname, 'bridge') + if tmp: options['bridge'] = next(iter(tmp)) options['interfaces'] = interfaces() for name in ct: |