From c1ad2a6461fc2e767d69567be9647150c3310569 Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Sun, 3 May 2020 13:28:11 +0200 Subject: configdict: T2241: get interface name in intf/vlan_from_dict This is needed as later functions depend on it --- src/conf_mode/interfaces-bonding.py | 1 - 1 file changed, 1 deletion(-) (limited to 'src/conf_mode/interfaces-bonding.py') diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index a174e33e4..76caefabe 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -119,7 +119,6 @@ def get_config(): conf.set_level(cfg_base) bond, disabled = intf_to_dict(conf, default_config_data) - bond['intf'] = ifname # ARP link monitoring frequency in milliseconds if conf.exists('arp-monitor interval'): -- cgit v1.2.3 From 900e75e387939a1d1d4d5b0b79809b8bb2305b91 Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Sun, 3 May 2020 13:46:47 +0200 Subject: validate: T2241: rewrite is_bridge_member to generic is_member - rewrite the function to support both bridge and bonding interface types, if the type is passed it searches only that type, otherwise it searches both - move is_member check out of the deleted condition - move is_member check to intf_from_dict for interfaces that use it --- python/vyos/configdict.py | 3 ++ python/vyos/validate.py | 44 ++++++++++++++++++++--------- src/conf_mode/interfaces-bonding.py | 3 -- src/conf_mode/interfaces-dummy.py | 7 +++-- src/conf_mode/interfaces-geneve.py | 7 +++-- src/conf_mode/interfaces-openvpn.py | 7 +++-- src/conf_mode/interfaces-pseudo-ethernet.py | 5 +--- src/conf_mode/interfaces-tunnel.py | 4 +-- src/conf_mode/interfaces-vxlan.py | 7 +++-- src/conf_mode/interfaces-wireguard.py | 7 +++-- src/conf_mode/interfaces-wireless.py | 7 +++-- src/conf_mode/interfaces-wirelessmodem.py | 7 +++-- 12 files changed, 65 insertions(+), 43 deletions(-) (limited to 'src/conf_mode/interfaces-bonding.py') diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index cd3364c94..ab72aac6c 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -23,6 +23,7 @@ from copy import deepcopy from vyos import ConfigError from vyos.ifconfig import Interface +from vyos.validate import is_member from vyos.util import ifname_from_config @@ -268,6 +269,8 @@ def intf_to_dict(conf, default): # Media Access Control (MAC) address if conf.exists('mac'): intf['mac'] = conf.return_value('mac') + # check if interface is member of a bridge + intf['is_bridge_member'] = is_member(conf, intf['intf'], 'bridge') # IPv6 Duplicate Address Detection (DAD) tries if conf.exists('ipv6 dup-addr-detect-transmits'): diff --git a/python/vyos/validate.py b/python/vyos/validate.py index 446f6e4ca..eb3f8bf52 100644 --- a/python/vyos/validate.py +++ b/python/vyos/validate.py @@ -241,25 +241,43 @@ def assert_mac(m): if octets[:5] == (0, 0, 94, 0, 1): raise ValueError(f'{m} is a VRRP MAC address') -def is_bridge_member(conf, interface): +def is_member(conf, interface, intftype=None): """ - Checks if passed interfaces is part of a bridge device or not. - - Returns a tuple: - None -> Interface not a bridge member - Bridge -> Interface is a member of this bridge + Checks if passed interface is member of other interface of specified type. + intftype is optional, if not passed it will search all known types + (currently bridge and bonding) + + Returns: + None -> Interface is not a member + interface name -> Interface is a member of this interface + False -> interface type cannot have members """ ret_val = None - old_level = conf.get_level() + + if intftype not in ['bonding', 'bridge', None]: + raise ValueError(( + f'unknown interface type "{intftype}" or it cannot ' + f'have member interfaces')) + + intftype = ['bonding', 'bridge'] if intftype == None else [intftype] # set config level to root + old_level = conf.get_level() conf.set_level([]) - base = ['interfaces', 'bridge'] - for bridge in conf.list_nodes(base): - members = conf.list_nodes(base + [bridge, 'member', 'interface']) - if interface in members: - ret_val = bridge - break + + for it in intftype: + base = 'interfaces ' + it + for intf in conf.list_nodes(base): + memberintf = f'{base} {intf} member interface' + if conf.is_tag(memberintf): + if interface in conf.list_nodes(memberintf): + ret_val = intf + break + elif conf.is_leaf(memberintf): + if ( conf.exists(memberintf) and + interface in conf.return_values(memberintf) ): + ret_val = intf + break old_level = conf.set_level(old_level) return ret_val diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index 76caefabe..0fc8cfa6c 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -25,7 +25,6 @@ from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config from vyos.configdict import list_diff, intf_to_dict, add_to_dict from vyos.config import Config from vyos.util import call, cmd -from vyos.validate import is_bridge_member from vyos import ConfigError default_config_data = { @@ -111,8 +110,6 @@ def get_config(): bond = deepcopy(default_config_data) bond['intf'] = ifname bond['deleted'] = True - # check if interface is member if a bridge - bond['is_bridge_member'] = is_bridge_member(conf, ifname) return bond # set new configuration level diff --git a/src/conf_mode/interfaces-dummy.py b/src/conf_mode/interfaces-dummy.py index 23eaa4ecb..7bc834be5 100755 --- a/src/conf_mode/interfaces-dummy.py +++ b/src/conf_mode/interfaces-dummy.py @@ -23,7 +23,7 @@ from netifaces import interfaces from vyos.ifconfig import DummyIf from vyos.configdict import list_diff from vyos.config import Config -from vyos.validate import is_bridge_member +from vyos.validate import is_member from vyos import ConfigError default_config_data = { @@ -47,11 +47,12 @@ def get_config(): dummy['intf'] = os.environ['VYOS_TAGNODE_VALUE'] + # check if we are a member of any bridge + dummy['is_bridge_member'] = is_member(conf, dummy['intf'], 'bridge') + # Check if interface has been removed if not conf.exists('interfaces dummy ' + dummy['intf']): dummy['deleted'] = True - # check if interface is member if a bridge - dummy['is_bridge_member'] = is_bridge_member(conf, dummy['intf']) return dummy # set new configuration level diff --git a/src/conf_mode/interfaces-geneve.py b/src/conf_mode/interfaces-geneve.py index 708a64474..98f0672c5 100755 --- a/src/conf_mode/interfaces-geneve.py +++ b/src/conf_mode/interfaces-geneve.py @@ -22,7 +22,7 @@ from netifaces import interfaces from vyos.config import Config from vyos.ifconfig import GeneveIf -from vyos.validate import is_bridge_member +from vyos.validate import is_member from vyos import ConfigError default_config_data = { @@ -49,11 +49,12 @@ def get_config(): geneve['intf'] = os.environ['VYOS_TAGNODE_VALUE'] + # check if interface is member if a bridge + geneve['is_bridge_member'] = is_member(conf, geneve['intf'], 'bridge') + # Check if interface has been removed if not conf.exists('interfaces geneve ' + geneve['intf']): geneve['deleted'] = True - # check if interface is member if a bridge - geneve['is_bridge_member'] = is_bridge_member(conf, geneve['intf']) return geneve # set new configuration level diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 029bc1d69..23a690bf2 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -29,7 +29,7 @@ from vyos.configdict import list_diff from vyos.ifconfig import VTunIf from vyos.template import render from vyos.util import call, chown, chmod_600, chmod_755 -from vyos.validate import is_addr_assigned, is_bridge_member, is_ipv4 +from vyos.validate import is_addr_assigned, is_member, is_ipv4 from vyos import ConfigError user = 'openvpn' @@ -199,11 +199,12 @@ def get_config(): openvpn['intf'] = os.environ['VYOS_TAGNODE_VALUE'] 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') + # Check if interface instance has been removed if not conf.exists('interfaces openvpn ' + openvpn['intf']): openvpn['deleted'] = True - # check if interface is member if a bridge - openvpn['is_bridge_member'] = is_bridge_member(conf, openvpn['intf']) return openvpn # Check if we belong to any bridge interface diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py index 67250ec9f..f9523ca8b 100755 --- a/src/conf_mode/interfaces-pseudo-ethernet.py +++ b/src/conf_mode/interfaces-pseudo-ethernet.py @@ -21,10 +21,9 @@ from sys import exit from netifaces import interfaces from vyos.config import Config -from vyos.configdict import list_diff, vlan_to_dict, intf_to_dict, add_to_dict +from vyos.configdict import list_diff, intf_to_dict, add_to_dict from vyos.ifconfig import MACVLANIf, Section from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config -from vyos.validate import is_bridge_member from vyos import ConfigError default_config_data = { @@ -77,8 +76,6 @@ def get_config(): if not conf.exists(cfg_base): peth = deepcopy(default_config_data) peth['deleted'] = True - # check if interface is member if a bridge - peth['is_bridge_member'] = is_bridge_member(conf, ifname) return peth # set new configuration level diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py index fc084814a..1916d2de2 100755 --- a/src/conf_mode/interfaces-tunnel.py +++ b/src/conf_mode/interfaces-tunnel.py @@ -25,7 +25,7 @@ from vyos.config import Config from vyos.ifconfig import Interface, GREIf, GRETapIf, IPIPIf, IP6GREIf, IPIP6If, IP6IP6If, SitIf, Sit6RDIf from vyos.ifconfig.afi import IP4, IP6 from vyos.configdict import list_diff -from vyos.validate import is_ipv4, is_ipv6, is_bridge_member +from vyos.validate import is_ipv4, is_ipv6, is_member from vyos import ConfigError from vyos.dicts import FixedDict @@ -410,7 +410,7 @@ def get_config(): options['tunnel'] = {} # check for bridges - options['bridge'] = is_bridge_member(conf, ifname) + options['bridge'] = is_member(conf, ifname, 'bridge') options['interfaces'] = interfaces() for name in ct: diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py index 74eae4281..334e418ab 100755 --- a/src/conf_mode/interfaces-vxlan.py +++ b/src/conf_mode/interfaces-vxlan.py @@ -22,7 +22,7 @@ from netifaces import interfaces from vyos.config import Config from vyos.ifconfig import VXLANIf, Interface -from vyos.validate import is_bridge_member +from vyos.validate import is_member from vyos import ConfigError default_config_data = { @@ -62,11 +62,12 @@ def get_config(): vxlan['intf'] = os.environ['VYOS_TAGNODE_VALUE'] + # check if interface is member if a bridge + vxlan['is_bridge_member'] = is_member(conf, vxlan['intf'], 'bridge') + # Check if interface has been removed if not conf.exists('interfaces vxlan ' + vxlan['intf']): vxlan['deleted'] = True - # check if interface is member if a bridge - vxlan['is_bridge_member'] = is_bridge_member(conf, vxlan['intf']) return vxlan # set new configuration level diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py index 01f84260d..98d5fcb27 100755 --- a/src/conf_mode/interfaces-wireguard.py +++ b/src/conf_mode/interfaces-wireguard.py @@ -25,7 +25,7 @@ from vyos.config import Config from vyos.configdict import list_diff from vyos.ifconfig import WireGuardIf from vyos.util import chown, chmod_750, call -from vyos.validate import is_bridge_member +from vyos.validate import is_member from vyos import ConfigError kdir = r'/config/auth/wireguard' @@ -78,11 +78,12 @@ def get_config(): wg = deepcopy(default_config_data) wg['intf'] = os.environ['VYOS_TAGNODE_VALUE'] + # check if interface is member if a bridge + wg['is_bridge_member'] = is_member(conf, wg['intf'], 'bridge') + # Check if interface has been removed if not conf.exists(base + [wg['intf']]): wg['deleted'] = True - # check if interface is member if a bridge - wg['is_bridge_member'] = is_bridge_member(conf, wg['intf']) return wg conf.set_level(base + [wg['intf']]) diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py index 148a7f6e0..04125ff31 100755 --- a/src/conf_mode/interfaces-wireless.py +++ b/src/conf_mode/interfaces-wireless.py @@ -29,7 +29,7 @@ from vyos.ifconfig import WiFiIf, Section from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config from vyos.template import render from vyos.util import chown, call -from vyos.validate import is_bridge_member +from vyos.validate import is_member from vyos import ConfigError default_config_data = { @@ -134,12 +134,13 @@ def get_config(): wifi['intf'] = os.environ['VYOS_TAGNODE_VALUE'] + # check if interface is member if a bridge + wifi['is_bridge_member'] = is_member(conf, wifi['intf'], 'bridge') + # check if wireless interface has been removed cfg_base = 'interfaces wireless ' + wifi['intf'] if not conf.exists(cfg_base): wifi['deleted'] = True - # check if interface is member if a bridge - wifi['is_bridge_member'] = is_bridge_member(conf, wifi['intf']) # we can not bail out early as wireless interface can not be removed # Kernel will complain with: RTNETLINK answers: Operation not supported. # Thus we need to remove individual settings diff --git a/src/conf_mode/interfaces-wirelessmodem.py b/src/conf_mode/interfaces-wirelessmodem.py index a3a2a2648..03832f345 100755 --- a/src/conf_mode/interfaces-wirelessmodem.py +++ b/src/conf_mode/interfaces-wirelessmodem.py @@ -23,7 +23,7 @@ from netifaces import interfaces from vyos.config import Config from vyos.template import render from vyos.util import chown, chmod_755, cmd, call -from vyos.validate import is_bridge_member +from vyos.validate import is_member from vyos import ConfigError default_config_data = { @@ -64,11 +64,12 @@ def get_config(): wwan['logfile'] = f"/var/log/vyatta/ppp_{wwan['intf']}.log" wwan['chat_script'] = f"/etc/ppp/peers/chat.{wwan['intf']}" + # check if interface is member if a bridge + wwan['is_bridge_member'] = is_member(conf, wwan['intf'], 'bridge') + # Check if interface has been removed if not conf.exists('interfaces wirelessmodem ' + wwan['intf']): wwan['deleted'] = True - # check if interface is member if a bridge - wwan['is_bridge_member'] = is_bridge_member(conf, wwan['intf']) return wwan # set new configuration level -- cgit v1.2.3 From 5ac80802197e1b5c17db96580b004391bde47bf2 Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Fri, 1 May 2020 16:26:50 +0200 Subject: bonding: T2241: make VRF and bridge membership mutually exclusive --- src/conf_mode/interfaces-bonding.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/conf_mode/interfaces-bonding.py') diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index 0fc8cfa6c..7681a8c71 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -184,9 +184,15 @@ def verify(bond): raise ConfigError('Mode dependency failed, primary not supported ' \ 'in mode "{}"!'.format(bond['mode'])) - vrf_name = bond['vrf'] - if vrf_name and vrf_name not in interfaces(): - raise ConfigError(f'VRF "{vrf_name}" does not exist') + if bond['vrf']: + if bond['vrf'] not in interfaces(): + raise ConfigError(f'VRF "{bond["vrf"]}" does not exist') + + if bond['is_bridge_member']: + raise ConfigError(( + f'Interface "{bond["intf"]}" cannot be member of VRF ' + f'"{bond["vrf"]}" and bridge {bond["is_bridge_member"]} ' + f'at the same time!')) # use common function to verify VLAN configuration verify_vlan_config(bond) -- cgit v1.2.3 From d3ac208e936caa172c575c953d3e8582286f70ef Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Sun, 3 May 2020 21:01:50 +0200 Subject: bonding: T2241: disallow adding interfaces with addresses to bond --- src/conf_mode/interfaces-bonding.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/conf_mode/interfaces-bonding.py') diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index 7681a8c71..ec9e0bb11 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -25,6 +25,7 @@ from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config from vyos.configdict import list_diff, intf_to_dict, add_to_dict from vyos.config import Config from vyos.util import call, cmd +from vyos.validate import has_address_configured from vyos import ConfigError default_config_data = { @@ -213,9 +214,10 @@ def verify(bond): 'belongs to {}'.format(intf, tmp)) # can not add interfaces with an assigned address to a bond - if conf.exists('interfaces ethernet ' + intf + ' address'): - raise ConfigError('can not enslave interface {} which has an address ' \ - 'assigned'.format(intf)) + if has_address_configured(conf, intf): + raise ConfigError(( + f'Cannot add interface "{intf}" to bond "{bond["intf"]}", ' + f'it has an address assigned!')) # bond members are not allowed to be bridge members, too for tmp in conf.list_nodes('interfaces bridge'): -- cgit v1.2.3 From 9998768f0c8edb3e72566f1d26bee67d42adc133 Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Sun, 3 May 2020 21:02:10 +0200 Subject: bonding: T2241: make address and bridge membership mutually exclusive Bridge members should not have addresses assigned. --- src/conf_mode/interfaces-bonding.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/conf_mode/interfaces-bonding.py') diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index ec9e0bb11..e5626ef6c 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -185,6 +185,14 @@ def verify(bond): raise ConfigError('Mode dependency failed, primary not supported ' \ 'in mode "{}"!'.format(bond['mode'])) + if ( bond['is_bridge_member'] + and ( bond['address'] + or bond['ipv6_eui64_prefix'] + or bond['ipv6_autoconf'] ) ): + raise ConfigError(( + f'Cannot assign address to interface "{bond["intf"]}" ' + f'as it is a member of bridge "{bond["is_bridge_member"]}"!')) + if bond['vrf']: if bond['vrf'] not in interfaces(): raise ConfigError(f'VRF "{bond["vrf"]}" does not exist') -- cgit v1.2.3 From 512510b887408d8263865197fc936501ee453064 Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Fri, 1 May 2020 16:31:35 +0200 Subject: bonding: T2241: cleanup verify section - use is_member function instead of checking config directly - make error output more user friendly - replace .format with f-strings - split into lines less than ~80 characters long --- src/conf_mode/interfaces-bonding.py | 66 ++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 31 deletions(-) (limited to 'src/conf_mode/interfaces-bonding.py') diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index e5626ef6c..235b24439 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -25,7 +25,7 @@ from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config from vyos.configdict import list_diff, intf_to_dict, add_to_dict from vyos.config import Config from vyos.util import call, cmd -from vyos.validate import has_address_configured +from vyos.validate import is_member, has_address_configured from vyos import ConfigError default_config_data = { @@ -172,18 +172,20 @@ def get_config(): def verify(bond): if bond['deleted']: if bond['is_bridge_member']: - interface = bond['intf'] - bridge = bond['is_bridge_member'] - raise ConfigError(f'Interface "{interface}" can not be deleted as it belongs to bridge "{bridge}"!') + raise ConfigError(( + f'Cannot delete interface "{bond["intf"]}" as it is a ' + f'member of bridge "{bond["is_bridge_member"]}"!')) + return None - if len (bond['arp_mon_tgt']) > 16: - raise ConfigError('The maximum number of targets that can be specified is 16') + if len(bond['arp_mon_tgt']) > 16: + raise ConfigError('The maximum number of arp-monitor targets is 16') if bond['primary']: if bond['mode'] not in ['active-backup', 'balance-tlb', 'balance-alb']: - raise ConfigError('Mode dependency failed, primary not supported ' \ - 'in mode "{}"!'.format(bond['mode'])) + raise ConfigError(( + 'Mode dependency failed, primary not supported in mode ' + f'"{bond["mode"]}"!')) if ( bond['is_bridge_member'] and ( bond['address'] @@ -210,16 +212,17 @@ def verify(bond): for intf in bond['member']: # check if member interface is "real" if intf not in interfaces(): - raise ConfigError('interface {} does not exist!'.format(intf)) + raise ConfigError(f'Interface {intf} does not exist!') # a bonding member interface is only allowed to be assigned to one bond! all_bonds = conf.list_nodes('interfaces bonding') # We do not need to check our own bond all_bonds.remove(bond['intf']) for tmp in all_bonds: - if conf.exists('interfaces bonding ' + tmp + ' member interface ' + intf): - raise ConfigError('can not enslave interface {} which already ' \ - 'belongs to {}'.format(intf, tmp)) + if conf.exists('interfaces bonding {tmp} member interface {intf}'): + raise ConfigError(( + f'Cannot add interface "{intf}" to bond "{bond["intf"]}", ' + f'it is already a member of bond "{tmp}"!')) # can not add interfaces with an assigned address to a bond if has_address_configured(conf, intf): @@ -227,35 +230,37 @@ def verify(bond): f'Cannot add interface "{intf}" to bond "{bond["intf"]}", ' f'it has an address assigned!')) - # bond members are not allowed to be bridge members, too - for tmp in conf.list_nodes('interfaces bridge'): - if conf.exists('interfaces bridge ' + tmp + ' member interface ' + intf): - raise ConfigError('can not enslave interface {} which belongs to ' \ - 'bridge {}'.format(intf, tmp)) + # bond members are not allowed to be bridge members + tmp = is_member(conf, intf, 'bridge') + if tmp: + raise ConfigError(( + f'Cannot add interface "{intf}" to bond "{bond["intf"]}", ' + f'it is already a member of bridge "{tmp}"!')) - # bond members are not allowed to be vrrp members, too + # bond members are not allowed to be vrrp members for tmp in conf.list_nodes('high-availability vrrp group'): - if conf.exists('high-availability vrrp group ' + tmp + ' interface ' + intf): - raise ConfigError('can not enslave interface {} which belongs to ' \ - 'VRRP group {}'.format(intf, tmp)) + if conf.exists('high-availability vrrp group {tmp} interface {intf}'): + raise ConfigError(( + f'Cannot add interface "{intf}" to bond "{bond["intf"]}", ' + f'it is already a member of VRRP group "{tmp}"!')) # bond members are not allowed to be underlaying psuedo-ethernet devices for tmp in conf.list_nodes('interfaces pseudo-ethernet'): - if conf.exists('interfaces pseudo-ethernet ' + tmp + ' link ' + intf): - raise ConfigError('can not enslave interface {} which belongs to ' \ - 'pseudo-ethernet {}'.format(intf, tmp)) + if conf.exists('interfaces pseudo-ethernet {tmp} link {intf}'): + raise ConfigError(( + f'Cannot add interface "{intf}" to bond "{bond["intf"]}", ' + f'it is already the link of pseudo-ethernet "{tmp}"!')) # bond members are not allowed to be underlaying vxlan devices for tmp in conf.list_nodes('interfaces vxlan'): - if conf.exists('interfaces vxlan ' + tmp + ' link ' + intf): - raise ConfigError('can not enslave interface {} which belongs to ' \ - 'vxlan {}'.format(intf, tmp)) - + if conf.exists('interfaces vxlan {tmp} link {intf}'): + raise ConfigError(( + f'Cannot add interface "{intf}" to bond "{bond["intf"]}", ' + f'it is already the link of VXLAN "{tmp}"!')) if bond['primary']: if bond['primary'] not in bond['member']: - raise ConfigError('primary interface must be a member interface of {}' \ - .format(bond['intf'])) + raise ConfigError(f'Bond "{bond["intf"]}" primary interface must be a member') if bond['mode'] not in ['active-backup', 'balance-tlb', 'balance-alb']: raise ConfigError('primary interface only works for mode active-backup, ' \ @@ -268,7 +273,6 @@ def verify(bond): return None - def generate(bond): return None -- cgit v1.2.3 From 600b3bc874355237d24bbe38660478a43ce6c946 Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Fri, 1 May 2020 16:33:31 +0200 Subject: bonding: T2241: fix falling out of bridge when changing settings Previously, set_vrf was always called, which uses the same master and nomaster commands as bridge, so it removed the interface from the bridge. - add checks to make VRF and bridge membership mutually exclusive - always re-add the interface back to any bridge it is part of in case it is deleted and recreated --- src/conf_mode/interfaces-bonding.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/conf_mode/interfaces-bonding.py') diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index 235b24439..a8900ac76 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -405,8 +405,14 @@ def apply(bond): for addr in bond['address']: b.add_addr(addr) - # assign/remove VRF - b.set_vrf(bond['vrf']) + # assign/remove VRF (ONLY when not a member of a bridge, + # otherwise 'nomaster' removes it from it) + if not bond['is_bridge_member']: + b.set_vrf(bond['vrf']) + + # re-add ourselves to any bridge we might have fallen out of + if bond['is_bridge_member']: + b.add_to_bridge(bond['is_bridge_member']) # remove no longer required service VLAN interfaces (vif-s) for vif_s in bond['vif_s_remove']: -- cgit v1.2.3 From b38326721ce6448a865100a7da200a291388c029 Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Sat, 2 May 2020 15:11:48 +0200 Subject: bonding: T2367: use simple 'ip addr flush' to flush member addresses We've already verified that all member interfaces don't have any addresses configured, so it should be safe to simply call 'ip addr flush' on them to flush the remaining addresses (e.g. IPv6 link-local) --- src/conf_mode/interfaces-bonding.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'src/conf_mode/interfaces-bonding.py') diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index a8900ac76..82b7c0f2a 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -20,7 +20,7 @@ from copy import deepcopy from sys import exit from netifaces import interfaces -from vyos.ifconfig import BondIf, Section +from vyos.ifconfig import BondIf from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config from vyos.configdict import list_diff, intf_to_dict, add_to_dict from vyos.config import Config @@ -381,12 +381,9 @@ def apply(bond): # Add (enslave) interfaces to bond for intf in bond['member']: - # flushes only children of Interfaces class (e.g. vlan are not) - if intf in Section.interfaces(): - klass = Section.klass(intf, vlan=False) - klass(intf, create=False).flush_addrs() - # flushes also vlan interfaces - call(f'ip addr flush dev "{intf}"') + # if we've come here we already verified the interface doesn't + # have addresses configured so just flush any remaining ones + cmd(f'ip addr flush dev "{intf}"') b.add_port(intf) # As the bond interface is always disabled first when changing -- cgit v1.2.3