#!/usr/bin/env python3
#
# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
#

import re
import json
import time
import itertools
from collections import Counter

try:
    from ifupdown2.lib.addon import Bridge

    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 ifaceRole, ifaceLinkKind, ifaceLinkPrivFlags, ifaceLinkType, ifaceDependencyType, ifaceStatus, iface
    from ifupdown2.ifupdown.utils import utils

    from ifupdown2.ifupdownaddons.cache import *
    from ifupdown2.ifupdownaddons.modulebase import moduleBase
except (ImportError, ModuleNotFoundError):
    from lib.addon import Bridge

    import ifupdown.exceptions as exceptions
    import ifupdown.policymanager as policymanager
    import ifupdown.ifupdownflags as ifupdownflags

    from nlmanager.nlmanager import Link

    from ifupdown.iface import ifaceRole, ifaceLinkKind, ifaceLinkPrivFlags, ifaceLinkType, ifaceDependencyType, ifaceStatus, iface
    from ifupdown.utils import utils

    from ifupdownaddons.cache import *
    from ifupdownaddons.modulebase import moduleBase


class bridgeFlags:
    PORT_PROCESSED = 0x1
    PORT_PROCESSED_OVERRIDE = 0x2


class BridgeVlanVniMapError(Exception):
    pass


class bridge(Bridge, moduleBase):
    """  ifupdown2 addon module to configure linux bridges """

    _modinfo = {
        "mhelp": "Bridge configuration module. Supports both vlan aware and non "
                 "vlan aware bridges. For the vlan aware bridge, the port "
                 "specific attributes must be specified under the port. And for "
                 "vlan unaware bridge port specific attributes must be specified "
                 "under the bridge.",
        "attrs": {
            "bridge-vlan-aware": {
                "help": "vlan aware bridge. Setting this "
                        "attribute to yes enables vlan filtering"
                        " on the bridge",
                "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)"
                ]
            },
            "bridge-stp": {
                "help": "bridge-stp yes/no",
                "example": ["bridge-stp no"],
                "validvals": ["yes", "on", "off", "no"],
                "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"
            },
            # XXX: recheck values
            "bridge-gcint": {
                "help": "bridge garbage collection interval in secs",
                "validrange": ["0", "255"],
                "example": ["bridge-gcint 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",
                "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",
                "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"
                ],
            },
            "bridge-mclmc": {
                "help": "set multicast last member count",
                "validrange": ["0", "255"],
                "example": ["bridge-mclmc 2"],
                "default": "2"
            },
            "bridge-mcrouter": {
                "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",
                "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",
                "validvals": ["yes", "no", "0", "1"],
                "default": "no",
                "example": ["bridge-mcqifaddr no"]
            },
            "bridge-mcquerier": {
                "help": "set multicast querier",
                "validvals": ["yes", "no", "0", "1"],
                "default": "no",
                "example": ["bridge-mcquerier no"]
            },
            "bridge-hashel": {
                "help": "set hash elasticity",
                "validrange": ["0", "4096"],
                "default": "4",
                "example": ["bridge-hashel 4096"]
            },
            "bridge-hashmax": {
                "help": "set hash max",
                "validrange": ["0", "65536"],
                "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)",
                "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: 0 - disabled, 1 - automatic (queried), 2 - permanently enabled",
                "validvals": ["<interface-disabled-automatic-enabled>"],
                "default": "1",
                "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.",
                "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,"
                        "if no ports are specified then those"
                        " specified on bridge-ports will be"
                        " used here. Specifying no ports here "
                        "should not be used if we are using "
                        "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 "
                        "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": {
                "help": "bridge port vids. Can be specified "
                        "under the bridge or under the port. "
                        "If specified under the bridge the ports "
                        "inherit it unless overridden by a "
                        "bridge-vids attribute under the port",
                "multivalue": True,
                "validvals": ["<number-comma-range-list>"],
                "example": [
                    "bridge-vids 4000",
                    "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,
                "example": ["bridge-port-vids bond0=1-1000,1010-1020"]
            },
            "bridge-port-pvids": {
                "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-broadcast-flood": {
                "help": "bridge port broadcast flood flag",
                "validvals": ["on", "off", "<interface-on-off-list>"],
                "default": "on",
                "example": [
                    "under the port (for vlan aware bridge): bridge-broadcast-flood on",
                    "under the bridge (for vlan unaware bridge): bridge-broadcast-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", "1", "0", "yes", "no"],
                "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"
                ]
            },
            "bridge-ports-condone-regex": {
                    "help": "bridge ports to ignore/condone when reloading config / removing interfaces",
                    "required": False,
                    "default": "^(tap|veth|fwpr)",
                    "example": ["bridge-ports-condone-regex ^[a-zA-Z0-9]+_v[0-9]{1,4}$"]
            },
            "bridge-vlan-vni-map": {
                "help": "Single vxlan support",
                "example": ["bridge-vlan-vni-map 1000-1001=1000-1001"],
            },
            "bridge-always-up": {
                "help": "Enabling this attribute on a bridge will enslave a dummy interface to the bridge",
                "required": False,
                "validvals": ["yes", "no", "on", "off"]
            }
        }
    }

    bridge_utils_missing_warning = True

    # 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.

    _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.upper),
            # 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,
        'bridge-broadcast-flood': 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_NEIGH_SUPPRESS,
    }

    _ifla_brport_multicast_router_dict_to_int = {
        'disabled': 0,
        '0': 0,
        'no': 0,
        'automatic': 1,
        '1': 1,
        'yes': 1,
        'enabled': 2,
        '2': 2,
    }

    _ifla_brport_multicast_router_dict_int_to_str = {
        0: "disabled",
        1: "automatic",
        2: "enabled"
    }

    # 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_BCAST_FLOOD, utils.get_boolean_from_string),
            (Link.IFLA_BRPORT_GROUP_FWD_MASK, lambda x: x),
            (Link.IFLA_BRPORT_NEIGH_SUPPRESS, utils.get_boolean_from_string)
        )
    )

    def __init__(self, *args, **kargs):
        Bridge.__init__(self)
        moduleBase.__init__(self, *args, **kargs)
        self.name = self.__class__.__name__
        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.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)

        self.bridge_always_up_dummy_brport = policymanager.policymanager_api.get_module_globals(
            module_name=self.__class__.__name__,
            attr='bridge_always_up_dummy_brport'
        )
        self.logger.debug('bridge: init: bridge_always_up_dummy_brport=%s' % self.bridge_always_up_dummy_brport)

        try:
            self.bridge_allow_multiple_vlans = utils.get_boolean_from_string(
                self.sysctl_get('net.bridge.bridge-allow-multiple-vlans')
            )
        except Exception:
            # 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 []
        # each bridge should have it's own tuple (ifname, mac)
        self.bridge_mac_iface = {}

        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.allow_vlan_sub_interface_in_vlan_aware_bridge = utils.get_boolean_from_string(
            policymanager.policymanager_api.get_module_globals(
                module_name=self.__class__.__name__,
                attr="allow-vlan-sub-interface-in-vlan-aware-bridge"
            ),
            default=True
        )

        self.bridge_vxlan_arp_nd_suppress = utils.get_boolean_from_string(
            policymanager.policymanager_api.get_module_globals(
                module_name=self.__class__.__name__,
                attr="bridge-vxlan-arp-nd-suppress"
            ),
            default=False
        )
        self.bridge_vxlan_arp_nd_suppress_int = int(self.bridge_vxlan_arp_nd_suppress)

        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
        }

        self._bridge_attribute_query_check_handler = {
            "bridge-maxwait": (self._query_check_br_attr_wait, None),
            "bridge-waitport": (self._query_check_br_attr_wait, None),

            "bridge-stp": (self._query_check_br_attr_stp, Link.IFLA_BR_STP_STATE),

            "bridge-mcstats": (self._query_check_br_attr_boolean_on_off, Link.IFLA_BR_MCAST_STATS_ENABLED),
            "bridge-vlan-stats": (self._query_check_br_attr_boolean_on_off, Link.IFLA_BR_VLAN_STATS_ENABLED),

            "bridge-vlan-aware": (self._query_check_br_attr_boolean, Link.IFLA_BR_VLAN_FILTERING),
            "bridge-mcqifaddr": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_QUERY_USE_IFADDR),
            "bridge-mcsnoop": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_SNOOPING),
            "bridge-mcquerier": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_QUERIER),
            "bridge-mcrouter": (self._query_check_br_attr_boolean, Link.IFLA_BR_MCAST_ROUTER),

            "bridge-vlan-protocol": (self._query_check_br_attr_string, Link.IFLA_BR_VLAN_PROTOCOL),

            "bridge-mcsqc": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT),
            "bridge-mclmc": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_LAST_MEMBER_CNT),
            "bridge-hashmax": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_HASH_MAX),
            "bridge-hashel": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_HASH_ELASTICITY),
            "bridge-bridgeprio": (self._query_check_br_attr_int, Link.IFLA_BR_PRIORITY),
            "bridge-igmp-version": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_IGMP_VERSION),
            "bridge-mld-version": (self._query_check_br_attr_int, Link.IFLA_BR_MCAST_MLD_VERSION),

            "bridge-maxage": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MAX_AGE),
            "bridge-fd": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_FORWARD_DELAY),
            "bridge-hello": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_HELLO_TIME),
            "bridge-ageing": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_AGEING_TIME),
            "bridge-mcmi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL),
            "bridge-mcsqi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL),
            "bridge-mclmi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL),
            "bridge-mcqri": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL),
            "bridge-mcqpi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERIER_INTVL),
            "bridge-mcqi": (self._query_check_br_attr_int_divided100, Link.IFLA_BR_MCAST_QUERY_INTVL),
        }

        self._brport_attribute_query_check_handler = {
            "bridge-pathcosts": self._query_check_brport_attr_int,
            "bridge-portprios": self._query_check_brport_attr_int,
            "bridge-portmcfl": self._query_check_brport_attr_boolean_yes_no,
            "bridge-learning": self._query_check_brport_attr_boolean_on_off,
            "bridge-arp-nd-suppress": self._query_check_brport_attr_boolean_on_off,
            "bridge-unicast-flood": self._query_check_brport_attr_boolean_on_off,
            "bridge-multicast-flood": self._query_check_brport_attr_boolean_on_off,
            "bridge-broadcast-flood": self._query_check_brport_attr_boolean_on_off,
            "bridge-portmcrouter": self._query_check_brport_attr_portmcrouter,
        }

        self.bridge_vxlan_port_learning = utils.get_boolean_from_string(
            policymanager.policymanager_api.get_module_globals(
                self.__class__.__name__,
                "bridge_vxlan_port_learning"
            ),
            default=True
        )

        # To avoid disabling ipv6 on SVD we need to keep track of them
        self.svd_list = set()

        # user defined limit of VNI per vlan on the same bridge
        # -1 = no limit
        self.bridge_vni_per_svi = {}
        try:
            self.bridge_vni_per_svi_limit = int(policymanager.policymanager_api.get_module_globals(
                module_name=self.__class__.__name__,
                attr="bridge_vni_per_svi_limit"
            ))
        except Exception:
            self.bridge_vni_per_svi_limit = -1

    @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

    @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)
        c3 = self.syntax_check_learning_l2_vni_evpn(ifaceobj)
        c4 = self.syntax_check_bridge_arp_vni_vlan(ifaceobj, ifaceobj_getfunc)
        c5 = self.syntax_check_bridge_vni_svi_limit(ifaceobj, ifaceobj_getfunc)
        return retval and c1 and c2 and c3 and c4 and c5

    def syntax_check_bridge_vni_svi_limit(self, ifaceobj, ifaceobj_getfunc):
        if self.bridge_vni_per_svi_limit > 0 and ifaceobj.link_kind & ifaceLinkKind.VXLAN:
            vni_name = ifaceobj.name
            bridge_name = self.__get_vxlan_bridge_name(ifaceobj, ifaceobj_getfunc)

            if not bridge_name:
                return True

            svi = ifaceobj.get_attr_value_first("bridge-access")

            if not svi:
                return True

            vni_per_svi = self.bridge_vni_per_svi.get(bridge_name, {}).get(svi)

            def err():
                self.logger.error(
                    "%s: misconfiguration detected: maximum vni allowed per bridge (%s) svi (%s) is limited to %s (policy: 'bridge_vni_per_svi_limit')" %
                    (vni_name,
                    bridge_name,
                    svi,
                    self.bridge_vni_per_svi_limit)
                )

            if vni_per_svi:
                err()
                return False
            else:
                if not bridge_name in self.bridge_vni_per_svi:
                    self.bridge_vni_per_svi[bridge_name] = {
                        svi: vni_name
                    }

                elif not svi in self.bridge_vni_per_svi[bridge_name]:
                    self.bridge_vni_per_svi[bridge_name][svi] = vni_name

                else:
                    err()
                    return False

        return True

    def __get_vxlan_bridge_name(self, ifaceobj, ifaceobj_getfunc):
        try:
            for intf in ifaceobj.upperifaces:
                for obj in ifaceobj_getfunc(intf):
                    if obj.link_kind & ifaceLinkKind.BRIDGE:
                        return obj.name
        except Exception:
            pass
        return None

    def syntax_check_bridge_arp_vni_vlan(self, ifaceobj, ifaceobj_getfunc):
        """
        Detect and warn when arp suppression is enabled and there is no vlan configured

        :param ifaceobj:
        :param ifaceobj_getfunc:
        :return boolean:
        """
        if ifaceobj.link_kind & ifaceLinkKind.VXLAN \
            and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT \
            and utils.get_boolean_from_string(ifaceobj.get_attr_value_first("bridge-arp-nd-suppress")) \
            and not ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN:

            bridge_access = ifaceobj.get_attr_value_first("bridge-access")

            if not bridge_access:
                return True

            for obj in ifaceobj_getfunc(ifaceobj.upperifaces[0]) or []:
                for upper_ifname in obj.upperifaces or []:
                    for upper_obj in ifaceobj_getfunc(upper_ifname) or []:
                        if upper_obj.link_kind & ifaceLinkKind.VLAN:
                            if str(self._get_vlan_id(upper_obj)) == bridge_access:
                                return True

            self.logger.warning(
                "%s: ARP suppression configured on %s and associated vlan %s not configured. "
                "This may result in unexpected behavior"
                % (ifaceobj.name, ifaceobj.name, bridge_access)
            )
            return False

        return True

    def syntax_check_learning_l2_vni_evpn(self, ifaceobj):
        result = True
        if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT and ifaceobj.link_kind & ifaceLinkKind.VXLAN:
            if utils.get_boolean_from_string(ifaceobj.get_attr_value_first("bridge-learning")) and \
                (not ifaceobj.get_attr_value_first("vxlan-remoteip") and not ifaceobj.get_attr_value_first("vxlan-remoteip-map")):
                self.logger.warning(
                    "%s: possible mis-configuration detected: l2-vni configured with bridge-learning ON "
                    "while EVPN is also configured - these two parameters conflict with each other."
                    % ifaceobj.name
                )
                result = False
        return result

    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.warning('%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 not self.allow_vlan_sub_interface_in_vlan_aware_bridge:
                    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):
        if not ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN:
            self.log_error('`bridge-access` attribute is mandatory when vxlan '
                           'device (%s) is part of vlan aware bridge (%s)'
                           % (ifaceobj.name, bridgename), ifaceobj)
            return False
        return True

    def syntax_check_vxlan_in_vlan_aware_br(self, ifaceobj, ifaceobj_getfunc):
        if not ifaceobj_getfunc:
            return True
        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 utils.compare_ids(bridge_vids,
                                                  vids,
                                                  pvid=pvid)):
                        if not 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 check_valid_bridge(self, ifaceobj, ifname):
        if self.cache.link_exists(ifname) and not self.cache.link_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, old_ifaceobjs=False):

        if not old_ifaceobjs and (ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN or ifaceobj.get_attr_value_first("bridge-vlan-vni-map")):
            self.svd_list.add(ifaceobj.name)

        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
        # 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

            if not old_ifaceobjs:
                # store the name of all bridge vlan aware in a global list
                self.bridge_vlan_aware_list.add(ifaceobj.name)

        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):
        if not self.cache.bridge_exists(ifaceobj.name):
            return None
        return self.cache.get_slaves(ifaceobj.name)

    def _get_bridge_port_list(self, ifaceobj):

        # port list is also available in the previously
        # parsed dependent list. Use that if available, instead
        # of parsing port expr again
        port_list = ifaceobj.lowerifaces
        if port_list:
            return port_list
        ports = self._get_ifaceobj_bridge_ports(ifaceobj)
        if 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 _get_bridge_port_condone_regex(self, ifaceobj, get_string = False):
        bridge_port_condone_regex = ifaceobj.get_attr_value_first('bridge-ports-condone-regex')
        # If bridge-ports-ignore-regex is configured, do NOT use the parse_port_list()
        # function to gather a list of ports matching the regex here and now but set
        # up a compiled regex to be used in a match later. This way we try to avoid
        # a race condition where an (possibly VM) interface is created after this
        # function has been called but before the bridgeports are validated.
        if not bridge_port_condone_regex:
            bridge_port_condone_regex = self.get_attr_default_value('bridge-ports-condone-regex')

        if bridge_port_condone_regex:
            if get_string:
                return bridge_port_condone_regex
            return re.compile (r"%s" % bridge_port_condone_regex)
        return None

    def _process_bridge_waitport(self, ifaceobj, portlist):
        waitport_value = ifaceobj.get_attr_value_first('bridge-waitport')
        if not waitport_value: return
        try:
            waitportvals = re.split(r'[\s\t]\s*', waitport_value, 1)
            if not waitportvals: return
            try:
                waitporttime = int(waitportvals[0])
            except Exception:
                self.log_warn('%s: invalid waitport value \'%s\''
                        %(ifaceobj.name, waitportvals[0]))
                return
            if waitporttime <= 0: return
            try:
                waitportlist = self.parse_port_list(ifaceobj.name,
                                                    waitportvals[1])
            except IndexError as e:
                # ignore error and use all bridge ports
                waitportlist = portlist
                pass
            if not waitportlist: return
            self.logger.info('%s: waiting for ports %s to exist ...'
                    %(ifaceobj.name, str(waitportlist)))
            starttime = time.time()
            while ((time.time() - starttime) < waitporttime):
                if all([False for p in waitportlist
                        if not self.cache.link_exists(p)]):
                    break;
                time.sleep(1)
        except Exception as e:
            self.log_warn('%s: unable to process waitport: %s'
                    %(ifaceobj.name, str(e)))

    def _enable_disable_ipv6(self, port, enable='1'):
        try:
            self.write_file('/proc/sys/net/ipv6/conf/%s/disable_ipv6' % port, enable)
        except Exception as e:
            self.logger.info(str(e))

    def handle_ipv6(self, ports, state):
        for p in ports:
            self._enable_disable_ipv6(p, state)

    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)
        bridgeportscondoneregex = self._get_bridge_port_condone_regex(ifaceobj)
        runningbridgeports = []

        # bridge-always-up #####################################################
        bridge_always_up = ifaceobj.get_attr_value_first("bridge-always-up")
        dummy_brport = None

        if utils.get_boolean_from_string(bridge_always_up):
            # the dummy port will be added to the bridgeports list so the
            # following code don't de-enslave the dummy device.
            dummy_brport = self.bridge_always_up(ifaceobj.name, bridgeports)

        ########################################################################

        self._process_bridge_waitport(ifaceobj, bridgeports)
        # Delete active ports not in the new port list
        if not ifupdownflags.flags.PERFMODE:
            runningbridgeports = self.cache.get_slaves(ifaceobj.name)
            if runningbridgeports:
                for bport in runningbridgeports:
                    if not bridgeports or bport not in bridgeports:
                        if bridgeportscondoneregex and bridgeportscondoneregex.match(bport):
                            self.logger.info("%s: port %s will stay enslaved as it matches with bridge-ports-condone-regex" % (ifaceobj.name, bport))
                            continue
                        self.netlink.link_set_nomaster(bport)
                        # set admin DOWN on all removed ports
                        # that don't have config outside bridge
                        if not ifaceobj_getfunc(bport):
                            self.netlink.link_down(bport)
                        # enable ipv6 for ports that were removed
                        self.handle_ipv6([bport], '0')
            else:
                runningbridgeports = []
        if not bridgeports:
            return []
        err = 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)

        if dummy_brport:
            # add the dummy port to the list of interface to enslave
            # link_set_master should make sure that the device is not
            # already enslaved.
            newbridgeports_ordered.append(dummy_brport)

        self.iproute2.batch_start()

        for bridgeport in newbridgeports_ordered:
            try:
                if (not ifupdownflags.flags.DRYRUN and
                    not self.cache.link_exists(bridgeport)):
                    self.log_error('%s: bridge port %s does not exist'
                                   %(ifaceobj.name, bridgeport), ifaceobj, raise_error=False)
                    err += 1
                    continue
                hwaddress = self.cache.get_link_address(bridgeport)
                if not ifupdownflags.flags.DRYRUN and not self._valid_ethaddr(hwaddress):
                    self.log_warn('%s: skipping port %s, ' %(ifaceobj.name,
                                  bridgeport) + 'invalid ether addr %s'
                                  %hwaddress)
                    continue
                self.iproute2.link_set_master(bridgeport, ifaceobj.name)
                newly_enslaved_ports.append(bridgeport)

                # dont disable ipv6 for SVD
                if bridgeport not in self.svd_list:
                    self.handle_ipv6([bridgeport], '1')

                self.iproute2.addr_flush(bridgeport)
            except Exception as e:
                self.logger.error(str(e))
                pass

        self.iproute2.batch_commit()
        self.cache.force_add_slave_list(ifaceobj.name, newly_enslaved_ports)

        if err:
            self.log_error('bridge configuration failed (missing ports)')

        try:
            # to avoid any side effect we remove the dummy brport from the
            # list of supposedly newly configured ports.
            newly_enslaved_ports.remove(dummy_brport)
        except Exception:
            pass

        return newly_enslaved_ports

    def get_dummy_brport_name_for_bridge(self, bridge_name):
        """
            dummy brport will have user provided name if it's defined in 'bridge_always_up_dummy_brport' policy
            Otherwise dummy brport will have pre-formated name: brport-if$BRIDGE_IFINDEX
            That way we can avoid collision with existing interfaces
        """
        if self.bridge_always_up_dummy_brport:
            return self.bridge_always_up_dummy_brport
        # this can raise: NetlinkCacheIfnameNotFoundError
        return "brport-if%d" % self.cache.get_ifindex(bridge_name)

    def bridge_always_up(self, bridge_name, newbridgeports_ordered):
        dummy_brport = self.get_dummy_brport_name_for_bridge(bridge_name)

        if not self.cache.link_exists(dummy_brport):
            self.logger.info("%s: bridge-always-up yes: enslaving dummy port: %s" % (bridge_name, dummy_brport))
            self.netlink.link_add(ifname=dummy_brport, kind="dummy")
            self.netlink.link_up_force(dummy_brport)

        newbridgeports_ordered.append(dummy_brport)
        return dummy_brport

    def _process_bridge_maxwait(self, ifaceobj, portlist):
        maxwait = ifaceobj.get_attr_value_first('bridge-maxwait')
        if not maxwait: return
        try:
            maxwait = int(maxwait)
        except Exception:
            self.log_warn('%s: invalid maxwait value \'%s\'' %(ifaceobj.name,
                    maxwait))
            return
        if not maxwait: return
        self.logger.info('%s: waiting for ports to go to fowarding state ..'
                %ifaceobj.name)
        try:
            starttime = time.time()
            while ((time.time() - starttime) < maxwait):
                if all([False for p in portlist
                    if self.read_file_oneline(
                            '/sys/class/net/%s/brif/%s/state'
                            %(ifaceobj.name, p)) != '3']):
                    break;
                time.sleep(1)
        except Exception as e:
            self.log_warn('%s: unable to process maxwait: %s'
                    %(ifaceobj.name, str(e)))

    def _set_bridge_mcqv4src_compat(self, ifaceobj):
        #
        # Sets old style igmp querier
        #
        attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
        if attrval:
            running_mcqv4src = {}
            if not ifupdownflags.flags.PERFMODE:
                running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobj.name)
            mcqs = {}
            srclist = attrval.split()
            for s in srclist:
                k, v = s.split('=')
                mcqs[k] = v

            k_to_del = set(list(running_mcqv4src.keys())).difference(list(mcqs.keys()))
            for v in k_to_del:
                self.iproute2.bridge_del_mcqv4src(ifaceobj.name, v)
            for v in list(mcqs.keys()):
                self.iproute2.bridge_set_mcqv4src(ifaceobj.name, v, mcqs[v])
        elif not ifupdownflags.flags.PERFMODE:
            running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobj.name)
            if running_mcqv4src:
                for v in list(running_mcqv4src.keys()):
                    self.iproute2.bridge_del_mcqv4src(ifaceobj.name, v)

    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
        # Install pvids
        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, 'bridge-port-pvids',
                                bridge_port_pvids))
                return
            for p in portlist:
                try:
                    (port, pvid) = p.split('=')
                    pvid = int(pvid)
                    running_pvid = self.cache.get_pvid(port)
                    if running_pvid:
                        if running_pvid == pvid:
                            continue
                        else:
                            self.iproute2.bridge_vlan_del_pvid(port, running_pvid)
                    self.iproute2.bridge_vlan_add_pvid(port, pvid)
                except Exception as e:
                    self.log_warn('%s: failed to set pvid `%s` (%s)'
                            %(ifaceobj.name, p, str(e)))

        # install port vids
        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,
                              'bridge-port-vids', bridge_port_vids))
                return
            for p in portlist:
                try:
                    (port, val) = p.split('=')
                    vids = val.split(',')
                    vids_int =  utils.ranges_to_ints(vids)
                    _, running_vids = self.cache.get_pvid_and_vids(port)
                    if running_vids:
                        (vids_to_del, vids_to_add) = \
                                utils.diff_ids(vids_int, running_vids)
                        if vids_to_del:
                            self.iproute2.bridge_vlan_del_vid_list(port,
                                    utils.compress_into_ranges(vids_to_del))
                        if vids_to_add:
                            self.iproute2.bridge_vlan_add_vid_list(port,
                                    utils.compress_into_ranges(vids_to_add))
                    else:
                        self.iproute2.bridge_vlan_add_vid_list(port, vids_int)
                except Exception as e:
                    self.log_warn('%s: failed to set vid `%s` (%s)'
                        %(ifaceobj.name, p, str(e)))

    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 Exception:
            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 mcsnoop:
            return mcsnoop

        if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN:
            if self._vxlan_bridge_default_igmp_snooping is not None:
                return self._vxlan_bridge_default_igmp_snooping

        return self.get_attr_default_value("bridge-mcsnoop")

    def fill_ifla_info_data_with_ifla_br_attribute(self,
                                                   ifla_info_data,
                                                   link_just_created,
                                                   ifname,
                                                   nl_attr,
                                                   attr_name,
                                                   user_config,
                                                   cached_value):
        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
                )

            if not link_just_created and cached_value is 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

            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: applying bridge settings' % ifname)

        cached_ifla_info_data = self.cache.get_link_info_data(ifname)

        try:
            # we compare the user value (or policy value) with the current running state
            # we need to divide the cached value by 100 to ignore small difference.
            # i.e. our default value is 31 but the kernel default seems to be 3125
            cached_ifla_info_data[Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL] //= 100
            cached_ifla_info_data[Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL] *= 100
        except Exception:
            pass

        for attr_name, nl_attr in self._ifla_br_attributes_map.items():
            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),
                cached_value=cached_ifla_info_data.get(nl_attr)
            )

        # special cases ########################################################

        # bridge-bridgeprio
        # if mstpctl-treeprio is configured on the bridge
        # do not reset the bridge-bridgeprio to the default value
        # NOTE: this is the case for every bridge/mstpctl attribute pairs.
        # TODO: more code should be added to handle this in the future.
        mstpctl_treeprio = ifaceobj.get_attr_value_first("mstpctl-treeprio")
        bridge_bridgeprio = ifaceobj.get_attr_value_first("bridge-bridgeprio")

        if mstpctl_treeprio:
            self.logger.info("%s: mstpctl-treeprio attribute is set - ignorning bridge-bridgeprio" % ifname)
        else:
            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_PRIORITY,
                attr_name='bridge-bridgeprio',
                user_config=bridge_bridgeprio,
                cached_value=cached_ifla_info_data.get(Link.IFLA_BR_PRIORITY)
            )

        # 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),
            cached_value=cached_ifla_info_data.get(Link.IFLA_BR_MCAST_SNOOPING)

        )

        # 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,
                cached_value=cached_ifla_info_data.get(Link.IFLA_BR_VLAN_STATS_ENABLED)
            )

        try:
            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
                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:
            self.netlink.link_set_bridge_info_data(ifname, ifla_info_data)

    def _check_vids(self, ifaceobj, vids):
        ret = True
        for v in vids:
            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.warning('%s: unable to parse vid \'%s\''
                                 %(ifaceobj.name, v))
        return ret

    def _get_running_vids_n_pvid_str(self, ifacename):
        pvid, vids = self.cache.get_pvid_and_vids(ifacename)

        if vids:
            ret_vids = utils.compress_into_ranges(vids)
        else:
            ret_vids = None

        if pvid:
            ret_pvid = '%s' %pvid
        else:
            ret_pvid = None
        return (ret_vids, ret_pvid)

    def config_check_bridge_vni_svi_limit(self, vxlan_brport_obj, ifaceobj_getfunc, pvid):
        """
        Multiple VXLANs can't be added to the same VLAN
        """
        ifname = vxlan_brport_obj.name

        for intf in vxlan_brport_obj.upperifaces:
            # find the bridge object to access the brport list

            for obj in ifaceobj_getfunc(intf):
                if obj.link_kind & ifaceLinkKind.BRIDGE:

                    for brport_name in self._get_bridge_port_list(obj):
                        # loop through the brports

                        if ifname == brport_name:
                            # ignore current brport
                            continue

                        for brport_obj in ifaceobj_getfunc(brport_name):
                            # loop through brport ifaceobjs and check for vxlan bridge-access value

                            if not brport_obj.link_kind & ifaceLinkKind.VXLAN:
                                continue

                            access = brport_obj.get_attr_value_first("bridge-access")
                            if access == pvid:
                                raise Exception(
                                    "%s: misconfiguration detected: vlan \"%s\" added to two or more VXLANS (%s, %s)" % (
                                        ifname,
                                        access,
                                        ifname,
                                        brport_obj.name
                                    )
                                )

    def _apply_bridge_vids_and_pvid(self, bportifaceobj, ifaceobj_getfunc, 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.

        """

        vids_int =  utils.ranges_to_ints(vids)
        try:
            pvid_int = int(pvid) if pvid else 0
        except Exception:
            self.logger.warning('%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_pvid, running_vids = self.cache.get_pvid_and_vids(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) = \
                    utils.diff_ids(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 as 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)

               vids_to_del = sorted(list(self.remove_bridge_vlans_mapped_to_vnis_from_vids_list(None, bportifaceobj, vids_to_del)))

               self.iproute2.batch_start()
               self.iproute2.bridge_vlan_del_vid_list_self(bportifaceobj.name,
                                          utils.compress_into_ranges(
                                          vids_to_del), isbridge)
               self.iproute2.batch_commit()
        except Exception as e:
                self.log_warn('%s: failed to del vid `%s` (%s)'
                        %(bportifaceobj.name, str(vids_to_del), str(e)))

        try:
            if pvid_to_del:
               self.iproute2.bridge_vlan_del_pvid(bportifaceobj.name,
                                               pvid_to_del)
        except Exception as e:
                self.log_warn('%s: failed to del pvid `%s` (%s)'
                        %(bportifaceobj.name, pvid_to_del, str(e)))

        try:

            if vids_to_add:
                self.iproute2.batch_start()
                self.iproute2.bridge_vlan_add_vid_list_self(
                    bportifaceobj.name,
                    utils.compress_into_ranges(sorted(list(vids_to_add))),
                    isbridge
                )
                self.iproute2.batch_commit()
        except Exception as 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.iproute2.bridge_vlan_add_pvid(bportifaceobj.name,
                                                pvid_to_add)
        except Exception as e:
                self.log_error('%s: failed to set pvid `%s` (%s)'
                               %(bportifaceobj.name, pvid_to_add, str(e)),
                               bportifaceobj)

    def get_bridge_vlans_mapped_to_vnis_as_integer_list(self, ifaceobj):
        """
            Get all vlans that the user wants to configured in vlan-vni maps
        """
        try:
            vids = []

            for vlans_vnis_map in ifaceobj.get_attr_value("bridge-vlan-vni-map"):
                for vlans_vni_map in vlans_vnis_map.split():
                    vids.extend(utils.ranges_to_ints([vlans_vni_map.split("=")[0]]))

            return vids
        except Exception as e:
            self.logger.debug("get_bridge_vlans_mapped_to_vnis_as_integer_list: %s" % str(e))
            return []

    def remove_bridge_vlans_mapped_to_vnis_from_vids_list(self, bridge_ifaceobj, vxlan_ifaceobj, vids_list):
        """
            For single vxlan we need to remove the vlans mapped to vnis
            from the vids list otherwise they will get removed from the brport
        """
        if not (vxlan_ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN):
            return vids_list

        user_config_vids = []

        if bridge_ifaceobj:
            for vid in self.get_bridge_vlans_mapped_to_vnis_as_integer_list(bridge_ifaceobj):
                user_config_vids.append(vid)

        if vxlan_ifaceobj:
            for vid in self.get_bridge_vlans_mapped_to_vnis_as_integer_list(vxlan_ifaceobj):
                user_config_vids.append(vid)

        for vlan in user_config_vids:
            try:
                vids_list.remove(vlan)
            except Exception:
                pass

        return vids_list

    def _apply_bridge_vlan_aware_port_settings_all(self, bportifaceobj, ifaceobj_getfunc,
                                                   bridge_vids=None,
                                                   bridge_pvid=None):
        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:
            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)

            bport_pvids = bportifaceobj.get_attr_value_first('bridge-pvid')
            if bport_pvids:
                pvids = re.split(r'[\s\t]\s*', bport_pvids)

        if vids:
            vids_final =  vids
        elif bridge_vids:
            vids_final = bridge_vids

        if allow_untagged == 'yes':
            if pvids:
                pvid_final = pvids[0]
            elif bridge_pvid:
                pvid_final = bridge_pvid
            else:
                pvid_final = '1'
        else:
            pvid_final = None

        self._apply_bridge_vids_and_pvid(bportifaceobj, ifaceobj_getfunc, 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')):
            # Old style bridge port vid info
            # skip new style setting on ports
            return
        self.logger.info('%s: applying bridge configuration '
                         %ifaceobj.name + 'specific to ports')

        bridge_vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
        if bridge_vids:
           bridge_vids = re.split(r'[\s\t,]\s*', bridge_vids)
        else:
           bridge_vids = None

        bridge_pvid = ifaceobj.get_attr_value_first('bridge-pvid')
        if bridge_pvid:
           bridge_pvid = re.split(r'[\s\t]\s*', bridge_pvid)[0]
        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.iproute2.batch_start()
        for bport in bridgeports:
            # on link_set_master we need to wait until we cache the correct
            # notification and register the brport as slave
            if not self.cache.bridge_port_exists(ifaceobj.name, bport):
                self.logger.info('%s: skipping bridge config' %ifaceobj.name +
                        ' for port %s (missing port)' %bport)
                continue
            self.logger.info('%s: processing bridge config for port %s'
                             %(ifaceobj.name, bport))
            bportifaceobjlist = ifaceobj_getfunc(bport)
            if not bportifaceobjlist:
                continue
            for bportifaceobj in bportifaceobjlist:
                # Dont process bridge port if it already has been 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, ifaceobj_getfunc, bridge_vids, bridge_pvid)
                    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 as e:
                    err = True
                    self.logger.warning('%s: %s' %(ifaceobj.name, str(e)))
                    pass
        self.iproute2.batch_commit()
        if err:
           raise Exception('%s: errors applying port settings' %ifaceobj.name)

    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.warning('%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.cache.get_bridge_name_from_port(ifaceobj.name)
        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.cache.link_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:
            self.netlink.link_set_master(ifaceobj.name, bridge_name)

            if ifaceobj.name not in self.svd_list:
                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:
            self._apply_bridge_vlan_aware_port_settings_all(ifaceobj, ifaceobj_getfunc, 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

        # check for bridge-learning on l2 vni in evpn setup
        self.syntax_check_learning_l2_vni_evpn(ifaceobj)

        # detect and warn when arp suppression is enabled and there is no vlan configured
        self.syntax_check_bridge_arp_vni_vlan(ifaceobj, ifaceobj_getfunc)

        vlan_aware_bridge = self.cache.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_just_created):
        if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
            if not self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc):
                return False
            if not link_just_created and not self.cache.bridge_is_vlan_aware(ifaceobj.name):
                # if bridge-vlan-aware was added on a existing old-bridge, we need to reprocess all ports
                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, brport_ifaceobj, brport_name, brport_ifla_info_slave_data, user_config_brport_learning_nl, cached_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 = {}

        if user_config_brport_learning_nl is None:
            user_config_brport_learning_nl = self.bridge_vxlan_port_learning
            # bridge-learning is not configured by the user or by a policy
            # use "bridge-vxlan-port-learning" policy to set bridge-learning (default on)

        if user_config_brport_learning_nl != cached_brport_learning:
            brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING] \
                = cached_brport_learning \
                = user_config_brport_learning_nl

            self.logger.info(
                "%s: %s: set bridge-learning %s"
                % (bridge_name, brport_name, "on" if user_config_brport_learning_nl else "off")
            )
        else:
            # in this case, the current bridge-learning value is properly configured and
            # doesn't need to be reset. We need to make sure that BRPORT_LEARNING is not
            # part of ifla_info_slave_data.
            try:
                del brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING]
            except Exception:
                pass

        #
        # vxlan-learning sync:
        #
        user_brport_vxlan_learning_config = brport_ifaceobj.get_attr_value_first("vxlan-learning")

        if not user_brport_vxlan_learning_config:
            # if vxlan-learning is not defined on the brport

            if user_config_brport_learning_nl is not None:
                # if bridge-learning is defined on the brport use it's value to sync vxlan-learning
                user_brport_vxlan_learning_config_nl = user_config_brport_learning_nl

            else:
                # if bridge-learning is not defined, we check for policy and convert it into netlink format
                brport_vxlan_learning_config = policymanager.policymanager_api.get_attr_default("vxlan", "vxlan-learning")

                if brport_vxlan_learning_config is not None:
                    user_brport_vxlan_learning_config_nl = utils.get_boolean_from_string(brport_vxlan_learning_config)

                else:
                    # None = no policy set, default to the current brport learning
                    user_brport_vxlan_learning_config_nl = cached_brport_learning

        else:
            # if vxlan-learning is set we need to honor the user config
            user_brport_vxlan_learning_config_nl = utils.get_boolean_from_string(user_brport_vxlan_learning_config)

        if user_brport_vxlan_learning_config_nl != self.cache.get_link_info_data_attribute(
            brport_name,
            Link.IFLA_VXLAN_LEARNING
        ):
            self.logger.info(
                "%s: %s: vxlan learning and bridge learning out of sync: set vxlan-learning %s"
                % (bridge_name, brport_name, "on" if user_brport_vxlan_learning_config_nl else "off")
            )
            ifla_info_data = {Link.IFLA_VXLAN_LEARNING: user_brport_vxlan_learning_config_nl}
            kind = "vxlan"
        # 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 up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware, target_ports=[], newly_enslaved_ports=[]):
        ifname = ifaceobj.name
        single_vxlan_device_ifaceobj = None

        try:
            brports_ifla_info_slave_data    = dict()
            brport_ifaceobj_dict            = dict()
            brport_name_list                = []

            cache_brports_ifla_info_slave_data = {}

            port_processed_override = ifaceobj.module_flags.get(self.name, 0x0) & bridgeFlags.PORT_PROCESSED_OVERRIDE

            running_brports = self.cache.get_slaves(ifname)

            # 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.
            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

            for port in running_brports:
                brport_list = ifaceobj_getfunc(port)
                if brport_list:
                    port_already_processed = False

                    # ports just added to the bridge have to be processed
                    if port not in newly_enslaved_ports:
                        # check if brport was already processed
                        for brportifaceobj in brport_list:
                            if not port_processed_override and brportifaceobj.module_flags.get(self.name, 0x0) & bridgeFlags.PORT_PROCESSED:
                                # skip port if already processed (probably by `up_bridge_port`)
                                port_already_processed = True
                                self.logger.info("%s: port %s: already processed" % (ifname, port))
                                break

                    if not port_already_processed:
                        brport_name_list.append(port)
                        brport_ifaceobj_dict[port] = brport_list[0]
                        brports_ifla_info_slave_data[port] = dict()

                        if not ifupdownflags.flags.PERFMODE and port not in newly_enslaved_ports:
                            # if the port has just been enslaved, info_slave_data is not cached yet
                            cache_brports_ifla_info_slave_data[port] = self.cache.get_link_info_slave_data(port)
                        else:
                            cache_brports_ifla_info_slave_data[port] = {}

            if not brport_name_list:
                self.bridge_process_vidinfo_mcqv4src_maxwait(ifaceobj)
                return

            self.logger.info('%s: applying bridge port configuration: %s' % (ifname, brport_name_list))

            cached_bridge_mcsnoop = self.cache.get_bridge_multicast_snooping(ifname)

            bridge_ports_learning = {}
            bridge_ports_vxlan_arp_suppress = {}
            cached_bridge_ports_learning = {}

            # we iterate through all IFLA_BRPORT supported attributes
            for attr_name, nl_attr in self._ifla_brport_attributes_map.items():
                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 Exception:
                            self.log_error('error while parsing \'%s %s\'' % (attr_name, br_config))
                            continue

                for brport_ifaceobj in list(brport_ifaceobj_dict.values()):
                    brport_config = brport_ifaceobj.get_attr_value_first(attr_name)
                    brport_name = brport_ifaceobj.name

                    if not ifupdownflags.flags.PERFMODE:
                        cached_value = cache_brports_ifla_info_slave_data.get(brport_name, {}).get(nl_attr, None)
                    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_NEIGH_SUPPRESS:
                        bridge_ports_vxlan_arp_suppress[brport_name] = user_config
                        try:
                            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))
                                    continue
                            elif bridge_vlan_aware:
                                if not self.arp_nd_suppress_only_on_vxlan:
                                    user_config = self.get_mod_subattr('bridge-arp-nd-suppress', 'default')
                                elif self.arp_nd_suppress_only_on_vxlan and brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
                                    # ignore the case of VXLAN brport - handled later in the code
                                    continue
                        except Exception:
                            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.
                            # we also store the cached value to avoid an extra cache lookup.
                            bridge_ports_learning[brport_name] = user_config_nl
                            cached_bridge_ports_learning[brport_name] = cached_value

                    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:
                                # 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.
                                # we also store the cached value to avoid an extra cache lookup.
                                cached_bridge_ports_learning[brport_name] = cached_value
                                bridge_ports_learning[brport_name] = self.bridge_vxlan_port_learning

                                if brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
                                    # bridge-learning for vxlan device is handled separatly in sync_bridge_learning_to_vxlan_brport
                                    continue

                                if not ifupdownflags.flags.PERFMODE and brport_name not in newly_enslaved_ports:
                                    # We don't query new slaves and not during boot
                                    try:
                                        if self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_PEER_LINK):
                                            if default_netlink != cached_value:
                                                self.logger.debug('%s: %s: bridge port peerlink: ignoring bridge-learning'
                                                                  % (ifname, brport_name))
                                            continue
                                    except Exception as e:
                                        self.logger.debug('%s: %s: peerlink check: %s' % (ifname, brport_name, str(e)))

                            if nl_attr == Link.IFLA_BRPORT_MULTICAST_ROUTER:

                                if (brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN
                                    and brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT) \
                                    and (
                                    (
                                        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, keep 'bridge-portmcrouter 2'
                                    # on vxlan ports (if not set by the user)
                                    if cached_value == 2:
                                        continue

                            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

            # is the current bridge (ifaceobj) a QinQ bridge?
            # This variable is initialized to None and will be
            # change to True/False, so that the check is only
            # performed once
            qinq_bridge = None

            # applying bridge port configuration via netlink
            for brport_name, brport_ifla_info_slave_data in list(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,
                                                                                     brport_ifaceobj,
                                                                                     brport_name,
                                                                                     brport_ifla_info_slave_data,
                                                                                     bridge_ports_learning.get(brport_name),
                                                                                     cached_bridge_ports_learning.get(brport_name))

                    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) \
                                and self.cache.get_bridge_port_multicast_router(brport_name) != 2:
                            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))

                    #
                    # handling attribute: bridge-arp-nd-suppress
                    # defaults to bridge-vxlan-arp-nd-suppress policy (default False)
                    #
                    user_config_neigh_suppress = bridge_ports_vxlan_arp_suppress.get(brport_name)

                    if user_config_neigh_suppress is None:

                        if qinq_bridge is None:
                            # QinQ bridge hasn't been checked yet
                            qinq_bridge = self.is_qinq_bridge(
                                ifaceobj,
                                brport_name,
                                running_brports,
                                brport_ifaceobj_dict,
                                ifaceobj_getfunc
                            )

                        if qinq_bridge:
                            # exclude QinQ bridge from arp-nd-suppress default policy on
                            config_neigh_suppress = 0
                            self.logger.info("%s: QinQ bridge detected: %s: set bridge-arp-nd-suppress off" % (ifname, brport_name))
                        else:
                            config_neigh_suppress = self.bridge_vxlan_arp_nd_suppress_int
                    else:
                        config_neigh_suppress = int(utils.get_boolean_from_string(user_config_neigh_suppress))

                    brport_neigh_suppress_cached_value = self.cache.get_link_info_slave_data_attribute(
                        brport_name,
                        Link.IFLA_BRPORT_NEIGH_SUPPRESS
                    )

                    if config_neigh_suppress != brport_neigh_suppress_cached_value:
                        brport_ifla_info_slave_data[Link.IFLA_BRPORT_NEIGH_SUPPRESS] = config_neigh_suppress

                        if not user_config_neigh_suppress:
                            # if the configuration is not explicitely defined by the user
                            # we need report that the default behavior is enabled by policy
                            self.logger.info(
                                "%s: set bridge-arp-nd-suppress %s by default on vxlan port (%s)"
                                % (ifname, "on" if self.bridge_vxlan_arp_nd_suppress else "off", brport_name)
                            )
                    else:
                        # the user configuration (or policy) is already configured and running
                        # we need to remove this attribute from the request dictionary
                        try:
                            del brport_ifla_info_slave_data[Link.IFLA_BRPORT_NEIGH_SUPPRESS]
                        except Exception:
                            pass

                    #
                    # SINGLE VXLAN - enable IFLA_BRPORT_VLAN_TUNNEL
                    #

                    if brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN:
                        single_vxlan_device_ifaceobj = brport_ifaceobj
                        brport_vlan_tunnel_cached_value = self.cache.get_link_info_slave_data_attribute(
                            brport_name,
                            Link.IFLA_BRPORT_VLAN_TUNNEL
                        )

                        if not brport_vlan_tunnel_cached_value:
                            self.logger.info("%s: %s: enabling vlan_tunnel on single vxlan device" % (ifname, brport_name))
                            brport_ifla_info_slave_data[Link.IFLA_BRPORT_VLAN_TUNNEL] = 1

                else:
                    kind = None
                    ifla_info_data = {}

                if brport_ifla_info_slave_data or ifla_info_data:
                    try:
                        self.netlink.link_set_brport_with_info_slave_data(
                            ifname=brport_name,
                            kind=kind,
                            ifla_info_data=ifla_info_data,
                            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.bridge_process_vidinfo_mcqv4src_maxwait(ifaceobj)

        except Exception as e:
            self.log_error(str(e), ifaceobj)

        if single_vxlan_device_ifaceobj:
            self.apply_bridge_port_vlan_vni_map(single_vxlan_device_ifaceobj)

    @staticmethod
    def range_to_string(range_start, range_end):
        return "%s" % range_start if range_start == range_end else "%s-%s" % (range_start, range_end)

    def range_list_to_string(self, ifname, vni_list):
        range_list = [v for v in utils.ints_to_ranges(vni_list)]

        if len(range_list) != 1 and len(range_list[0]) > 0:
            self.logger.debug("%s: vlan-vni-map has duplicated ranges: %s" % (ifname, json.dumps(range_list, indent=4)))
            self.log_error("misconfiguration detected - see debug output for details")

        return self.range_to_string(range_list[0][0], range_list[0][1])

    def check_duplicate_vnis(self, ifaceobj, vlan_vni_dict):
        rev = {}

        for key, value in vlan_vni_dict.items():
            rev.setdefault(value, set()).add(key)

        duplicates = [(key, values) for key, values in rev.items() if len(values) > 1]

        if duplicates:
            err_msg = ["duplicate vnis detected - see details below"]

            for vni, vlans in duplicates:
                err_msg.append("\tvni %s assigned to vlans: %s" % (vni, ", ".join(map(str, vlans))))

            self.log_error("\n".join(err_msg), ifaceobj)
            return False

        return True

    def get_vlan_vni_ranges_from_dict(self, ifname, vlan_vni_dict):
        """
        Since bridge-vlan-vni-map is a multiline attribute, we expend all the ranges
        and have all the vlan-vni mapping in vlan_vni_dict. We need to reconstruct the
        ranges to execute iproute2 commands.

        i.e. for a multiline vlan-vni configuration:
            bridge-vlan-vni-map 1=1
            bridge-vlan-vni-map 2=2
            bridge-vlan-vni-map 3=3
            bridge-vlan-vni-map 4=4

        we will only execute a single ranged-command: vlan add dev vxlan48 vid 1-4 tunnel_info id 1-4

        If we find duplicated vlan/vnis in ranges we raise an exception
        """
        vlan_vni_ranges = {}

        def list_to_range(vlan_list, vni_list, range_dict):
            if not vlan_list and not vni_list:
                return
            vlans = self.range_list_to_string(ifname, vlan_list)
            vnis = self.range_list_to_string(ifname, vni_list)
            range_dict[vlans] = vnis

        current_vlan_range = []
        current_vni_range = []

        for vlan in sorted(vlan_vni_dict.keys()):
            vni = vlan_vni_dict[vlan]

            if not current_vlan_range:
                current_vlan_range.append(vlan)
                current_vni_range.append(vni)

            else:
                if vlan - 1 == current_vlan_range[-1] and vni - 1 == current_vni_range[-1]:
                    current_vlan_range.append(vlan)
                    current_vni_range.append(vni)
                else:
                    list_to_range(current_vlan_range, current_vni_range, vlan_vni_ranges)
                    current_vlan_range = [vlan]
                    current_vni_range = [vni]

        list_to_range(current_vlan_range, current_vni_range, vlan_vni_ranges)
        return vlan_vni_ranges

    def check_bridge_vlan_vni_map_reserved(self, ifaceobj, vlan_to_add):
        for vlan in sorted(vlan_to_add):
            self._handle_reserved_vlan(vlan, ifaceobj.name)

    def apply_bridge_port_vlan_vni_map(self, ifaceobj):
        """
        bridge vlan add vid <vlan-id> dev vxlan0
        bridge vlan add dev vxlan0 vid <vlan-id> tunnel_info id <vni>
        """
        vxlan_name = ifaceobj.name
        try:
            self.iproute2.batch_start()

            bridge_vlan_tunnel_info_running_config = self.iproute2.bridge_vlan_tunnel_show(vxlan_name)
            all_user_config = {}

            for bridge_vlan_vni_map_entry in ifaceobj.get_attr_value("bridge-vlan-vni-map"):
                if not bridge_vlan_vni_map_entry:
                    continue

                for vlan_vni_map_entry in bridge_vlan_vni_map_entry.split():
                    try:
                        vlans_str, vni_str = utils.get_vlan_vni_in_map_entry(vlan_vni_map_entry)
                    except Exception:
                        return self.__warn_bridge_vlan_vni_map_syntax_error(vxlan_name, vlan_vni_map_entry)

                    # we need to convert vlan_str and vni_str back to a map {vlan: vni}
                    for vlan, vni in zip(utils.ranges_to_ints([vlans_str]), utils.ranges_to_ints([vni_str])):

                        if vlan in all_user_config:
                            self.log_error("duplicate vlan found: %s" % vlan, ifaceobj)

                        all_user_config[vlan] = vni

            vlan_vni_to_remove = {}
            for k, v in set(bridge_vlan_tunnel_info_running_config.items()) - set(all_user_config.items()):
                vlan_vni_to_remove[k] = v

            vlan_vni_to_add = {}
            for k, v in set(all_user_config.items()) - set(bridge_vlan_tunnel_info_running_config.items()):
                vlan_vni_to_add[k] = v

            vlan_vni_ranges_to_remove = self.get_vlan_vni_ranges_from_dict(ifaceobj.name, vlan_vni_to_remove)

            # check if we have duplicated vnis in the user configuration
            self.check_duplicate_vnis(ifaceobj, vlan_vni_to_add)

            # check reserved vlans
            self.check_bridge_vlan_vni_map_reserved(ifaceobj, vlan_vni_to_add.keys())

            vlan_vni_ranges_to_add = self.get_vlan_vni_ranges_from_dict(ifaceobj.name, vlan_vni_to_add)

            for vlan_range, vni_range in vlan_vni_ranges_to_remove.items():
                self.iproute2.bridge_vlan_del_vid_list_self(vxlan_name, [vlan_range], False)
                self.iproute2.bridge_vlan_del_vlan_tunnel_info(vxlan_name, vlan_range, vni_range)

            for vlan_range, vni_range in vlan_vni_ranges_to_add.items():
                self.iproute2.bridge_vlan_add_vid_list_self(vxlan_name, [vlan_range], False)
                self.iproute2.bridge_vlan_add_vlan_tunnel_info(vxlan_name, vlan_range, vni_range)

            self.iproute2.batch_commit()
        except Exception as e:
            ifaceobj.set_status(ifaceStatus.ERROR)
            raise BridgeVlanVniMapError("%s: error while processing bridge-vlan-vni-map: %s" % (vxlan_name, str(e)))

    def __warn_bridge_vlan_vni_map_syntax_error(self, ifname, user_config_vlan_vni_map):
        self.logger.warning("%s: syntax error: bridge-vlan-vni-map %s" % (ifname, user_config_vlan_vni_map))

    def is_qinq_bridge(self, ifaceobj, brport_name, running_brports, brport_ifaceobj_dict, ifaceobj_getfunc):
        """ Detect QinQ bridge
        Potential improvement: We could add a ifaceobj.link_privflags called
        BRIDGE_QINQ but for now it is not necessary.
        """

        # bridge-vlan-aware case
        if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
            return (ifaceobj.get_attr_value_first("bridge-vlan-protocol") or "").lower() == "802.1ad"

        # old-bridge
        else:
            for qinq_running_brport in running_brports:
                if qinq_running_brport == brport_name:
                    continue

                qinq_running_brport_ifaceobj = brport_ifaceobj_dict.get(qinq_running_brport)

                if not qinq_running_brport_ifaceobj:
                    continue

                if qinq_running_brport_ifaceobj.link_kind & ifaceLinkKind.VLAN:
                    for lower_iface in qinq_running_brport_ifaceobj.lowerifaces or []:
                        for lower_ifaceobj in ifaceobj_getfunc(lower_iface) or []:
                            if (lower_ifaceobj.get_attr_value_first("vlan-protocol") or "").lower() == "802.1ad":
                                return True
        return False

    def bridge_process_vidinfo_mcqv4src_maxwait(self, ifaceobj):
        self._set_bridge_vidinfo_compat(ifaceobj)
        self._set_bridge_mcqv4src_compat(ifaceobj)
        self._process_bridge_maxwait(ifaceobj, self._get_bridge_port_list(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 user_config.replace(",", " ").split():
                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.cache.get_link_info_slave_data_attribute(brport_name, 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 get_bridge_mtu(self, ifaceobj):
        user_config_mtu = ifaceobj.get_attr_value_first("mtu")

        if not user_config_mtu:
            user_config_mtu = policymanager.policymanager_api.get_attr_default(
                module_name="address",
                attr="mtu"
            )

        try:
            if user_config_mtu:
                int(user_config_mtu)
                self.logger.info("%s: set bridge mtu %s" % (ifaceobj.name, user_config_mtu))
                return str(user_config_mtu)
        except Exception as e:
            self.logger.warning("%s: invalid bridge mtu %s: %s" % (ifaceobj.name, user_config_mtu, str(e)))
        return None

    def up_bridge(self, ifaceobj, ifaceobj_getfunc):
        ifname = ifaceobj.name

        if ifupdownflags.flags.PERFMODE:
            link_exists = False
        else:
            link_exists = self.cache.link_exists(ifaceobj.name)

        if not link_exists:
            self.netlink.link_add_bridge(ifname)
            link_just_created = True

            bridge_mtu = self.get_bridge_mtu(ifaceobj)
            if bridge_mtu:
                self.sysfs.link_set_mtu(ifname, bridge_mtu, int(bridge_mtu))
        else:
            link_just_created = False
            self.logger.info('%s: bridge already exists' % ifname)

        bridge_vlan_aware = self.up_check_bridge_vlan_aware(ifaceobj, ifaceobj_getfunc, link_just_created)

        self.up_apply_bridge_settings(ifaceobj, link_just_created, bridge_vlan_aware)

        try:
            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 BridgeVlanVniMapError:
            raise
        except Exception as e:
            self.logger.warning('%s: apply bridge ports settings: %s' % (ifname, str(e)))

        running_ports = ''
        try:
            running_ports = self.cache.get_slaves(ifaceobj.name)
            if not running_ports:
                return
            self._apply_bridge_port_settings_all(ifaceobj,
                                                 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:
                self.iproute2.batch_start()
                for p in running_ports:
                    ifaceobj_list = ifaceobj_getfunc(p)
                    if (ifaceobj_list and ifaceobj_list[0].link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN):
                        self.iproute2.link_down(p)
                        continue
                    self.iproute2.link_up(p)
                try:
                    self.iproute2.batch_commit()
                except Exception as e:
                    # link set up on bridge ports failed - ignore and log debug
                    self.logger.debug("%s: %s" % (ifname, str(e)))

        try:
            self._up_bridge_mac(ifaceobj, link_just_created, 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, link_just_created, ifaceobj_getfunc):
        bridge_mac_iface = self.bridge_mac_iface.get(ifname)

        if bridge_mac_iface and bridge_mac_iface[0] and bridge_mac_iface[1]:
            return 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.cache.link_exists(bridge_mac_intf):
                    continue

                if not iface_mac:
                    iface_mac = self.cache.get_link_address(bridge_mac_intf)
                    # if hwaddress attribute is not configured we use the running mac addr

                self.bridge_mac_iface[ifname] = (bridge_mac_intf, iface_mac)
                return self.bridge_mac_iface[ifname]
        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

            # first we need to make sure that the bridge mac is not already inherited from one of it's port
            bridge_ports = self._get_bridge_port_list_user_ordered(ifaceobj)

            # if the bridge was just created we need to set it's mac address to the first port and not look at the
            # current bridge mac (the bridge driver probably chose the lowest mac of it's port)
            if not link_just_created:
                current_mac = self.cache.get_link_address(ifname)

                for port in bridge_ports or []:
                    if not self.is_vxlan(ifaceobj_getfunc(port)):
                        port_mac = self.cache.get_link_address(port)

                        if current_mac == port_mac:
                            self.logger.info("bridge mac is already inherited from %s" % port)
                            self.bridge_mac_iface[ifname] = (port, port_mac)
                            return self.bridge_mac_iface[ifname]

            for port in bridge_ports 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.cache.get_link_address(port)

                        if iface_mac:
                            self.bridge_mac_iface[ifname] = (port, iface_mac)
                            return self.bridge_mac_iface[ifname]

        return None, None

    @staticmethod
    def is_vxlan(port_obj_list):
        # checking if the port is a vxlan by checking the ifaceobjs
        # instead of checking to cache (saving on locking time)
        for _port_obj in port_obj_list or []:
            if _port_obj.link_kind == ifaceLinkKind.VXLAN:
                return True
        return False

    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.iproute2.bridge_fdb_add(ifaceobj.name, bridge_mac, vlan=None, bridge=True, remote=None)

    def _up_bridge_mac(self, ifaceobj, link_just_created, 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, link_just_created, 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.cache.get_link_address(ifname)
            self.logger.debug('%s: cached hwaddress value: %s' % (ifname, cached_value))
            bridge_mac_int = utils.mac_str_to_int(bridge_mac)

            # if the bridge was just created (link_just_created) we should force-set the mac address
            if not link_just_created and cached_value and utils.mac_str_to_int(cached_value) == bridge_mac_int:
                # 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.netlink.link_set_address(ifname, bridge_mac, bridge_mac_int)  # 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.cache.get_link_address(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 = list(self._modinfo.get('attrs', {}).keys())

            for ifaceobj_config_attr in list(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.cache.link_exists(ifname):
            return

        try:
            self.netlink.link_del(self.get_dummy_brport_name_for_bridge(ifname))
        except Exception:
            pass

        try:
            running_ports = self.cache.get_slaves(ifname)
            if running_ports:
                self.handle_ipv6(running_ports, '0')
                if ifaceobj.link_type != ifaceLinkType.LINK_NA:
                    for p in running_ports:
                        self.netlink.link_down(p)
        except Exception as e:
            self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
        try:
            self.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 = {}
        if ports:
            running_bridge_port_vids = ''
            for p in ports:
                try:
                    _, running_vids = self.cache.get_pvid_and_vids(p)
                    if running_vids:
                        running_bridge_port_vids += ' %s=%s' %(p,
                                                      ','.join(running_vids))
                except Exception:
                    pass
            running_attrs['bridge-port-vids'] = running_bridge_port_vids

            running_bridge_port_pvid = ''
            for p in ports:
                try:
                    running_pvid = self.cache.get_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_pvid

        _, running_bridge_vids = self.cache.get_pvid_and_vids(ifaceobjrunning.name)
        if running_bridge_vids:
            running_attrs['bridge-vids'] = ','.join(utils.compress_into_ranges(running_bridge_vids))
        return running_attrs

    def _query_running_vidinfo(self, ifaceobjrunning, ifaceobj_getfunc,
                               bridgeports=None):
        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, pvid) = self._get_running_vids_n_pvid_str(bport)
            if vids:
                running_bridgeport_vids.append(' '.join(vids))
            if pvid:
                running_bridgeport_pvids.append(pvid)

        bridge_vids = None
        if running_bridgeport_vids:
           (vidval, freq) = Counter(running_bridgeport_vids).most_common()[0]
           if freq == len(bridgeports):
              running_attrs['bridge-vids'] = vidval
              bridge_vids = vidval.split()

        bridge_pvid = None
        if running_bridgeport_pvids:
           (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()[0]

        # Go through all bridge ports and find their vids
        for bport in bridgeports:
            bportifaceobj = ifaceobj_getfunc(bport)
            if not bportifaceobj:
               continue
            bport_vids = []
            bport_pvid = None
            (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
            if vids and vids != bridge_vids:
               bport_vids = vids
            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_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')
               if bport_vids:
                  bportifaceobj[0].replace_config('bridge-vids',
                                                  ' '.join(bport_vids))
               else:
                  # delete any stale bridge-vids under ports
                  bportifaceobj[0].delete_config('bridge-vids')
        return running_attrs

    def _query_running_mcqv4src(self, ifaceobjrunning):
        running_mcqv4src = self.sysfs.bridge_get_mcqv4src(ifaceobjrunning.name)
        mcqs = ['%s=%s' %(v, i) for v, i in list(running_mcqv4src.items())]
        mcqs.sort()
        mcq = ' '.join(mcqs)
        return mcq

    def _query_running_attrs(self, ifaceobjrunning, ifaceobj_getfunc,
                             bridge_vlan_aware=False):

        ifname = ifaceobjrunning.name
        bridgeattrdict = {}
        userspace_stp = 0
        ports = None
        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)))

        bridge_ifla_info_data = self.cache.get_link_info_data(ifname)


        # Fill bridge_ports and bridge stp attributes first
        #
        # bridge-ports
        #
        bridgeattrdict["bridge-ports"] = [" ".join(self.cache.get_slaves(ifname))]

        #
        # bridge-stp
        #
        cached_stp = bool(bridge_ifla_info_data.get(Link.IFLA_BR_STP_STATE))

        if cached_stp != utils.get_boolean_from_string(
                self.get_mod_subattr("bridge-stp", "default")
        ):
            bridgeattrdict['bridge-stp'] = ["yes" if cached_stp else "no"]

        skip_kernel_stp_attrs = cached_stp and userspace_stp

        if skip_kernel_stp_attrs:
            bridge_attributes_map = {
                "bridge-mcqifaddr": Link.IFLA_BR_MCAST_QUERY_USE_IFADDR,
                "bridge-mcquerier": Link.IFLA_BR_MCAST_QUERIER,
                "bridge-mcrouter": Link.IFLA_BR_MCAST_ROUTER,
                "bridge-mcstats": Link.IFLA_BR_MCAST_STATS_ENABLED,
                "bridge-mcsnoop": Link.IFLA_BR_MCAST_SNOOPING,
                "bridge-mclmc": Link.IFLA_BR_MCAST_LAST_MEMBER_CNT,
                "bridge-mclmi": Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL,
                "bridge-mcqri": Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
                "bridge-mcqpi": Link.IFLA_BR_MCAST_QUERIER_INTVL,
                "bridge-mcsqc": Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT,
                "bridge-mcsqi": Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
                "bridge-mcmi": Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
                "bridge-mcqi": Link.IFLA_BR_MCAST_QUERY_INTVL,
            }
        else:
            bridge_attributes_map = dict(self._ifla_br_attributes_map)
            try:
                del bridge_attributes_map[Link.IFLA_BR_STP_STATE]
            except Exception:
                pass

        #
        # bridge-vlan-stats
        #
        cached_vlan_stats = bridge_ifla_info_data.get(Link.IFLA_BR_VLAN_STATS_ENABLED)

        if cached_vlan_stats != utils.get_boolean_from_string(
                self.get_mod_subattr("bridge-vlan-stats", "default")
        ):
            bridgeattrdict['bridge-vlan-stats'] = ["on" if cached_vlan_stats else "off"]

        try:
            del bridge_attributes_map[Link.IFLA_BR_VLAN_STATS_ENABLED]
        except Exception:
            pass

        lambda_nl_value_int_divide100 = lambda x: str(x // 100)
        lambda_nl_value_to_yes_no_boolean = lambda x: "yes" if x else "no"

        bridge_attr_value_netlink_to_string_dict = {
            Link.IFLA_BR_VLAN_PROTOCOL: lambda x: x.lower(),  # return lower case vlan protocol
            Link.IFLA_BR_AGEING_TIME: lambda_nl_value_int_divide100,
            Link.IFLA_BR_MAX_AGE: lambda_nl_value_int_divide100,
            Link.IFLA_BR_FORWARD_DELAY: lambda_nl_value_int_divide100,
            Link.IFLA_BR_HELLO_TIME: lambda_nl_value_int_divide100,
            Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL: lambda_nl_value_int_divide100,
            Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL: lambda_nl_value_int_divide100,
            Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL: lambda_nl_value_int_divide100,
            Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: lambda_nl_value_int_divide100,
            Link.IFLA_BR_MCAST_QUERIER_INTVL: lambda_nl_value_int_divide100,
            Link.IFLA_BR_MCAST_QUERY_INTVL: lambda_nl_value_int_divide100,
            Link.IFLA_BR_VLAN_FILTERING: lambda_nl_value_to_yes_no_boolean,
            Link.IFLA_BR_MCAST_QUERY_USE_IFADDR: lambda_nl_value_to_yes_no_boolean,
            Link.IFLA_BR_MCAST_SNOOPING: lambda_nl_value_to_yes_no_boolean,
            Link.IFLA_BR_MCAST_QUERIER: lambda_nl_value_to_yes_no_boolean,
            Link.IFLA_BR_MCAST_ROUTER: lambda_nl_value_to_yes_no_boolean,
        }

        for attr_name, attr_nl in bridge_attributes_map.items():
            default_value = self.get_mod_subattr(attr_name, "default")
            cached_value = bridge_ifla_info_data.get(attr_nl)

            if cached_value is None:
                continue

            cached_value_string = bridge_attr_value_netlink_to_string_dict.get(attr_nl, str)(cached_value)

            if default_value != cached_value_string:
                bridgeattrdict[attr_name] = [cached_value_string]

        if bridge_vlan_aware:
            if not ports:
                ports = {}
            bridgevidinfo = self._query_running_vidinfo(ifaceobjrunning,
                                                        ifaceobj_getfunc,
                                                        list(ports.keys()))
        else:
            bridgevidinfo = self._query_running_vidinfo_compat(ifaceobjrunning,
                                                               ports)
        if bridgevidinfo:
           bridgeattrdict.update({k : [v] for k, v in list(bridgevidinfo.items())
                                  if v})

        mcq = self._query_running_mcqv4src(ifaceobjrunning)
        if mcq:
            bridgeattrdict['bridge-mcqv4src'] = [mcq]

        if skip_kernel_stp_attrs:
            return bridgeattrdict

        # Do this only for vlan-UNAWARE-bridge
        if ports and not bridge_vlan_aware:
            portconfig = {'bridge-pathcosts' : '',
                          'bridge-portprios' : '',
                          'bridge-learning' : '',
                          'bridge-unicast-flood' : '',
                          'bridge-multicast-flood' : '',
                          'bridge-broadcast-flood' : '',
                          'bridge-arp-nd-suppress' : '',
                         }
            for p, v in list(ports.items()):
                v = str(self.cache.get_brport_cost(p))
                if v and v != self.get_mod_subattr('bridge-pathcosts',
                                                   'default'):
                    portconfig['bridge-pathcosts'] += ' %s=%s' %(p, v)

                v = str(self.cache.get_brport_priority(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.cache.get_brport_learning(p))
                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.cache.get_brport_unicast_flood(p))
                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.cache.get_brport_multicast_flood(p))
                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.cache.get_brport_broadcast_flood(p))
                if (v and
                    v != self.get_mod_subattr('bridge-broadcast-flood',
                                              'default')):
                    portconfig['bridge-broadcast-flood'] += ' %s=%s' %(p, v)

                v = utils.get_onff_from_onezero(self.cache.get_brport_neigh_suppress(p))
                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 list(portconfig.items())
                                    if v})

        return bridgeattrdict

    def _query_check_mcqv4src(self, ifaceobj, ifaceobjcurr):
        running_mcqs = self._query_running_mcqv4src(ifaceobj)
        attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
        if attrval:
            mcqs = attrval.split()
            mcqs.sort()
            mcqsout = ' '.join(mcqs)
            ifaceobjcurr.update_config_with_status('bridge-mcqv4src',
                         running_mcqs, 1 if running_mcqs != mcqsout else 0)

    def _query_check_bridge_vidinfo(self, ifname, ifaceobj, ifaceobjcurr):
        #
        # bridge-port-vids
        #
        bridge_port_vids_user_config = ifaceobj.get_attr_value_first("bridge-port-vids")
        if bridge_port_vids_user_config:

            port_list = self.parse_port_list(ifname, bridge_port_vids_user_config)

            if not port_list:
                self.log_warn("%s: could not parse 'bridge-port-vids %s'"
                              % (ifname, bridge_port_vids_user_config))
                ifaceobjcurr.update_config_with_status("bridge-port-vids", "ERROR", 1)
                return

            error = False
            for port_config in port_list:
                try:
                    port, vids_raw = port_config.split("=")
                    packed_vids = vids_raw.split(",")

                    running_pvid, running_vids = self.cache.get_pvid_and_vids(port)

                    if not utils.compare_ids(packed_vids, running_vids, pvid=running_pvid, expand_range=False):
                        error = True

                except Exception as e:
                    self.log_warn("%s: failure checking vid %s (%s)" % (ifname, port_config, str(e)))

            ifaceobjcurr.update_config_with_status("bridge-port-vids", bridge_port_vids_user_config, error)

        #
        # bridge-port-pvids
        #
        attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
        if attrval:
            portlist = self.parse_port_list(ifaceobj.name, attrval)
            if not portlist:
                self.log_warn('%s: could not parse \'bridge-port-pvids %s\''
                              % (ifname, attrval))
                return

            error = False
            running_pvid_config = []
            for p in portlist:
                (port, pvid) = p.split('=')
                running_pvid, _ = self.cache.get_pvid_and_vids(port)

                running_pvid_config.append("%s=%s" % (port, running_pvid))

                if running_pvid != int(pvid):
                    error = True

            ifaceobjcurr.update_config_with_status(
                "bridge-port-pvids",
                " ".join(running_pvid_config),
                int(error)
            )

        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):
        if not self._is_bridge(ifaceobj):
            return

        ifname = ifaceobj.name

        if not self.cache.bridge_exists(ifname):
            self.logger.info("%s: bridge: does not exist" % (ifname))
            return

        self._query_check_snooping_wdefault(ifaceobj)

        user_config_attributes = 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 user_config_attributes:
            user_config_attributes.append('bridge-stp')

        if not user_config_attributes:
            return

        if "bridge-ports" in user_config_attributes:
            self.query_check_bridge_ports(ifaceobj, ifaceobjcurr, self.cache.get_slaves(ifname), ifaceobj_getfunc)

        if "bridge-ports-condone-regex" in user_config_attributes:
            ifaceobjcurr.update_config_with_status(
                "bridge-ports-condone-regex",
                self._get_bridge_port_condone_regex(ifaceobj, True),
                0
            )

        # Those attributes require separate handling
        filter_attributes = [
            "bridge-trunk",
            "bridge-ports",
            "bridge-vids",
            "bridge-trunk",
            "bridge-mcqv4src",
            "bridge-port-vids",
            "bridge-port-pvids",
            "bridge-l2protocol-tunnel",
            "bridge-ports-condone-regex"
        ]

        ignore_attributes = (
            # 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.
            "bridge-pvid",
            "bridge-allow-untagged",
        )
        for attr in ignore_attributes:
            if attr in user_config_attributes:
                ifaceobjcurr.update_config_with_status(attr, ifaceobj.get_attr_value_first(attr), 2)
                filter_attributes.append(attr)

        bridge_config = set(user_config_attributes).difference(filter_attributes)
        cached_ifla_info_data = self.cache.get_link_info_data(ifname)

        self._query_check_bridge_attributes(ifaceobj, ifaceobjcurr, bridge_config, cached_ifla_info_data)
        self._query_check_brport_attributes_on_bridge(ifname, ifaceobj, ifaceobjcurr, bridge_config)
        self._query_check_bridge_vidinfo(ifname, ifaceobj, ifaceobjcurr)
        self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
        self._query_check_l2protocol_tunnel_on_bridge(ifname, ifaceobj, ifaceobjcurr)

    def _query_check_bridge_always_up(self, ifname, ifaceobj, ifaceobjcurr, bridge_config):
        bridge_always_up = ifaceobj.get_attr_value_first("bridge-always-up")

        if bridge_always_up:
            bridge_config.remove("bridge-always-up")

        if utils.get_boolean_from_string(bridge_always_up):
            try:
                link_exists = self.cache.link_exists(self.get_dummy_brport_name_for_bridge(ifname))
            except Exception:
                link_exists = False

            ifaceobjcurr.update_config_with_status(
                "bridge-always-up",
                "yes" if link_exists else "no",
                not link_exists
            )

    def _query_check_bridge_attributes(self, ifaceobj, ifaceobjcurr, bridge_config, cached_ifla_info_data):
        for attr in list(bridge_config):
            query_check_handler, netlink_attr = self._bridge_attribute_query_check_handler.get(attr, (None, None))

            if callable(query_check_handler):
                query_check_handler(attr, ifaceobj.get_attr_value_first(attr), ifaceobjcurr, cached_ifla_info_data.get(netlink_attr))
                bridge_config.remove(attr)

        self._query_check_bridge_always_up(ifaceobj.name, ifaceobj, ifaceobjcurr, bridge_config)

    def _query_check_brport_attributes_on_bridge(self, ifname, ifaceobj, ifaceobjcurr, bridge_config):
        brports_info_slave_data = {}
        # bridge_config should only have bridge-port-list attributes
        for attr in bridge_config:
            attr_nl = self._ifla_brport_attributes_map.get(attr)
            brport_query_check_handler = self._brport_attribute_query_check_handler.get(attr)

            if not attr_nl or not brport_query_check_handler:
                self.logger.warning("%s: query-check: missing handler for attribute: %s (%s)" % (ifname, attr, attr_nl))
                continue

            running_config = []
            status = 0

            for port_config in self.parse_port_list(ifname, ifaceobj.get_attr_value_first(attr)) or []:
                port, config = port_config.split("=")

                if not port in brports_info_slave_data:
                    info_slave_data = brports_info_slave_data[port] = self.cache.get_link_info_slave_data(port)
                else:
                    info_slave_data = brports_info_slave_data[port]

                port_config, port_status = brport_query_check_handler(port, config, info_slave_data.get(attr_nl))

                running_config.append(port_config)

                if port_status:
                    status = 1

            ifaceobjcurr.update_config_with_status(
                attr,
                " ".join(running_config),
                status
            )

    @staticmethod
    def _query_check_br_attr_wait(attr, wait_value, ifaceobjcurr, __):
        ifaceobjcurr.update_config_with_status(attr, wait_value, 0)

    def _query_check_br_attr_stp(self, attr, stp_value, ifaceobjcurr, cached_value):
        if not stp_value:
            if ifupdownflags.flags.WITHDEFAULTS:
                stp_value = "on" if self.default_stp_on else "off"
            else:
                return

        user_config_to_nl = utils.get_boolean_from_string(stp_value)

        ifaceobjcurr.update_config_with_status(
            attr,
            "yes" if cached_value else "no",
            user_config_to_nl != bool(cached_value)
        )

    @staticmethod
    def _query_check_br_attr_int(attr, user_config, ifaceobjcurr, cached_value):
        ifaceobjcurr.update_config_with_status(
            attr,
            str(cached_value),
            int(user_config) != cached_value
        )

    @staticmethod
    def _query_check_br_attr_int_divided100(attr, user_config, ifaceobjcurr, cached_value):
        value = cached_value // 100
        ifaceobjcurr.update_config_with_status(
            attr,
            str(value),
            int(user_config) != value
        )

    @staticmethod
    def _query_check_br_attr_boolean(attr, user_config, ifaceobjcurr, cached_value):
        ifaceobjcurr.update_config_with_status(
            attr,
            "yes" if cached_value else "no",
            utils.get_boolean_from_string(user_config) != cached_value
        )

    @staticmethod
    def _query_check_br_attr_boolean_on_off(attr, user_config, ifaceobjcurr, cached_value):
        ifaceobjcurr.update_config_with_status(
            attr,
            "on" if cached_value else "off",
            utils.get_boolean_from_string(user_config) != cached_value
        )

    @staticmethod
    def _query_check_br_attr_string(attr, user_config, ifaceobjcurr, cached_value):
        ifaceobjcurr.update_config_with_status(
            attr,
            cached_value,
            user_config.lower() != cached_value
        )

    @staticmethod
    def _query_check_brport_attr_boolean_on_off(port, user_config, cached_value):
        return "%s=%s" % (port, "on" if cached_value else "off"), utils.get_boolean_from_string(user_config) != cached_value

    @staticmethod
    def _query_check_brport_attr_boolean_yes_no(port, user_config, cached_value):
        return "%s=%s" % (port, "yes" if cached_value else "no"), utils.get_boolean_from_string(user_config) != cached_value

    @staticmethod
    def _query_check_brport_attr_int(port, user_config, cached_value):
        return "%s=%s" % (port, cached_value), int(user_config) != cached_value

    @classmethod
    def _query_check_brport_attr_portmcrouter(cls, port, user_config, cached_value):
        return (
            "%s=%s" % (port, cls._ifla_brport_multicast_router_dict_int_to_str.get(cached_value)),
            cls._ifla_brport_multicast_router_dict_to_int.get(user_config) != cached_value
        )

    ####################################################################################################################

    def query_check_bridge_ports(self, ifaceobj, ifaceobjcurr, running_port_list, ifaceobj_getfunc):

        # if bridge-always-up is set we need to remove the dummy brport from the running_port_list
        if utils.get_boolean_from_string(ifaceobj.get_attr_value_first("bridge-always-up")):
            try:
                running_port_list.remove(self.get_dummy_brport_name_for_bridge(ifaceobj.name))
            except Exception:
                pass

        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

        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 Exception:
            port_list = running_port_list

        difference = set(running_port_list).symmetric_difference(bridge_all_ports)
        bridge_port_condone_regex = self._get_bridge_port_condone_regex(ifaceobj)

        if bridge_port_condone_regex:
            # Drop any condoned port from the difference set
            condone_ports = [port for port in difference if bridge_port_condone_regex.match(port)]

            for port in condone_ports:
                try:
                    difference.remove(port)
                except ValueError:
                    pass

                # Tag all condoned ports in brackets in output
                if port not in bridge_all_ports:
                    port_list.append("(%s)" % port)

        ifaceobjcurr.update_config_with_status(
            "bridge-ports",
            " ".join(port_list) if port_list else "",
            0 if not difference else 1
        )

    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 = self.get_ifaceobj_bridge_vids_value(ifaceobj)
            if vids: return re.split(r'[\s\t,]\s*', vids)
        return None

    def _get_bridge_pvid(self, bridgename, ifaceobj_getfunc):
        ifaceobjs = ifaceobj_getfunc(bridgename)
        pvid = None
        for ifaceobj in ifaceobjs:
            pvid = ifaceobj.get_attr_value_first('bridge-pvid')
            if pvid:
                break
        return pvid

    def _query_check_bridge_port_vidinfo(self, ifname, bridge_name, ifaceobj, ifaceobjcurr, ifaceobj_getfunc):
        running_pvid, running_vids = self.cache.get_pvid_and_vids(ifname)

        if (ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN):
            return

        #
        # bridge-access
        #
        brport_vid_access_user_config = ifaceobj.get_attr_value_first("bridge-access")

        if brport_vid_access_user_config:
            try:
                vid_int = int(brport_vid_access_user_config)
            except ValueError as e:
                ifaceobjcurr.update_config_with_status("bridge-access", brport_vid_access_user_config, 1)
                raise Exception("%s: bridge-access invalid value: %s" % (ifname, str(e)))

            ifaceobjcurr.update_config_with_status(
                "bridge-access",
                str(running_pvid),
                running_pvid != vid_int or running_vids[0] != vid_int
            )
            return

        #
        # bridge-pvid
        #
        brport_pvid_user_config = ifaceobj.get_attr_value_first("bridge-pvid")

        if brport_pvid_user_config:
            try:
                pvid = int(brport_pvid_user_config)
            except ValueError as e:
                ifaceobjcurr.update_config_with_status("bridge-pvid", brport_pvid_user_config, 1)
                raise Exception("%s: bridge-pvid invalid value: %s" % (ifname, str(e)))

            ifaceobjcurr.update_config_with_status(
                "bridge-pvid",
                str(running_pvid),
                running_pvid != pvid
            )
        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
            try:
                pvid = int(self._get_bridge_pvid(bridge_name, ifaceobj_getfunc))
            except (TypeError, ValueError):
                pvid = 0
            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)

           # Special treatment to make sure that the vlans mapped with vnis
           # (in single-vxlan context) are not mistaken for regular vlans.
           # We need to proactively remove them from the "running_vids"
           vlans_mapped_with_vnis = self.get_bridge_vlans_mapped_to_vnis_as_integer_list(ifaceobj)
           new_running_vids = []
           user_config_vids = utils.ranges_to_ints(vids)
           for v in running_vids:
               if v in user_config_vids:
                   new_running_vids.append(v)
               elif v not in vlans_mapped_with_vnis:
                   new_running_vids.append(v)
           running_vids = new_running_vids
           #####################################################################

           if not running_vids or not utils.compare_ids(vids, running_vids, running_pvid, expand_range=False):
               running_vids = [str(o) for o in running_vids]
               ifaceobjcurr.update_config_with_status(attr_name,
                                            ' '.join(running_vids), 1)
           else:
               ifaceobjcurr.update_config_with_status(attr_name,
                                            ' '.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(bridge_name, ifaceobj_getfunc)
           if (bridge_vids and (not running_vids  or
                   not utils.compare_ids(bridge_vids, running_vids, running_pvid, expand_range=False))):
              ifaceobjcurr.status = ifaceStatus.ERROR
              ifaceobjcurr.status_str = 'bridge vid error'

    _query_check_brport_attributes = (
        "bridge-pvid",
        "bridge-vids",
        "bridge-trunk",
        "bridge-access",
        "bridge-pathcosts",
        "bridge-portprios",
        "bridge-portmcrouter",
        "bridge-learning",
        "bridge-portmcfl",
        "bridge-unicast-flood",
        "bridge-multicast-flood",
        "bridge-broadcast-flood",
        "bridge-arp-nd-suppress",
        "bridge-l2protocol-tunnel"
    )

    def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr,
                                 ifaceobj_getfunc):

        ifname = ifaceobj.name

        if not self.cache.link_is_bridge_port(ifname):
            # Mark all bridge brport attributes as failed
            ifaceobjcurr.check_n_update_config_with_status_many(
                ifaceobj, self._query_check_brport_attributes, 1
            )
            return

        bridge_name = self.cache.get_bridge_name_from_port(ifname)
        if not bridge_name:
            self.logger.warning("%s: unable to determine bridge name" % ifname)
            return

        if self.cache.bridge_is_vlan_aware(bridge_name):
            self._query_check_bridge_port_vidinfo(ifname, bridge_name, ifaceobj, ifaceobjcurr, ifaceobj_getfunc)

        brport_info_slave_data = self.cache.get_link_info_slave_data(ifname)

        #
        # bridge-portmcfl
        #
        portmcfl = ifaceobj.get_attr_value_first("bridge-portmcfl")

        if portmcfl:
            cached_value = brport_info_slave_data.get(Link.IFLA_BRPORT_FAST_LEAVE)

            ifaceobjcurr.update_config_with_status(
                "bridge-portmcfl",
                "yes" if cached_value else "no",
                utils.get_boolean_from_string(portmcfl) != cached_value
            )

        #
        # bridge-portmcrouter
        #
        portmcrouter = ifaceobj.get_attr_value_first("bridge-portmcrouter")

        if portmcrouter:
            cached_value = brport_info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER)

            ifaceobjcurr.update_config_with_status(
                "bridge-portmcrouter",
                self._ifla_brport_multicast_router_dict_int_to_str.get(cached_value),
                self._ifla_brport_multicast_router_dict_to_int.get(portmcrouter) != cached_value
            )

        #
        # bridge-learning
        # bridge-unicast-flood
        # bridge-multicast-flood
        # bridge-broadcast-flood
        # bridge-arp-nd-suppress
        #
        for attr_name, attr_nl in (
                ("bridge-learning", Link.IFLA_BRPORT_LEARNING),
                ("bridge-unicast-flood", Link.IFLA_BRPORT_UNICAST_FLOOD),
                ("bridge-multicast-flood", Link.IFLA_BRPORT_MCAST_FLOOD),
                ("bridge-broadcast-flood", Link.IFLA_BRPORT_BCAST_FLOOD),
                ("bridge-arp-nd-suppress", Link.IFLA_BRPORT_NEIGH_SUPPRESS),
        ):
            attribute_value = ifaceobj.get_attr_value_first(attr_name)

            if not attribute_value:
                continue

            cached_value = brport_info_slave_data.get(attr_nl)

            ifaceobjcurr.update_config_with_status(
                attr_name,
                "on" if cached_value else "off",
                utils.get_boolean_from_string(attribute_value) != cached_value
                )

        #
        # bridge-pathcosts
        # bridge-portprios
        #
        for attr_name, attr_nl in (
                ("bridge-pathcosts", Link.IFLA_BRPORT_COST),
                ("bridge-portprios", Link.IFLA_BRPORT_PRIORITY),
        ):
            attribute_value = ifaceobj.get_attr_value_first(attr_name)

            if not attribute_value:
                continue

            cached_value = brport_info_slave_data.get(attr_nl)

            try:
                ifaceobjcurr.update_config_with_status(
                    attr_name,
                    str(cached_value),
                    int(attribute_value) != cached_value
                )
            except ValueError as e:
                ifaceobjcurr.update_config_with_status(attr_name, str(cached_value), 1)
                raise Exception("%s: %s invalid value: %s" % (ifname, attr_name, str(e)))

        self._query_check_l2protocol_tunnel_on_port(ifaceobj, ifaceobjcurr)

        #
        # bridge-vlan-vni-map
        #
        cached_vlans, cached_vnis = self.get_vlan_vni_ranges(self.cache.get_vlan_vni(ifaceobj.name))

        for bridge_vlan_vni_map_entry in ifaceobj.get_attr_value("bridge-vlan-vni-map") or []:
            fail = False

            for vlan_vni in bridge_vlan_vni_map_entry.split():
                try:
                    vlans_str, vni_str = utils.get_vlan_vni_in_map_entry(vlan_vni)
                except Exception:
                    fail = True
                    self.__warn_bridge_vlan_vni_map_syntax_error(ifname, vlan_vni)
                    continue

                if fail:
                    # if we already have detected an error on this entry there's
                    # no point doing anything else than syntax check on the rest
                    continue

                vlans_list = utils.ranges_to_ints([vlans_str])
                vnis_list = utils.ranges_to_ints([vni_str])

                try:
                    for i, vlan in enumerate(vlans_list):
                        index = cached_vnis.index(vlan)

                        if vlan != cached_vnis[index] or vnis_list[i] != cached_vlans[index]:
                            fail = True
                except Exception:
                    fail = True

            ifaceobjcurr.update_config_with_status("bridge-vlan-vni-map", bridge_vlan_vni_map_entry, fail)

    @staticmethod
    def get_vlan_vni_ranges(bridge_vlan_tunnel, compress=False):
        vlans = []
        vnis = []

        if not bridge_vlan_tunnel:
            return vlans, vnis

        tunnel_vlan_range = None
        tunnel_vni_range = None

        for tunnel_vlan, tunnel_vni, tunnel_flags in bridge_vlan_tunnel:

            if tunnel_flags & Link.BRIDGE_VLAN_INFO_RANGE_BEGIN:
                tunnel_vlan_range = tunnel_vlan
                tunnel_vni_range = tunnel_vni

            elif tunnel_flags & Link.BRIDGE_VLAN_INFO_RANGE_END:

                if compress:
                    vlans.append("%s-%s" % (tunnel_vlan_range, tunnel_vlan))
                    vnis.append("%s-%s" % (tunnel_vni_range, tunnel_vni))
                else:
                    vlans.extend(range(tunnel_vlan_range, tunnel_vlan + 1))
                    vnis.extend(range(tunnel_vni_range, tunnel_vni + 1))

            else:
                vlans.append(tunnel_vlan)
                vnis.append(tunnel_vni)

        return vlans, vnis

    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, ifname, ifaceobj, ifaceobjcurr):
        """
            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 = list(config_per_port_dict.keys())
                except Exception:
                    ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, 1)
                    return
            else:
                config_per_port_dict = {}
                brport_list = self.cache.get_slaves(ifname)

            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.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI)
        cached_ifla_brport_group_mask = self.cache.get_link_info_slave_data_attribute(brport_name, 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.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI)
        cached_ifla_brport_group_mask = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASK)
        running_protocols = []
        for protocol_name, callback in list(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 = list(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, ifaceobj_getfunc)
        else:
            self._query_check_bridge_port(ifaceobj, ifaceobjcurr,
                                          ifaceobj_getfunc)

    def _query_running_bridge(self, ifaceobjrunning, ifaceobj_getfunc):
        if self.cache.bridge_is_vlan_aware(ifaceobjrunning.name):
            ifaceobjrunning.update_config('bridge-vlan-aware', 'yes')
            ifaceobjrunning.update_config_dict(self._query_running_attrs(
                                               ifaceobjrunning,
                                               ifaceobj_getfunc,
                                               bridge_vlan_aware=True))
        else:
            ifaceobjrunning.update_config_dict(self._query_running_attrs(
                                               ifaceobjrunning, None))

    def _query_running_bridge_port_attrs(self, ifaceobjrunning, bridgename):
        if self.systcl_get_net_bridge_stp_user_space() == '1':
            return

        v = str(self.cache.get_brport_cost(ifaceobjrunning.name))
        if v and v != self.get_mod_subattr('bridge-pathcosts', 'default'):
            ifaceobjrunning.update_config('bridge-pathcosts', v)

        v = str(self.cache.get_brport_priority(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.cache.get_bridge_name_from_port(
                                                ifaceobjrunning.name)
        bridge_vids = None
        bridge_pvid = None
        if not bridgename:
            self.logger.warning('%s: unable to find bridgename'
                             %ifaceobjrunning.name)
            return

        if not self.cache.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

        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:
           bridge_vids = bridgeifaceobjlist[0].get_attr_value('bridge-vids')
           bridge_pvid = bridgeifaceobjlist[0].get_attr_value_first('bridge-pvid')

        if not bridge_port_vids and bridge_port_pvid:
            # must be an access port
            if bridge_port_pvid != '1':
               ifaceobjrunning.update_config('bridge-access',
                                          bridge_port_pvid)
        else:
            if bridge_port_vids:
                if (not bridge_vids or bridge_port_vids != bridge_vids):
                   ifaceobjrunning.update_config('bridge-vids',
                                        ' '.join(bridge_port_vids))
            if bridge_port_pvid and bridge_port_pvid != '1':
                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.cache.get_brport_learning(ifaceobjrunning.name))
        if v and v != self.get_mod_subattr('bridge-learning', 'default'):
            ifaceobjrunning.update_config('bridge-learning', v)

        v = utils.get_onff_from_onezero(self.cache.get_brport_unicast_flood(ifaceobjrunning.name))
        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.cache.get_brport_multicast_flood(ifaceobjrunning.name))
        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.cache.get_brport_broadcast_flood(ifaceobjrunning.name))
        if v and v != self.get_mod_subattr('bridge-broadcast-flood', 'default'):
            ifaceobjrunning.update_config('bridge-broadcast-flood', v)

        v = utils.get_onff_from_onezero(self.cache.get_brport_neigh_suppress(ifaceobjrunning.name))
        # 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)

        #
        # bridge-vlan-vni-map
        #
        try:
            # there's a mix-up vlan_vni should return vlans/vnis and not vnis/vlans
            # ifquery-check is also using this function and has already code to work
            # around the issue, so to fix our ordering problem we will simply and
            # temporarily, swap the two return values
            cached_vnis, cached_vlans = self.get_vlan_vni_ranges(
                self.cache.get_vlan_vni(ifaceobjrunning.name), compress=True
            )

            if cached_vlans and cached_vnis:
                ifaceobjrunning.update_config(
                    "bridge-vlan-vni-map",
                    " ".join(["%s=%s" % (vlan, vni) for vlan, vni in zip(cached_vlans, cached_vnis)])
                )
        except Exception as e:
            self.logger.debug("bridge-vlan-vni-map: exception: %s" % str(e))

    def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
        try:
            if self.cache.bridge_exists(ifaceobjrunning.name):
                self._query_running_bridge(ifaceobjrunning, ifaceobj_getfunc)
            elif self.cache.link_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 self.bridge_vxlan_arp_nd_suppress \
                and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT \
                and ifaceobj.link_kind & ifaceLinkKind.VXLAN:
            ifaceobj.update_config("bridge-arp-nd-suppress", "on")

        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')


    _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 list(self._run_ops.keys())

    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

        Args:
            **ifaceobj** (object): iface object

            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
                                 'query-running'

        Kwargs:
            **query_ifaceobj** (object): query check ifaceobject. This is only
                valid when op is 'query-checkcurr'. It is an object same as
                ifaceobj, but contains running attribute values and its config
                status. The modules can use it to return queried running state
                of interfaces. status is success if the running state is same
                as user required state in ifaceobj. error otherwise.
        """
        op_handler = self._run_ops.get(operation)
        if not op_handler:
           return

        if (not self.requirements.bridge_utils_is_installed
                and (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT or ifaceobj.link_kind & ifaceLinkKind.BRIDGE)
                    and self.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)
            self.bridge_utils_missing_warning = False

        # make sure BRIDGE_VXLAN is set if we have a vxlan port
        self._re_evaluate_bridge_vxlan(ifaceobj, ifaceobj_getfunc)

        if operation == 'query-checkcurr':
            op_handler(self, ifaceobj, query_ifaceobj,
                       ifaceobj_getfunc=ifaceobj_getfunc)
        else:
            op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
