#!/usr/bin/python
#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
#
-from sets import Set
-from ifupdown.iface import *
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.bridgeutils import brctl
-from ifupdownaddons.iproute2 import iproute2
-from collections import Counter
-import ifupdown.rtnetlink_api as rtnetlink_api
-import itertools
import re
import time
+import itertools
+
+from sets import Set
+from collections import Counter
+
+try:
+ import ifupdown2.ifupdown.exceptions as exceptions
+ import ifupdown2.ifupdown.policymanager as policymanager
+ import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+
+ from ifupdown2.nlmanager.nlmanager import Link
+
+ from ifupdown2.ifupdown.iface import *
+ from ifupdown2.ifupdown.utils import utils
+ from ifupdown2.ifupdown.netlink import netlink
+
+ from ifupdown2.ifupdownaddons.cache import *
+ from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+ from ifupdown2.ifupdownaddons.modulebase import moduleBase
+except ImportError:
+ import ifupdown.exceptions as exceptions
+ import ifupdown.policymanager as policymanager
+ import ifupdown.ifupdownflags as ifupdownflags
+
+ from nlmanager.nlmanager import Link
+
+ from ifupdown.iface import *
+ from ifupdown.utils import utils
+ from ifupdown.netlink import netlink
+
+ from ifupdownaddons.cache import *
+ from ifupdownaddons.LinkUtils import LinkUtils
+ from ifupdownaddons.modulebase import moduleBase
+
+
+class bridgeFlags:
+ PORT_PROCESSED = 0x1
+ PORT_PROCESSED_OVERRIDE = 0x2
+
class bridge(moduleBase):
""" ifupdown2 addon module to configure linux bridges """
{'help' : 'vlan aware bridge. Setting this ' +
'attribute to yes enables vlan filtering' +
' on the bridge',
- 'example' : ['bridge-vlan-aware yes/no']},
+ 'validvals' : ['yes', 'no'],
+ 'example' : ['bridge-vlan-aware yes/no'],
+ 'default': 'no'
+ },
'bridge-ports' :
{'help' : 'bridge ports',
+ 'multivalue' : True,
'required' : True,
+ 'validvals': ['<interface-list>', 'none'],
'example' : ['bridge-ports swp1.100 swp2.100 swp3.100',
'bridge-ports glob swp1-3.100',
'bridge-ports regex (swp[1|2|3].100)']},
'default' : 'no'},
'bridge-bridgeprio' :
{'help': 'bridge priority',
+ 'validrange' : ['0', '65535'],
'example' : ['bridge-bridgeprio 32768'],
'default' : '32768'},
'bridge-ageing' :
{'help': 'bridge ageing',
+ 'validrange' : ['0', '65535'],
'example' : ['bridge-ageing 300'],
'default' : '300'},
'bridge-fd' :
{ 'help' : 'bridge forward delay',
+ 'validrange' : ['0', '255'],
'example' : ['bridge-fd 15'],
'default' : '15'},
'bridge-gcint' :
# XXX: recheck values
{ 'help' : 'bridge garbage collection interval in secs',
+ 'validrange' : ['0', '255'],
'example' : ['bridge-gcint 4'],
- 'default' : '4'},
+ 'default' : '4',
+ 'compat' : True,
+ 'deprecated': True},
'bridge-hello' :
{ 'help' : 'bridge set hello time',
+ 'validrange' : ['0', '255'],
'example' : ['bridge-hello 2'],
'default' : '2'},
'bridge-maxage' :
{ 'help' : 'bridge set maxage',
+ 'validrange' : ['0', '255'],
'example' : ['bridge-maxage 20'],
'default' : '20'},
'bridge-pathcosts' :
{ 'help' : 'bridge set port path costs',
- 'example' : ['bridge-pathcosts swp1=100 swp2=100'],
+ 'validvals': ['<interface-range-list>'],
+ 'validrange' : ['0', '65535'],
+ 'example' : ['under the port (for vlan aware bridge): bridge-pathcosts 100',
+ 'under the bridge (for vlan unaware bridge): bridge-pathcosts swp1=100 swp2=100'],
'default' : '100'},
'bridge-portprios' :
{ 'help' : 'bridge port prios',
- 'example' : ['bridge-portprios swp1=32 swp2=32'],
+ 'validvals': ['<interface-range-list>'],
+ 'validrange' : ['0', '65535'],
+ 'example' : ['under the port (for vlan aware bridge): bridge-portprios 32',
+ 'under the bridge (for vlan unaware bridge): bridge-portprios swp1=32 swp2=32'],
'default' : '32'},
'bridge-mclmc' :
{ 'help' : 'set multicast last member count',
+ 'validrange' : ['0', '255'],
'example' : ['bridge-mclmc 2'],
'default' : '2'},
'bridge-mcrouter' :
- { 'help' : 'set multicast router',
- 'default' : '1',
- 'example' : ['bridge-mcrouter 1']},
+ { 'help': 'Set bridge multicast routers: 0 - disabled - no, 1 - automatic (queried), 2 - permanently enabled - yes',
+ 'validvals' : ['yes', 'no', '0', '1', '2'],
+ 'example' : ['bridge-mcrouter 1'],
+ 'default': 'yes'
+ },
'bridge-mcsnoop' :
{ 'help' : 'set multicast snooping',
- 'default' : '1',
- 'example' : ['bridge-mcsnoop 1']},
+ 'validvals' : ['yes', 'no', '0', '1'],
+ 'default' : 'yes',
+ 'example' : ['bridge-mcsnoop yes']},
'bridge-mcsqc' :
{ 'help' : 'set multicast startup query count',
+ 'validrange' : ['0', '255'],
'default' : '2',
'example' : ['bridge-mcsqc 2']},
'bridge-mcqifaddr' :
{ 'help' : 'set multicast query to use ifaddr',
- 'default' : '0',
- 'example' : ['bridge-mcqifaddr 0']},
+ 'validvals' : ['yes', 'no', '0', '1'],
+ 'default' : 'no',
+ 'example' : ['bridge-mcqifaddr no']},
'bridge-mcquerier' :
{ 'help' : 'set multicast querier',
- 'default' : '0',
- 'example' : ['bridge-mcquerier 0']},
+ 'validvals' : ['yes', 'no', '0', '1'],
+ 'default' : 'no',
+ 'example' : ['bridge-mcquerier no']},
'bridge-hashel' :
{ 'help' : 'set hash elasticity',
- 'default' : '4096',
+ 'validrange' : ['0', '4096'],
+ 'default' : '4',
'example' : ['bridge-hashel 4096']},
'bridge-hashmax' :
{ 'help' : 'set hash max',
- 'default' : '4096',
+ 'validrange' : ['0', '4096'],
+ 'default' : '512',
'example' : ['bridge-hashmax 4096']},
'bridge-mclmi' :
{ 'help' : 'set multicast last member interval (in secs)',
+ 'validrange' : ['0', '255'],
'default' : '1',
'example' : ['bridge-mclmi 1']},
'bridge-mcmi' :
{ 'help' : 'set multicast membership interval (in secs)',
+ 'validrange' : ['0', '255'],
'default' : '260',
'example' : ['bridge-mcmi 260']},
'bridge-mcqpi' :
{ 'help' : 'set multicast querier interval (in secs)',
+ 'validrange' : ['0', '255'],
'default' : '255',
'example' : ['bridge-mcqpi 255']},
'bridge-mcqi' :
{ 'help' : 'set multicast query interval (in secs)',
+ 'validrange' : ['0', '255'],
'default' : '125',
'example' : ['bridge-mcqi 125']},
'bridge-mcqri' :
{ 'help' : 'set multicast query response interval (in secs)',
+ 'validrange' : ['0', '255'],
'default' : '10',
'example' : ['bridge-mcqri 10']},
'bridge-mcsqi' :
{ 'help' : 'set multicast startup query interval (in secs)',
+ 'validrange' : ['0', '255'],
'default' : '31',
'example' : ['bridge-mcsqi 31']},
'bridge-mcqv4src' :
{ 'help' : 'set per VLAN v4 multicast querier source address',
+ 'validvals' : ['<number-ipv4-list>', ],
+ 'multivalue' : True,
'compat' : True,
'example' : ['bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1']},
- 'bridge-portmcrouter' :
- { 'help' : 'set port multicast routers',
- 'default' : '1',
- 'example' : ['under the bridge: bridge-portmcrouter swp1=1 swp2=1',
- 'under the port: bridge-portmcrouter 1']},
+ 'bridge-portmcrouter':
+ {
+ 'help': 'Set port multicast routers: 0 - disabled, 1 - automatic (queried), 2 - permanently enabled',
+ 'validvals': ['<interface-disabled-automatic-enabled>'],
+ 'example': [
+ 'under the port (for vlan aware bridge): bridge-portmcrouter 0',
+ 'under the port (for vlan aware bridge): bridge-portmcrouter 1',
+ 'under the port (for vlan aware bridge): bridge-portmcrouter 2',
+ 'under the port (for vlan aware bridge): bridge-portmcrouter disabled',
+ 'under the port (for vlan aware bridge): bridge-portmcrouter automatic',
+ 'under the port (for vlan aware bridge): bridge-portmcrouter enabled',
+ 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=0 swp2=1 swp2=2',
+ 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=disabled swp2=automatic swp3=enabled',
+ 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=2 swp2=disabled swp3=1',
+ ]
+ },
'bridge-portmcfl' :
{ 'help' : 'port multicast fast leave.',
- 'default' : '0',
- 'example' : ['under the bridge: bridge-portmcfl swp1=0 swp2=0',
- 'under the port: bridge-portmcfl 0']},
+ 'validvals': ['<interface-yes-no-0-1-list>'],
+ 'default' : 'no',
+ 'example' : ['under the port (for vlan aware bridge): bridge-portmcfl no',
+ 'under the bridge (for vlan unaware bridge): bridge-portmcfl swp1=no swp2=no']},
'bridge-waitport' :
{ 'help' : 'wait for a max of time secs for the' +
' specified ports to become available,' +
'regex or \"all\" on bridge_ports,' +
'as it wouldnt work.',
'default' : '0',
+ 'validvals': ['<number-interface-list>'],
'example' : ['bridge-waitport 4 swp1 swp2']},
'bridge-maxwait' :
{ 'help' : 'forces to time seconds the maximum time ' +
- 'that the Debian bridge setup scripts will ' +
+ 'that the Debian bridge setup scripts will ' +
'wait for the bridge ports to get to the ' +
'forwarding status, doesn\'t allow factional ' +
'part. If it is equal to 0 then no waiting' +
' is done',
+ 'validrange' : ['0', '255'],
'default' : '0',
'example' : ['bridge-maxwait 3']},
'bridge-vids' :
'under the bridge or under the port. ' +
'If specified under the bridge the ports ' +
'inherit it unless overridden by a ' +
- 'bridge-vids attribuet under the port',
+ 'bridge-vids attribute under the port',
+ 'multivalue' : True,
+ 'validvals': ['<number-comma-range-list>'],
'example' : ['bridge-vids 4000',
- 'bridge-vids 2000 2200-3000']},
+ 'bridge-vids 2000 2200-3000'],
+ 'aliases': ['bridge-trunk']},
'bridge-pvid' :
{ 'help' : 'bridge port pvid. Must be specified under' +
' the bridge port',
+ 'validrange' : ['0', '4096'],
'example' : ['bridge-pvid 1']},
'bridge-access' :
{ 'help' : 'bridge port access vlan. Must be ' +
'specified under the bridge port',
+ 'validrange' : ['1', '4094'],
'example' : ['bridge-access 300']},
+ 'bridge-allow-untagged' :
+ { 'help' : 'indicate if the bridge port accepts ' +
+ 'untagged packets or not. Must be ' +
+ 'specified under the bridge port. ' +
+ 'Default is \'yes\'',
+ 'validvals' : ['yes', 'no'],
+ 'example' : ['bridge-allow-untagged yes'],
+ 'default' : 'yes'},
'bridge-port-vids' :
{ 'help' : 'bridge vlans',
'compat': True,
{ 'help' : 'bridge port vlans',
'compat': True,
'example' : ['bridge-port-pvids bond0=100 bond1=200']},
+ 'bridge-learning' :
+ { 'help' : 'bridge port learning flag',
+ 'validvals': ['on', 'off', '<interface-on-off-list>'],
+ 'default': 'on',
+ 'example' : ['bridge-learning off']},
+ 'bridge-igmp-version' :
+ { 'help' : 'mcast igmp version',
+ 'validvals': ['2', '3'],
+ 'default' : '2',
+ 'example' : ['bridge-igmp-version 2']},
+ 'bridge-mld-version':
+ { 'help' : 'mcast mld version',
+ 'validvals': ['1', '2'],
+ 'default' : '1',
+ 'example' : ['bridge-mld-version 1']},
+ 'bridge-unicast-flood' :
+ { 'help' : 'bridge port unicast flood flag',
+ 'validvals': ['on', 'off', '<interface-on-off-list>'],
+ 'default': 'on',
+ 'example' : ['under the port (for vlan aware bridge): bridge-unicast-flood on',
+ 'under the bridge (for vlan unaware bridge): bridge-unicast-flood swp1=on swp2=on']},
+ 'bridge-multicast-flood' :
+ { 'help' : 'bridge port multicast flood flag',
+ 'validvals': ['on', 'off', '<interface-on-off-list>'],
+ 'default': 'on',
+ 'example' : ['under the port (for vlan aware bridge): bridge-multicast-flood on',
+ 'under the bridge (for vlan unaware bridge): bridge-multicast-flood swp1=on swp2=on']},
+ 'bridge-vlan-protocol' :
+ { 'help' : 'bridge vlan protocol',
+ 'default' : '802.1q',
+ 'validvals': ['802.1q', '802.1ad'],
+ 'example' : ['bridge-vlan-protocol 802.1q']},
+ 'bridge-vlan-stats' :
+ { 'help' : 'bridge vlan stats',
+ 'default' : 'off',
+ 'validvals': ['on', 'off'],
+ 'example' : ['bridge-vlan-stats off']},
+ 'bridge-arp-nd-suppress' :
+ { 'help' : 'bridge port arp nd suppress flag',
+ 'validvals': ['on', 'off', '<interface-on-off-list>'],
+ 'default': 'off',
+ 'example' : ['under the port (for vlan aware bridge): bridge-arp-nd-suppress on',
+ 'under the bridge (for vlan unaware bridge): bridge-arp-nd-suppress swp1=on swp2=on']},
+ 'bridge-mcstats' :
+ { 'help' : 'bridge multicast stats',
+ 'default' : 'off',
+ 'validvals': ['on', 'off'],
+ 'example' : ['bridge-mcstats off']},
+ 'bridge-l2protocol-tunnel': {
+ 'help': 'layer 2 protocol tunneling',
+ 'validvals': [ # XXX: lists all combinations, should move to
+ # a better representation
+ 'all',
+ 'cdp',
+ 'cdp lacp',
+ 'cdp lacp lldp',
+ 'cdp lacp lldp pvst',
+ 'cdp lacp lldp stp',
+ 'cdp lacp pvst',
+ 'cdp lacp pvst stp',
+ 'cdp lacp stp',
+ 'cdp lldp',
+ 'cdp lldp pvst',
+ 'cdp lldp pvst stp',
+ 'cdp lldp stp',
+ 'cdp pvst',
+ 'cdp pvst stp',
+ 'cdp stp',
+ 'lacp',
+ 'lacp lldp',
+ 'lacp lldp pvst',
+ 'lacp lldp pvst stp',
+ 'lacp lldp stp',
+ 'lacp pvst',
+ 'lacp pvst stp',
+ 'lacp stp',
+ 'lldp',
+ 'lldp pvst',
+ 'lldp pvst stp',
+ 'lldp stp',
+ 'pvst',
+ 'pvst stp',
+ 'stp',
+ '<interface-l2protocol-tunnel-list>'],
+ 'example': [
+ 'under the bridge (for vlan unaware bridge): bridge-l2protocol-tunnel swpX=lacp,stp swpY=cdp swpZ=all',
+ 'under the port (for vlan aware bridge): bridge-l2protocol-tunnel lacp stp lldp cdp pvst',
+ 'under the port (for vlan aware bridge): bridge-l2protocol-tunnel lldp pvst',
+ 'under the port (for vlan aware bridge): bridge-l2protocol-tunnel stp',
+ 'under the port (for vlan aware bridge): bridge-l2protocol-tunnel all'
+ ]
+ }
}}
- # declare some ifaceobj priv_flags.
- # XXX: This assumes that the priv_flags is owned by this module
- # which it is not.
- _BRIDGE_PORT_PROCESSED = 0x1
+ # Netlink attributes not associated with ifupdown2
+ # attributes are left commented-out for a future use
+ # and kept in order :)
+ _ifla_br_attributes_map = (
+ # Link.IFLA_BR_UNSPEC,
+ ('bridge-fd', Link.IFLA_BR_FORWARD_DELAY),
+ ('bridge-hello', Link.IFLA_BR_HELLO_TIME),
+ ('bridge-maxage', Link.IFLA_BR_MAX_AGE),
+ ('bridge-ageing', Link.IFLA_BR_AGEING_TIME),
+ ('bridge-stp', Link.IFLA_BR_STP_STATE),
+ ('bridge-bridgeprio', Link.IFLA_BR_PRIORITY),
+ ('bridge-vlan-aware', Link.IFLA_BR_VLAN_FILTERING),
+ ('bridge-vlan-protocol', Link.IFLA_BR_VLAN_PROTOCOL),
+ # Link.IFLA_BR_GROUP_FWD_MASK,
+ # Link.IFLA_BR_ROOT_ID,
+ # Link.IFLA_BR_BRIDGE_ID,
+ # Link.IFLA_BR_ROOT_PORT,
+ # (Link.IFLA_BR_ROOT_PATH_COST,,
+ # Link.IFLA_BR_TOPOLOGY_CHANGE,
+ # Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
+ # Link.IFLA_BR_HELLO_TIMER,
+ # Link.IFLA_BR_TCN_TIMER,
+ # Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER,
+ # Link.IFLA_BR_GC_TIMER,
+ # Link.IFLA_BR_GROUP_ADDR,
+ # Link.IFLA_BR_FDB_FLUSH,
+ ('bridge-mcrouter', Link.IFLA_BR_MCAST_ROUTER),
+ #('bridge-mcsnoop', Link.IFLA_BR_MCAST_SNOOPING), # requires special handling so we won't loop on this attr
+ ('bridge-mcqifaddr', Link.IFLA_BR_MCAST_QUERY_USE_IFADDR),
+ ('bridge-mcquerier', Link.IFLA_BR_MCAST_QUERIER),
+ ('bridge-hashel', Link.IFLA_BR_MCAST_HASH_ELASTICITY),
+ ('bridge-hashmax', Link.IFLA_BR_MCAST_HASH_MAX),
+ ('bridge-mclmc', Link.IFLA_BR_MCAST_LAST_MEMBER_CNT),
+ ('bridge-mcsqc', Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT),
+ ('bridge-mclmi', Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL),
+ ('bridge-mcmi', Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL),
+ ('bridge-mcqpi', Link.IFLA_BR_MCAST_QUERIER_INTVL),
+ ('bridge-mcqi', Link.IFLA_BR_MCAST_QUERY_INTVL),
+ ('bridge-mcqri', Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL),
+ ('bridge-mcsqi', Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL),
+ # Link.IFLA_BR_NF_CALL_IPTABLES,
+ # Link.IFLA_BR_NF_CALL_IP6TABLES,
+ # Link.IFLA_BR_NF_CALL_ARPTABLES,
+ # Link.IFLA_BR_VLAN_DEFAULT_PVID,
+ # Link.IFLA_BR_PAD,
+ # (Link.IFLA_BR_VLAN_STATS_ENABLED, 'bridge-vlan-stats'), # already dealt with, in a separate loop
+ ('bridge-igmp-version', Link.IFLA_BR_MCAST_IGMP_VERSION, ),
+ ('bridge-mcstats', Link.IFLA_BR_MCAST_STATS_ENABLED),
+ ('bridge-mld-version', Link.IFLA_BR_MCAST_MLD_VERSION)
+ )
+ # 'bridge-vlan-stats & bridge-mcstat are commented out even though, today
+ # they are supported. It is done this way because this dictionary is used
+ # in a loop, but these attributes require additional work. Thus they are
+ # excluded from this loop without overhead.
+
+ # we are still using the old linkCache we need an easy way
+ # to use this cache with the new full-netlink approach
+ _ifla_br_attributes_old_cache_key_map = dict(
+ (
+ (Link.IFLA_BR_FORWARD_DELAY, 'fd'),
+ (Link.IFLA_BR_HELLO_TIME, 'hello'),
+ (Link.IFLA_BR_MAX_AGE, 'maxage'),
+ (Link.IFLA_BR_AGEING_TIME, 'ageing'),
+ (Link.IFLA_BR_STP_STATE, 'stp'),
+ (Link.IFLA_BR_PRIORITY, 'bridgeprio'),
+ (Link.IFLA_BR_VLAN_FILTERING, 'vlan_filtering'),
+ (Link.IFLA_BR_VLAN_PROTOCOL, 'vlan-protocol'),
+ (Link.IFLA_BR_MCAST_ROUTER, 'mcrouter'),
+ (Link.IFLA_BR_MCAST_SNOOPING, 'mcsnoop'),
+ (Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, 'mcqifaddr'),
+ (Link.IFLA_BR_MCAST_QUERIER, 'mcquerier'),
+ (Link.IFLA_BR_MCAST_HASH_ELASTICITY, 'hashel'),
+ (Link.IFLA_BR_MCAST_HASH_MAX, 'hashmax'),
+ (Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, 'mclmc'),
+ (Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, 'mcsqc'),
+ (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, 'mclmi'),
+ (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, 'mcmi'),
+ (Link.IFLA_BR_MCAST_QUERIER_INTVL, 'mcqpi'),
+ (Link.IFLA_BR_MCAST_QUERY_INTVL, 'mcqi'),
+ (Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, 'mcqri'),
+ (Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, 'mcsqi'),
+ (Link.IFLA_BR_VLAN_STATS_ENABLED, 'vlan-stats'),
+ (Link.IFLA_BR_MCAST_STATS_ENABLED, 'mcstats'),
+ (Link.IFLA_BR_MCAST_IGMP_VERSION, 'igmp-version'),
+ (Link.IFLA_BR_MCAST_MLD_VERSION, 'mld-version')
+ )
+ )
+
+ _ifla_br_attributes_translate_user_config_to_netlink_map = dict(
+ (
+ # Link.IFLA_BR_UNSPEC,
+ (Link.IFLA_BR_FORWARD_DELAY, lambda x: int(x) * 100),
+ (Link.IFLA_BR_HELLO_TIME, lambda x: int(x) * 100),
+ (Link.IFLA_BR_MAX_AGE, lambda x: int(x) * 100),
+ (Link.IFLA_BR_AGEING_TIME, lambda x: int(x) * 100),
+ # Link.IFLA_BR_STP_STATE, # STP is treated outside the loop
+ (Link.IFLA_BR_PRIORITY, int),
+ (Link.IFLA_BR_VLAN_FILTERING, utils.get_boolean_from_string),
+ (Link.IFLA_BR_VLAN_PROTOCOL, str),
+ # Link.IFLA_BR_GROUP_FWD_MASK,
+ # Link.IFLA_BR_ROOT_ID,
+ # Link.IFLA_BR_BRIDGE_ID,
+ # Link.IFLA_BR_ROOT_PORT,
+ # Link.IFLA_BR_ROOT_PATH_COST,
+ # Link.IFLA_BR_TOPOLOGY_CHANGE,
+ # Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
+ # Link.IFLA_BR_HELLO_TIMER,
+ # Link.IFLA_BR_TCN_TIMER,
+ # Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER,
+ # Link.IFLA_BR_GC_TIMER,
+ # Link.IFLA_BR_GROUP_ADDR,
+ # Link.IFLA_BR_FDB_FLUSH,
+ (Link.IFLA_BR_MCAST_ROUTER, utils.get_int_from_boolean_and_string),
+ (Link.IFLA_BR_MCAST_SNOOPING, utils.get_boolean_from_string),
+ (Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, utils.get_boolean_from_string),
+ (Link.IFLA_BR_MCAST_QUERIER, utils.get_boolean_from_string),
+ (Link.IFLA_BR_MCAST_HASH_ELASTICITY, int),
+ (Link.IFLA_BR_MCAST_HASH_MAX, int),
+ (Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, int),
+ (Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, int),
+ (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, lambda x: int(x) * 100),
+ (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, lambda x: int(x) * 100),
+ (Link.IFLA_BR_MCAST_QUERIER_INTVL, lambda x: int(x) * 100),
+ (Link.IFLA_BR_MCAST_QUERY_INTVL, lambda x: int(x) * 100),
+ (Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, lambda x: int(x) * 100),
+ (Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, lambda x: int(x) * 100),
+ # Link.IFLA_BR_NF_CALL_IPTABLES,
+ # Link.IFLA_BR_NF_CALL_IP6TABLES,
+ # Link.IFLA_BR_NF_CALL_ARPTABLES,
+ # Link.IFLA_BR_VLAN_DEFAULT_PVID,
+ # Link.IFLA_BR_PAD,
+ (Link.IFLA_BR_VLAN_STATS_ENABLED, utils.get_boolean_from_string),
+ (Link.IFLA_BR_MCAST_IGMP_VERSION, int),
+ (Link.IFLA_BR_MCAST_STATS_ENABLED, utils.get_boolean_from_string),
+ (Link.IFLA_BR_MCAST_MLD_VERSION, int)
+ )
+ )
+
+ _ifla_brport_attributes_map = (
+ # Link.IFLA_BRPORT_UNSPEC,
+ # Link.IFLA_BRPORT_STATE,
+ ('bridge-portprios', Link.IFLA_BRPORT_PRIORITY),
+ ('bridge-pathcosts', Link.IFLA_BRPORT_COST),
+ # Link.IFLA_BRPORT_MODE,
+ # Link.IFLA_BRPORT_GUARD,
+ # Link.IFLA_BRPORT_PROTECT,
+ ('bridge-portmcfl', Link.IFLA_BRPORT_FAST_LEAVE),
+ ('bridge-learning', Link.IFLA_BRPORT_LEARNING),
+ ('bridge-unicast-flood', Link.IFLA_BRPORT_UNICAST_FLOOD),
+ # Link.IFLA_BRPORT_PROXYARP,
+ # Link.IFLA_BRPORT_LEARNING_SYNC,
+ # Link.IFLA_BRPORT_PROXYARP_WIFI,
+ # Link.IFLA_BRPORT_ROOT_ID,
+ # Link.IFLA_BRPORT_BRIDGE_ID,
+ # Link.IFLA_BRPORT_DESIGNATED_PORT,
+ # Link.IFLA_BRPORT_DESIGNATED_COST,
+ # Link.IFLA_BRPORT_ID,
+ # Link.IFLA_BRPORT_NO,
+ # Link.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
+ # Link.IFLA_BRPORT_CONFIG_PENDING,
+ # Link.IFLA_BRPORT_MESSAGE_AGE_TIMER,
+ # Link.IFLA_BRPORT_FORWARD_DELAY_TIMER,
+ # Link.IFLA_BRPORT_HOLD_TIMER,
+ # Link.IFLA_BRPORT_FLUSH,
+ ('bridge-portmcrouter', Link.IFLA_BRPORT_MULTICAST_ROUTER),
+ # Link.IFLA_BRPORT_PAD,
+ ('bridge-multicast-flood', Link.IFLA_BRPORT_MCAST_FLOOD),
+ # Link.IFLA_BRPORT_MCAST_TO_UCAST,
+ # Link.IFLA_BRPORT_VLAN_TUNNEL,
+ # Link.IFLA_BRPORT_BCAST_FLOOD
+ ('bridge-l2protocol-tunnel', Link.IFLA_BRPORT_GROUP_FWD_MASK),
+ # Link.IFLA_BRPORT_PEER_LINK,
+ # Link.IFLA_BRPORT_DUAL_LINK,
+ ('bridge-arp-nd-suppress', Link.IFLA_BRPORT_ARP_SUPPRESS),
+ )
+
+ _ifla_brport_multicast_router_dict_to_int = {
+ 'disabled': 0,
+ '0': 0,
+ 'no': 0,
+ 'automatic': 1,
+ '1': 1,
+ 'yes': 1,
+ 'enabled': 2,
+ '2': 2,
+ }
+
+ # callable to translate <interface-yes-no-0-1-list> to netlink value
+ _ifla_brport_attributes_translate_user_config_to_netlink_map = dict(
+ (
+ (Link.IFLA_BRPORT_PRIORITY, int),
+ (Link.IFLA_BRPORT_COST, int),
+ (Link.IFLA_BRPORT_MULTICAST_ROUTER, lambda x: bridge._ifla_brport_multicast_router_dict_to_int.get(x, 0)),
+ (Link.IFLA_BRPORT_FAST_LEAVE, utils.get_boolean_from_string),
+ (Link.IFLA_BRPORT_LEARNING, utils.get_boolean_from_string),
+ (Link.IFLA_BRPORT_UNICAST_FLOOD, utils.get_boolean_from_string),
+ (Link.IFLA_BRPORT_MCAST_FLOOD, utils.get_boolean_from_string),
+ (Link.IFLA_BRPORT_GROUP_FWD_MASK, lambda x: x),
+ (Link.IFLA_BRPORT_ARP_SUPPRESS, utils.get_boolean_from_string)
+ )
+ )
def __init__(self, *args, **kargs):
moduleBase.__init__(self, *args, **kargs)
self.ipcmd = None
+ self.name = self.__class__.__name__
self.brctlcmd = None
self._running_vidinfo = {}
self._running_vidinfo_valid = False
self._resv_vlan_range = self._get_reserved_vlan_range()
- self.logger.debug('%s: using reserved vlan range %s'
- %(self.__class__.__name__, str(self._resv_vlan_range)))
+ self.logger.debug('%s: using reserved vlan range %s' % (self.__class__.__name__, str(self._resv_vlan_range)))
+
+ self.default_stp_on = utils.get_boolean_from_string(
+ policymanager.policymanager_api.get_attr_default(
+ module_name=self.__class__.__name__,
+ attr='bridge-stp'
+ )
+ )
+
+ self.default_vlan_stats = policymanager.policymanager_api.get_attr_default(
+ module_name=self.__class__.__name__,
+ attr='bridge-vlan-stats'
+ )
+
+ self.warn_on_untagged_bridge_absence = utils.get_boolean_from_string(
+ policymanager.policymanager_api.get_module_globals(
+ module_name=self.__class__.__name__,
+ attr='warn_on_untagged_bridge_absence'
+ )
+ )
+ self.logger.debug('bridge: init: warn_on_untagged_bridge_absence=%s'
+ % self.warn_on_untagged_bridge_absence)
+
+ self._vxlan_bridge_default_igmp_snooping = policymanager.policymanager_api.get_module_globals(
+ self.__class__.__name__,
+ 'vxlan_bridge_default_igmp_snooping'
+ )
+ self.logger.debug('bridge: init: vxlan_bridge_default_igmp_snooping=%s'
+ % self._vxlan_bridge_default_igmp_snooping)
+
+ self.arp_nd_suppress_only_on_vxlan = utils.get_boolean_from_string(
+ policymanager.policymanager_api.get_module_globals(
+ module_name=self.__class__.__name__,
+ attr='allow_arp_nd_suppress_only_on_vxlan'
+ )
+ )
+ self.logger.debug('bridge: init: arp_nd_suppress_only_on_vxlan=%s' % self.arp_nd_suppress_only_on_vxlan)
+
+ try:
+ self.bridge_allow_multiple_vlans = utils.get_boolean_from_string(
+ self.sysctl_get('net.bridge.bridge-allow-multiple-vlans')
+ )
+ except:
+ # Cumulus Linux specific variable. Failure probably means that
+ # ifupdown2 is running a a different system.
+ self.bridge_allow_multiple_vlans = True
+ self.logger.debug('bridge: init: multiple vlans allowed %s' % self.bridge_allow_multiple_vlans)
+
+ self.bridge_mac_iface_list = policymanager.policymanager_api.get_module_globals(self.__class__.__name__, 'bridge_mac_iface') or []
+ self.bridge_mac_iface = None, None # ifname, mac
+
+ self.bridge_set_static_mac_from_port = utils.get_boolean_from_string(
+ policymanager.policymanager_api.get_module_globals(
+ self.__class__.__name__, 'bridge_set_static_mac_from_port'
+ )
+ )
+
+ self.vxlan_bridge_igmp_snooping_enable_port_mcrouter = utils.get_boolean_from_string(
+ policymanager.policymanager_api.get_module_globals(
+ module_name=self.__class__.__name__,
+ attr="vxlan_bridge_igmp_snooping_enable_port_mcrouter"
+ ),
+ default=True
+ )
+
+ self.l2protocol_tunnel_callback = {
+ 'all': self._l2protocol_tunnel_set_all,
+ 'stp': self._l2protocol_tunnel_set_stp,
+ 'cdp': self._l2protocol_tunnel_set_cdp,
+ 'pvst': self._l2protocol_tunnel_set_pvst,
+ 'lldp': self._l2protocol_tunnel_set_lldp,
+ 'lacp': self._l2protocol_tunnel_set_lacp
+ }
+
+ self.query_check_l2protocol_tunnel_callback = {
+ 'all': self._query_check_l2protocol_tunnel_all,
+ 'stp': self._query_check_l2protocol_tunnel_stp,
+ 'cdp': self._query_check_l2protocol_tunnel_cdp,
+ 'pvst': self._query_check_l2protocol_tunnel_pvst,
+ 'lldp': self._query_check_l2protocol_tunnel_lldp,
+ 'lacp': self._query_check_l2protocol_tunnel_lacp
+ }
+
+ @staticmethod
+ def _l2protocol_tunnel_set_pvst(ifla_brport_group_mask, ifla_brport_group_maskhi):
+ if not ifla_brport_group_maskhi:
+ ifla_brport_group_maskhi = 0x1
+ else:
+ ifla_brport_group_maskhi |= 0x1
+ return ifla_brport_group_mask, ifla_brport_group_maskhi
+
+ @staticmethod
+ def _l2protocol_tunnel_set_cdp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+ if not ifla_brport_group_maskhi:
+ ifla_brport_group_maskhi = 0x2
+ else:
+ ifla_brport_group_maskhi |= 0x2
+ return ifla_brport_group_mask, ifla_brport_group_maskhi
+
+ @staticmethod
+ def _l2protocol_tunnel_set_stp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+ if not ifla_brport_group_mask:
+ ifla_brport_group_mask = 0x1
+ else:
+ ifla_brport_group_mask |= 0x1
+ return ifla_brport_group_mask, ifla_brport_group_maskhi
+
+ @staticmethod
+ def _l2protocol_tunnel_set_lacp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+ if not ifla_brport_group_mask:
+ ifla_brport_group_mask = 0x4
+ else:
+ ifla_brport_group_mask |= 0x4
+ return ifla_brport_group_mask, ifla_brport_group_maskhi
- def _is_bridge(self, ifaceobj):
- if ifaceobj.get_attr_value_first('bridge-ports'):
+ @staticmethod
+ def _l2protocol_tunnel_set_lldp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+ if not ifla_brport_group_mask:
+ ifla_brport_group_mask = 0x4000
+ else:
+ ifla_brport_group_mask |= 0x4000
+ return ifla_brport_group_mask, ifla_brport_group_maskhi
+
+ @staticmethod
+ def _l2protocol_tunnel_set_all(ifla_brport_group_mask, ifla_brport_group_maskhi):
+ # returns new values for ifla_brport_group_mask and ifla_brport_group_maskhi
+ return 0x1 | 0x4 | 0x4000, 0x1 | 0x2
+
+ @staticmethod
+ def _query_check_l2protocol_tunnel_stp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+ return ifla_brport_group_mask and ifla_brport_group_mask & 0x1
+
+ @staticmethod
+ def _query_check_l2protocol_tunnel_cdp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+ return ifla_brport_group_maskhi and ifla_brport_group_maskhi & 0x2
+
+ @staticmethod
+ def _query_check_l2protocol_tunnel_pvst(ifla_brport_group_mask, ifla_brport_group_maskhi):
+ return ifla_brport_group_maskhi and ifla_brport_group_maskhi & 0x1
+
+ @staticmethod
+ def _query_check_l2protocol_tunnel_lldp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+ return ifla_brport_group_mask and ifla_brport_group_mask & 0x4000
+
+ @staticmethod
+ def _query_check_l2protocol_tunnel_lacp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+ return ifla_brport_group_mask and ifla_brport_group_mask & 0x4
+
+ @staticmethod
+ def _query_check_l2protocol_tunnel_all(ifla_brport_group_mask, ifla_brport_group_maskhi):
+ return ifla_brport_group_mask == (0x1 | 0x4 | 0x4000) and ifla_brport_group_maskhi == (0x1 | 0x2)
+
+ def syntax_check(self, ifaceobj, ifaceobj_getfunc):
+ retval = self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc)
+ if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
+ if not self.check_bridge_port_vid_attrs(ifaceobj):
+ retval = False
+ c1 = self.syntax_check_vxlan_in_vlan_aware_br(ifaceobj, ifaceobj_getfunc)
+ c2 = self.syntax_check_bridge_allow_multiple_vlans(ifaceobj, ifaceobj_getfunc)
+ return retval and c1 #and c2
+
+ def syntax_check_bridge_allow_multiple_vlans(self, ifaceobj, ifaceobj_getfunc):
+ result = True
+ if not self.bridge_allow_multiple_vlans and ifaceobj.link_kind & ifaceLinkKind.BRIDGE and ifaceobj.lowerifaces:
+ vlan_id = None
+ for brport_name in ifaceobj.lowerifaces:
+ for obj in ifaceobj_getfunc(brport_name) or []:
+ if obj.link_kind & ifaceLinkKind.VLAN:
+ sub_intf_vlan_id = self._get_vlan_id(obj)
+ if vlan_id and vlan_id != sub_intf_vlan_id:
+ self.logger.error('%s: ignore %s: multiple vlans not allowed under bridge '
+ '(sysctl net.bridge.bridge-allow-multiple-vlans not set)'
+ % (ifaceobj.name, brport_name))
+ result = False
+ continue
+ vlan_id = sub_intf_vlan_id
+ return result
+
+ def check_bridge_port_vid_attrs(self, ifaceobj):
+ if (ifaceobj.get_attr_value('bridge-access') and
+ (self.get_ifaceobj_bridge_vids_value(ifaceobj) or
+ ifaceobj.get_attr_value('bridge-pvid'))):
+ self.logger.warn('%s: bridge-access given, bridge-vids and bridge-pvid '
+ 'will be ignored' % ifaceobj.name)
+ return False
+ return True
+
+ def check_bridge_vlan_aware_port(self, ifaceobj, ifaceobj_getfunc):
+ if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
+ ports = self._get_bridge_port_list(ifaceobj)
+ if not ports:
+ return True
+ result = True
+ for port_name in ports:
+ port_obj_l = ifaceobj_getfunc(port_name)
+ if port_obj_l and port_obj_l[0].link_kind & ifaceLinkKind.VLAN:
+ self.logger.error('%s: %s: vlan sub-interface is not '
+ 'supported in a vlan-aware bridge'
+ % (ifaceobj.name, port_name))
+ result = False
+ if (port_obj_l and
+ port_obj_l[0].get_attr_value('bridge-arp-nd-suppress') and
+ self.arp_nd_suppress_only_on_vxlan and
+ not port_obj_l[0].link_kind & ifaceLinkKind.VXLAN):
+ self.log_error('\'bridge-arp-nd-suppress\' is not '
+ 'supported on a non-vxlan port %s'
+ %port_obj_l[0].name)
+ result = False
+ return result
+ return True
+
+ def _error_vxlan_in_vlan_aware_br(self, ifaceobj, bridgename):
+ self.log_error('`bridge-access` attribute is mandatory when vxlan '
+ 'device (%s) is part of vlan aware bridge (%s)'
+ % (ifaceobj.name, bridgename), ifaceobj)
+
+ def syntax_check_vxlan_in_vlan_aware_br(self, ifaceobj, ifaceobj_getfunc):
+ if not ifaceobj_getfunc:
return True
- return False
+ if (ifaceobj.link_kind & ifaceLinkKind.VXLAN
+ and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
+ if ifaceobj.get_attr_value('bridge-access'):
+ return True
+ for iface in ifaceobj.upperifaces if ifaceobj.upperifaces else []:
+ ifaceobj_upper_list = ifaceobj_getfunc(iface)
+ if not ifaceobj_upper_list:
+ continue
+ ifaceobj_upper = ifaceobj_upper_list[0]
+ bridge_vids = self._get_bridge_vids(iface, ifaceobj_getfunc)
+ if ifaceobj_upper.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
+ vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
+ pvid = ifaceobj.get_attr_value_first('bridge-pvid')
+ if (not vids
+ or not pvid
+ or not self._compare_vids(bridge_vids,
+ vids,
+ pvid=pvid)):
+ self._error_vxlan_in_vlan_aware_br(ifaceobj,
+ ifaceobj_upper.name)
+ return False
+ return True
+
+ @staticmethod
+ def _is_bridge(ifaceobj):
+ return (ifaceobj.link_kind & ifaceLinkKind.BRIDGE or
+ ifaceobj.get_attr_value_first('bridge-ports') or
+ ifaceobj.get_attr_value_first('bridge-vlan-aware'))
+
+ def _get_ifaceobj_bridge_ports(self, ifaceobj):
+ bridge_ports = []
+
+ for brport in ifaceobj.get_attr_value('bridge-ports') or []:
+ if brport != 'none':
+ bridge_ports.extend(brport.split())
+
+ return ' '.join(bridge_ports)
def _is_bridge_port(self, ifaceobj):
if self.brctlcmd.is_bridge_port(ifaceobj.name):
return True
return False
+ def check_valid_bridge(self, ifaceobj, ifname):
+ if LinkUtils.link_exists_nodryrun(ifname) and not LinkUtils.is_bridge(ifname):
+ self.log_error('misconfiguration of bridge attribute(s) on existing non-bridge interface (%s)' % ifname, ifaceobj=ifaceobj)
+ return False
+ return True
+
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
- if not self._is_bridge(ifaceobj):
+ if not self._is_bridge(ifaceobj) or not self.check_valid_bridge(ifaceobj, ifaceobj.name):
return None
if ifaceobj.link_type != ifaceLinkType.LINK_NA:
ifaceobj.link_type = ifaceLinkType.LINK_MASTER
- ifaceobj.link_kind = ifaceLinkKind.BRIDGE
- return self.parse_port_list(ifaceobj.get_attr_value_first(
- 'bridge-ports'), ifacenames_all)
+ ifaceobj.link_kind |= ifaceLinkKind.BRIDGE
+ # for special vlan aware bridges, we need to add another bit
+ if utils.get_boolean_from_string(ifaceobj.get_attr_value_first('bridge-vlan-aware')):
+ ifaceobj.link_kind |= ifaceLinkKind.BRIDGE
+ ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE
+ ifaceobj.role |= ifaceRole.MASTER
+ ifaceobj.dependency_type = ifaceDependencyType.MASTER_SLAVE
+ return self.parse_port_list(ifaceobj.name,
+ self._get_ifaceobj_bridge_ports(ifaceobj),
+ ifacenames_all)
def get_dependent_ifacenames_running(self, ifaceobj):
self._init_command_handlers()
port_list = ifaceobj.lowerifaces
if port_list:
return port_list
- ports = ifaceobj.get_attr_value_first('bridge-ports')
+ ports = self._get_ifaceobj_bridge_ports(ifaceobj)
if ports:
- return self.parse_port_list(ports)
+ return self.parse_port_list(ifaceobj.name, ports)
else:
return None
+ def _get_bridge_port_list_user_ordered(self, ifaceobj):
+ # When enslaving bridge-ports we need to return the exact user
+ # configured bridge ports list (bridge will inherit the mac of the
+ # first device.
+ ports = self._get_ifaceobj_bridge_ports(ifaceobj)
+ return self.parse_port_list(ifaceobj.name, ports) if ports else None
+
def _process_bridge_waitport(self, ifaceobj, portlist):
waitport_value = ifaceobj.get_attr_value_first('bridge-waitport')
if not waitport_value: return
waitporttime = int(waitportvals[0])
except:
self.log_warn('%s: invalid waitport value \'%s\''
- %(ifaceobj.name, waitporttime))
+ %(ifaceobj.name, waitportvals[0]))
return
if waitporttime <= 0: return
try:
- waitportlist = self.parse_port_list(waitportvals[1])
+ waitportlist = self.parse_port_list(ifaceobj.name,
+ waitportvals[1])
except IndexError, e:
# ignore error and use all bridge ports
waitportlist = portlist
self.log_warn('%s: unable to process waitport: %s'
%(ifaceobj.name, str(e)))
- def _ports_enable_disable_ipv6(self, ports, enable='1'):
+ def _enable_disable_ipv6(self, port, enable='1'):
+ try:
+ self.write_file('/proc/sys/net/ipv6/conf/%s/disable_ipv6' % port, enable)
+ except Exception, e:
+ self.logger.info(str(e))
+
+ def handle_ipv6(self, ports, state, ifaceobj=None):
+ if (ifaceobj and
+ (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN) and
+ not ifaceobj.get_attr_value('address')):
+ self._enable_disable_ipv6(ifaceobj.name, state)
for p in ports:
- try:
- self.write_file('/proc/sys/net/ipv6/conf/%s' %p +
- '/disable_ipv6', enable)
- except Exception, e:
- self.logger.info(str(e))
- pass
+ self._enable_disable_ipv6(p, state)
- def _add_ports(self, ifaceobj):
+ def _pretty_print_add_ports_error(self, errstr, bridgeifaceobj, bridgeports):
+ """ pretty print bridge port add errors.
+ since the commands are batched and the kernel only returns error
+ codes, this function tries to interpret some error codes
+ and prints clearer errors """
+
+ if re.search('RTNETLINK answers: Invalid argument', errstr):
+ # Cumulus Linux specific error checks
+ try:
+ if self.sysctl_get('net.bridge.bridge-allow-multiple-vlans') == '0':
+ vlanid = None
+ for bport in bridgeports:
+ currvlanid = self._get_vlan_id_from_ifacename(bport)
+ if vlanid:
+ if currvlanid != vlanid:
+ self.log_error('%s: ' %bridgeifaceobj.name +
+ 'net.bridge.bridge-allow-multiple-vlans not set, multiple vlans not allowed', bridgeifaceobj)
+ break
+ if currvlanid:
+ vlanid = currvlanid
+ except Exception as e:
+ errstr += '\n%s' % str(e)
+ self.log_error(bridgeifaceobj.name + ': ' + errstr, bridgeifaceobj)
+
+ def _add_ports(self, ifaceobj, ifaceobj_getfunc):
bridgeports = self._get_bridge_port_list(ifaceobj)
runningbridgeports = []
- removedbridgeports = []
self.ipcmd.batch_start()
self._process_bridge_waitport(ifaceobj, bridgeports)
self.ipcmd.batch_start()
# Delete active ports not in the new port list
- if not self.PERFMODE:
+ if not ifupdownflags.flags.PERFMODE:
runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
if runningbridgeports:
for bport in runningbridgeports:
if not bridgeports or bport not in bridgeports:
self.ipcmd.link_set(bport, 'nomaster')
- removedbridgeports.append(bport)
+ # set admin DOWN on all removed ports
+ # that don't have config outside bridge
+ if not ifaceobj_getfunc(bport):
+ netlink.link_set_updown(bport, "down")
+ # enable ipv6 for ports that were removed
+ self.handle_ipv6([bport], '0')
else:
runningbridgeports = []
if not bridgeports:
self.ipcmd.batch_commit()
- return
+ return []
err = 0
- for bridgeport in Set(bridgeports).difference(Set(runningbridgeports)):
+ ports = 0
+ newbridgeports = Set(bridgeports).difference(Set(runningbridgeports))
+ newly_enslaved_ports = []
+
+ newbridgeports_ordered = []
+ for br_port in self._get_bridge_port_list_user_ordered(ifaceobj):
+ if br_port in newbridgeports:
+ newbridgeports_ordered.append(br_port)
+
+ for bridgeport in newbridgeports_ordered:
try:
- if not self.DRYRUN and not self.ipcmd.link_exists(bridgeport):
- self.log_warn('%s: bridge port %s does not exist'
- %(ifaceobj.name, bridgeport))
+ if (not ifupdownflags.flags.DRYRUN and
+ not self.ipcmd.link_exists(bridgeport)):
+ self.log_error('%s: bridge port %s does not exist'
+ %(ifaceobj.name, bridgeport), ifaceobj)
err += 1
continue
hwaddress = self.ipcmd.link_get_hwaddress(bridgeport)
%hwaddress)
continue
self.ipcmd.link_set(bridgeport, 'master', ifaceobj.name)
+ newly_enslaved_ports.append(bridgeport)
+ self.handle_ipv6([bridgeport], '1')
self.ipcmd.addr_flush(bridgeport)
+ ports += 1
+ if ports == 250:
+ ports = 0
+ self.ipcmd.batch_commit()
+ self.ipcmd.batch_start()
except Exception, e:
self.logger.error(str(e))
pass
try:
self.ipcmd.batch_commit()
except Exception, e:
- self.logger.error(str(e))
+ self._pretty_print_add_ports_error(str(e), ifaceobj,
+ bridgeports)
pass
- # enable ipv6 for ports that were removed
- self._ports_enable_disable_ipv6(removedbridgeports, '0')
if err:
self.log_error('bridge configuration failed (missing ports)')
+ return newly_enslaved_ports
def _process_bridge_maxwait(self, ifaceobj, portlist):
maxwait = ifaceobj.get_attr_value_first('bridge-maxwait')
example: ['1', '2-4', '6'] returns [1, 2, 3, 4, 6]
"""
result = []
- for part in rangelist:
- if '-' in part:
- a, b = part.split('-')
- a, b = int(a), int(b)
- result.extend(range(a, b + 1))
- else:
- a = int(part)
- result.append(a)
+ try:
+ for part in rangelist:
+ if '-' in part:
+ a, b = part.split('-')
+ a, b = int(a), int(b)
+ result.extend(range(a, b + 1))
+ else:
+ a = int(part)
+ result.append(a)
+ except:
+ self.logger.warn('unable to parse vids \'%s\''
+ %''.join(rangelist))
+ pass
return result
- def _diff_vids(self, vids1, vids2):
- vids_to_add = None
- vids_to_del = None
+ def _compress_into_ranges(self, vids_ints):
+ return ['%d' %start if start == end else '%d-%d' %(start, end)
+ for start, end in self._ints_to_ranges(vids_ints)]
- vids1_ints = self._ranges_to_ints(vids1)
- vids2_ints = self._ranges_to_ints(vids2)
- vids1_diff = Set(vids1_ints).difference(vids2_ints)
- vids2_diff = Set(vids2_ints).difference(vids1_ints)
- if vids1_diff:
- vids_to_add = ['%d' %start if start == end else '%d-%d' %(start, end)
- for start, end in self._ints_to_ranges(vids1_diff)]
- if vids2_diff:
- vids_to_del = ['%d' %start if start == end else '%d-%d' %(start, end)
- for start, end in self._ints_to_ranges(vids2_diff)]
- return (vids_to_del, vids_to_add)
-
- def _compare_vids(self, vids1, vids2):
+ def _diff_vids(self, vids1_ints, vids2_ints):
+ return Set(vids2_ints).difference(vids1_ints), Set(vids1_ints).difference(vids2_ints)
+
+ def _compare_vids(self, vids1, vids2, pvid=None):
""" Returns true if the vids are same else return false """
vids1_ints = self._ranges_to_ints(vids1)
vids2_ints = self._ranges_to_ints(vids2)
- if Set(vids1_ints).symmetric_difference(vids2_ints):
+ set_diff = Set(vids1_ints).symmetric_difference(vids2_ints)
+ if pvid and int(pvid) in set_diff:
+ set_diff.remove(int(pvid))
+ if set_diff:
return False
else:
return True
attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
if attrval:
running_mcqv4src = {}
- if not self.PERFMODE:
- running_mcqv4src = self.brctlcmd.get_mcqv4src(ifaceobj.name)
+ if not ifupdownflags.flags.PERFMODE:
+ running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(ifaceobj.name)
mcqs = {}
srclist = attrval.split()
for s in srclist:
k_to_del = Set(running_mcqv4src.keys()).difference(mcqs.keys())
for v in k_to_del:
- self.brctlcmd.del_mcqv4src(ifaceobj.name, v)
+ self.brctlcmd.bridge_del_mcqv4src(ifaceobj.name, v)
for v in mcqs.keys():
- self.brctlcmd.set_mcqv4src(ifaceobj.name, v, mcqs[v])
+ self.brctlcmd.bridge_set_mcqv4src(ifaceobj.name, v, mcqs[v])
+ elif not ifupdownflags.flags.PERFMODE:
+ running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(ifaceobj.name)
+ if running_mcqv4src:
+ for v in running_mcqv4src.keys():
+ self.brctlcmd.bridge_del_mcqv4src(ifaceobj.name, v)
def _get_running_vidinfo(self):
if self._running_vidinfo_valid:
return self._running_vidinfo
self._running_vidinfo = {}
- if not self.PERFMODE:
- self._running_vidinfo = self.ipcmd.bridge_port_vids_get_all()
+
+ # Removed check for PERFMODE. Need the get in all cases
+ # including reboot, so that we can configure the pvid correctly.
+ self._running_vidinfo = self.ipcmd.bridge_port_vids_get_all_json()
self._running_vidinfo_valid = True
return self._running_vidinfo
- def _flush_running_vidinfo(self):
- self._running_vidinfo = {}
- self._running_vidinfo_valid = False
-
def _set_bridge_vidinfo_compat(self, ifaceobj):
#
# Supports old style vlan vid info format
# for compatibility
#
+ bridge_port_pvids = ifaceobj.get_attr_value_first('bridge-port-pvids')
+ bridge_port_vids = ifaceobj.get_attr_value_first('bridge-port-vids')
+ if not bridge_port_pvids and not bridge_port_vids:
+ return
# Handle bridge vlan attrs
- running_vidinfo = self._get_running_vidinfo()
-
# Install pvids
- attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
- if attrval:
- portlist = self.parse_port_list(attrval)
+ if bridge_port_pvids:
+ portlist = self.parse_port_list(ifaceobj.name, bridge_port_pvids)
if not portlist:
self.log_warn('%s: could not parse \'%s %s\''
- %(ifaceobj.name, attrname, attrval))
+ %(ifaceobj.name, 'bridge-port-pvids',
+ bridge_port_pvids))
return
for p in portlist:
try:
(port, pvid) = p.split('=')
- running_pvid = running_vidinfo.get(port, {}).get('pvid')
+ pvid = int(pvid)
+ running_pvid = self._get_running_pvid(port)
if running_pvid:
if running_pvid == pvid:
continue
%(ifaceobj.name, p, str(e)))
# install port vids
- attrval = ifaceobj.get_attr_value_first('bridge-port-vids')
- if attrval:
- portlist = self.parse_port_list(attrval)
+ if bridge_port_vids:
+ portlist = self.parse_port_list(ifaceobj.name, bridge_port_vids)
if not portlist:
- self.log_warn('%s: could not parse \'%s %s\''
- %(ifaceobj.name, attrname, attrval))
+ self.log_warn('%s: could not parse \'%s %s\'' %(ifaceobj.name,
+ 'bridge-port-vids', bridge_port_vids))
return
for p in portlist:
try:
(port, val) = p.split('=')
vids = val.split(',')
- if running_vidinfo.get(port):
+ vids_int = self._ranges_to_ints(vids)
+ running_vids = self.ipcmd.bridge_vlan_get_vids(port)
+ if running_vids:
(vids_to_del, vids_to_add) = \
- self._diff_vids(vids,
- running_vidinfo.get(port).get('vlan'))
+ self._diff_vids(vids_int, running_vids)
if vids_to_del:
- self.ipcmd.bridge_port_vids_del(port, vids_to_del)
+ self.ipcmd.bridge_port_vids_del(port,
+ self._compress_into_ranges(vids_to_del))
if vids_to_add:
- self.ipcmd.bridge_port_vids_add(port, vids_to_add)
+ self.ipcmd.bridge_port_vids_add(port,
+ self._compress_into_ranges(vids_to_add))
else:
- self.ipcmd.bridge_port_vids_add(port, vids)
+ self.ipcmd.bridge_port_vids_add(port, vids_int)
except Exception, e:
self.log_warn('%s: failed to set vid `%s` (%s)'
%(ifaceobj.name, p, str(e)))
- # install vids
- # XXX: Commenting out this code for now because it was decided
- # that this is not needed
- #attrval = ifaceobj.get_attr_value_first('bridge-vids')
- #if attrval:
- # vids = re.split(r'[\s\t]\s*', attrval)
- # if running_vidinfo.get(ifaceobj.name):
- # (vids_to_del, vids_to_add) = \
- # self._diff_vids(vids,
- # running_vidinfo.get(ifaceobj.name).get('vlan'))
- # if vids_to_del:
- # self.ipcmd.bridge_vids_del(ifaceobj.name, vids_to_del)
- # if vids_to_add:
- # self.ipcmd.bridge_vids_add(ifaceobj.name, vids_to_add)
- # else:
- # self.ipcmd.bridge_vids_add(ifaceobj.name, vids)
- #else:
- # running_vids = running_vidinfo.get(ifaceobj.name)
- # if running_vids:
- # self.ipcmd.bridge_vids_del(ifaceobj.name, running_vids)
-
- def _apply_bridge_settings(self, ifaceobj):
+ def _is_running_stp_state_on(self, bridgename):
+ """ Returns True if running stp state is on, else False """
+
+ stp_state_file = '/sys/class/net/%s/bridge/stp_state' %bridgename
+ try:
+ running_stp_state = self.read_file_oneline(stp_state_file)
+ return running_stp_state and running_stp_state != '0'
+ except:
+ return False
+
+ def _is_config_stp_state_on(self, ifaceobj):
+ """ Returns true if user specified stp state is on, else False """
+
+ stp_attr = ifaceobj.get_attr_value_first('bridge-stp')
+ if not stp_attr:
+ return self.default_stp_on
+ return utils.get_boolean_from_string(stp_attr)
+
+ def get_bridge_mcsnoop_value(self, ifaceobj):
+ mcsnoop = ifaceobj.get_attr_value_first('bridge-mcsnoop')
+ if not mcsnoop and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN:
+ return self._vxlan_bridge_default_igmp_snooping
+ return mcsnoop
+
+ def fill_ifla_info_data_with_ifla_br_attribute(self,
+ ifla_info_data,
+ link_just_created,
+ ifname,
+ nl_attr,
+ attr_name,
+ user_config):
+ try:
+ translate_func = self._ifla_br_attributes_translate_user_config_to_netlink_map.get(nl_attr)
+
+ if not callable(translate_func):
+ return
+
+ if not user_config:
+ user_config = policymanager.policymanager_api.get_iface_default(
+ module_name=self.__class__.__name__,
+ ifname=ifname,
+ attr=attr_name
+ )
+
+ old_cache_key = self._ifla_br_attributes_old_cache_key_map.get(nl_attr)
+ if old_cache_key and not link_just_created:
+ cached_value = self.brctlcmd.link_cache_get([ifname, 'linkinfo', old_cache_key])
+ if not cached_value or cached_value == "None":
+ # the link already exists but we don't have any value
+ # cached for this attr, it probably means that the
+ # capability is not available on this system (i.e old kernel)
+ self.logger.debug('%s: ignoring %s %s: capability '
+ 'probably not supported on this system'
+ % (ifname, attr_name, user_config))
+ return
+ # we need to convert the cache value to "netlink" format
+ cached_value = translate_func(cached_value.lower())
+ else:
+ cached_value = None
+
+ if not user_config and not link_just_created and cached_value is not None:
+ # there is no user configuration for this attribute
+ # if the bridge existed before we need to check if
+ # this attribute needs to be reset to default value
+ default_value = self.get_attr_default_value(attr_name)
+
+ if default_value:
+ # the attribute has a default value, we need to convert it to
+ # netlink format to compare it with the cache value
+ default_value_nl = translate_func(default_value) # default_value.lower()
+
+ if default_value_nl != cached_value:
+ # the running value difers from the default value
+ # but the user didn't specify any config
+ # resetting attribute to default
+ ifla_info_data[nl_attr] = default_value_nl
+ self.logger.info('%s: reset %s to default: %s' % (ifname, attr_name, default_value))
+ elif user_config:
+ user_config_nl = translate_func(user_config) # user_config.lower()
+
+ if user_config_nl != cached_value:
+ ifla_info_data[nl_attr] = user_config_nl
+
+ if cached_value is not None:
+ self.logger.info('%s: set %s %s (cache %s)' % (ifname, attr_name, user_config, cached_value))
+ else:
+ self.logger.info('%s: set %s %s' % (ifname, attr_name, user_config))
+ except Exception as e:
+ self.logger.warning('%s: %s: %s' % (ifname, attr_name, str(e)))
+
+ def up_apply_bridge_settings(self, ifaceobj, link_just_created, bridge_vlan_aware):
+ ifla_info_data = dict()
+ ifname = ifaceobj.name
+
+ self.logger.info('%s: apply bridge settings' % ifname)
+
+ for attr_name, nl_attr in self._ifla_br_attributes_map:
+ self.fill_ifla_info_data_with_ifla_br_attribute(
+ ifla_info_data=ifla_info_data,
+ link_just_created=link_just_created,
+ ifname=ifname,
+ nl_attr=nl_attr,
+ attr_name=attr_name,
+ user_config=ifaceobj.get_attr_value_first(attr_name)
+ )
+
+ # bridge-mcsnoop
+ self.fill_ifla_info_data_with_ifla_br_attribute(
+ ifla_info_data=ifla_info_data,
+ link_just_created=link_just_created,
+ ifname=ifname,
+ nl_attr=Link.IFLA_BR_MCAST_SNOOPING,
+ attr_name='bridge-mcsnoop',
+ user_config=self.get_bridge_mcsnoop_value(ifaceobj)
+ )
+
+ # bridge-vlan-stats
+ if bridge_vlan_aware:
+ self.fill_ifla_info_data_with_ifla_br_attribute(
+ ifla_info_data=ifla_info_data,
+ link_just_created=link_just_created,
+ ifname=ifname,
+ nl_attr=Link.IFLA_BR_VLAN_STATS_ENABLED,
+ attr_name='bridge-vlan-stats',
+ user_config=ifaceobj.get_attr_value_first('bridge-vlan-stats') or self.default_vlan_stats
+ )
+
try:
- stp = ifaceobj.get_attr_value_first('bridge-stp')
- if stp:
- self.brctlcmd.set_stp(ifaceobj.name, stp)
+ if self._is_config_stp_state_on(ifaceobj):
+ if not self._is_running_stp_state_on(ifname):
+ ifla_info_data[Link.IFLA_BR_STP_STATE] = 1
+ self.logger.info('%s: stp state reset, reapplying port settings' % ifname)
+ ifaceobj.module_flags[ifaceobj.name] = \
+ ifaceobj.module_flags.setdefault(self.name, 0) | \
+ bridgeFlags.PORT_PROCESSED_OVERRIDE
else:
# If stp not specified and running stp state on, set it to off
- running_stp_state = self.read_file_oneline(
- '/sys/class/net/%s/bridge/stp_state' %ifaceobj.name)
- if running_stp_state and running_stp_state != '0':
- self.brctlcmd.set_stp(ifaceobj.name, 'no')
-
- if ifaceobj.get_attr_value_first('bridge-vlan-aware') == 'yes':
- self.write_file('/sys/class/net/%s/bridge/vlan_filtering'
- %ifaceobj.name, '1')
- # Use the brctlcmd bulk set method: first build a dictionary
- # and then call set
- bridgeattrs = { k:v for k,v in
- {'ageing' :
- ifaceobj.get_attr_value_first('bridge-ageing'),
- 'bridgeprio' :
- ifaceobj.get_attr_value_first(
- 'bridge-bridgeprio'),
- 'fd' :
- ifaceobj.get_attr_value_first('bridge-fd'),
- 'gcint' :
- ifaceobj.get_attr_value_first('bridge-gcint'),
- 'hello' :
- ifaceobj.get_attr_value_first('bridge-hello'),
- 'maxage' :
- ifaceobj.get_attr_value_first('bridge-maxage'),
- 'mclmc' :
- ifaceobj.get_attr_value_first('bridge-mclmc'),
- 'mcrouter' :
- ifaceobj.get_attr_value_first(
- 'bridge-mcrouter'),
- 'mcsnoop' :
- ifaceobj.get_attr_value_first('bridge-mcsnoop'),
- 'mcsqc' :
- ifaceobj.get_attr_value_first('bridge-mcsqc'),
- 'mcqifaddr' :
- ifaceobj.get_attr_value_first(
- 'bridge-mcqifaddr'),
- 'mcquerier' :
- ifaceobj.get_attr_value_first(
- 'bridge-mcquerier'),
- 'hashel' :
- ifaceobj.get_attr_value_first('bridge-hashel'),
- 'hashmax' :
- ifaceobj.get_attr_value_first('bridge-hashmax'),
- 'mclmi' :
- ifaceobj.get_attr_value_first('bridge-mclmi'),
- 'mcmi' :
- ifaceobj.get_attr_value_first('bridge-mcmi'),
- 'mcqpi' :
- ifaceobj.get_attr_value_first('bridge-mcqpi'),
- 'mcqi' :
- ifaceobj.get_attr_value_first('bridge-mcqi'),
- 'mcqri' :
- ifaceobj.get_attr_value_first('bridge-mcqri'),
- 'mcsqi' :
- ifaceobj.get_attr_value_first('bridge-mcsqi')
- }.items()
- if v }
- if bridgeattrs:
- self.brctlcmd.set_bridge_attrs(ifaceobj.name, bridgeattrs)
- portattrs = {}
- for attrname, dstattrname in {'bridge-pathcosts' : 'pathcost',
- 'bridge-portprios' : 'portprio',
- 'bridge-portmcrouter' : 'portmcrouter',
- 'bridge-portmcfl' : 'portmcfl'}.items():
- attrval = ifaceobj.get_attr_value_first(attrname)
- if not attrval:
- continue
- portlist = self.parse_port_list(attrval)
- if not portlist:
- self.log_warn('%s: could not parse \'%s %s\''
- %(ifaceobj.name, attrname, attrval))
- continue
- for p in portlist:
- try:
- (port, val) = p.split('=')
- if not portattrs.get(port):
- portattrs[port] = {}
- portattrs[port].update({dstattrname : val})
- except Exception, e:
- self.log_warn('%s: could not parse %s (%s)'
- %(ifaceobj.name, attrname, str(e)))
- for port, attrdict in portattrs.iteritems():
- try:
- self.brctlcmd.set_bridgeport_attrs(ifaceobj.name, port,
- attrdict)
- except Exception, e:
- self.log_warn('%s: %s', str(e))
- pass
- self._set_bridge_vidinfo_compat(ifaceobj)
- self._set_bridge_mcqv4src_compat(ifaceobj)
- self._process_bridge_maxwait(ifaceobj,
- self._get_bridge_port_list(ifaceobj))
- except Exception, e:
- self.log_warn(str(e))
+ if self._is_running_stp_state_on(ifname):
+ self.logger.info('%s: bridge-stp not specified but running: turning stp off')
+ ifla_info_data[Link.IFLA_BR_STP_STATE] = 0
+ except Exception as e:
+ self.logger.warning('%s: bridge stp: %s' % (ifname, str(e)))
+
+ if ifla_info_data:
+ netlink.link_add_set(ifname=ifname, kind='bridge', ifla_info_data=ifla_info_data, link_exists=True)
def _check_vids(self, ifaceobj, vids):
ret = True
for v in vids:
- if '-' in v:
- va, vb = v.split('-')
- va, vb = int(va), int(vb)
- if (self._handle_reserved_vlan(va, ifaceobj.name) or
- self._handle_reserved_vlan(vb, ifaceobj.name)):
- ret = False
- else:
- va = int(v)
- if self._handle_reserved_vlan(va, ifaceobj.name):
- ret = False
+ try:
+ if '-' in v:
+ va, vb = v.split('-')
+ va, vb = int(va), int(vb)
+ self._handle_reserved_vlan(va, ifaceobj.name, end=vb)
+ else:
+ va = int(v)
+ self._handle_reserved_vlan(va, ifaceobj.name)
+ except exceptions.ReservedVlanException as e:
+ raise e
+ except Exception:
+ self.logger.warn('%s: unable to parse vid \'%s\''
+ %(ifaceobj.name, v))
return ret
-
- def _apply_bridge_vids(self, bportifaceobj, vids, running_vids, isbridge):
+
+ def _get_running_pvid(self, ifacename):
+ pvid = 0
+
+ running_vidinfo = self._get_running_vidinfo()
+ for vinfo in running_vidinfo.get(ifacename, {}):
+ v = vinfo.get('vlan')
+ pvid = v if 'PVID' in vinfo.get('flags', []) else 0
+ if pvid:
+ return pvid
+ return pvid
+
+ def _get_running_vids_n_pvid_str(self, ifacename):
+ vids = []
+ pvid = None
+
+ (vids, pvid) = self.ipcmd.bridge_vlan_get_vids_n_pvid(ifacename)
+
+ if vids:
+ ret_vids = self._compress_into_ranges(vids)
+ else:
+ ret_vids = None
+
+ if pvid:
+ ret_pvid = '%s' %pvid
+ else:
+ ret_pvid = None
+ return (ret_vids, ret_pvid)
+
+ def _apply_bridge_vids_and_pvid(self, bportifaceobj, vids, pvid,
+ isbridge):
+ """ This method is a combination of methods _apply_bridge_vids and
+ _apply_bridge_port_pvids above. A combined function is
+ found necessary to do the deletes first and the adds later
+ because kernel does honor vid info flags during deletes.
+
+ """
+ if not isbridge and bportifaceobj.link_kind & ifaceLinkKind.VXLAN:
+ if not vids or not pvid or len(vids) > 1 or vids[0] != pvid:
+ self._error_vxlan_in_vlan_aware_br(bportifaceobj,
+ bportifaceobj.upperifaces[0])
+ return
+
+ vids_int = self._ranges_to_ints(vids)
+ try:
+ pvid_int = int(pvid) if pvid else 0
+ except Exception:
+ self.logger.warn('%s: unable to parse pvid \'%s\''
+ %(bportifaceobj.name, pvid))
+ pvid_int = 0
+ pass
+
+ vids_to_del = []
+ vids_to_add = vids_int
+ pvid_to_del = None
+ pvid_to_add = pvid_int
+
try:
if not self._check_vids(bportifaceobj, vids):
return
+
+ (running_vids, running_pvid) = self.ipcmd.bridge_vlan_get_vids_n_pvid(
+ bportifaceobj.name)
+
+ if not running_vids and not running_pvid:
+ # There cannot be a no running pvid.
+ # It might just not be in our cache:
+ # this can happen if at the time we were
+ # creating the bridge vlan cache, the port
+ # was not part of the bridge. And we need
+ # to make sure both vids and pvid is not in
+ # the cache, to declare that our cache may
+ # be stale.
+ running_pvid = 1
+ running_vids = [1]
+
if running_vids:
(vids_to_del, vids_to_add) = \
- self._diff_vids(vids, running_vids)
- if vids_to_del:
- self.ipcmd.bridge_vids_del(bportifaceobj.name,
- vids_to_del, isbridge)
- if vids_to_add:
- self.ipcmd.bridge_vids_add(bportifaceobj.name,
- vids_to_add, isbridge)
- else:
- self.ipcmd.bridge_vids_add(bportifaceobj.name, vids, isbridge)
+ self._diff_vids(vids_to_add, running_vids)
+
+ if running_pvid:
+ if running_pvid != pvid_int and running_pvid != 0:
+ pvid_to_del = running_pvid
+
+ if (pvid_to_del and (pvid_to_del in vids_int) and
+ (pvid_to_del not in vids_to_add)):
+ # kernel deletes dont take into account
+ # bridge vid flags and its possible that
+ # the pvid deletes we do end up deleting
+ # the vids. Be proactive and add the pvid
+ # to the vid add list if it is in the vids
+ # and not already part of vids_to_add.
+ # This helps with a small corner case:
+ # - running
+ # pvid 100
+ # vid 101 102
+ # - new change is going to move the state to
+ # pvid 101
+ # vid 100 102
+ vids_to_add.add(pvid_to_del)
+ except exceptions.ReservedVlanException as e:
+ raise e
except Exception, e:
- self.log_warn('%s: failed to set vid `%s` (%s)'
- %(bportifaceobj.name, str(vids), str(e)))
+ self.log_error('%s: failed to process vids/pvids'
+ %bportifaceobj.name + ' vids = %s' %str(vids) +
+ 'pvid = %s ' %pvid + '(%s)' %str(e),
+ bportifaceobj, raise_error=False)
+ try:
+ if vids_to_del:
+ if pvid_to_add in vids_to_del:
+ vids_to_del.remove(pvid_to_add)
+ self.ipcmd.bridge_vids_del(bportifaceobj.name,
+ self._compress_into_ranges(
+ vids_to_del), isbridge)
+ except Exception, e:
+ self.log_warn('%s: failed to del vid `%s` (%s)'
+ %(bportifaceobj.name, str(vids_to_del), str(e)))
- def _apply_bridge_port_pvids(self, bportifaceobj, pvid, running_pvid):
- # Install pvids
try:
- if running_pvid:
- if running_pvid != pvid:
- self.ipcmd.bridge_port_pvid_del(bportifaceobj.name,
- running_pvid)
- self.ipcmd.bridge_port_pvid_add(bportifaceobj.name, pvid)
- else:
- self.ipcmd.bridge_port_pvid_add(bportifaceobj.name, pvid)
+ if pvid_to_del:
+ self.ipcmd.bridge_port_pvid_del(bportifaceobj.name,
+ pvid_to_del)
+ except Exception, e:
+ self.log_warn('%s: failed to del pvid `%s` (%s)'
+ %(bportifaceobj.name, pvid_to_del, str(e)))
+
+ try:
+ if vids_to_add:
+ self.ipcmd.bridge_vids_add(bportifaceobj.name,
+ self._compress_into_ranges(
+ vids_to_add), isbridge)
+ except Exception, e:
+ self.log_error('%s: failed to set vid `%s` (%s)'
+ %(bportifaceobj.name, str(vids_to_add),
+ str(e)), bportifaceobj, raise_error=False)
+
+ try:
+ if pvid_to_add and pvid_to_add != running_pvid:
+ self.ipcmd.bridge_port_pvid_add(bportifaceobj.name,
+ pvid_to_add)
except Exception, e:
- self.log_warn('%s: failed to set pvid `%s` (%s)'
- %(bportifaceobj.name, pvid, str(e)))
+ self.log_error('%s: failed to set pvid `%s` (%s)'
+ %(bportifaceobj.name, pvid_to_add, str(e)),
+ bportifaceobj)
def _apply_bridge_vlan_aware_port_settings_all(self, bportifaceobj,
bridge_vids=None,
bridge_pvid=None):
- running_vidinfo = self._get_running_vidinfo()
vids = None
pvids = None
+ vids_final = []
+ pvid_final = None
bport_access = bportifaceobj.get_attr_value_first('bridge-access')
if bport_access:
vids = re.split(r'[\s\t]\s*', bport_access)
pvids = vids
+ allow_untagged = 'yes'
+ self.check_bridge_port_vid_attrs(bportifaceobj)
else:
- bport_vids = bportifaceobj.get_attr_value_first('bridge-vids')
+ allow_untagged = bportifaceobj.get_attr_value_first('bridge-allow-untagged') or 'yes'
+
+ bport_vids = self.get_ifaceobj_bridge_vids_value(bportifaceobj)
if bport_vids:
vids = re.split(r'[\s\t,]\s*', bport_vids)
if bport_pvids:
pvids = re.split(r'[\s\t]\s*', bport_pvids)
- if pvids:
- self._apply_bridge_port_pvids(bportifaceobj, pvids[0],
- running_vidinfo.get(bportifaceobj.name, {}).get('pvid'))
- elif bridge_pvid:
- self._apply_bridge_port_pvids(bportifaceobj,
- bridge_pvid, running_vidinfo.get(bportifaceobj.name,
- {}).get('pvid'))
- # XXX: default pvid is already one
- #else:
- # self._apply_bridge_port_pvids(bportifaceobj,
- # '1', running_vidinfo.get(bportifaceobj.name,
- # {}).get('pvid'))
-
if vids:
- self._apply_bridge_vids(bportifaceobj, vids,
- running_vidinfo.get(bportifaceobj.name,
- {}).get('vlan'), False)
+ vids_final = vids
elif bridge_vids:
- self._apply_bridge_vids(bportifaceobj,
- bridge_vids, running_vidinfo.get(
- bportifaceobj.name, {}).get('vlan'), False)
-
-
- def _apply_bridge_port_settings(self, bportifaceobj, bridgename=None,
- bridgeifaceobj=None):
- if not bridgename and bridgeifaceobj:
- bridgename = bridgeifaceobj.name
- # Set other stp and igmp attributes
- portattrs = {}
- for attrname, dstattrname in {
- 'bridge-pathcosts' : 'pathcost',
- 'bridge-portprios' : 'portprio',
- 'bridge-portmcrouter' : 'portmcrouter',
- 'bridge-portmcfl' : 'portmcfl'}.items():
- attrval = bportifaceobj.get_attr_value_first(attrname)
- if not attrval:
- # Check if bridge has that attribute
- #if bridgeifaceobj:
- # attrval = bridgeifaceobj.get_attr_value_first(attrname)
- # if not attrval:
- # continue
- #else:
- continue
- portattrs[dstattrname] = attrval
- try:
- self.brctlcmd.set_bridgeport_attrs(bridgename,
- bportifaceobj.name, portattrs)
- except Exception, e:
- self.log_warn(str(e))
+ vids_final = bridge_vids
- def _apply_bridge_port_settings_all(self, ifaceobj,
- ifaceobj_getfunc=None):
- err = False
- bridge_vlan_aware = ifaceobj.get_attr_value_first(
- 'bridge-vlan-aware')
- if bridge_vlan_aware and bridge_vlan_aware == 'yes':
- bridge_vlan_aware = True
+ if allow_untagged == 'yes':
+ if pvids:
+ pvid_final = pvids[0]
+ elif bridge_pvid:
+ pvid_final = bridge_pvid
+ else:
+ pvid_final = '1'
else:
- bridge_vlan_aware = False
+ pvid_final = None
+
+ self._apply_bridge_vids_and_pvid(bportifaceobj, vids_final,
+ pvid_final, False)
+
+ def _apply_bridge_port_settings_all(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware):
+ err = False
if (ifaceobj.get_attr_value_first('bridge-port-vids') and
ifaceobj.get_attr_value_first('bridge-port-pvids')):
self.logger.info('%s: applying bridge configuration '
%ifaceobj.name + 'specific to ports')
- bridge_vids = ifaceobj.get_attr_value_first('bridge-vids')
+ bridge_vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
if bridge_vids:
bridge_vids = re.split(r'[\s\t,]\s*', bridge_vids)
else:
else:
bridge_pvid = None
+ if (ifaceobj.module_flags.get(self.name, 0x0) &
+ bridgeFlags.PORT_PROCESSED_OVERRIDE):
+ port_processed_override = True
+ else:
+ port_processed_override = False
+
bridgeports = self._get_bridge_port_list(ifaceobj)
if not bridgeports:
self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
return
+ self.ipcmd.batch_start()
for bport in bridgeports:
# Use the brctlcmd bulk set method: first build a dictionary
# and then call set
continue
for bportifaceobj in bportifaceobjlist:
# Dont process bridge port if it already has been processed
- if bportifaceobj.priv_flags & self._BRIDGE_PORT_PROCESSED:
+ # and there is no override on port_processed
+ if (not port_processed_override and
+ (bportifaceobj.module_flags.get(self.name,0x0) &
+ bridgeFlags.PORT_PROCESSED)):
continue
try:
# Add attributes specific to the vlan aware bridge
if bridge_vlan_aware:
self._apply_bridge_vlan_aware_port_settings_all(
bportifaceobj, bridge_vids, bridge_pvid)
- self._apply_bridge_port_settings(bportifaceobj,
- bridgeifaceobj=ifaceobj)
+ elif self.warn_on_untagged_bridge_absence:
+ self._check_untagged_bridge(ifaceobj.name, bportifaceobj, ifaceobj_getfunc)
+ except exceptions.ReservedVlanException as e:
+ raise e
except Exception, e:
err = True
self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
pass
+ self.ipcmd.bridge_batch_commit()
if err:
raise Exception('%s: errors applying port settings' %ifaceobj.name)
- def _up(self, ifaceobj, ifaceobj_getfunc=None):
- # Check if bridge port
+ def _check_untagged_bridge(self, bridgename, bridgeportifaceobj, ifaceobj_getfunc):
+ if bridgeportifaceobj.link_kind & ifaceLinkKind.VLAN:
+ lower_ifaceobj_list = ifaceobj_getfunc(bridgeportifaceobj.lowerifaces[0])
+ if lower_ifaceobj_list and lower_ifaceobj_list[0] and \
+ not lower_ifaceobj_list[0].link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
+ self.logger.warn('%s: untagged bridge not found. Please configure a bridge with untagged bridge ports to avoid Spanning Tree Interoperability issue.' % bridgename)
+ self.warn_on_untagged_bridge_absence = False
+
+ def bridge_port_get_bridge_name(self, ifaceobj):
bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
- if bridgename:
- if self.ipcmd.bridge_is_vlan_aware(bridgename):
- bridge_vids = self._get_bridge_vids(bridgename,
- ifaceobj_getfunc)
- bridge_pvid = self._get_bridge_pvid(bridgename,
- ifaceobj_getfunc)
- self._apply_bridge_vlan_aware_port_settings_all(ifaceobj,
- bridge_vids,
- bridge_pvid)
- self._apply_bridge_port_settings(ifaceobj, bridgename=bridgename)
- ifaceobj.priv_flags |= self._BRIDGE_PORT_PROCESSED
- return
- if not self._is_bridge(ifaceobj):
- return
- err = False
- errstr = ''
- running_ports = ''
+ if not bridgename:
+ # bridge port is not enslaved to a bridge we need to find
+ # the bridge in it's upper ifaces then enslave it
+ for u in ifaceobj.upperifaces:
+ if self.ipcmd.is_bridge(u):
+ return True, u
+ return False, None
+ # return should_enslave port, bridgename
+ return False, bridgename
+
+ def up_bridge_port_vlan_aware_bridge(self, ifaceobj, ifaceobj_getfunc, bridge_name, should_enslave_port):
+ if should_enslave_port:
+ netlink.link_set_master(ifaceobj.name, bridge_name)
+ self.handle_ipv6([ifaceobj.name], '1')
+
+ bridge_vids = self._get_bridge_vids(bridge_name, ifaceobj_getfunc)
+ bridge_pvid = self._get_bridge_pvid(bridge_name, ifaceobj_getfunc)
try:
- if not self.PERFMODE:
- if not self.ipcmd.link_exists(ifaceobj.name):
- self.ipcmd.link_create(ifaceobj.name, 'bridge')
- else:
- self.ipcmd.link_create(ifaceobj.name, 'bridge')
- except Exception, e:
- raise Exception(str(e))
+ self._apply_bridge_vlan_aware_port_settings_all(ifaceobj, bridge_vids, bridge_pvid)
+ except Exception as e:
+ self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
+ return
+
+ def up_bridge_port(self, ifaceobj, ifaceobj_getfunc):
+ should_enslave_port, bridge_name = self.bridge_port_get_bridge_name(ifaceobj)
+
+ if not bridge_name:
+ # bridge doesn't exist
+ return
+
+ vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridge_name)
+ if vlan_aware_bridge:
+ self.up_bridge_port_vlan_aware_bridge(ifaceobj,
+ ifaceobj_getfunc,
+ bridge_name,
+ should_enslave_port)
+
+ bridge_ifaceobj = ifaceobj_getfunc(bridge_name)[0]
+
+ self.up_apply_brports_attributes(target_ports=[ifaceobj.name],
+ ifaceobj=bridge_ifaceobj,
+ ifaceobj_getfunc=ifaceobj_getfunc,
+ bridge_vlan_aware=vlan_aware_bridge)
+
+ ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED
+
+ def up_check_bridge_vlan_aware(self, ifaceobj, ifaceobj_getfunc, link_exists):
+ if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
+ if not self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc):
+ return False
+ if link_exists:
+ ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED_OVERRIDE
+ return True
+ return False
+
+ @staticmethod
+ def parse_interface_list_value(user_config):
+ config = dict()
+ for entry in user_config.split():
+ ifname, value = entry.split('=')
+ config[ifname] = value
+ return config
+
+ def sync_bridge_learning_to_vxlan_brport(self, bridge_name, bridge_vlan_aware, brport_ifaceobj,
+ brport_name, brport_ifla_info_slave_data, brport_learning):
+ """
+ brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN
+ and
+ brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT
+
+ Checks are not performed in this function and must be verified
+ before. This is done this way to avoid calling this method on
+ non vlan & bridge port interfaces thus wasting a bit less time
+ """
+
+ kind = None
+ ifla_info_data = {}
+
+ brport_vxlan_learning_config = brport_ifaceobj.get_attr_value_first('vxlan-learning')
+ # if the user explicitly defined vxlan-learning we need to honor his config
+ # and not sync vxlan-learning with bridge-learning
+
+ brport_vxlan_learning = self.ipcmd.get_vxlandev_learning(brport_name)
+
+ # if BRIDGE_LEARNING is in the desired configuration
+ # and differs from the running vxlan configuration
+ if brport_learning is not None and brport_learning != brport_vxlan_learning and not brport_vxlan_learning_config:
+ kind = 'vxlan'
+ ifla_info_data = {Link.IFLA_VXLAN_LEARNING: brport_learning}
+ self.logger.info('%s: %s: vxlan learning and bridge learning out of sync: set %s'
+ % (bridge_name, brport_name, brport_learning))
+
+ elif brport_learning is None and bridge_vlan_aware:
+ # is bridge-learning is not configured but the bridge is vlan-aware
+
+ running_value = self.ipcmd.get_brport_learning_bool(brport_name)
+ default_value = utils.get_boolean_from_string(self.get_mod_subattr('bridge-learning', 'default'))
+
+ if default_value != running_value:
+ brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING] = default_value
+
+ if not brport_vxlan_learning_config:
+ kind = 'vxlan'
+ ifla_info_data = {Link.IFLA_VXLAN_LEARNING: default_value}
+ self.logger.info('%s: %s: reset brport learning to %s and sync vxlan learning'
+ % (bridge_name, brport_name, default_value))
+
+ # if kind and ifla_info_data are set they will be added to the
+ # netlink request on the VXLAN brport, to sync IFLA_VXLAN_LEARNING
+ return kind, ifla_info_data
+
+ def check_vxlan_brport_arp_suppress(self, ifaceobj, bridge_vlan_aware, brport_ifaceobj, brport_name, user_config):
+ if user_config:
+ if self.arp_nd_suppress_only_on_vxlan and not brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
+ self.logger.warning('%s: %s: \'bridge-arp-nd-suppress\' '
+ 'is not supported on a non-vxlan port'
+ % (ifaceobj.name, brport_name))
+ raise Exception()
+ elif (bridge_vlan_aware and
+ (not self.arp_nd_suppress_only_on_vxlan or
+ (self.arp_nd_suppress_only_on_vxlan and
+ brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN))):
+ return self.get_mod_subattr('bridge-arp-nd-suppress', 'default')
+ return None
+
+ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware, target_ports=[], newly_enslaved_ports=[]):
+ ifname = ifaceobj.name
+
try:
- self._add_ports(ifaceobj)
- except Exception, e:
- err = True
- errstr = str(e)
- pass
+ brports_ifla_info_slave_data = dict()
+ brport_ifaceobj_dict = dict()
+
+ running_brports = self.brctlcmd.get_bridge_ports(ifname)
+
+ if target_ports:
+ new_targets = []
+ for brport_name in target_ports:
+ if brport_name not in running_brports:
+ self.logger.info('%s: not enslaved to bridge %s: ignored for now' % (brport_name, ifname))
+ else:
+ new_targets.append(brport_name)
+ running_brports = new_targets
+
+ self.logger.info('%s: applying bridge port configuration: %s' % (ifname, running_brports))
+
+ # If target_ports is specified we want to configure only this
+ # sub-list of port we need to check if these ports are already
+ # enslaved, if not they will be ignored.
+ # If target_ports is not populated we will apply the brport
+ # attributes on all running brport.
+
+ for port in running_brports:
+ brport_list = ifaceobj_getfunc(port)
+ if brport_list:
+ brport_ifaceobj_dict[port] = brport_list[0]
+ brports_ifla_info_slave_data[port] = dict()
+
+ bridge_ports_learning = {}
+
+ # we iterate through all IFLA_BRPORT supported attributes
+ for attr_name, nl_attr in self._ifla_brport_attributes_map:
+ br_config = ifaceobj.get_attr_value_first(attr_name)
+ translate_func = self._ifla_brport_attributes_translate_user_config_to_netlink_map.get(nl_attr)
+
+ if not translate_func:
+ # if no translation function is found,
+ # we ignore this attribute and continue
+ continue
+
+ if not br_config:
+ # user didn't specify any value for this attribute
+ # looking at policy overrides
+ br_config = policymanager.policymanager_api.get_iface_default(
+ module_name=self.__class__.__name__,
+ ifname=ifname,
+ attr=attr_name
+ )
+
+ if br_config:
+ #if bridge_vlan_aware:
+ # self.logger.info('%s: is a vlan-aware bridge, "%s %s" '
+ # 'should be configured under the ports'
+ # % (ifname, attr_name, br_config))
+
+ # convert the <interface-yes-no-0-1-list> and <interface-range-list> value to subdict
+ # brport_name: { attr: value }
+ # example:
+ # bridge-portprios swp1=5 swp2=32
+ # swp1: { bridge-portprios: 5 } swp2: { bridge-portprios: 32}
+ if '=' in br_config:
+ try:
+ br_config = self.parse_interface_list_value(br_config)
+ except:
+ self.log_error('error while parsing \'%s %s\'' % (attr_name, br_config))
+ continue
+
+ for brport_ifaceobj in brport_ifaceobj_dict.values():
+ brport_config = brport_ifaceobj.get_attr_value_first(attr_name)
+ brport_name = brport_ifaceobj.name
+
+ if not ifupdownflags.flags.PERFMODE and brport_name not in newly_enslaved_ports:
+ # if the port has just been enslaved, info_slave_data is not cached yet
+ cached_value = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', nl_attr])
+ else:
+ cached_value = None
+
+ if not brport_config:
+ # if a brport attribute was specified under the bridge and not under the port
+ # we assign the bridge value to the port. If an attribute is both defined under
+ # the bridge and the brport we keep the value of the port and ignore the br val.
+ if type(br_config) == dict:
+ # if the attribute value was in the format interface-list-value swp1=XX swp2=YY
+ # br_config is a dictionary, example:
+ # bridge-portprios swp1=5 swp2=32 = {swp1: 5, swp2: 32}
+ brport_config = br_config.get(brport_name)
+ else:
+ brport_config = br_config
+
+ if not brport_config:
+ brport_config = policymanager.policymanager_api.get_iface_default(
+ module_name=self.__class__.__name__,
+ ifname=brport_name,
+ attr=attr_name
+ )
+
+ user_config = brport_config
+
+ # attribute specific work
+ # This shouldn't be here but we don't really have a choice otherwise this
+ # will require too much code duplication and will make the code very complex
+ if nl_attr == Link.IFLA_BRPORT_ARP_SUPPRESS:
+ try:
+ arp_suppress = self.check_vxlan_brport_arp_suppress(ifaceobj,
+ bridge_vlan_aware,
+ brport_ifaceobj,
+ brport_name,
+ user_config)
+ if arp_suppress:
+ user_config = arp_suppress
+ except:
+ continue
+ elif nl_attr == Link.IFLA_BRPORT_GROUP_FWD_MASK:
+ # special handking for group_fwd_mask because Cisco proprietary
+ # protocol needs to be set via a private netlink attribute
+ self.ifla_brport_group_fwd_mask(ifname, brport_name,
+ brports_ifla_info_slave_data,
+ user_config, cached_value)
+ continue
+
+ #if brport_config:
+ # if not bridge_vlan_aware:
+ # self.logger.info('%s: %s: is not a vlan-aware bridge, "%s %s" '
+ # 'should be configured under the bridge'
+ # % (ifname, brport_name,
+ # attr_name, brport_config))
+
+ if user_config:
+ user_config_nl = translate_func(user_config)
+ # check config value against running value
+ if user_config_nl != cached_value:
+ brports_ifla_info_slave_data[brport_name][nl_attr] = user_config_nl
+ self.logger.info('%s: %s: set %s %s' % (ifname, brport_name, attr_name, user_config))
+ self.logger.debug('(cache %s)' % cached_value)
+
+ if nl_attr == Link.IFLA_BRPORT_LEARNING:
+ # for vxlan-learning sync purposes we need to save the user config for each brports.
+ # The dictionary 'brports_ifla_info_slave_data' might not contain any value for
+ # IFLA_BRPORT_LEARNING if the user value is already configured and running
+ # nevertheless we still need to check if the vxlan-learning is rightly synced with
+ # the brport since it might go out of sync for X and Y reasons.
+ bridge_ports_learning[brport_name] = user_config_nl
+
+ elif cached_value is not None:
+ # no config found, do we need to reset to default?
+ default = self.get_attr_default_value(attr_name)
+ if default:
+ default_netlink = translate_func(default)
+
+ if (nl_attr == Link.IFLA_BRPORT_LEARNING
+ and not ifupdownflags.flags.PERFMODE
+ and brport_name not in newly_enslaved_ports):
+ try:
+ if self.ipcmd.get_brport_peer_link(brport_name):
+ if default_netlink != cached_value:
+ self.logger.debug('%s: %s: bridge port peerlink: ignoring bridge-learning'
+ % (ifname, brport_name))
+ continue
+ bridge_ports_learning[brport_name] = default_netlink
+ except Exception as e:
+ self.logger.debug('%s: %s: peerlink check: %s' % (ifname, brport_name, str(e)))
+
+ if default_netlink != cached_value:
+ self.logger.info('%s: %s: %s: no configuration detected, resetting to default %s'
+ % (ifname, brport_name, attr_name, default))
+ self.logger.debug('(cache %s)' % cached_value)
+ brports_ifla_info_slave_data[brport_name][nl_attr] = default_netlink
+
+ # applying bridge port configuration via netlink
+ for brport_name, brport_ifla_info_slave_data in brports_ifla_info_slave_data.items():
+
+ brport_ifaceobj = brport_ifaceobj_dict.get(brport_name)
+ if (brport_ifaceobj
+ and brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN
+ and brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
+ # if the brport is a VXLAN, we might need to sync the VXLAN learning with the brport_learning val
+ # we use the same netlink request, by specfying kind=vxlan and ifla_info_data={vxlan_learning=0/1}
+ kind, ifla_info_data = self.sync_bridge_learning_to_vxlan_brport(ifaceobj.name,
+ bridge_vlan_aware,
+ brport_ifaceobj,
+ brport_name,
+ brport_ifla_info_slave_data,
+ bridge_ports_learning.get(brport_name))
+
+ cached_bridge_mcsnoop = self.brctlcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BR_MCAST_SNOOPING])
+
+ if (self.vxlan_bridge_igmp_snooping_enable_port_mcrouter and utils.get_boolean_from_string(
+ self.get_bridge_mcsnoop_value(ifaceobj)
+ )) or cached_bridge_mcsnoop:
+ # if policy "vxlan_bridge_igmp_snooping_enable_port_mcrouter"
+ # is on and mcsnoop is on (or mcsnoop is already enabled on the
+ # bridge, set 'bridge-portmcrouter 2' on vxlan ports (if not set by the user)
+ if not brport_ifla_info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER):
+ brport_ifla_info_slave_data[Link.IFLA_BRPORT_MULTICAST_ROUTER] = 2
+ self.logger.info("%s: %s: vxlan bridge igmp snooping: enable port multicast router" % (ifname, brport_name))
+ else:
+ kind = None
+ ifla_info_data = {}
+
+ if brport_ifla_info_slave_data or ifla_info_data:
+ try:
+ netlink.link_add_set(ifname=brport_name,
+ kind=kind,
+ ifla_info_data=ifla_info_data,
+ slave_kind='bridge',
+ ifla_info_slave_data=brport_ifla_info_slave_data)
+ except Exception as e:
+ self.logger.warning('%s: %s: %s' % (ifname, brport_name, str(e)))
+
+ self._set_bridge_vidinfo_compat(ifaceobj)
+ self._set_bridge_mcqv4src_compat(ifaceobj)
+ self._process_bridge_maxwait(ifaceobj, self._get_bridge_port_list(ifaceobj))
+
+ except Exception as e:
+ self.log_error(str(e), ifaceobj)
+
+ def ifla_brport_group_fwd_mask(self, ifname, brport_name, brports_ifla_info_slave_data, user_config, cached_ifla_brport_group_fwd_mask):
+ """
+ Support for IFLA_BRPORT_GROUP_FWD_MASK and IFLA_BRPORT_GROUP_FWD_MASKHI
+ Since this is the only ifupdown2 attribute dealing with more than 1 netlink
+ field we need to have special handling for that.
+ """
+ ifla_brport_group_fwd_mask = 0
+ ifla_brport_group_fwd_maskhi = 0
+ if user_config:
+ for group in re.split(',|\s*', user_config):
+ if not group:
+ continue
+
+ callback = self.l2protocol_tunnel_callback.get(group)
+
+ if not callable(callback):
+ self.logger.warning('%s: %s: bridge-l2protocol-tunnel ignoring invalid parameter \'%s\'' % (ifname, brport_name, group))
+ else:
+ ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi = callback(ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi)
+
+ # cached_ifla_brport_group_fwd_mask is given as parameter because it was already pulled out from the cache in the functio above
+ cached_ifla_brport_group_fwd_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI])
+
+ log_mask_change = True
+ # if user specify bridge-l2protocol-tunnel stp cdp
+ # we need to set both MASK and MASKHI but we only want to log once
+
+ if cached_ifla_brport_group_fwd_mask is None:
+ cached_ifla_brport_group_fwd_mask = 0
+ if cached_ifla_brport_group_fwd_maskhi is None:
+ cached_ifla_brport_group_fwd_maskhi = 0
+
+ # if the cache value is None it means that the kernel doesn't support this attribute
+ # or that the cache is stale, we dumped this intf before it was enslaved in the bridge
+
+ if ifla_brport_group_fwd_mask != cached_ifla_brport_group_fwd_mask:
+ if log_mask_change:
+ self.logger.info('%s: %s: set bridge-l2protocol-tunnel %s' % (ifname, brport_name, user_config))
+ self.logger.debug('(cache %s)' % cached_ifla_brport_group_fwd_mask)
+ log_mask_change = False
+ brports_ifla_info_slave_data[brport_name][Link.IFLA_BRPORT_GROUP_FWD_MASK] = ifla_brport_group_fwd_mask
+
+ if ifla_brport_group_fwd_maskhi != cached_ifla_brport_group_fwd_maskhi:
+ if log_mask_change:
+ self.logger.info('%s: %s: set bridge-l2protocol-tunnel %s' % (ifname, brport_name, user_config))
+ self.logger.debug('(cache %s)' % cached_ifla_brport_group_fwd_maskhi)
+ brports_ifla_info_slave_data[brport_name][Link.IFLA_BRPORT_GROUP_FWD_MASKHI] = ifla_brport_group_fwd_maskhi
+
+ def up_bridge(self, ifaceobj, ifaceobj_getfunc):
+ ifname = ifaceobj.name
+
+ if ifupdownflags.flags.PERFMODE:
+ link_just_created = True
+ link_exists = False
+ else:
+ link_exists = self.ipcmd.link_exists(ifaceobj.name)
+ link_just_created = not link_exists
+
+ if not link_exists:
+ netlink.link_add_bridge(ifname)
+ else:
+ self.logger.info('%s: bridge already exists' % ifname)
+
+ bridge_vlan_aware = self.up_check_bridge_vlan_aware(ifaceobj, ifaceobj_getfunc, not link_just_created)
+
+ self.up_apply_bridge_settings(ifaceobj, link_just_created, bridge_vlan_aware)
try:
- self._apply_bridge_settings(ifaceobj)
- except Exception, e:
- err = True
- errstr = str(e)
- pass
+ newly_enslaved_ports = self._add_ports(ifaceobj, ifaceobj_getfunc)
+ self.up_apply_brports_attributes(ifaceobj, ifaceobj_getfunc, bridge_vlan_aware,
+ newly_enslaved_ports=newly_enslaved_ports)
+ except Exception as e:
+ self.logger.warning('%s: apply bridge ports settings: %s' % (ifname, str(e)))
+ running_ports = ''
try:
running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
if not running_ports:
- return
- # disable ipv6 for ports that were added to bridge
- self._ports_enable_disable_ipv6(running_ports, '1')
+ return
+ self.handle_ipv6([], '1', ifaceobj=ifaceobj)
self._apply_bridge_port_settings_all(ifaceobj,
- ifaceobj_getfunc=ifaceobj_getfunc)
- except Exception, e:
- err = True
- errstr = str(e)
- pass
- #self._flush_running_vidinfo()
+ ifaceobj_getfunc=ifaceobj_getfunc,
+ bridge_vlan_aware=bridge_vlan_aware)
+ except exceptions.ReservedVlanException as e:
+ raise e
+ except Exception as e:
+ self.logger.warning('%s: apply bridge settings: %s' % (ifname, str(e)))
finally:
if ifaceobj.link_type != ifaceLinkType.LINK_NA:
for p in running_ports:
+ ifaceobj_list = ifaceobj_getfunc(p)
+ if (ifaceobj_list and ifaceobj_list[0].link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN):
+ netlink.link_set_updown(p, "down")
+ continue
try:
- rtnetlink_api.rtnl_api.link_set(p, "up")
+ netlink.link_set_updown(p, "up")
except Exception, e:
self.logger.debug('%s: %s: link set up (%s)'
- %(ifaceobj.name, p, str(e)))
+ % (ifaceobj.name, p, str(e)))
pass
- if ifaceobj.addr_method == 'manual':
- rtnetlink_api.rtnl_api.link_set(ifaceobj.name, "up")
- if err:
- raise Exception(errstr)
+ try:
+ self._up_bridge_mac(ifaceobj, ifaceobj_getfunc)
+ except Exception as e:
+ self.logger.warning('%s: setting bridge mac address: %s' % (ifaceobj.name, str(e)))
+
+ def _get_bridge_mac(self, ifaceobj, ifname, ifaceobj_getfunc):
+ if self.bridge_mac_iface and self.bridge_mac_iface[0] and self.bridge_mac_iface[1]:
+ return self.bridge_mac_iface
+
+ if self.bridge_mac_iface_list:
+ self.logger.debug('bridge mac iface list: %s' % self.bridge_mac_iface_list)
+
+ for bridge_mac_intf in self.bridge_mac_iface_list:
+ ifaceobj_list = ifaceobj_getfunc(bridge_mac_intf)
+ iface_mac = None
+
+ if ifaceobj_list:
+ for obj in ifaceobj_list:
+ iface_user_configured_hwaddress = utils.strip_hwaddress(obj.get_attr_value_first('hwaddress'))
+ # if user did configured 'hwaddress' we need to use this value instead of the cached value.
+ if iface_user_configured_hwaddress:
+ iface_mac = iface_user_configured_hwaddress
+
+ if not iface_mac and not self.ipcmd.link_exists(bridge_mac_intf):
+ continue
+
+ if not iface_mac:
+ iface_mac = self.ipcmd.cache_get('link', [bridge_mac_intf, 'hwaddress'])
+ # if hwaddress attribute is not configured we use the running mac addr
+
+ self.bridge_mac_iface = (bridge_mac_intf, iface_mac)
+ return self.bridge_mac_iface
+ elif self.bridge_set_static_mac_from_port:
+ # no policy was provided, we need to get the first physdev or bond ports
+ # and use its hwaddress to set the bridge mac
+ for port in self._get_bridge_port_list_user_ordered(ifaceobj) or []:
+ # iterate through the bridge-port list
+ for port_obj in ifaceobj_getfunc(port) or []:
+ # check if the port is a physdev (link_kind is null) or a bon
+ if port_obj.link_kind != ifaceLinkKind.VXLAN:
+ iface_user_configured_hwaddress = utils.strip_hwaddress(port_obj.get_attr_value_first('hwaddress'))
+ # if user did configured 'hwaddress' we need to use this value instead of the cached value.
+ if iface_user_configured_hwaddress:
+ iface_mac = iface_user_configured_hwaddress.lower()
+ # we need to "normalize" the user provided MAC so it can match with
+ # what we have in the cache (data retrieved via a netlink dump by
+ # nlmanager). nlmanager return all macs in lower-case
+ else:
+ iface_mac = self.ipcmd.link_get_hwaddress(port)
+
+ if iface_mac:
+ self.bridge_mac_iface = (port, iface_mac)
+ return self.bridge_mac_iface
+
+ return None, None
+
+ def _add_bridge_mac_to_fdb(self, ifaceobj, bridge_mac):
+ if not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE and bridge_mac and ifaceobj.get_attr_value('address'):
+ self.ipcmd.bridge_fdb_add(ifaceobj.name, bridge_mac, vlan=None, bridge=True, remote=None)
+
+ def _up_bridge_mac(self, ifaceobj, ifaceobj_getfunc):
+ """
+ We have a day one bridge mac changing problem with changing ports
+ (basically bridge mac changes when the port it inherited the mac from
+ gets de-enslaved).
+
+ We have discussed this problem many times before and tabled it.
+ The issue has aggravated with vxlan bridge ports having auto-generated
+ random macs...which change on every reboot.
+
+ ifupdown2 extract from policy files an iface to select a mac from and
+ configure it automatically.
+ """
+ if ifaceobj.get_attr_value('hwaddress'):
+ # if the user configured a static hwaddress
+ # there is no need to assign one
+ return
+
+ ifname = ifaceobj.name
+ mac_intf, bridge_mac = self._get_bridge_mac(ifaceobj, ifname, ifaceobj_getfunc)
+ self.logger.debug("%s: _get_bridge_mac returned (%s, %s)"
+ %(ifname, mac_intf, bridge_mac))
+
+ if bridge_mac:
+ # if an interface is configured with the following attribute:
+ # hwaddress 08:00:27:42:42:4
+ # the cache_check won't match because nlmanager return "08:00:27:42:42:04"
+ # from the kernel. The only way to counter that is to convert all mac to int
+ # and compare the ints, it will increase perfs and be safer.
+ cached_value = self.ipcmd.cache_get('link', [ifname, 'hwaddress'])
+ self.logger.debug('%s: cached hwaddress value: %s' % (ifname, cached_value))
+ if cached_value and cached_value == bridge_mac:
+ # the bridge mac is already set to the bridge_mac_intf's mac
+ return
+
+ self.logger.info('%s: setting bridge mac to port %s mac' % (ifname, mac_intf))
+ try:
+ self.ipcmd.link_set(ifname, 'address', value=bridge_mac, force=True)
+ except Exception as e:
+ self.logger.info('%s: %s' % (ifname, str(e)))
+ # log info this error because the user didn't explicitly configured this
+ else:
+ self._add_bridge_mac_to_fdb(ifaceobj, self.ipcmd.link_get_hwaddress(ifname))
+
+ def _up(self, ifaceobj, ifaceobj_getfunc=None):
+ if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
+ self.up_bridge_port(ifaceobj, ifaceobj_getfunc)
+
+ elif ifaceobj.link_kind & ifaceLinkKind.BRIDGE:
+ self.up_bridge(ifaceobj, ifaceobj_getfunc)
+
+ else:
+ bridge_attributes = self._modinfo.get('attrs', {}).keys()
+
+ for ifaceobj_config_attr in ifaceobj.config.keys():
+ if ifaceobj_config_attr in bridge_attributes:
+ self.logger.warning('%s: invalid use of bridge attribute (%s) on non-bridge stanza'
+ % (ifaceobj.name, ifaceobj_config_attr))
def _down(self, ifaceobj, ifaceobj_getfunc=None):
+ if not self._is_bridge(ifaceobj):
+ return
+ ifname = ifaceobj.name
+ if not self.ipcmd.link_exists(ifname):
+ return
try:
- if ifaceobj.get_attr_value_first('bridge-ports'):
- ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
- self.brctlcmd.delete_bridge(ifaceobj.name)
- if ports:
- self._ports_enable_disable_ipv6(ports, '0')
- if ifaceobj.link_type != ifaceLinkType.LINK_NA:
- map(lambda p: rtnetlink_api.rtnl_api.link_set(p,
- "down"), ports)
- except Exception, e:
- self.log_error(str(e))
+ running_ports = self.brctlcmd.get_bridge_ports(ifname)
+ if running_ports:
+ self.handle_ipv6(running_ports, '0')
+ if ifaceobj.link_type != ifaceLinkType.LINK_NA:
+ map(lambda p: netlink.link_set_updown(p, 'down'), running_ports)
+ except Exception as e:
+ self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
+ try:
+ netlink.link_del(ifname)
+ except Exception as e:
+ ifaceobj.set_status(ifaceStatus.ERROR)
+ self.logger.error(str(e))
+ # netlink exception already contains the ifname
def _query_running_vidinfo_compat(self, ifaceobjrunning, ports):
running_attrs = {}
- running_vidinfo = self._get_running_vidinfo()
if ports:
running_bridge_port_vids = ''
for p in ports:
try:
- running_vids = running_vidinfo.get(p, {}).get('vlan')
+ running_vids = self._get_runing_vids(p)
if running_vids:
running_bridge_port_vids += ' %s=%s' %(p,
','.join(running_vids))
pass
running_attrs['bridge-port-vids'] = running_bridge_port_vids
- running_bridge_port_pvids = ''
+ running_bridge_port_pvid = ''
for p in ports:
try:
- running_pvids = running_vidinfo.get(p, {}).get('pvid')
- if running_pvids:
- running_bridge_port_pvids += ' %s=%s' %(p,
- running_pvids)
+ running_pvid = self._get_runing_pvid(p)
+ if running_pvid:
+ running_bridge_port_pvid += ' %s=%s' %(p,
+ running_pvid)
except Exception:
pass
- running_attrs['bridge-port-pvids'] = running_bridge_port_pvids
+ running_attrs['bridge-port-pvids'] = running_bridge_port_pvid
- running_bridge_vids = running_vidinfo.get(ifaceobjrunning.name,
- {}).get('vlan')
+ running_bridge_vids = self.ipcmd.bridge_vlan_get_vids(ifaceobjrunning.name)
if running_bridge_vids:
- running_attrs['bridge-vids'] = ','.join(running_bridge_vids)
+ running_attrs['bridge-vids'] = ','.join(self._compress_into_ranges(running_bridge_vids))
return running_attrs
def _query_running_vidinfo(self, ifaceobjrunning, ifaceobj_getfunc,
bridgeports=None):
running_attrs = {}
- running_vidinfo = self._get_running_vidinfo()
- if not running_vidinfo:
- return running_attrs
# 'bridge-vids' under the bridge is all about 'vids' on the port.
# so query the ports
running_bridgeport_vids = []
running_bridgeport_pvids = []
for bport in bridgeports:
- vids = running_vidinfo.get(bport, {}).get('vlan')
+ (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
if vids:
running_bridgeport_vids.append(' '.join(vids))
- pvids = running_vidinfo.get(bport, {}).get('pvid')
- if pvids:
- running_bridgeport_pvids.append(pvids[0])
+ if pvid:
+ running_bridgeport_pvids.append(pvid)
bridge_vids = None
- if running_bridgeport_vids:
+ if running_bridgeport_vids:
(vidval, freq) = Counter(running_bridgeport_vids).most_common()[0]
if freq == len(bridgeports):
running_attrs['bridge-vids'] = vidval
(vidval, freq) = Counter(running_bridgeport_pvids).most_common()[0]
if freq == len(bridgeports) and vidval != '1':
running_attrs['bridge-pvid'] = vidval
- bridge_pvid = vidval.split()
+ bridge_pvid = vidval.split()[0]
# Go through all bridge ports and find their vids
for bport in bridgeports:
bportifaceobj = ifaceobj_getfunc(bport)
if not bportifaceobj:
continue
- bport_vids = None
- bport_pvids = None
- vids = running_vidinfo.get(bport, {}).get('vlan')
+ bport_vids = []
+ bport_pvid = None
+ (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
if vids and vids != bridge_vids:
bport_vids = vids
- pvids = running_vidinfo.get(bport, {}).get('pvid')
- if pvids and pvids[0] != bridge_pvid:
- bport_pvids = pvids
- if not bport_vids and bport_pvids and bport_pvids[0] != '1':
- bportifaceobj[0].replace_config('bridge-access', bport_pvids[0])
+ if pvid and pvid != bridge_pvid:
+ bport_pvid = pvid
+ if bport_vids and bport_pvid in bport_vids:
+ bport_vids.remove(bport_pvid)
+ if (not bport_vids and bport_pvid and bport_pvid != '1'):
+ bportifaceobj[0].replace_config('bridge-access', bport_pvid)
+ bportifaceobj[0].delete_config('bridge-pvid')
+ bportifaceobj[0].delete_config('bridge-vids')
else:
- if bport_pvids and bport_pvids[0] != '1':
- bportifaceobj[0].replace_config('bridge-pvid', bport_pvids[0])
+ if bport_pvid and bport_pvid != '1':
+ bportifaceobj[0].replace_config('bridge-pvid', bport_pvid)
else:
# delete any stale bridge-vids under ports
bportifaceobj[0].delete_config('bridge-pvid')
return running_attrs
def _query_running_mcqv4src(self, ifaceobjrunning):
- running_mcqv4src = self.brctlcmd.get_mcqv4src(ifaceobjrunning.name)
+ running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src_sysfs(ifaceobjrunning.name)
mcqs = ['%s=%s' %(v, i) for v, i in running_mcqv4src.items()]
mcqs.sort()
mcq = ' '.join(mcqs)
ports = None
skip_kernel_stp_attrs = 0
- if self.sysctl_get('net.bridge.bridge-stp-user-space') == '1':
- userspace_stp = 1
+ try:
+ if self.systcl_get_net_bridge_stp_user_space() == '1':
+ userspace_stp = 1
+ except Exception as e:
+ self.logger.info('%s: %s' % (ifaceobjrunning.name, str(e)))
tmpbridgeattrdict = self.brctlcmd.get_bridge_attrs(ifaceobjrunning.name)
if not tmpbridgeattrdict:
if stp == 'yes' and userspace_stp:
skip_kernel_stp_attrs = 1
+ vlan_stats = utils.get_onff_from_onezero(
+ tmpbridgeattrdict.get('vlan-stats', None))
+ if (vlan_stats and
+ vlan_stats != self.get_mod_subattr('bridge-vlan-stats', 'default')):
+ bridgeattrdict['bridge-vlan-stats'] = [vlan_stats]
+
+ bool2str = {'0': 'no', '1': 'yes'}
# pick all other attributes
for k,v in tmpbridgeattrdict.items():
if not v:
# only include igmp attributes if kernel stp is off
continue
attrname = 'bridge-' + k
- if v != self.get_mod_subattr(attrname, 'default'):
+ mod_default = self.get_mod_subattr(attrname, 'default')
+ if v != mod_default:
+ # convert '0|1' running values to 'no|yes'
+ if v in bool2str.keys() and bool2str[v] == mod_default:
+ continue
bridgeattrdict[attrname] = [v]
if bridge_vlan_aware:
+ if not ports:
+ ports = {}
bridgevidinfo = self._query_running_vidinfo(ifaceobjrunning,
ifaceobj_getfunc,
ports.keys())
if skip_kernel_stp_attrs:
return bridgeattrdict
- if ports:
+ # Do this only for vlan-UNAWARE-bridge
+ if ports and not bridge_vlan_aware:
portconfig = {'bridge-pathcosts' : '',
- 'bridge-portprios' : ''}
+ 'bridge-portprios' : '',
+ 'bridge-learning' : '',
+ 'bridge-unicast-flood' : '',
+ 'bridge-multicast-flood' : '',
+ 'bridge-arp-nd-suppress' : '',
+ }
for p, v in ports.items():
- v = self.brctlcmd.get_pathcost(ifaceobjrunning.name, p)
+ v = self.brctlcmd.bridge_get_pathcost(ifaceobjrunning.name, p)
if v and v != self.get_mod_subattr('bridge-pathcosts',
'default'):
portconfig['bridge-pathcosts'] += ' %s=%s' %(p, v)
- v = self.brctlcmd.get_portprio(ifaceobjrunning.name, p)
+ v = self.brctlcmd.bridge_get_portprio(ifaceobjrunning.name, p)
if v and v != self.get_mod_subattr('bridge-portprios',
'default'):
portconfig['bridge-portprios'] += ' %s=%s' %(p, v)
+ v = utils.get_onff_from_onezero(
+ self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+ p, 'learning'))
+ if (v and
+ v != self.get_mod_subattr('bridge-learning', 'default')):
+ portconfig['bridge-learning'] += ' %s=%s' %(p, v)
+
+ v = utils.get_onff_from_onezero(
+ self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+ p, 'unicast-flood'))
+ if (v and
+ v != self.get_mod_subattr('bridge-unicast-flood',
+ 'default')):
+ portconfig['bridge-unicast-flood'] += ' %s=%s' %(p, v)
+
+ v = utils.get_onff_from_onezero(
+ self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+ p, 'multicast-flood'))
+ if (v and
+ v != self.get_mod_subattr('bridge-multicast-flood',
+ 'default')):
+ portconfig['bridge-multicast-flood'] += ' %s=%s' %(p, v)
+
+ v = utils.get_onff_from_onezero(
+ self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+ p, 'arp-nd-suppress'))
+ if (v and
+ v != self.get_mod_subattr('bridge-arp-nd-suppress',
+ 'default')):
+ portconfig['bridge-arp-nd-suppress'] += ' %s=%s' %(p, v)
+
bridgeattrdict.update({k : [v] for k, v in portconfig.items()
if v})
def _query_check_bridge_vidinfo(self, ifaceobj, ifaceobjcurr):
err = 0
- running_vidinfo = self._get_running_vidinfo()
attrval = ifaceobj.get_attr_value_first('bridge-port-vids')
if attrval:
running_bridge_port_vids = ''
- portlist = self.parse_port_list(attrval)
+ portlist = self.parse_port_list(ifaceobj.name, attrval)
if not portlist:
- self.log_warn('%s: could not parse \'%s %s\''
- %(ifaceobj.name, attrname, attrval))
+ self.log_warn('%s: could not parse \'bridge-port-vids %s\''
+ %(ifaceobj.name, attrval))
return
err = 0
for p in portlist:
try:
(port, val) = p.split('=')
vids = val.split(',')
- running_vids = running_vidinfo.get(port, {}).get('vlan')
+ running_vids = self.ipcmd.bridge_vlan_get_vids(port)
if running_vids:
if not self._compare_vids(vids, running_vids):
err += 1
attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
if attrval:
- portlist = self.parse_port_list(attrval)
+ portlist = self.parse_port_list(ifaceobj.name, attrval)
if not portlist:
- self.log_warn('%s: could not parse \'%s %s\''
- %(ifaceobj.name, attrname, attrval))
+ self.log_warn('%s: could not parse \'bridge-port-pvids %s\''
+ %(ifaceobj.name, attrval))
return
running_bridge_port_pvids = ''
err = 0
for p in portlist:
try:
(port, pvid) = p.split('=')
- running_pvid = running_vidinfo.get(port, {}).get('pvid')
+ running_pvid = self.ipcmd.bridge_vlan_get_vids(port)
if running_pvid and running_pvid == pvid:
running_bridge_port_pvids += ' %s' %p
else:
ifaceobjcurr.update_config_with_status('bridge-port-pvids',
running_bridge_port_pvids, 0)
- # XXX: No need to check for bridge-vids on the bridge
- # This is used by the ports. The vids on the bridge
- # come from the vlan interfaces on the bridge.
- #
- attrval = ifaceobj.get_attr_value_first('bridge-vids')
- #if attrval:
- # vids = re.split(r'[\s\t]\s*', attrval)
- # running_vids = running_vidinfo.get(ifaceobj.name, {}).get('vlan')
- # if running_vids:
- # if self._compare_vids(vids, running_vids):
- # ifaceobjcurr.update_config_with_status('bridge-vids',
- # attrval, 0)
- # else:
- # ifaceobjcurr.update_config_with_status('bridge-vids',
- # ','.join(running_vids), 1)
- # else:
- # ifaceobjcurr.update_config_with_status('bridge-vids', attrval,
- # 1)
- if attrval:
- ifaceobjcurr.update_config_with_status('bridge-vids', attrval, -1)
+ vids = self.get_ifaceobj_bridge_vids(ifaceobj)
+ if vids[1]:
+ ifaceobjcurr.update_config_with_status(vids[0], vids[1], -1)
+
+ def _query_check_snooping_wdefault(self, ifaceobj):
+ if (ifupdownflags.flags.WITHDEFAULTS
+ and not self._vxlan_bridge_default_igmp_snooping
+ and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN):
+ ifaceobj.replace_config('bridge-mcsnoop', 'no')
def _query_check_bridge(self, ifaceobj, ifaceobjcurr,
ifaceobj_getfunc=None):
self.logger.info('%s: bridge: does not exist' %(ifaceobj.name))
return
+ self._query_check_snooping_wdefault(ifaceobj)
+
ifaceattrs = self.dict_key_subset(ifaceobj.config,
self.get_mod_attrs())
+ #Add default attributes if --with-defaults is set
+ if ifupdownflags.flags.WITHDEFAULTS and 'bridge-stp' not in ifaceattrs:
+ ifaceattrs.append('bridge-stp')
if not ifaceattrs:
return
try:
except Exception, e:
self.logger.warn(str(e))
runningattrs = {}
- filterattrs = ['bridge-vids', 'bridge-port-vids',
+
+ self._query_check_support_yesno_attrs(runningattrs, ifaceobj)
+
+ filterattrs = ['bridge-vids', 'bridge-trunk', 'bridge-port-vids',
'bridge-port-pvids']
- for k in Set(ifaceattrs).difference(filterattrs):
+
+ diff = Set(ifaceattrs).difference(filterattrs)
+
+ if 'bridge-l2protocol-tunnel' in diff:
+ diff.remove('bridge-l2protocol-tunnel')
+ # bridge-l2protocol-tunnel requires separate handling
+
+ if 'bridge-ports' in diff:
+ self.query_check_bridge_ports(ifaceobj, ifaceobjcurr, runningattrs.get('ports', {}).keys(), ifaceobj_getfunc)
+ diff.remove('bridge-ports')
+
+ for k in diff:
# get the corresponding ifaceobj attr
v = ifaceobj.get_attr_value_first(k)
if not v:
- continue
+ if ifupdownflags.flags.WITHDEFAULTS and k == 'bridge-stp':
+ v = 'on' if self.default_stp_on else 'off'
+ else:
+ continue
rv = runningattrs.get(k[7:])
if k == 'bridge-mcqv4src':
continue
- if k == 'bridge-vlan-aware' and v == 'yes':
- if self.ipcmd.bridge_is_vlan_aware(ifaceobj.name):
+ if k == 'bridge-maxwait' or k == 'bridge-waitport':
+ ifaceobjcurr.update_config_with_status(k, v, 0)
+ continue
+ if k == 'bridge-vlan-aware':
+ rv = self.ipcmd.bridge_is_vlan_aware(ifaceobj.name)
+ if (rv and v == 'yes') or (not rv and v == 'no'):
ifaceobjcurr.update_config_with_status('bridge-vlan-aware',
v, 0)
else:
# special case stp compare because it may
# contain more than one valid values
stp_on_vals = ['on', 'yes']
- stp_off_vals = ['off']
+ stp_off_vals = ['off', 'no']
if ((v in stp_on_vals and rv in stp_on_vals) or
(v in stp_off_vals and rv in stp_off_vals)):
ifaceobjcurr.update_config_with_status('bridge-stp',
- v, 0)
+ rv, 0)
else:
ifaceobjcurr.update_config_with_status('bridge-stp',
- v, 1)
- elif k == 'bridge-ports':
- # special case ports because it can contain regex or glob
- running_port_list = rv.keys() if rv else []
- bridge_port_list = self._get_bridge_port_list(ifaceobj)
- if not running_port_list and not bridge_port_list:
- continue
- portliststatus = 1
- if running_port_list and bridge_port_list:
- difference = set(running_port_list
- ).symmetric_difference(bridge_port_list)
- if not difference:
- portliststatus = 0
- ifaceobjcurr.update_config_with_status('bridge-ports',
- ' '.join(running_port_list)
- if running_port_list else '', portliststatus)
- elif (k == 'bridge-pathcosts' or
- k == 'bridge-portprios' or k == 'bridge-portmcrouter'
- or k == 'bridge-portmcfl'):
- brctlcmdattrname = k[11:].rstrip('s')
+ rv, 1)
+ elif k in ['bridge-pathcosts',
+ 'bridge-portprios',
+ 'bridge-portmcrouter',
+ 'bridge-portmcfl',
+ 'bridge-learning',
+ 'bridge-unicast-flood',
+ 'bridge-multicast-flood',
+ 'bridge-arp-nd-suppress',
+ ]:
+ if k == 'bridge-arp-nd-suppress':
+ brctlcmdattrname = k[7:]
+ else:
+ brctlcmdattrname = k[7:].rstrip('s')
# for port attributes, the attributes are in a list
# <portname>=<portattrvalue>
status = 0
currstr = ''
- vlist = self.parse_port_list(v)
+ vlist = self.parse_port_list(ifaceobj.name, v)
if not vlist:
continue
for vlistitem in vlist:
try:
(p, v) = vlistitem.split('=')
- currv = self.brctlcmd.get_bridgeport_attr(
+ if k in ['bridge-learning',
+ 'bridge-unicast-flood',
+ 'bridge-multicast-flood',
+ 'bridge-arp-nd-suppress',
+ ]:
+ currv = utils.get_onoff_bool(
+ self.brctlcmd.get_bridgeport_attr(
+ ifaceobj.name, p,
+ brctlcmdattrname))
+ else:
+ currv = self.brctlcmd.get_bridgeport_attr(
ifaceobj.name, p,
brctlcmdattrname)
if currv:
currstr += ' %s=%s' %(p, currv)
else:
currstr += ' %s=%s' %(p, 'None')
- if currv != v:
+
+ if k == 'bridge-portmcrouter':
+ if self._ifla_brport_multicast_router_dict_to_int.get(v) != int(currv):
+ status = 1
+ elif currv != v:
status = 1
except Exception, e:
self.log_warn(str(e))
pass
ifaceobjcurr.update_config_with_status(k, currstr, status)
+ elif k == 'bridge-vlan-stats' or k == 'bridge-mcstats':
+ rv = utils.get_onff_from_onezero(rv)
+ if v != rv:
+ ifaceobjcurr.update_config_with_status(k, rv, 1)
+ else:
+ ifaceobjcurr.update_config_with_status(k, rv, 0)
elif not rv:
- ifaceobjcurr.update_config_with_status(k, 'notfound', 1)
+ if k == 'bridge-pvid' or k == 'bridge-vids' or k == 'bridge-trunk' or k == 'bridge-allow-untagged':
+ # bridge-pvid and bridge-vids on a bridge does
+ # not correspond directly to a running config
+ # on the bridge. They correspond to default
+ # values for the bridge ports. And they are
+ # already checked against running config of the
+ # bridge port and reported against a bridge port.
+ # So, ignore these attributes under the bridge.
+ # Use '2' for ignore today. XXX: '2' will be
+ # mapped to a defined value in subsequent patches.
+ ifaceobjcurr.update_config_with_status(k, v, 2)
+ else:
+ ifaceobjcurr.update_config_with_status(k, 'notfound', 1)
continue
- elif v != rv:
+ elif v.upper() != rv.upper():
ifaceobjcurr.update_config_with_status(k, rv, 1)
else:
ifaceobjcurr.update_config_with_status(k, rv, 0)
self._query_check_bridge_vidinfo(ifaceobj, ifaceobjcurr)
self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
+ self._query_check_l2protocol_tunnel_on_bridge(ifaceobj, ifaceobjcurr, runningattrs)
+
+ def query_check_bridge_ports(self, ifaceobj, ifaceobjcurr, running_port_list, ifaceobj_getfunc):
+ bridge_all_ports = []
+ for obj in ifaceobj_getfunc(ifaceobj.name) or []:
+ bridge_all_ports.extend(self._get_bridge_port_list(obj) or [])
+
+ if not running_port_list and not bridge_all_ports:
+ return
+
+ ports_list_status = 0 if not set(running_port_list).symmetric_difference(bridge_all_ports) else 1
+
+ try:
+ port_list = self._get_ifaceobj_bridge_ports(ifaceobj).split()
+ # we want to display the same bridge-ports list as provided
+ # in the interfaces file but if this list contains regexes or
+ # globs, for now, we won't try to change it.
+ if 'regex' in port_list or 'glob' in port_list:
+ port_list = running_port_list
+ else:
+ ordered = []
+ for i in range(0, len(port_list)):
+ if port_list[i] in running_port_list:
+ ordered.append(port_list[i])
+ port_list = ordered
+ except:
+ port_list = running_port_list
+ ifaceobjcurr.update_config_with_status('bridge-ports', (' '.join(port_list) if port_list else ''), ports_list_status)
+
+ def get_ifaceobj_bridge_vids(self, ifaceobj):
+ vids = ('bridge-vids', ifaceobj.get_attr_value_first('bridge-vids'))
+ if not vids[1]:
+ vids = ('bridge-trunk', ifaceobj.get_attr_value_first('bridge-trunk'))
+ return vids
+
+ def get_ifaceobj_bridge_vids_value(self, ifaceobj):
+ return self.get_ifaceobj_bridge_vids(ifaceobj)[1]
def _get_bridge_vids(self, bridgename, ifaceobj_getfunc):
ifaceobjs = ifaceobj_getfunc(bridgename)
for ifaceobj in ifaceobjs:
- vids = ifaceobj.get_attr_value_first('bridge-vids')
+ vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
if vids: return re.split(r'[\s\t,]\s*', vids)
return None
pvid = None
for ifaceobj in ifaceobjs:
pvid = ifaceobj.get_attr_value_first('bridge-pvid')
+ if pvid:
+ break
return pvid
def _get_bridge_name(self, ifaceobj):
def _query_check_bridge_port_vidinfo(self, ifaceobj, ifaceobjcurr,
ifaceobj_getfunc, bridgename):
- running_vidinfo = self._get_running_vidinfo()
-
attr_name = 'bridge-access'
- vids = ifaceobj.get_attr_value_first(attr_name)
- if vids:
- running_pvids = running_vidinfo.get(ifaceobj.name,
- {}).get('pvid')
- running_vids = running_vidinfo.get(ifaceobj.name,
- {}).get('vlan')
- if (not running_pvids or running_pvids != vids or
- running_vids):
+ vid = ifaceobj.get_attr_value_first(attr_name)
+ if vid:
+ (running_vids, running_pvid) = self._get_running_vids_n_pvid_str(
+ ifaceobj.name)
+ if (not running_pvid or running_pvid != vid or
+ (running_vids and running_vids[0] != vid)):
ifaceobjcurr.update_config_with_status(attr_name,
- running_pvids, 1)
- else:
- ifaceobjcurr.update_config_with_status(attr_name, vids, 0)
- return
+ running_pvid, 1)
+ else:
+ ifaceobjcurr.update_config_with_status(attr_name, vid, 0)
+ return
- attr_name = 'bridge-vids'
- vids = ifaceobj.get_attr_value_first(attr_name)
+ (running_vids, running_pvid) = self._get_running_vids_n_pvid_str(
+ ifaceobj.name)
+ attr_name = 'bridge-pvid'
+ pvid = ifaceobj.get_attr_value_first('bridge-pvid')
+ if pvid:
+ if running_pvid and running_pvid == pvid:
+ ifaceobjcurr.update_config_with_status(attr_name,
+ running_pvid, 0)
+ else:
+ ifaceobjcurr.update_config_with_status(attr_name,
+ running_pvid, 1)
+ elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
+ ((ifaceobj.flags & iface.HAS_SIBLINGS) and
+ (ifaceobj.flags & iface.OLDEST_SIBLING))):
+ # if the interface has multiple iface sections,
+ # we check the below only for the oldest sibling
+ # or the last iface section
+ pvid = self._get_bridge_pvid(bridgename, ifaceobj_getfunc)
+ if pvid:
+ if not running_pvid or running_pvid != pvid:
+ ifaceobjcurr.status = ifaceStatus.ERROR
+ ifaceobjcurr.status_str = 'bridge pvid error'
+ elif not running_pvid or running_pvid != '1':
+ ifaceobjcurr.status = ifaceStatus.ERROR
+ ifaceobjcurr.status_str = 'bridge pvid error'
+
+ attr_name, vids = self.get_ifaceobj_bridge_vids(ifaceobj)
if vids:
vids = re.split(r'[\s\t]\s*', vids)
- running_vids = running_vidinfo.get(ifaceobj.name,
- {}).get('vlan')
- if not running_vids or not self._compare_vids(vids, running_vids):
+ if not running_vids or not self._compare_vids(vids, running_vids,
+ running_pvid):
ifaceobjcurr.update_config_with_status(attr_name,
- ' '.join(running_vids), 1)
+ ' '.join(running_vids), 1)
else:
ifaceobjcurr.update_config_with_status(attr_name,
- ' '.join(running_vids), 0)
- else:
+ ' '.join(vids), 0)
+ elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
+ ((ifaceobj.flags & iface.HAS_SIBLINGS) and
+ (ifaceobj.flags & iface.OLDEST_SIBLING))):
+ # if the interface has multiple iface sections,
+ # we check the below only for the oldest sibling
+ # or the last iface section
+
# check if it matches the bridge vids
bridge_vids = self._get_bridge_vids(bridgename, ifaceobj_getfunc)
- running_vids = running_vidinfo.get(ifaceobj.name,
- {}).get('vlan')
if (bridge_vids and (not running_vids or
- not self._compare_vids(bridge_vids, running_vids))):
+ not self._compare_vids(bridge_vids, running_vids, running_pvid))):
ifaceobjcurr.status = ifaceStatus.ERROR
ifaceobjcurr.status_str = 'bridge vid error'
- running_pvid = running_vidinfo.get(ifaceobj.name,
- {}).get('pvid')
- attr_name = 'bridge-pvid'
- pvid = ifaceobj.get_attr_value_first(attr_name)
- if pvid:
- if running_pvid and running_pvid == pvid:
- ifaceobjcurr.update_config_with_status(attr_name,
- running_pvid, 0)
- else:
- ifaceobjcurr.update_config_with_status(attr_name,
- running_pvid, 1)
- elif not running_pvid or running_pvid != '1':
- ifaceobjcurr.status = ifaceStatus.ERROR
- ifaceobjcurr.status_str = 'bridge pvid error'
-
def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr,
ifaceobj_getfunc):
if not self._is_bridge_port(ifaceobj):
# Mark all bridge attributes as failed
ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
- ['bridge-vids', 'bridge-pvid', 'bridge-access',
+ ['bridge-vids', 'bridge-trunk', 'bridge-pvid', 'bridge-access',
'bridge-pathcosts', 'bridge-portprios',
'bridge-portmcrouter',
- 'bridge-portmcfl'], 1)
+ 'bridge-learning',
+ 'bridge-portmcfl', 'bridge-unicast-flood',
+ 'bridge-multicast-flood',
+ 'bridge-arp-nd-suppress', 'bridge-l2protocol-tunnel'
+ ], 1)
return
bridgename = self._get_bridge_name(ifaceobj)
if not bridgename:
ifaceobj_getfunc,
bridgename)
for attr, dstattr in {'bridge-pathcosts' : 'pathcost',
- 'bridge-portprios' : 'priority',
- 'bridge-portmcrouter' : 'mcrouter',
- 'bridge-portmcfl' : 'mcfl' }.items():
+ 'bridge-portprios' : 'portprio',
+ 'bridge-portmcrouter' : 'portmcrouter',
+ 'bridge-portmcfl' : 'portmcfl',
+ 'bridge-learning' : 'learning',
+ 'bridge-unicast-flood' : 'unicast-flood',
+ 'bridge-multicast-flood' : 'multicast-flood',
+ 'bridge-arp-nd-suppress' : 'arp-nd-suppress',
+ }.items():
attrval = ifaceobj.get_attr_value_first(attr)
if not attrval:
continue
try:
running_attrval = self.brctlcmd.get_bridgeport_attr(
bridgename, ifaceobj.name, dstattr)
+
+ if dstattr == 'portmcfl':
+ if not utils.is_binary_bool(attrval) and running_attrval:
+ running_attrval = utils.get_yesno_boolean(
+ utils.get_boolean_from_string(running_attrval))
+ elif dstattr == 'portmcrouter':
+ if self._ifla_brport_multicast_router_dict_to_int.get(attrval) == int(running_attrval):
+ ifaceobjcurr.update_config_with_status(attr, attrval, 0)
+ else:
+ ifaceobjcurr.update_config_with_status(attr, attrval, 1)
+ continue
+ elif dstattr in ['learning',
+ 'unicast-flood',
+ 'multicast-flood',
+ 'arp-nd-suppress',
+ ]:
+ if not utils.is_binary_bool(attrval) and running_attrval:
+ running_attrval = utils.get_onff_from_onezero(
+ running_attrval)
+
if running_attrval != attrval:
ifaceobjcurr.update_config_with_status(attr,
running_attrval, 1)
except Exception, e:
self.log_warn('%s: %s' %(ifaceobj.name, str(e)))
+ self._query_check_l2protocol_tunnel_on_port(ifaceobj, ifaceobjcurr)
+
+ def _query_check_l2protocol_tunnel_on_port(self, ifaceobj, ifaceobjcurr):
+ user_config_l2protocol_tunnel = ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
+
+ if user_config_l2protocol_tunnel:
+ result = 0
+ try:
+ self._query_check_l2protocol_tunnel(ifaceobj.name, user_config_l2protocol_tunnel)
+ except Exception as e:
+ self.logger.debug('query: %s: %s' % (ifaceobj.name, str(e)))
+ result = 1
+ ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result)
+
+ def _query_check_l2protocol_tunnel_on_bridge(self, ifaceobj, ifaceobjcurr, bridge_running_attrs):
+ """
+ In case the bridge-l2protocol-tunnel is specified under the bridge and not the brport
+ We need to make sure that all ports comply with the mask given under the bridge
+ """
+ user_config_l2protocol_tunnel = ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
+
+ if user_config_l2protocol_tunnel:
+ if '=' in user_config_l2protocol_tunnel:
+ try:
+ config_per_port_dict = self.parse_interface_list_value(user_config_l2protocol_tunnel)
+ brport_list = config_per_port_dict.keys()
+ except:
+ ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, 1)
+ return
+ else:
+ config_per_port_dict = {}
+ brport_list = bridge_running_attrs.get('ports', {}).keys()
+ result = 1
+ try:
+ for brport_name in brport_list:
+ self._query_check_l2protocol_tunnel(
+ brport_name,
+ config_per_port_dict.get(brport_name) if config_per_port_dict else user_config_l2protocol_tunnel
+ )
+ result = 0
+ except Exception as e:
+ self.logger.debug('query: %s: %s' % (ifaceobj.name, str(e)))
+ result = 1
+ ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result)
+
+ def _query_check_l2protocol_tunnel(self, brport_name, user_config_l2protocol_tunnel):
+ cached_ifla_brport_group_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI])
+ cached_ifla_brport_group_mask = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASK])
+
+ for protocol in re.split(',|\s*', user_config_l2protocol_tunnel):
+ callback = self.query_check_l2protocol_tunnel_callback.get(protocol)
+
+ if callable(callback):
+ if not callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
+ raise Exception('%s: bridge-l2protocol-tunnel: protocol \'%s\' not present (cached value: %d | %d)'
+ % (brport_name, protocol, cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi))
+
+ def _query_running_bridge_l2protocol_tunnel(self, brport_name, brport_ifaceobj=None, bridge_ifaceobj=None):
+ cached_ifla_brport_group_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI])
+ cached_ifla_brport_group_mask = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASK])
+ running_protocols = []
+ for protocol_name, callback in self.query_check_l2protocol_tunnel_callback.items():
+ if protocol_name == 'all' and callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
+ running_protocols = self.query_check_l2protocol_tunnel_callback.keys()
+ running_protocols.remove('all')
+ break
+ elif callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
+ running_protocols.append(protocol_name)
+ if running_protocols:
+ if brport_ifaceobj:
+ brport_ifaceobj.update_config('bridge-l2protocol-tunnel', ' '.join(running_protocols))
+ elif bridge_ifaceobj:
+ current_config = bridge_ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
+
+ if current_config:
+ bridge_ifaceobj.replace_config('bridge-l2protocol-tunnel', '%s %s=%s' % (current_config, brport_name, ','.join(running_protocols)))
+ else:
+ bridge_ifaceobj.replace_config('bridge-l2protocol-tunnel', '%s=%s' % (brport_name, ','.join(running_protocols)))
+
def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
if self._is_bridge(ifaceobj):
- self._query_check_bridge(ifaceobj, ifaceobjcurr)
+ self._query_check_bridge(ifaceobj, ifaceobjcurr, ifaceobj_getfunc)
else:
self._query_check_bridge_port(ifaceobj, ifaceobjcurr,
ifaceobj_getfunc)
ifaceobjrunning,
ifaceobj_getfunc,
bridge_vlan_aware=True))
- else:
+ else:
ifaceobjrunning.update_config_dict(self._query_running_attrs(
ifaceobjrunning, None))
def _query_running_bridge_port_attrs(self, ifaceobjrunning, bridgename):
- if self.sysctl_get('net.bridge.bridge-stp-user-space') == '1':
+ if self.systcl_get_net_bridge_stp_user_space() == '1':
return
- v = self.brctlcmd.get_pathcost(bridgename, ifaceobjrunning.name)
+ v = self.brctlcmd.bridge_get_pathcost(bridgename, ifaceobjrunning.name)
if v and v != self.get_mod_subattr('bridge-pathcosts', 'default'):
ifaceobjrunning.update_config('bridge-pathcosts', v)
- v = self.brctlcmd.get_pathcost(bridgename, ifaceobjrunning.name)
+ v = self.brctlcmd.bridge_get_pathcost(bridgename, ifaceobjrunning.name)
if v and v != self.get_mod_subattr('bridge-portprios', 'default'):
ifaceobjrunning.update_config('bridge-portprios', v)
def _query_running_bridge_port(self, ifaceobjrunning,
ifaceobj_getfunc=None):
+
bridgename = self.ipcmd.bridge_port_get_bridge_name(
ifaceobjrunning.name)
bridge_vids = None
self.logger.warn('%s: unable to find bridgename'
%ifaceobjrunning.name)
return
+
if not self.ipcmd.bridge_is_vlan_aware(bridgename):
+ try:
+ self._query_running_bridge_l2protocol_tunnel(ifaceobjrunning.name, bridge_ifaceobj=ifaceobj_getfunc(bridgename)[0])
+ except Exception as e:
+ self.logger.debug('%s: q_query_running_bridge_l2protocol_tunnel: %s' % (ifaceobjrunning.name, str(e)))
return
- running_vidinfo = self._get_running_vidinfo()
- bridge_port_vids = running_vidinfo.get(ifaceobjrunning.name,
- {}).get('vlan')
- bridge_port_pvid = running_vidinfo.get(ifaceobjrunning.name,
- {}).get('pvid')
+ self._query_running_bridge_l2protocol_tunnel(ifaceobjrunning.name, brport_ifaceobj=ifaceobjrunning)
+
+ (bridge_port_vids, bridge_port_pvid) = self._get_running_vids_n_pvid_str(
+ ifaceobjrunning.name)
+ if bridge_port_vids and bridge_port_pvid in bridge_port_vids:
+ bridge_port_vids.remove(bridge_port_pvid)
bridgeifaceobjlist = ifaceobj_getfunc(bridgename)
if bridgeifaceobjlist:
if (not bridge_pvid or (bridge_port_pvid != bridge_pvid)):
ifaceobjrunning.update_config('bridge-pvid',
bridge_port_pvid)
+
+ v = utils.get_onff_from_onezero(
+ self.brctlcmd.get_bridgeport_attr(bridgename,
+ ifaceobjrunning.name,
+ 'learning'))
+ if v and v != self.get_mod_subattr('bridge-learning', 'default'):
+ ifaceobjrunning.update_config('bridge-learning', v)
+
+ v = utils.get_onff_from_onezero(
+ self.brctlcmd.get_bridgeport_attr(bridgename,
+ ifaceobjrunning.name,
+ 'unicast-flood'))
+ if v and v != self.get_mod_subattr('bridge-unicast-flood', 'default'):
+ ifaceobjrunning.update_config('bridge-unicast-flood', v)
+
+ v = utils.get_onff_from_onezero(
+ self.brctlcmd.get_bridgeport_attr(bridgename,
+ ifaceobjrunning.name,
+ 'multicast-flood'))
+ if v and v != self.get_mod_subattr('bridge-multicast-flood', 'default'):
+ ifaceobjrunning.update_config('bridge-multicast-flood', v)
+
+ v = utils.get_onff_from_onezero(
+ self.brctlcmd.get_bridgeport_attr(bridgename,
+ ifaceobjrunning.name,
+ 'arp-nd-suppress'))
+ # Display running 'arp-nd-suppress' only on vxlan ports
+ # if 'allow_arp_nd_suppress_only_on_vxlan' is set to 'yes'
+ # otherwise, display on all bridge-ports
+
+ bportifaceobj = ifaceobj_getfunc(ifaceobjrunning.name)[0]
+ if (v and
+ v != self.get_mod_subattr('bridge-arp-nd-suppress', 'default') and
+ (not self.arp_nd_suppress_only_on_vxlan or
+ (self.arp_nd_suppress_only_on_vxlan and
+ bportifaceobj.link_kind & ifaceLinkKind.VXLAN))):
+ ifaceobjrunning.update_config('bridge-arp-nd-suppress', v)
+
self._query_running_bridge_port_attrs(ifaceobjrunning, bridgename)
def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
- if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
- self._query_running_bridge(ifaceobjrunning, ifaceobj_getfunc)
- elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
- self._query_running_bridge_port(ifaceobjrunning, ifaceobj_getfunc)
+ try:
+ if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
+ self._query_running_bridge(ifaceobjrunning, ifaceobj_getfunc)
+ elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
+ self._query_running_bridge_port(ifaceobjrunning, ifaceobj_getfunc)
+ except Exception as e:
+ raise Exception('%s: %s' % (ifaceobjrunning.name, str(e)))
+
+ def _query(self, ifaceobj, **kwargs):
+ """ add default policy attributes supported by the module """
+ if (not (ifaceobj.link_kind & ifaceLinkKind.BRIDGE) or
+ ifaceobj.get_attr_value_first('bridge-stp')):
+ return
+ if self.default_stp_on:
+ ifaceobj.update_config('bridge-stp', 'yes')
+
+ def _query_check_support_yesno_attrs(self, runningattrs, ifaceobj):
+ for attrl in [['mcqifaddr', 'bridge-mcqifaddr'],
+ ['mcquerier', 'bridge-mcquerier'],
+ ['mcsnoop', 'bridge-mcsnoop']]:
+ value = ifaceobj.get_attr_value_first(attrl[1])
+ if value and not utils.is_binary_bool(value):
+ if attrl[0] in runningattrs:
+ bool = utils.get_boolean_from_string(runningattrs[attrl[0]])
+ runningattrs[attrl[0]] = utils.get_yesno_boolean(bool)
+
+ self._query_check_mcrouter(ifaceobj, runningattrs)
+ self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'portmcfl', ifaceobj.get_attr_value_first('bridge-portmcfl'))
+ self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'learning', ifaceobj.get_attr_value_first('bridge-learning'))
+ self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'unicast-flood', ifaceobj.get_attr_value_first('bridge-unicast-flood'))
+ self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'multicast-flood', ifaceobj.get_attr_value_first('bridge-multicast-flood'))
+ self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'arp-nd-suppress', ifaceobj.get_attr_value_first('bridge-arp-nd-suppress'))
+
+ def _query_check_mcrouter(self, ifaceobj, running_attrs):
+ """
+ bridge-mcrouter and bridge-portmcrouter supports: yes-no-0-1-2
+ """
+ if 'mcrouter' in running_attrs:
+ value = ifaceobj.get_attr_value_first('bridge-mcrouter')
+ if value:
+ try:
+ int(value)
+ except:
+ running_attrs['mcrouter'] = 'yes' if utils.get_boolean_from_string(running_attrs['mcrouter']) else 'no'
- _run_ops = {'pre-up' : _up,
- 'post-down' : _down,
- 'query-checkcurr' : _query_check,
- 'query-running' : _query_running}
+ def _query_check_support_yesno_attr_port(self, runningattrs, ifaceobj, attr, attrval):
+ if attrval:
+ portlist = self.parse_port_list(ifaceobj.name, attrval)
+ if portlist:
+ to_convert = []
+ for p in portlist:
+ (port, val) = p.split('=')
+ if not utils.is_binary_bool(val):
+ to_convert.append(port)
+ for port in to_convert:
+ runningattrs['ports'][port][attr] = utils.get_yesno_boolean(
+ utils.get_boolean_from_string(runningattrs['ports'][port][attr]))
+
+ _run_ops = {
+ 'pre-up': _up,
+ 'post-down': _down,
+ 'query-checkcurr': _query_check,
+ 'query-running': _query_running,
+ 'query': _query
+ }
def get_ops(self):
""" returns list of ops supported by this module """
return self._run_ops.keys()
def _init_command_handlers(self):
- flags = self.get_flags()
if not self.ipcmd:
- self.ipcmd = iproute2(**flags)
- if not self.brctlcmd:
- self.brctlcmd = brctl(**flags)
+ self.ipcmd = self.brctlcmd = LinkUtils()
- def run(self, ifaceobj, operation, query_ifaceobj=None,
- ifaceobj_getfunc=None):
+ def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None):
""" run bridge configuration on the interface object passed as
argument. Can create bridge interfaces if they dont exist already
if not op_handler:
return
self._init_command_handlers()
- self._flush_running_vidinfo()
+
+ if (not LinkUtils.bridge_utils_is_installed
+ and (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT or ifaceobj.link_kind & ifaceLinkKind.BRIDGE)
+ and LinkUtils.bridge_utils_missing_warning):
+ self.logger.warning('%s: missing - bridge operation may not work as expected. '
+ 'Please check if \'bridge-utils\' package is installed' % utils.brctl_cmd)
+ LinkUtils.bridge_utils_missing_warning = False
+
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj,
ifaceobj_getfunc=ifaceobj_getfunc)