#!/usr/bin/python3 -u

#-------------------------------------------------------------------------------
#
# Copyright 2014, 2020 Cumulus Networks, inc  all rights reserved
#
#-------------------------------------------------------------------------------

#
#   Import the necessary modules
#
try:
    import sys
    import gc
    import resource 
    import platform
    import ctypes
    import argparse
    import clag.clagnetlink
    import clag.clagthread
    import clag.cmdsrv
    import clag.stpsync
    import clag.fdbsync
    import clag.healthcheck
    import clag.lacpsync
    import clag.mdbsync
    import clag.neighsync
    import clag.reloadconfig as reloadconfig
    import clag.threadmonitor
    import clag.util as util
    import clag.vlansync
    import clag.vxlansync
    import clag.mroutesync
    import clag.clagdPeerXchange_pb2 as peerXchange
    import clag.clagdStream_lib as stream_lib
    import copy
    import clag.clagSdnotify as clagSdnotify
    import daemon
    import fcntl
    import glob
    import ipaddress
    import json
    import lockfile
    import logging
    import logging.handlers
    import nlmanager.nlmanager
    import nlmanager.nlpacket
    import os
    import queue
    import re
    import select
    import signal
    import socket
    import struct
    import subprocess
    import sys
    import syslog
    import threading
    import time
    import traceback
    import xml.etree.ElementTree as ET
    import clag.clagdcsu as clagdcsu
    import tracemalloc
    import linecache
except ImportError as e:
    raise ImportError (str(e) + "- required module not found")

#
#   Define constants
#
_InterfacePath          = "/sys/class/net/"

#
#   Global Variables
#
# Except for the initialization routine these are read-only.
Log                     = None
Parser                  = None
Globcc                  = None
Daemon                  = None
Intf                    = None
Cmd                     = None
StpSync                 = None
FdbSync                 = None
NeighSync               = None
LacpSync                = None
VxLanSync               = None
HealthCheck             = None
ClagNetLink             = None
MrouteSync              = None
VlanSync                = None
DataSyncs               = []
csu_client              = None
#
#   Events for waking up threads
#
stopEvent               = clag.clagthread.Event()
exitEvent               = None
peerlinkEvent           = clag.clagthread.Event()
memTraceEvent           = clag.clagthread.Event()

#
#   Misc - These are miscellaneous global variables. It is possible that these
#   should be protected by a threading lock, but they are not.
#
serverSock              = None
serverSockPair          = None
inputSocks              = []
ourPeerIp               = None
clientSock              = None
clientSockPair          = None
clientSockInit          = False
keep_going              = True
peerSendQueue           = queue.Queue()
watchintv = 0
mstpClagState           = None
peerConfigMismatch      = False
vxlanAnycastMismatch    = False
peerIpMismatch          = False
sysMacMismatch          = False
remoteIpMismatchOnPeer  = False
peerlinkMTUMismatch     = False
bridgePriorityMismatch  = False
bridgeStpStateMismatch  = False
packageVersionMismatch    = False
sendMsgSeq              = 0
system_mac              = None
global_nlm              = nlmanager.nlmanager.NetlinkManager(util.CLAG_NLM_GLOBAL_ID)

#Memory Allocations Tracing threads
memTraceThreadT         = None

#-------------------------------------------------------------------------------
#
#   Threads - The clagd daemon is implemented with six threads, each handling
#   a specfic task. Communication between the threads is handled using events
#   and the global data structures, above. The threads are:
#
#   CollectSysInfo - Periodically runs, getting current infromation on this  
#   system, like LACP partner MACs and dynamically learned MACs.  Signals the
#   PeerSend thread after reading data by setting the peerSendEvent.  When   
#   the LACP information changes the FindDualConnectedIfs thread is signaled 
#   by setting the newLacpDataEvent.                                         
#
#   PeerSend - Waits for the CollectSysInfo thread to retrieve data, opens a    
#   socket to the peer, sends our information to the peer, and closes the       
#   socket.                                                                     
#
#   PeerRecv - Periodically attempts to create a socket with the peer to allow  
#   the peer to connect with us. Once successful, waits for the peer to open
#   a connection. Reads data from that connection, decodes the data, and closes
#   that connection. Then signals the PeerTimeout thread to let it know that
#   the peer is still alive, and if the data changed from the last time we
#   received it from the peer, signals the FindDualConnectedIfs thread or 
#   UpdateMacsFromPeer thread.
#
#   PeerTimeout - Waits for a peerRecvEvent from the PeerRecv thread or times
#   out waiting for that event. If the event is received, but we had previously
#   timed out (peer has just become alive) then change the sysMac of all bonds
#   to the common value. If we timed out, and had previously not timed out,
#   undo everything and act like a standalone switch.
#
#   FindDualConnectedIfs - Waits for a newLacpDataEvent from either the PeerRecv 
#   or CollectSysInfo threads. Compares the partner MAC addresses we collected
#   from our bonds to those received from our peer. Any new matches place the
#   bond in the dual-connected state. Any former matches that are no longer
#   matching, take the bond out of the dual connected state.
#
#   UpdateMacsFromPeer - Waits for a newMacDataEvent from the PeerRecv thread.
#   Compare the MACs we've learned to the MACs the peer has learned and add or
#   remove MACs from our bonds to keep in sync with the peer's dynamically
#   learned MAC addresses.
#
#-------------------------------------------------------------------------------

def SendInitialXmlSyncDone():
    if Intf.isPeerAlive() and not Parser.syncDoneToPeer:
        # Once initial data sync is done we send a sync_done notification to
        # the peer. This is used by the peer as a handshake-done indication.
        Parser.syncDoneToPeer = True
        clagDataAttr = {'version' : Parser._ClagDataVersion, 'packageVersion' : Parser.packageVersion}
        root = ET.Element("clag_data", clagDataAttr)
        ET.SubElement(root, "sync_done")
        xmlStr = ET.tostring(root)
        peerSendQueue.put((None, xmlStr))
        util.queueSetMaxLen('peerSendQueue')
        Log.log("Initial XML data sync to peer done.")

def SendInitialSyncDone():
    if Intf.isPeerAlive() and not Parser.syncDoneToPeer:
        # Once initial data sync is done we send a sync_done notification to
        # the peer. This is used by the peer as a handshake-done indication.
        Parser.syncDoneToPeer = True
        msgHdr = peerXchange.Header()
        msgHdr.type = peerXchange.Header.MessageType.Value('CLAG_PERIODIC_SYNC')
        msgData = peerXchange.ClagPeerSync()
        msgData.version = Parser._ClagDataVersion
        msgData.packageVersion = Parser.packageVersion
        msgData.syncDone = True
        Log.log_debug(Log._LOG_DEBUG_INFORM_PEER, "TXing initial sync data: %s" % str(msgData))
        msgHdr.data = msgData.SerializeToString()
        peerSendQueue.put((msgHdr, None))
        util.queueSetMaxLen('peerSendQueue')
        Log.log("Initial data sync to peer done.")

def CollectSysInfo():
    '''
    This function operates as an independent thread of the script. It retrieves
    information about the system which is used by CLAG and possibly sent to
    the peer switch. There are two types of information collected:
        1. The 802.3ad partner MAC addresses of each bond -> ourLacpDB
        2. The dynamically learned MAC addresses of dual connected bonds -> ourMacDB
    '''
    Log.log_verbose(Log._LOG_DEBUG_COLLECT_SYS, "Beginning execution of the thread CollectSysInfo")
    nlm = nlmanager.nlmanager.NetlinkManager(util.CLAG_NLM_COLLECT_ID)
    while keep_going:
        # Collect all of the local switch's information
        for sync in DataSyncs:
            sync.CollectLocalInfo(nlm)

        # Send this information to our peer
        if Parser and Parser.peerProtoBuf:
            MakeProtoBufMessage()
            if Intf.isPeerAlive():
                for sync in DataSyncs:
                    sync.MakeProtoBufMessage()
        else:
            MakeXmlMessage()

        if Parser and Parser.peerProtoBuf:
            SendInitialSyncDone()
        else:
            SendInitialXmlSyncDone()

        # Indicate that we are alive
        Daemon.TouchKeepAlive()

        Log.log_verbose(Log._LOG_DEBUG_COLLECT_SYS, "Waiting %d seconds before continuing execution of the thread CollectSysInfo" % (Parser.getCurrLacpPoll(),))
        stopEvent.wait(Parser.getCurrLacpPoll())
        stopEvent.clear()
        Log.log_verbose(Log._LOG_DEBUG_COLLECT_SYS, "Executing the thread CollectSysInfo")

    Log.log("Finished execution of the thread CollectSysInfo")


def PeerSend():
    '''
    This function is an independent thread of the script. It opens a socket to
    the peer and sends the LACP data retrieved by the CollectSysInfo thread 
    to the peer, formatted in XML. The socket is then closed and the thread goes
    to sleep waiting for more data from the CollectSysInfo thread.
    '''
    global clientSock
    global clientSockInit
    global sendMsgSeq
    global ourPeerIp
    Log.log_verbose(Log._LOG_DEBUG_INFORM_PEER, "Beginning execution of the thread PeerSend")

    clientSock = None
    while keep_going and not Parser.sendGoodbye:

        # Get the next message to send to the peer
        Log.log_verbose(Log._LOG_DEBUG_INFORM_PEER, "Waiting for an event before continuing execution of the thread PeerSend")
        sendMsg = None
        sendStr = None
        queueEmpty = False
        try:
            (sendMsg, sendStr) = peerSendQueue.get(True, Parser.getPeriodicRun())
        except queue.Empty:
            queueEmpty = True
        Log.log_verbose(Log._LOG_DEBUG_INFORM_PEER, "Executing the thread PeerSend.")

        # Create a socket to the peer switch if necessary
        if not Parser.remotePeerIp:
            if Parser.args.peerIp == 'linklocal':
                remotePeerIp = None
                try:
                    subprocess.run(['/bin/ping6', '-L', '-c', '1', 'ff02::1', '-I', Parser.args.peerIf])
                    neighbors = subprocess.check_output(['/bin/ip', '-6', 'n', 's', 'dev', Parser.args.peerIf]).decode()
                    remotePeerIp = re.findall(r'^(fe80:\S+)', neighbors, re.MULTILINE)
                except (subprocess.CalledProcessError, AttributeError) as e:
                    util.queueClear(peerSendQueue)
                    pass
                if remotePeerIp:
                    try:
                        remotePeerIp.remove(ourPeerIp)
                    except ValueError:
                        pass

                    if len(remotePeerIp) != 1:
                        Log.log_warn("Multiple linklocal neighbors found on peer interface %s: %s" % (Parser.args.peerIf, remotePeerIp))
                    Parser.remotePeerIp = remotePeerIp[0]
                    Log.log("Using %s as remote Peer IP" % remotePeerIp[0])
                else:
                    try:
                        subprocess.check_output(['/bin/ping6', '-L', '-c', '1', 'ff02::1', '-I', Parser.args.peerIf]).decode()
                    except:
                        util.queueClear(peerSendQueue)
                        pass
            else:
                Parser.remotePeerIp = Parser.args.peerIp
        if not queueEmpty and keep_going and Parser.remotePeerIp and clientSock is None:
            for (family, socktype, proto, canonname, sockaddr) in \
                socket.getaddrinfo(Parser.remotePeerIp, Parser.args.peerPort, socket.AF_UNSPEC,
                                   socket.SOCK_STREAM, 0, socket.AI_PASSIVE):

                try:
                    clientSock = socket.socket(family, socktype, proto)
                except socket.error:
                    clientSock = None
                    Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "Unable to create socket for %s:%d" % (Parser.remotePeerIp, Parser.args.peerPort))
                    util.queueClear(peerSendQueue)
                    continue
                # Attempt to connect to the peer
                try:
                    clientSockInit = True
                    clientSock.setsockopt(socket.SOL_SOCKET, 25, bytes(Parser.args.peerIf, "utf-8"))
                    # We need to set TCP_NODELAY option as well as TCP_MAXSEG option to handle socket getting stuck
                    # and packets not able to process on receive side: RM-3061322 RM-3070808
                    # Refer to https://www.techrepublic.com/article/tcp-ip-options-for-high-performance-data-transmission/
                    # In my experiments, we found that the TCP socket fails to receive any data when we have communication
                    # between a hardnode and a softnode just after the hardnode sends a TCP data of size more than 9K.
                    # When we configure the TCP_MAXSEG to a static value (preventing auto negotiation of the MTU), then we do
                    # not see this issue. The value of 5000 was chosen just from experimentation where even setting it to 1000
                    # was avoiding the issue of socket getting stuck.
                    clientSock.setsockopt(socket.SOL_IP, socket.IP_TOS, 0xc0)
                    clientSock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
                    clientSock.setsockopt(socket.SOL_TCP, socket.TCP_MAXSEG, 5000)
                    clientSock.settimeout(Parser.args.peerConnect)
                    clientSock.connect(sockaddr)
                    clientSock.settimeout(Parser.args.sendTimeout)
                    clientSock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, Parser.args.sendBufSize)
                    clientSockInit = False
                    Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "The client socket for sending data to the peer is connected.")
                except Exception as e:
                    if Parser:
                        Parser.CloseClientSock()
                    clientSockInit = False
                    Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "Unable to connect socket to %s:%d: %s " % (Parser.remotePeerIp, Parser.args.peerPort, str(e)))
                    util.queueClear(peerSendQueue)
                    continue

                Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "Opened a socket to the peer switch")
                Intf.SetPeerDeathPending(False)
                Parser.NotifyPeerCommChange()
                break

        while not queueEmpty and clientSock is not None and (sendMsg or sendStr):
            if sendMsg:
                sendMsg.seq = sendMsgSeq
                sendMsg = sendMsg.SerializeToString()
                Log.log_debug(Log._LOG_DEBUG_INFORM_PEER, "Sending data to peer, length: %d, seq: %d" % (len(sendMsg), sendMsgSeq))
                sockInfo = stream_lib.StreamInfo(clientSock, None)
                # Send the data to the peer
                sockInfo.build_write_data(sendMsg)
                try:
                    numSent = clientSock.send(sockInfo.writeBuf)
                    sendMsgSeq += 1
                except Exception as e:
                    Log.log_debug(Log._LOG_DEBUG_INFORM_PEER, "There was an error sending data to the peer switch: %s" % str(e))
                    numSent = 0
                if numSent == 0:
                    if Parser:
                        Parser.CloseClientSock()
                    Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "Error sending data to peer, connection broken.")
                    util.queueClear(peerSendQueue)
                if sendMsgSeq > 99999:
                    sendMsgSeq = 0

            else:
                # Format the data in XML
                Log.log_debug(Log._LOG_DEBUG_INFORM_PEER, "Sending data to peer (%d): %s" % (len(sendStr), sendStr[:100]))
                sendStr += bytes(chr(0), "utf-8")
                while sendStr:
                    try:
                        numSent = clientSock.send(sendStr)
                    except Exception as e:
                        Log.log_debug(Log._LOG_DEBUG_INFORM_PEER, "There was an error sending data to the peer switch: %s" % str(e))
                        numSent = 0
                    if numSent == 0:
                        numSent = len(sendStr)
                        if Parser:
                            Parser.CloseClientSock()
                        Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "Error sending data to peer, connection broken.")
                        util.queueClear(peerSendQueue)
                    sendStr = sendStr[numSent:]

            try:
                (sendMsg, sendStr) = peerSendQueue.get(True, 0.001)
            except queue.Empty:
                queueEmpty = True



    Log.log("Finished execution of the thread PeerSend")


def PeerRecv():
    '''
    This function is an independent thread of the script. It creates a socket,
    serverSock, and accepts connections on that socket. It receives the information
    send through those connections and signals the PeerTimeout thread to let it
    know that the peer is alive. If the information we receive has changed, it 
    signals the LacpDualConnectedIfs thread.
    '''
    global serverSock
    global inputSocks
    global ourPeerIp
    global clientSockPair
    global serverSockPair

    Log.log_verbose(Log._LOG_DEBUG_RECV_PEER, "Beginning execution of the thread PeerRecv")

    clientSockPair, serverSockPair = socket.socketpair()

    # Initially, all bonds have unique system IDs
    nlm = nlmanager.nlmanager.NetlinkManager(util.CLAG_NLM_RECV_ID)
    if not Parser.args.backupIp:
        Intf.SetAllClagBondsSysMac(nlm, "00:00:00:00:00:00", [Parser.args.peerIf])

    myPeerSockInfo = {}
    while keep_going:
        # If we don't have a socket accepting connections from the peer, create one.
        if serverSock is None:
            ourPeerIp = Intf.GetIpAddressInSameNet(Parser.args.peerIf, Parser.args.peerIp)
            Log.log_debug(Log._LOG_DEBUG_RECV_PEER, "The IP address of the %s interface is %s." % (Parser.args.peerIf, ourPeerIp))

            if ourPeerIp:
                for (family, socktype, proto, canonname, sockaddr) in \
                    socket.getaddrinfo(ourPeerIp, Parser.args.peerPort, socket.AF_UNSPEC,
                                       socket.SOCK_STREAM, 0, socket.AI_PASSIVE):

                    try:
                        serverSock = socket.socket(family, socktype, proto)
                    except socket.error as msg:
                        serverSock = None
                        Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "Unable to create socket for %s:%d" % (ourPeerIp, Parser.args.peerPort))
                        continue

                    try:
                        serverSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                        serverSock.setsockopt(socket.SOL_SOCKET, 25, bytes(Parser.args.peerIf, "utf-8"))
                        serverSock.setsockopt(socket.SOL_IP, socket.IP_TOS, 0xc0)
                        serverSock.bind(sockaddr)
                        serverSock.listen(5)
                        Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "Server socket is open for accepting connections from the peer.")
                    except socket.error as msg:
                        serverSock.close()
                        serverSock = None
                        Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "Unable to bind socket to %s:%d (%s) %s" % (ourPeerIp, Parser.args.peerPort, sockaddr, msg))
                        continue
                    except AttributeError:
                        Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "Unable to find server socket")
                        serverSock = None
                        continue

                    Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "Server socket to %s:%d was created" % (ourPeerIp, Parser.args.peerPort))
                    break

        if serverSock is not None:
            inputSocks  = [serverSock, serverSockPair]
            while inputSocks and keep_going and serverSock:

                # Wait until there is a socket to read or an error
                Log.log_verbose(Log._LOG_DEBUG_RECV_PEER, "Waiting for something to happen")

                # If the user supplied a timeout, use it
                currTimeout = Parser.getPeerTimeout()
                Parser.peerTimeoutStart = int(os.times()[4])

                try:
                    (readable, writable, exceptional) = select.select(inputSocks, [], inputSocks, currTimeout)
                except (select.error, socket.error) as e:
                    Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "An error occurred: %s" % (e,))
                    (readable, writable, exceptional) = ([],[],[])
                else:
                    if keep_going and (readable, writable, exceptional) != ([],[],[]):
                        if not Intf.isPeerLinkDown:
                            # Did the peer just become alive?
                            PeerAliveProc(nlm)
                    else:
                        # Has the peer just gone silent?
                        if Intf.isPeerAlive():
                            if keep_going:
                                Log.log("Peer communication timeout. No data has been received from the peer for %d seconds." % (currTimeout,))
                            PeerIsNotActiveGraceful(nlm)

                # An error occured on a socket, gracefully shut it down
                for errSock in exceptional:
                    Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "Closing a socket because of an error")
                    inputSocks.remove(errSock)
                    errSock.close()
                    if errSock in readable:
                        readable.remove(errSock)
                    if errSock in myPeerSockInfo:
                        del myPeerSockInfo[errSock]
                        if Intf.isPeerAlive():
                            PeerIsNotActiveGraceful(nlm)
                    if errSock is serverSock:
                        Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "   The socket was the server socket")
                        serverSock = None

                if keep_going:
                    for readSock in readable:
                        if readSock is serverSock:
                            # Accept a new socket connection
                            Log.log_debug(Log._LOG_DEBUG_RECV_PEER, "Accepting a new peer connection to receive data from the peer.")
                            (newSock, clientAddr) = readSock.accept()
                            # Add the new socket to our list, and clear its receive data buffer
                            if not Parser.readyToAcceptConnFromPeer():
                                Log.log_debug(Log._LOG_DEBUG_RECV_PEER, "Closing stale Connections")
                                srvConn = [serverSock, serverSockPair]
                                stale_conn = set(inputSocks)- set(srvConn)
                                for sock in stale_conn:
                                     if sock in inputSocks:
                                         inputSocks.remove(sock)
                                     if sock in myPeerSockInfo:
                                         del myPeerSockInfo[sock]
                                     if sock in readable:
                                         readable.remove(sock)
                                     sock.close()
                            sockInfo  = stream_lib.StreamInfo(newSock, sockaddr)
                            inputSocks.append(newSock)
                            myPeerSockInfo[newSock] = sockInfo
                        else:
                            # Read data from this socket
                            Log.log_debug(Log._LOG_DEBUG_RECV_PEER, "Reading data from the peer")
                            sockInfo = myPeerSockInfo.get(readSock, None)
                            myPeerData = 0
                            myPeerDataBytes = 0
                            if Parser and Parser.peerProtoBuf:
                                try:
                                    readlen = sockInfo.get_read_data_len()
                                    myPeerData = readSock.recv(readlen)
                                except:
                                    myPeerData = 0
                            else:
                                try:
                                    myPeerDataBytes = readSock.recv(2048)
                                except:
                                    myPeerDataBytes = 0

                            if myPeerData:
                                myPeerMsg = sockInfo.proc_read_data(myPeerData)
                                if Intf.GetPeerDeathPending():
                                    # ignore the data - peer conn. is gone; we
                                    # don't want to accidentally the peer as alive
                                    Log.log_debug(Log._LOG_DEBUG_RECV_PEER,\
                                                  "Ignoring data from the peer")
                                elif myPeerMsg:
                                    Log.log_debug(Log._LOG_DEBUG_RECV_PEER, "PeerData length: (%d)" % (len(myPeerMsg),))
                                    ParseProtoBufMessage(nlm, myPeerMsg)
                            elif myPeerDataBytes:
                                try:
                                    myPeerDataStr = myPeerDataBytes.decode("utf-8")
                                except:
                                    myPeerDataStr = 0
                                if Intf.GetPeerDeathPending() or not myPeerDataStr:
                                    Log.log_debug(Log._LOG_DEBUG_RECV_PEER,\
                                             "Ignoring data from the peer")
                                else:
                                    # Add the data read to this socket's receive buffer
                                    try:
                                        myPeerSockInfo[readSock] += myPeerDataStr
                                    except:
                                        myPeerSockInfo[readSock] = myPeerDataStr
                                    while chr(0) in myPeerSockInfo[readSock]:
                                        inEnd = myPeerSockInfo[readSock].find(chr(0))
                                        inCmd = myPeerSockInfo[readSock][:inEnd]
                                        myPeerSockInfo[readSock] = myPeerSockInfo[readSock][inEnd+1:]
                                        Log.log_debug(Log._LOG_DEBUG_RECV_PEER, "PeerData: (%d) %s" % (len(inCmd), inCmd[:100],))
                                        ParseXmlMessage(nlm, inCmd)
                            else:
                                # If no data was available, the socket has been closed, end of message.
                                Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "Closing a peer receive socket because there's nothing to read")
                                inputSocks.remove(readSock)
                                readSock.close()
                                myPeerSockInfo.pop(readSock, None)
                                if Intf.isPeerAlive():
                                    PeerIsNotActiveGraceful(nlm)

            if keep_going and not serverSock:
                for inputSock in inputSocks:
                    if inputSock is not serverSockPair:
                        myPeerSockInfo.pop(inputSock, None)
                        inputSock.close()
                        Log.log_debug(Log._LOG_DEBUG_SOCKET_INFO, "Closing a peer receive socket because the server socket is closed.")

        if keep_going:
            Log.log_verbose(Log._LOG_DEBUG_RECV_PEER, "Waiting %d seconds before continuing execution of the thread PeerRecv" % (Parser.args.peerConnect,))
            stopEvent.wait(Parser.args.peerConnect)
            stopEvent.clear()
            Log.log_verbose(Log._LOG_DEBUG_RECV_PEER, "Executing the thread PeerRecv.")

    clientSockPair.close()
    serverSockPair.close()
    Log.log("Finished execution of the thread PeerRecv")

def PeerAliveProc(nlm):
    if not Intf.isPeerAlive():
        HealthCheck.healthFailRecv = False
        Intf.peerDownLock.acquire()
        Log.log("The peer switch is active.")
        Parser.ClearRemoteIpMismatchOnPeer()
        Parser.ClearPeerConfigMismatch()
        Intf.SetPeerAlive(nlm, True)
        Parser.ListenersStateUpdate("up")
        Intf.SetAllClagBondsSysMac(nlm, Parser.args.sysMac, [Parser.args.peerIf])
        if not Intf.initHandShakeDone:
            if not Parser.isPeerStaticClagStateAware():
                # Peer versions less than 1.1.0 are not capable of 
                # sending initial sync done message so move to handshake
                # done right away.
                Intf.SetInitHandShakeDone(nlm)
        Intf.SetPeerDeathPending(False)
        Log.log("Using current peerlink role: %s" % Parser.clagRole)
        Parser.ListenersRoleUpdate()
        if MrouteSync:
            MrouteSync.UpdateLocalZebraStatus()
            MrouteSync.ZebraNotifyClagStatusUpdate()
        if VlanSync:
            # Sync the vlan info with the peer once peerlink comes up again.
            VlanSync.SendInfoToPeer()
            VlanSync.SendRequestToPeer()
        Parser.requestResyncFromPeer()
        Intf.peerDownLock.release()


def PeerLinkChange():
    '''
    This function is an independent thread of the script. It checks the state
    of the interface(s) to the peer switch. If the interface(s) to the peer
    switch go down, then the peer switch is marked as no longer active.
    '''
    Log.log_verbose(Log._LOG_DEBUG_PEER_LINK, "Beginning execution of the thread PeerLinkChange")
    nlm = nlmanager.nlmanager.NetlinkManager(util.CLAG_NLM_CHANGE_ID)
    prevPeerIfDown = True;
    prevAllClagBondDown = False

    while keep_going:

        # Determine if the interfaces to the peer are down
        peerIfDown = Intf.isPeerLinkDown()
        allClagBondDown = Intf.allClagBondDownEvent

        # GetPeerDeathPendingEvent is set after ClearPeerDeathPending is done.
        # We want to set peer not active from the peerlinkChange thread
        # instead of doing it from the HelloRx Thread which is expensive
        if Intf.GetPeerDeathPendingEvent():
            peerSwitchDead = False if HealthCheck.GetBackupActive() else True
            if peerSwitchDead:
                # Take over as primary
                Intf.SetClagRole(nlm, Parser._ROLE_PRIMARY, "backup became inactive", doBackupElection=False)
                HealthCheck.ForceInitDelayExpiry("peer is dead")
            Log.log_debug(Log._LOG_DEBUG_PEER_LINK, "Trigger peerlink-down evaluation after trigger is set in ClearPeerDeathPending")
            PeerIsNotActive(nlm, peerSwitchDead)
            Intf.ClearPeerDeathPendingEvent()
            Intf.SetPeerDeathPending(False)

        # Has the peer link just gone down?
        if not prevPeerIfDown and peerIfDown:
            PeerIsNotActiveGraceful(nlm)

        if not prevAllClagBondDown and allClagBondDown:
            Intf.PeerLinkChangeDelayedUpdates(nlm)

        # Has the peer link just come up?
        if prevPeerIfDown and not peerIfDown:
            ConfigurePeerLearning(nlm, True)
            peerLink = Parser.args.peerIf
            if Intf.isSubIfName(peerLink):
                peerLink = Intf.GetLogicalMastersOfSubIf(peerLink)[0]
            Log.log_debug(Log._LOG_DEBUG_FSM, "toListeners: add peer-link = %s" % peerLink)
            Cmd.CmdGetAllOutput("add peer-link = %s" % (peerLink,))
            if MrouteSync:
                MrouteSync.ZebraNotifyClagStatusUpdate()
        prevPeerIfDown = peerIfDown
        prevAllClagBondDown = allClagBondDown

        Log.log_verbose(Log._LOG_DEBUG_PEER_LINK, "Waiting %d seconds or peerlink event before continuing execution of the thread PeerLinkChange" % (Parser.getPeerLinkPoll(),))
        peerlinkEvent.wait(Parser.getPeerLinkPoll())
        peerlinkEvent.clear()
        Log.log_verbose(Log._LOG_DEBUG_PEER_LINK, "Executing the thread PeerLinkChange.")

    Log.log("Finished execution of the thread PeerLinkChange")

def MemTrace():
    Log.log_verbose(Log._LOG_DEBUG_MEM_TRACE, "Beginning execution of the thread MemTrace")
    tracemalloc.start()
    currentAlloc = tracemalloc.take_snapshot()
    while keep_going and Parser.args.memTrace:
        newAlloc = tracemalloc.take_snapshot()
        memStats = newAlloc.compare_to(currentAlloc, 'lineno')
        Log.log_debug(Log._LOG_DEBUG_MEM_TRACE, "==========MEMORY SNAPSHOT=========")
        for index, stat in enumerate(memStats[:10], 1):
            frame = stat.traceback[0]
            Log.log_debug(Log._LOG_DEBUG_MEM_TRACE, "#%s:%s %s:%s: %.1f KiB"
                    % (index, str(stat), frame.filename, frame.lineno, stat.size / 1024))
            line = linecache.getline(frame.filename, frame.lineno).strip()
            if line:
                Log.log_debug(Log._LOG_DEBUG_MEM_TRACE, "     %s" % str(line))
        currentAlloc = newAlloc
        memTraceEvent.wait(Parser.args.memSnapshotIntv)
        memTraceEvent.clear()
    tracemalloc.stop()
    Log.log_verbose(Log._LOG_DEBUG_MEM_TRACE, "Stopping Memory Tracer")
    Log.log_verbose(Log._LOG_DEBUG_MEM_TRACE, "Finished execution of the thread MemTrace")
            

#-------------------------------------------------------------------------------
#
#   Thread Support Routines - These functions are called by the above threads.
#
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
#
#   clagdata XML format - The messages sent between peers use XML to encode the
#   data. This provides a convienient and extensible way to transfer data
#   between peers. There are many libraries available which can encode and
#   parse XML-formatted data. The XML format used is shown by this example:
#
#       <clag_data version="0.1.0">
#         <clagid uniqueId="44:38:39:00:11:16" priority=32768 role=Primary />
#         <update_interval units="seconds">4</update_interval>
#         <mgmt_if ipaddr="10.0.1.5" />
#         <sync_done />
#         <i8023ad system_id="00:00:00:01:01:01">
#           <bond ad_partner_mac="00:02:00:00:00:09" name="bond4" portId="8.003">
#             <mac vlan="101">00:02:00:00:00:09</mac>
#             <multicast vlan="100" dev="br0">234.10.10.10</multicast>
#             <vlan id="101" name="bond4.101" />
#             <vlan id="102" name="bond4.102" />
#             <vlan id="103" name="bond4.103" />
#           </bond>
#           <bond ad_partner_mac="00:02:00:00:00:0b" name="bond5" portId="8.005" />
#           <bond ad_partner_mac="00:02:00:00:00:24" name="bond0" portId="8.009" />
#           <bond ad_partner_mac="00:02:00:00:00:03" name="bond1" portId="8.001">
#             <mac vlan="101">00:02:00:00:00:03</mac>
#             <vlan id="101" name="bond1.101"/>
#           </bond>
#           <bond ad_partner_mac="00:02:00:00:00:05" name="bond2" portId="8.004" />
#             <vlan id="101" name="bond2.101" />
#           <bond ad_partner_mac="00:02:00:00:00:07" name="bond3" portId="None" />
#           <neighbor ipaddr="10.1.0.4" dev="br100" mac="00:02:00:00:00:08" flag="reachable" bond="bond4" vlan="100" />
#           <neighbor ipaddr="10.1.0.12" dev="br10" mac="00:02:00:00:00:28" flag="stale" bond="bond3" vlan=None />
#         </i8023ad>
#       </clag_data>
#
#   The root element tag is "clag_data" and the required "version" attribute
#   defines the format of the child elements. Different major versions indicate 
#   entirely different formats. New minor versions indicate additions of data
#   while retaining all previous data. New sub-versions indicate cosmeic changes
#   which don't affect the data itself.
#
#   The sender of the XML data indicates how frequently it sends the data with
#   the update_interval element. Receivers can use this to know how long before
#   it should expect to wait before receiving the next data from the sender.
#
#   IEEE 802.3ad (LAG) information is contained as children of the i8023ad 
#   element. The system_id attribute contains the ad_actor_system that the
#   sender has assigned to all of its bonds.
#
#   The "bond" elements contain a "name" attribute which is the ifname of
#   the bond on the sender's system. It also contains the ad_partner_mac
#   which is the MAC address of the partner of the bond (used by the receiver 
#   to determine if the bond is dual connected).
#
#   Bonds which are dual connected may have "mac" sub elements, which are the
#   MAC addresses which have been dynamically learned on the interface. If the
#   bond has vlan sub-interfaces, then a vlan attribute will include the VLAN
#   ID associated with the mac addres.
#
#   Bonds can also have "vlan" sub elements, which are the VLANs configured
#   on that bond. 
#
#-------------------------------------------------------------------------------

def PrintElement(node, level):
    Log.log_debug(Log._LOG_DEBUG_COLLECT_SYS, "%s%s: %s: %s" % (" " * (level*3), node.tag, node.text, str(node.attrib)))
    for child in node.findall('*'):
        PrintElement(child, level+1)

def MakeXmlMessage():
    '''
    This function uses this switch's LACP information and clagd state and
    formulates an XML message which will be sent to the peer.
    '''
    clagDataAttr = {'version' : Parser._ClagDataVersion, 'packageVersion' : Parser.packageVersion}
    if Parser.sendGoodbye:
        clagDataAttr['goodbye'] = 'yes'
    root = ET.Element("clag_data", clagDataAttr)
    ET.SubElement(root, "capabilities", {"redirectEnable" : str(Parser.args.redirectEnable), \
                                    "redirect2Enable" : str(Parser.args.redirect2Enable)})
    ET.SubElement(root, "clagid", {"priority" : str(Parser.clagId[0]), "uniqueId" : Parser.clagId[1], "role" : Parser.clagRole})
    ET.SubElement(root, "update_interval", {"units" : "seconds"}).text = str(Parser.getCfgLacpPoll())
    if serverSock is not None and ourPeerIp is not None:
        ET.SubElement(root, "mgmt_if", {"ipaddr" : ourPeerIp})
    if Parser.syncDoneToPeer:
        ET.SubElement(root, "sync_done")
    ET.SubElement(root, "i8023ad", {"system_id" : Parser.args.sysMac})
    ET.SubElement(root, "vxlan_config", {"anycast_ip" : str(Parser.args.vxlanAnycast)})
    if Intf.isPeerAlive():
        for sync in DataSyncs:
            sync.EncodeXmlInfo(root)
    #PrintElement(root, 0)
    xmlStr = ET.tostring(root)
    if root:
        del root
    peerSendQueue.put((None, xmlStr))
    util.queueSetMaxLen('peerSendQueue')

def MakeProtoBufMessage():
    msgHdr = peerXchange.Header()
    msgHdr.type = peerXchange.Header.MessageType.Value('CLAG_PERIODIC_SYNC')
    msgData = peerXchange.ClagPeerSync()
    msgData.version = Parser._ClagDataVersion
    msgData.packageVersion = Parser.packageVersion
    msgData.redirectEnable = Parser.args.redirectEnable
    msgData.redirect2Enable = Parser.args.redirect2Enable
    msgData.priority = Parser.clagId[0]
    msgData.uniqueId = Parser.clagId[1]
    msgData.role = Parser.clagRole
    msgData.peerlinkMtu = Parser.peerlinkMtu
    msgData.bridgeType = Parser.bridgeType
    if Parser.bridgeType == "vlan-aware":
        msgData.peerlinkBridgeMember = "Yes" if Parser.peerlinkVlanAwareBridgeAdded \
                            else "No"
    msgData.updateInterval = Parser.getCfgLacpPoll()
    msgData.updateIntervalUnit = "seconds"
    msgData.systemId = Parser.args.sysMac
    if serverSock and ourPeerIp:
        msgData.ourIpAddress = ourPeerIp
    if Parser.syncDoneToPeer:
        msgData.syncDone = True
    if Parser.sendGoodbye:
        msgData.goodbye = True
    msgData.anycastIp = str(Parser.args.vxlanAnycast) if Parser.args.vxlanAnycast else "None"
    if Parser.bridgePriority:
        msgData.bridgePriority = Parser.bridgePriority
    msgData.numOfBridges = Parser.getBridgeCount()
    # bridgeStpMode in the proto message refers to bridgeStpState in Parser code.
    # We did not change the name in the proto message for backward compatibility and upgrade scenarios
    msgData.bridgeStpMode = True if Parser.bridgeStpState else False
    Log.log_debug(Log._LOG_DEBUG_INFORM_PEER, "TXing periodic data: %s" % str(msgData))
    msgHdr.data = msgData.SerializeToString()
    peerSendQueue.put((msgHdr, None))
    util.queueSetMaxLen('peerSendQueue')

def ParseProtoBufPeriodicMessage(nlm, data):
    verStr = data.version
    # Is the version something we understand?
    (peerMajVer, peerMinVer, peerSubVer) = verStr.split(".")
    (myMajVer,   myMinVer,   mySubVer)   = Parser._ClagDataVersion.split(".")
    if Parser.peerDataVersion != verStr:
        Parser.peerDataVersion = verStr

    if int(peerMajVer) > int(myMajVer):
        Log.log_error("The peer is running a version I do not understand. My version: %s, Peer version: %s" % (Parser._ClagDataVersion, verStr))
        Daemon.SignalAllThreadsToStop()
        return

    # Update the global conflict
    if Parser.getMyDataVer() != Parser.getPeerDataVer():
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_CLAG_VERSION_MISMATCH
        Globcc.UpdateGlobalConflicts(conflict, 0)
    else:
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_CLAG_VERSION_MISMATCH
        Globcc.UpdateGlobalConflicts(0, conflict)

    # Is there a goodbye attribute?
    if data.goodbye:
        Intf.SetPeerDeathPending(True)
        Parser.ClearConfigMismatch(nlm)
        StpSync.ClearConfigMismatch(nlm)
        if HealthCheck:
            HealthCheck.healthFailRecv = True
            HealthCheck.ForceInitDelayExpiry("goodbye received")
        Intf.SetClagRole(nlm, Parser._ROLE_PRIMARY, "peer sent goodbye")
        return

    # Peer indicated that he has synced all the initial data
    if data.syncDone and not Parser.syncDoneFromPeer:
        Log.log("Initial data sync from peer done.")
        Parser.SetSyncDoneFromPeer(True)
        Intf.SetInitHandShakeDone(nlm)
        # Check if any of the dormant bonds can be moved to up without
        # involving the peer
        Intf.UpdatePendingDormantBonds(nlm)
        return

    if data.resync:
        Parser.resyncClagDataToPeer()
        return

    peerRedirectEnable = data.redirectEnable
    peerRedirect2Enable = False if not Parser.isPeerRedirect2Capable() else data.redirect2Enable
    if peerRedirectEnable != Parser.args.redirectEnable:
        Log.log_warn("Mismatch in redirectEnable flag between Clag Peers")
    if peerRedirect2Enable != Parser.peerRedirect2Enable:
        Parser.peerRedirect2Enable = peerRedirect2Enable
        ConfigureBridgeBackupPort(nlm, Parser.isRedirectEnabled())

    if peerRedirect2Enable == Parser.args.redirect2Enable:
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_REDIRECT_ENABLE_MISMATCH
        Globcc.UpdateGlobalConflicts(0, conflict)
    else:
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_REDIRECT_ENABLE_MISMATCH
        Globcc.UpdateGlobalConflicts(conflict, 0)

    Parser.peerId[0] = data.priority
    Parser.peerId[1] = data.uniqueId
    Parser.peerRole = data.role
    PeerAliveProc(nlm)
    if Parser.clagId[0] < Parser.peerId[0] or (Parser.clagId[0] == Parser.peerId[0] and Parser.clagId[1] < Parser.peerId[1]):
        Intf.SetClagRole(nlm, Parser._ROLE_PRIMARY, "elected")
    else:
        Intf.SetClagRole(nlm, Parser._ROLE_SECONDARY, "elected")

    if data.updateInterval:
        Parser.setPeerRate(data.updateInterval)

    Parser.peerIpAddr = data.ourIpAddress
    ourPeerIp = Intf.GetIpAddressInSameNet(Parser.args.peerIf, Parser.args.peerIp)
    peerIpAddr = data.ourIpAddress
    if peerIpAddr:
        if Parser.remotePeerIp not in [None, peerIpAddr] or peerIpAddr == ourPeerIp:
            Parser.SetPeerIpMismatch(nlm, peerIpAddr, ourPeerIp)
    else:
        return

    Parser.peerSysMac = data.systemId
    if not data.systemId or data.systemId != Parser.args.sysMac:
        Parser.SetSysMacMismatch(nlm, data.systemId)
    else:
        Parser.ClearSysMacMismatch(nlm)

    peerAnycastIp = None if (data.anycastIp == "None" or not data.anycastIp) else data.anycastIp
    Parser.peerAnycastIp = peerAnycastIp
    if peerAnycastIp != Parser.args.vxlanAnycast:
        Parser.SetAnycastMismatch(nlm, peerAnycastIp)
    else:
        Parser.ClearAnycastMismatch(nlm)

    Parser.resetPeerBridgeConsistencyCheckSupport(data.numOfBridges)

    Parser.peerBridgePriority = data.bridgePriority
    if data.bridgePriority and not Parser.isBridgeCCSuspended():
        if Parser.bridgePriority and data.bridgePriority != Parser.bridgePriority:
            Parser.SetBridgePriorityMismatch(nlm, data.bridgePriority)
        else:
            Parser.ClearBridgePriorityMismatch(nlm)

    Parser.peerBridgeStpState = data.bridgeStpMode
    if Parser.isPeerConsistencyCheckSupported() and not Parser.isBridgeCCSuspended():
        if data.bridgeStpMode != Parser.bridgeStpState:
            Parser.SetBridgeStpStateMismatch(nlm, data.bridgeStpMode)
        else:
            Parser.ClearBridgeStpStateMismatch(nlm)

    Parser.peerPackageVersion = data.packageVersion
    if not data.packageVersion or data.packageVersion != Parser.packageVersion:
        Parser.SetPackageVersionMismatch(data.packageVersion)
    else:
        Parser.ClearPackageVersionMismatch(data.packageVersion)

    if not Parser.peerPeerlinkBridgeMember.__eq__(data.peerlinkBridgeMember):
        Parser.peerPeerlinkBridgeMember = data.peerlinkBridgeMember
        if VlanSync:
            VlanSync.setVlanCCInterfaceAll()
            VlanSync.compareVlanMapEvent.set()

    Parser.peerPeerlinkMtu = data.peerlinkMtu
    if data.peerlinkMtu != Parser.peerlinkMtu:
        Parser.SetPeerlinkMTUMismatch(nlm)
    else:
        Parser.ClearPeerlinkMTUMismatch(nlm)

    Parser.peerBridgeType = data.bridgeType
    if set(data.bridgeType.split(',')) != set(Parser.bridgeType.split(',')):
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_BRIDGE_TYPE_MISMATCH
        Globcc.UpdateGlobalConflicts(conflict, 0)
    else:
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_BRIDGE_TYPE_MISMATCH
        Globcc.UpdateGlobalConflicts(0, conflict)

def ParseProtoBufMessage(nlm, data):
    # Ignore the the message if the peer link is down (enqueued prior to
    # link going down).
    if Intf.isPeerLinkDown():
        return

    msgHdr = peerXchange.Header()
    msgHdr.ParseFromString(data)
    seq = msgHdr.seq
    msgData = None
    if msgHdr.type == peerXchange.Header.MessageType.Value('CLAG_PERIODIC_SYNC'):
        msgData = peerXchange.ClagPeerSync()
        msgData.ParseFromString(msgHdr.data)
        ParseProtoBufPeriodicMessage(nlm, msgData)
    elif msgHdr.type == peerXchange.Header.MessageType.Value('CLAG_STP_SYNC'):
        msgData = StpSync.ParseProtoBufStpMessage(nlm, msgHdr)
    elif msgHdr.type == peerXchange.Header.MessageType.Value('CLAG_BOND_SYNC'):
        msgData = LacpSync.ParseProtoBufMessage(msgHdr)
    elif msgHdr.type == peerXchange.Header.MessageType.Value('CLAG_VLAN_SYNC'):
        msgData = VlanSync.ParseProtoBufMessage(msgHdr)
    elif msgHdr.type == peerXchange.Header.MessageType.Value('CLAG_VXLAN_SYNC'):
        msgData = VxLanSync.ParseProtoBufMessage(msgHdr)
    elif msgHdr.type == peerXchange.Header.MessageType.Value('CLAG_FDB_SYNC'):
        msgData = FdbSync.ParseProtoBufMessage(msgHdr)
    elif msgHdr.type == peerXchange.Header.MessageType.Value('CLAG_NEIGH_SYNC'):
        msgData = NeighSync.ParseProtoBufMessage(msgHdr) if NeighSync else None
    elif msgHdr.type == peerXchange.Header.MessageType.Value('CLAG_MDB_SYNC'):
        msgData = MdbSync.ParseProtoBufMessage(msgHdr)
    elif msgHdr.type == peerXchange.Header.MessageType.Value('CLAG_MROUTE_SYNC'):
        msgData = MrouteSync.ParseProtoBufMessage(msgHdr)
    Log.log_debug(Log._LOG_DEBUG_RECV_PEER, "RXed Data: seq %d - %s." % (seq, str(msgData)))

def ParseXmlMessage(nlm, xmlStr):
    '''
    This function takes XML received from the peer and parses it.
    '''

    # If we're in the process of quitting, don't do any processing
    if Parser.sendGoodbye:
        return

    # Convert the XML string into an ElementTree element
    try:
        root = ET.fromstring(xmlStr)
    except ET.ParseError:
        Log.log_error("The data received from the peer is not in a valid format:\n%s" % (xmlStr,))
        return

    # Is the root element named properly?
    if root is None or root.tag != "clag_data":
        Log.log_error("The data received from the peer is not in a valid format:\n%s" % (xmlStr,))
        Daemon.SignalAllThreadsToStop()
        return

    # Is there a version attribute with the root element?
    verStr = root.get("version")
    if verStr is None:
        Log.log_error("The data received from the peer does not include a version:\n%s" % (xmlStr,))
        Daemon.SignalAllThreadsToStop()
        return

    # Is the version something we understand?
    (peerMajVer, peerMinVer, peerSubVer) = verStr.split(".")
    (myMajVer,   myMinVer,   mySubVer)   = Parser._ClagDataVersion.split(".")
    if Parser.peerDataVersion != verStr:
        Parser.peerDataVersion = verStr

    if int(peerMajVer) > int(myMajVer):
        Log.log_error("The peer is running a version I do not understand. My version: %s, Peer version: %s" % (Parser._ClagDataVersion, verStr))
        Daemon.SignalAllThreadsToStop()
        return

    # Update the global conflict for clag version mismatch even when peer is running a lower version
    # which does not support protobuf message parsing
    if Parser.getMyDataVer() != Parser.getPeerDataVer():
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_CLAG_VERSION_MISMATCH
        Globcc.UpdateGlobalConflicts(conflict, 0)
    else:
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_CLAG_VERSION_MISMATCH
        Globcc.UpdateGlobalConflicts(0, conflict)

    # Is there a goodbye attribute with the root element?
    byeStr = root.get("goodbye")
    if byeStr == 'yes': 
        Intf.SetPeerDeathPending(True)
        Parser.ClearConfigMismatch(nlm)
        StpSync.ClearConfigMismatch(nlm)
        Intf.SetClagRole(nlm, Parser._ROLE_PRIMARY, "peer sent goodbye")
        if HealthCheck:
            HealthCheck.ForceInitDelayExpiry("goodbye received")
        return

    # Ignore the rest of the message if the peer link is down (enqueued prior to
    # link going down).
    if Intf.isPeerLinkDown():
        return

    peerPkgVerStr = root.get('packageVersion')
    if peerPkgVerStr is None:
        Parser.SetPackageVersionMismatch(peerPkgVerStr)
    else:
        # Is the peer's clag package version the same as ours?
        if Parser.packageVersion != peerPkgVerStr:
            Parser.SetPackageVersionMismatch(peerPkgVerStr)
        else:
            Parser.ClearPackageVersionMismatch(peerPkgVerStr)
     
    capabilities = root.find("capabilities")
    if capabilities is not None or not Parser.isPeerRedirect2Capable():
        if capabilities is not None:
            peerRedirectEnableStr  = capabilities.get("redirectEnable")
            peerRedirectEnable = True if peerRedirectEnableStr == "True" else False
            if peerRedirectEnable != Parser.args.redirectEnable:
                Log.log_warn("Mismatch in redirectEnable flag between Clag Peers")
            peerRedirect2EnableStr = capabilities.get("redirect2Enable")
            peerRedirect2Enable = True if peerRedirect2EnableStr == "True" else False
        elif not Parser.isPeerRedirect2Capable():
            peerRedirect2Enable = False
        if peerRedirect2Enable != Parser.peerRedirect2Enable:
            Parser.peerRedirect2Enable = peerRedirect2Enable
            if Parser.isRedirectEnabled():
                ConfigureBridgeBackupPort(nlm, True)
            else:
                ConfigureBridgeBackupPort(nlm, False)
    
    clagIdNode = root.find('clagid')
    if clagIdNode is not None:
        Parser.peerId[0] = int(clagIdNode.get('priority'))
        Parser.peerId[1] = clagIdNode.get('uniqueId')
        Parser.peerRole = clagIdNode.get('role')
        PeerAliveProc(nlm)
        if Parser.clagId[0] < Parser.peerId[0] or (Parser.clagId[0] == Parser.peerId[0] and Parser.clagId[1] < Parser.peerId[1]):
            Intf.SetClagRole(nlm, Parser._ROLE_PRIMARY, "elected")
        else:
            Intf.SetClagRole(nlm, Parser._ROLE_SECONDARY, "elected")

    # Was a transmit interval supplied?
    interval = root.find('update_interval')
    if interval is not None:
        # If a units attribute was supplied it must be "seconds"
        units = interval.get('units')
        if units is not None and units != 'seconds':
            Log.log_warn("The units for the update_interval: %s, are not recognized. Skipping the interval" % (units,))
        else:
            # Get the peer's send rate. XXX: This checking can be tightened up.
            Parser.setPeerRate(int(interval.text))

    # Was a mgmt IP address supplied?
    mgmt_if = root.find('mgmt_if')
    if mgmt_if is not None:
        # Get the IP address
        peerIpAddr = mgmt_if.get('ipaddr')
        ourPeerIp = Intf.GetIpAddressInSameNet(Parser.args.peerIf, Parser.args.peerIp)
        if peerIpAddr is not None:
            if Parser.remotePeerIp not in [None, peerIpAddr] or peerIpAddr == ourPeerIp:
                Parser.SetPeerIpMismatch(nlm, peerIpAddr, ourPeerIp)
        else:
            return

    # Peer indicated that he has synced all the initial data
    sync_done = root.find('sync_done')
    if sync_done is not None:
        if not Parser.syncDoneFromPeer:
            Log.log("Initial data sync from peer done.")
            Parser.SetSyncDoneFromPeer(True)
            Intf.SetInitHandShakeDone(nlm)
            # Check if any of the dormant bonds can be moved to up without 
            # involving the peer
            Intf.UpdatePendingDormantBonds(nlm)

    # Go through all of the i8023ad elements
    i8023ad = root.find('i8023ad')
    if i8023ad is not None:
        # Is the peer's sysMac the same as ours?
        sysId = i8023ad.get('system_id')
        if sysId is None or sysId != Parser.args.sysMac:
            Parser.SetSysMacMismatch(nlm, sysId)
        else:
            Parser.ClearSysMacMismatch(nlm)

    # Go through all of the vxlan_config elements
    vxlan_config = root.find('vxlan_config')
    if vxlan_config is not None:
        peerAnycast = vxlan_config.get('anycast_ip')
        if peerAnycast == "None":
            peerAnycast = None
        # Is the peer's vxlan anycast IP the same as ours?
        if peerAnycast != Parser.args.vxlanAnycast:
            Parser.SetAnycastMismatch(nlm, peerAnycast)
        else:
            Parser.ClearAnycastMismatch(nlm)

    # Let each of the data syncs process their portion of the XML
    for sync in DataSyncs:
        sync.DecodeXmlInfo(root)

    if root:
        del root

    return


def ConfigureBridgeBackupPort(nlm, enable, ignoreLocks=False):
    if not ignoreLocks:
        Intf.bridgeAttrLock.acquire()
    clagBonds = Parser.GetClagBondDB()
    masterVlanMap = Intf.GetMasterIfVlanMap()
    clagBondSet = set(clagBonds)
    for clagBond in clagBonds:
        clagBondSet |= set(masterVlanMap.get(clagBond, {}).values())
    Intf.SetBridgeBackupPort(nlm, clagBondSet, enable)
    if not ignoreLocks:
        Intf.bridgeAttrLock.release()

def ConfigurePeerLearning(nlm, enable, ignoreLocks=False):
    if not ignoreLocks:
        Intf.bridgeAttrLock.acquire()
    masterVlanMap = Intf.GetMasterIfVlanMap()
    peerMasters = Intf.GetLogicalMastersOfSubIf(Parser.args.peerIf)
    if not peerMasters:
        peerMasters = [Parser.args.peerIf]
    allPeerIfs = list(peerMasters)
    for peerMaster in peerMasters:
        allPeerIfs += list(masterVlanMap.get(peerMaster, {}).values())
    if enable or \
        (not enable and Parser.args.peerlinkLearnEnable):
        Intf.SetBridgeLearning(nlm, allPeerIfs, not enable)
    if not ignoreLocks:
        Intf.bridgeAttrLock.release()


def PeerIsNotActive(nlm, peerSwitchDead):
    Intf.peerDownLock.acquire()
    Log.log("The peer switch is no longer active")
    Parser.setPeerRate(None)
    Parser.syncDoneToPeer = False
    Parser.SetSyncDoneFromPeer(False)
    Intf.initHandShakeDone = False
    Intf.SetPeerAlive(nlm, False)
    if Parser:
        Parser.CloseClientSock()

    global serverSock
    if serverSock:
        myServerSock = serverSock
        serverSock = None
        try:
            myServerSock.shutdown(socket.SHUT_RDWR)
            myServerSock.close()
        except:
            pass

    for sync in DataSyncs:
        sync.ClearPeerInfo()

    # We will clear the mismatch state on the vlan consistency-check between the clag bonds
    # and also move any suspended vlans to forwarding state on the clag bonds when peer is not alive
    if VlanSync and Intf:
        ourVlanDB = VlanSync.GetOurVlanDB()
        for clagId in ourVlanDB:
            bond = ourVlanDB[clagId][VlanSync.VLAN_DB_INTF_NAME]
            # Instead of clearing the vlan cc suspend list here, let this be done in the vlansync thread
            # This is to avoid contention between different threads where vlanCC is triggered from
            # other threads - PeerlinkChangeT and CompareVlanMapIntfCC thread
            VlanSync.addVlanCCInterface(bond)
            VlanSync.compareVlanMapEvent.set()

    if HealthCheck and HealthCheck.IsBackupRoleAvailable():
        Log.log("Using current backup role: %s" % HealthCheck.GetBackupRole())

    if not Intf.waitAllBondDownEvent.is_set():
        Parser.ListenersStateUpdate("down")
    if MrouteSync:
        MrouteSync.ZebraNotifyClagStatusUpdate()

    # Now that the peer is dead bonds stuck in a dormant state can be moved to 
    # up
    Intf.UpdatePendingDormantBonds(nlm)
    Intf.peerDownLock.release()
    Parser.ClearConfigMismatch(nlm)
    StpSync.ClearConfigMismatch(nlm)

    # Reset the peer protocol version since the clag peer is no longer active
    Parser.peerDataVersion = "0.0.0"
    # Reset the global conflicts when peer goes down
    if Globcc:
        Globcc.UpdateGlobalConflicts(0, Globcc._CLAG_GLOBAL_CONFLICT_ALL)

def PeerIsNotActiveGraceful(nlm):
    if not keep_going or not HealthCheck or not HealthCheck.GetBackupActive() or Intf.GetBackupRole() == Parser._ROLE_PRIMARY or not Intf.isPeerAlive():
        if HealthCheck and not HealthCheck.GetBackupActive() and  Parser.clagRole != Parser._ROLE_PRIMARY:
            Intf.SetAllClagBondsSysMac(nlm, "00:00:00:00:00:00", [Parser.args.peerIf])
        PeerIsNotActive(nlm, True)
        return

    # we need to check if peer switch is still accessible via the backup link
    if not Intf.GetPeerDeathPending():
        Intf.SetPeerDeathPending(True)
        HealthCheck.HelloRapidDetectionSetup()

def signal_handler(signum, frame):
    Log.log("Signal handler invoked with signal %d" % (signum))
    nlm = nlmanager.nlmanager.NetlinkManager(util.CLAG_NLM_SIG_HNDL_INIT)
    def switchd_state_starting():
        Log.log("Switchd Process is starting")
        if Parser:
            Parser.switchdState = ClagParser._SWITCHD_STARTING

    def switchd_state_ready():
        Log.log("SwitchdState is ready")
        nlmsgs = bytes()
        if Parser: 
            Parser.switchdState = ClagParser._SWITCHD_READY
            if HealthCheck and not Parser.args.vm:
                HealthCheck.goodbyeSentLock.acquire()
                HealthCheck.goodbyeSent = False
                HealthCheck.goodbyeSentLock.release()
                if not HealthCheck.initDelayTimerStartEvent.is_set():
                    HealthCheck.initDelayTimerStartEvent.set()
                    for intf in list(Parser.GetClagIntfDB().values()):
                        if intf.intfType == Parser._CLAG_INTF_TYPE_BOND:
                            nlmsgs += intf.UpdateProtoDownFlags(nlm,  \
                            intf._PROTO_DOWNF_INIT_DELAY, intf._PROTO_DOWNF_NONE, batch=True)
                    if nlmsgs:
                        nlm.tx_nlpacket_raw(nlmsgs)
                if VxLanSync:
                    VxLanSync.UpdateAnycastInfoToClients(switchd_ready=True)

    def switchd_state_dead():
        Log.log("Switchd Process is dead")
        if Parser:
            Parser.switchdState = ClagParser._SWITCHD_DEAD
            if HealthCheck and not Parser.args.vm:
                Log.log("Sending goodBye over backup")
                HealthCheck.HelloSendGoodbye()
            # Reset switchd fuse node cached values (anycastIp, localIp) when switchd is dead
            if VxLanSync:
                VxLanSync.lastAnycastIp = "0.0.0.0"
                VxLanSync.lastLocalIp = "0.0.0.0"

    sigrt_map = {
        signal.SIGRTMIN   : switchd_state_starting,
        signal.SIGRTMIN+1 : switchd_state_ready,
        signal.SIGRTMIN+2 : switchd_state_dead,
    }
    if signum == signal.SIGHUP:
        reopen()
    elif signum == signal.SIGTERM:
        if exitEvent is None:
            do_exit(None, 0, True)
        else:
            exitEvent.set()
    elif signum == signal.SIGRTMIN+3:
        Log.log("System Reboot/ Networking Service Restart Initiated")
        Parser.systemRebootTriggered = True
    elif signum in sigrt_map:
        switchd_sighndl = sigrt_map.get(signum)
        return switchd_sighndl()

def reopen():
    if Log:
        handlerStr = Log.getHandlerStr()
        if handlerStr not in ["syslog", "stdout"]:
            try:
                Log.setHandler(handlerStr)
                Log.log("Continuing logging after SIGHUP (possibly due to log rotation)")
            except IOError:
                pass

def do_exit(nlm, status=0, ignoreLocks=False):
    '''
    Clean up and exit.
    '''
    global Cmd
    global HealthCheck
    global ClagNetLink

    if HealthCheck:
        HealthCheck.exitInProcess = True

    # RM-3038443: Acquire this lock for lacpsync to prevent CollectLocalInfo
    # from processing clag bonds until we send goodbye to the peer over peerlink/backup link
    # There was a race condition hit when we set the fuse node for switchd to
    # shutdown the dual bonds which was bringing down the bonds before goodbye could be sent
    if LacpSync:
        LacpSync.collectLocalInfoLock.acquire()

    if Parser.systemRebootTriggered:
        Log.log("System Reboot/ Networking Service Restart In Progress")
        try:
            if Intf and Parser and nlm:
                if VxLanSync and Parser.args.vxlanAnycast:
                    Log.log("Deleting the anycast Ip")
                    Parser.vxlanAnycastDeleted = True
                    Intf.DelIpAddress(nlm, "lo", str(Parser.args.vxlanAnycast), 32)
            Log.log("Updating switchd fuse node")
            with open("/cumulus/switchd/ctrl/clag_shutdown_duallinks", "w") as f:
                f.write(str(1))
                f.flush()
        except IOError:
            pass
    else:
        try:
            with open("/cumulus/switchd/config/vxlan/local_ip", "w") as f:
                f.write("0.0.0.0 0.0.0.0")
                f.flush()
        except IOError:
            pass

    # Set protodown on clag bonds and vxlan devices before sending goodbye
    if Intf and Parser and nlm:
        Intf.SetProtoDownOnExit(nlm, ignoreLocks)

    # If the peer is alive, send a goodbye message
    if Intf and Intf.isPeerAlive():
        root = ET.Element("clag_data", {'version' : Parser._ClagDataVersion, 'goodbye' : 'yes'})
        xmlStr = ET.tostring(root)
        msgHdr = peerXchange.Header()
        msgHdr.type = peerXchange.Header.MessageType.Value('CLAG_PERIODIC_SYNC')
        msgData = peerXchange.ClagPeerSync()
        msgData.version = Parser._ClagDataVersion
        msgData.goodbye = True
        # Flush the send queue
        queueEmpty = False
        while not queueEmpty:
            try:
                peerSendQueue.get_nowait()
            except queue.Empty:
                queueEmpty = True
        Parser.sendGoodbye = True
        # Since the peer will now be primary, change our role to secondary
        if Parser and Parser.clagRole != Parser._ROLE_SECONDARY:
            Parser.clagRole = Parser._ROLE_SECONDARY
            if Log:
                Log.log("Role is now secondary; sent goodbye to peer")
            if Cmd:
                Log.log_debug(Log._LOG_DEBUG_FSM, "toListeners: add clag-role = %s" % (str(Parser.clagRole)))
                Cmd.CmdGetAllOutput("add clag-role = %s" % (str(Parser.clagRole),), ignoreLocks)
            if MrouteSync:
                MrouteSync.ZebraNotifyClagStatusUpdate()
        # Send the goodbye message
        if Parser and Parser.peerProtoBuf:
            Log.log_debug(Log._LOG_DEBUG_INFORM_PEER, "TXing goodbye data: %s" % str(msgData))
            msgHdr.data = msgData.SerializeToString()
            peerSendQueue.put((msgHdr, None))
        else:
            peerSendQueue.put((None, xmlStr))
        util.queueSetMaxLen('peerSendQueue')

    if FdbSync:
        FdbSync.ClearPeerInfo()

    if HealthCheck:
        Log.log("Sending goodbye over backup")
        HealthCheck.HelloSendGoodbye()

    # Wait for a short time so goodbye messages can go out
    time.sleep(0.5)

    # Now that we have sent the goodbye to the peer, we can release this lock
    # to let lacpsync process the clag bonds info and also send the info to the peer
    if LacpSync:
        LacpSync.collectLocalInfoLock.release()

    if Intf and Parser and nlm:
        if Parser.isRedirectEnabled():
            Log.log("Disabling Backup Port On Clag Bonds")
            ConfigureBridgeBackupPort(nlm, False, ignoreLocks)
        ConfigurePeerLearning(nlm, False, ignoreLocks)
        Intf.ClearDormantMode(nlm, ignoreLocks)

    if Daemon:
        Log.log("Signalling all threads to stop")
        Daemon.SignalAllThreadsToStop()
    for thread in Threads:
        if Threads[thread][1]:
            Threads[thread][1].join()
    Log.log("Main Threads have been joined")
    if memTraceThreadT:
        memTraceThreadT.join()

    if csu_client:
        Log.log("Cleaning up CSU Client")
        csu_client.cleanup()
        Log.log("CSU Client cleanup completed")
    for sync in DataSyncs:
        sync.JoinThreads()
    Log.log("Data Sync Threads have been joined")
    if Cmd:
        Cmd.JoinThreads()
    Log.log("Cmd Threads have been joined")
    Cmd = None

    if HealthCheck:
        HealthCheck.JoinThreads()
        HealthCheck = None
    Log.log("HealthCheck Threads have been joined")

    if ClagNetLink:
        ClagNetLink.JoinThreads()
        ClagNetLink = None
    Log.log("ClagNetLink Threads have been joined")

    if Daemon:
        if Daemon.started:
            if Daemon.pid_fd:
                Daemon.pid_fd.unlock()
            if Daemon.pid_file:
                os.unlink(Daemon.pid_file)
            

    if Log:
        if status == 0:
            Log.log("clean exit")
        else:
            Log.log_error("exit with status %d" % status)
    sys.exit(status)



#-------------------------------------------------------------------------------
#
#   This dictionary contains the threads which we run. The key is the name of
#   the thread and the value is an array. The first elemenet is the address of
#   the function which implements the thread, and the second is the thread
#   object for this thread (filled in when the thread is created).
#
#-------------------------------------------------------------------------------

def CollectSysInfoT():
    try:
        CollectSysInfo()
    except:
        Daemon.DumpTraceback()

def PeerSendT():
    try:
        Log.log("[%d]Init PeerSendT" % (Parser.getTid()))
        PeerSend()
    except:
        Daemon.DumpTraceback()

def PeerRecvT():
    try:
        Log.log("[%d]Init PeerRecvT" % (Parser.getTid()))
        PeerRecv()
    except:
        Daemon.DumpTraceback()

def PeerLinkChangeT():
    try:
        Log.log("[%d]Init PeerlinkChangeT" % (Parser.getTid()))
        PeerLinkChange()
    except:
        Daemon.DumpTraceback()

def MemTraceT():
    try:
        Log.log("[%d]Init MemTraceT" % (Parser.getTid()))
        MemTrace()
    except:
        Daemon.DumpTraceback()

Threads = {
    "CollectSysInfo"       : [CollectSysInfoT,      None],
    "PeerSendThread"       : [PeerSendT,            None],
    "PeerRecvThread"       : [PeerRecvT,            None],
    "PeerLinkChange"       : [PeerLinkChangeT,      None],
}


#-------------------------------------------------------------------------------
#
#   Error handling
#
#-------------------------------------------------------------------------------

class ClagdError(RuntimeError):
    '''
    Standard exception for all our runtime errors.
    The only argument is a string.
    '''
    pass

class ArgumentParsingError(Exception):
    '''
    Exception for argparse errors.
    '''
    pass


#-------------------------------------------------------------------------------
#
#   Daemon handling code
#
#-------------------------------------------------------------------------------

class Pidfile(object):
    """ Checks if another instance of the program is running.
    Explanation from http://stackoverflow.com/questions/220525/
    ensure-a-single-instance-of-an-application-in-linux
    The Right Thing is advisory locking using flock(LOCK_EX); in Python,
    this is found in the fcntl module.
    Unlike pidfiles, these locks are always automatically released when
    your process dies for any reason, have no race conditions exist
    relating to file deletion (as the file doesn't need to be deleted to
    release the lock), and there's no chance of a different process
    inheriting the PID and thus appearing to validate a stale lock.
    If you want unclean shutdown detection, you can write a marker
    (such as your PID, for traditionalists) into the file after
    grabbing the lock, and then truncate the file to 0-byte status
    before a clean shutdown (while the lock is being held); thus, if the
    lock is not held and the file is non-empty, an unclean shutdown is
    indicated.
    """

    def __init__(self, pidfile, procname, uuid=None):
        try:
            self.__pidfd = open(pidfile, 'w')
        except IOError as e:
            Log.log_error('Failed to open pidfile: %s: %s' % (pidfile, str(e)))
            sys.exit(-1)
        self.__pidfile = pidfile
        self.__procname = procname
        self.__uuid = uuid

    def __str__(self):
        return self.__pidfile

    def lock(self):
        try:
            fcntl.flock(self.__pidfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
        except IOError:
            Log.log_error('Another instance of this program is already '
                               'running.')
            sys.exit(-1) 

    def read(self):
        try:
            self.__pidfd.seek(0)
            return int(self.__pidfd.readline().strip())
        except Exception:
            return

    def unlock(self):
        try:
            fcntl.flock(self.__pidfd, fcntl.LOCK_UN)
        except IOError:
            Log.log('Unable to unlock pid file.')
            pass

    def write(self, pid):
        self.__pidfd.write("{0}\n".format(pid))
        self.__pidfd.flush()

class ClagDaemon:
    '''
    Process management for a daemon.
    An instance should be created as early as possible.
    '''

    def __init__(self, name = None):
        '''
        name is the program name (defaulting to basename(argv[0]))
        '''
        if name is None:
            name = os.path.basename(sys.argv[0])
        self.name = name
        self.tick = 0
        self.pid_file = os.path.abspath("/var/run/%s.pid" % self.name)
        self.started = False
        self.pid_fd = None

    def start(self):
        '''
        Start running.
        Check for other instances of this program and create pid file.
        This should be called after early initialization (like
        argument parsing), before any serious work.
        '''
        global watchintv

        tries = 3
        for i in range(tries):
            try:
                pid = str(os.getpid())
                self.pid_fd = Pidfile(self.pid_file, self.name)
                self.pid_fd.lock()
                self.pid_fd.write(pid)
            except Exception as e:
                time.sleep(1)
                if (i < tries -1):
                    if self.pid_fd:
                        self.pid_fd.unlock()
                    if self.pid_file:
                        os.unlink(self.pid_file)
                    continue
                else:
                    raise ClagdError("Unable to write to PID file - %s." % (str(e),))
            break
        self.started = True

        # the clag period for calling the watchdog is 2 seconds
        # (default_lacp_poll), and is rarely changed, and never
        # to large values, so no need to verify that the watchdog
        # interval is sufficiently longer than the period.
        watchintv = int(os.getenv('WATCHDOG_USEC', '0')) / 1000000

    def isClagAlreadyRunning(self):
        '''
        Check if another instance of clagd is already running. Check the PID file
        and if the PID file contents point to a valid running clagd instance.
        '''
        running = False
        try:
            with open(self.pid_file) as f:
                clag_pid = f.readline().strip()
            with open("/proc/" + clag_pid + "/cmdline") as f:
                cmdline = f.readline().strip().split('\x00')[2]
                if cmdline == "/usr/sbin/clagd":
                    running = True
        except (IOError, ValueError, IndexError):
            pass

        return running

    def DumpTraceback(self):
        (exc_type, exc_value, exc_traceback) = sys.exc_info()
        err = "".join(traceback.format_exception(exc_type, exc_value,
                                                 exc_traceback))
        if Log:
            Log.log_error("unhandled exception:")
            for line in err.split('\n'):
                Log.log_error("    %s" % (line,))
        else:
            print("unhandled exception:")
            for line in err.split('\n'):
                print(("    %s" % (line,)))
        self.SignalAllThreadsToStop()

    def TouchKeepAlive(self):
        global watchintv
        if watchintv:
            pass
            clagSdnotify.sd_notify(0, bytes("WATCHDOG=1", "utf-8"))
        try:
            # The file alive code should be removed as soon as
            # we are sure that no test or other code requires it.
            aliveFile = "/var/run/%s.alive" % (self.name,)
            with open(aliveFile, 'a'):
                os.utime(aliveFile, None)
        except:
            pass

    def SignalAllThreadsToStop(self):
        '''
        This function wakes up all threads which are waiting on a signal so that
        they can realize that they should stop execution.
        '''
        global keep_going
        global serverSock
        keep_going = False

        if serverSock is not None:
            myServerSock = serverSock
            serverSock = None
            myServerSock.shutdown(socket.SHUT_RDWR)

        # send data to our server socket pair to force select() to return in PeerRcvT
        try:
            clientSockPair.sendall(b'0')
        except:
            pass

        for sync in DataSyncs:
            sync.ClearPeerInfo()
            sync.SignalThreads()
        if Cmd is not None:
            Cmd.SignalThreads()
        if HealthCheck:
            HealthCheck.SignalThreads()
        if ClagNetLink:
            ClagNetLink.SignalThreads()

        try:
            peerSendQueue.put_nowait((None, None))
            util.queueSetMaxLen('peerSendQueue')
        except queue.Full:
            pass
        Intf.waitAllBondDownEvent.set()
        memTraceEvent.set()
        peerlinkEvent.set()
        stopEvent.set()
        if exitEvent is not None:
            exitEvent.set()



#-------------------------------------------------------------------------------
#
#   Log handling code
#
#-------------------------------------------------------------------------------

class HostAddFilter(logging.Filter):
    hostname = socket.gethostname()

    def filter(self, record):
        record.hostname = HostAddFilter.hostname
        return True


class Logger:

    _LOG_DEBUG_HOST_EVENTS  = 0x00000001
    _LOG_DEBUG_DAEMON       = 0x00000002
    _LOG_DEBUG_COLLECT_SYS  = 0x00000004
    _LOG_DEBUG_INFORM_PEER  = 0x00000008
    _LOG_DEBUG_RECV_PEER    = 0x00000010
    _LOG_DEBUG_PEER_TIMEOUT = 0x00000020
    _LOG_DEBUG_LACP_SYNC    = 0x00000040
    _LOG_DEBUG_PARSER       = 0x00000080
    _LOG_DEBUG_NEIGH_SYNC   = 0x00000200
    _LOG_DEBUG_FDB_SYNC     = 0x00000400
    _LOG_DEBUG_CMD_SERVER   = 0x00000800
    _LOG_DEBUG_PEER_LINK    = 0x00001000
    _LOG_DEBUG_MDB_SYNC     = 0x00002000
    _LOG_DEBUG_HEALTH_CHECK = 0x00004000
    _LOG_DEBUG_FSM          = 0x00008000
    _LOG_DEBUG_NETLINK      = 0x00010000
    _LOG_DEBUG_VXLAN_SYNC   = 0x00020000
    _LOG_DEBUG_INTF         = 0x00040000
    _LOG_DEBUG_RELOAD       = 0x00080000
    _LOG_DEBUG_SOCKET_INFO  = 0x00100000
    _LOG_DEBUG_MROUTE_SYNC  = 0x00200000
    _LOG_DEBUG_THREAD_MONITOR  = 0x00400000
    _LOG_DEBUG_CSU           = 0x00800000
    _LOG_DEBUG_MEM_TRACE     = 0x01000000
    _LOG_DEBUG_VLAN_SYNC     = 0x02000000

    debug_flags_dict = {
                'lacp_sync':            0x00000040,
                'parser':               0x00000080,
                'neigh_sync':           0x00000200,
                'fdb_sync':             0x00000400,
                'cmd_server':           0x00000800,
                'collect_sys':          0x00000004,
                'peerlink_tx':          0x00000008,
                'peerlink_rx':          0x00000010,
                'peerlink_event':       0x00001000,
                'mdb_sync':             0x00002000,
                'healthcheck':          0x00004000,
                'fsm':                  0x00008000,
                'netlink':              0x00010000,
                'vxlan_sync':           0x00020000,
                'intf':                 0x00040000,
                'reload':               0x00080000,
                'socket':               0x00100000,
                'mroute_sync':          0x00200000,
                'thread_monitor':       0x00400000,
                'csu':                  0x00800000,
                'mem_trace':            0x01000000,
                'vlan_sync':            0x02000000
    }

    #
    #   Default logging values
    #
    default_quiet           = False
    default_debug           = 0
    default_verbose         = False
    default_handlerStr      = "syslog"
    syslog_format           = "clagd[%(process)d]: %(message)s"
    file_format             = "%(asctime)s %(hostname)s clagd[%(process)d]: %(message)s"
    file_date               = "%Y-%m-%dT%H:%M:%S.000000%z "

    def __init__(self):
        self.logger  = logging.getLogger('clagd')
        self.logger.addFilter(HostAddFilter())
        self.handler = None
        self.setHandler(Logger.default_handlerStr)
        self.quiet   = Logger.default_quiet
        self.debug   = Logger.default_debug
        self.verbose = Logger.default_verbose
        self.setLogLevel()

    def setDebug(self, debug):
        self.debug = debug
        self.setLogLevel()

    def getDebugFlags(self):
        flagsList = []
        for k,v in list(self.debug_flags_dict.items()):
            debugBits = Parser.GetNonZeroBits(self.debug)
            for bit in debugBits:
                if v == bit:
                    flagsList.append(k)
        return flagsList

    def setDebugFlags(self, debug):
        self.debug += self.debug_flags_dict[debug]
        self.setLogLevel()

    def clearDebugFlags(self, debug = 0):
        if debug:
            self.debug -= self.debug_flags_dict[debug]
        else:
            self.debug = 0
        self.setLogLevel()

    def setQuiet(self, quiet):
        self.quiet = quiet
        self.setLogLevel()

    def setVerbose(self, verbose):
        self.verbose = verbose
        self.setLogLevel()

    def setHandler(self, handlerStr):
        if self.handler:
            self.logger.removeHandler(self.handler)
        self.handlerStr = handlerStr
        formatStr = Logger.syslog_format
        dateStr = None
        if handlerStr == "syslog":
            facility = logging.handlers.SysLogHandler.LOG_DAEMON
            self.handler = logging.handlers.SysLogHandler(address="/dev/log", facility=facility)
        elif handlerStr == "stdout":
            self.handler = logging.StreamHandler()
            formatStr = Logger.file_format
            dateStr = Logger.file_date
        else:
            self.handler = logging.FileHandler(handlerStr)
            formatStr = Logger.file_format
            dateStr = Logger.file_date
        self.formatter = logging.Formatter(formatStr, dateStr)
        self.handler.setFormatter(self.formatter)
        self.logger.addHandler(self.handler)

    def setLogLevel(self):
        if self.quiet:
            self.logger.setLevel(logging.CRITICAL)
        elif self.debug != 0:
            self.logger.setLevel(logging.DEBUG)
        else:
            self.logger.setLevel(logging.INFO)

    def getDebug(self):
        return self.debug

    def getQuiet(self):
        return self.quiet

    def getVerbose(self):
        return self.verbose

    def getHandlerStr(self):
        return self.handlerStr

    def log(self, *args, **kwargs):
        #self.logger.info(args, kwargs)
        self.logger.info(''.join(args))

    def log_debug(self, debugBit, *args, **kwargs):
        if self.debug & debugBit:
            #self.logger.debug(args, kwargs)
            self.logger.debug('[' + threading.current_thread().name + '] ' + ''.join(args))

    def log_verbose(self, debugBit, *args, **kwargs):
        if self.verbose and self.debug & debugBit:
            #self.logger.info(args, kwargs)
            self.logger.info('[' + threading.current_thread().name + '] ' + ''.join(args))

    def log_error(self, *args, **kwargs):
        #self.logger.error(args, kwargs)
        self.logger.error(''.join(args))

    def log_warn(self, *args, **kwargs):
        #self.logger.warning(args, kwargs)
        self.logger.warning(''.join(args))

    def log_crit(self, *args, **kwargs):
        #self.logger.critical(args, kwargs)
        self.logger.critical(''.join(args))


#-------------------------------------------------------------------------------
#       
#   CLAG interface manager
#           
#-------------------------------------------------------------------------------
class ClagIntf:
    '''
    utility class to represent a single CLAG intf/bond
    ''' 
    # when clagd comes up all clag bonds are place in a protoDown state. This
    # is cleared when the initial handshake complete or once the reload timer 
    # fires
    _PROTO_DOWNF_NONE = 0
    _PROTO_DOWNF_INIT  = (1 << 0)
    _PROTO_DOWNF_PEER_LINK_DOWN_BACKUP_ACTIVE  = (1 << 1)
    # as part of clagd exit handling all clag bonds are placed in a protoDown 
    # state (this flag can only be set; never cleared)
    _PROTO_DOWNF_SHUTDOWN  = (1 << 2)
    _PROTO_DOWNF_SINGLE_VXLAN = (1 << 3)
    _PROTO_DOWNF_NO_ANYCAST   = (1 << 4)
    _PROTO_DOWNF_ANYCAST_MISMATCH   = (1 << 5)
    _PROTO_DOWNF_PEER_IP_MISMATCH   = (1 << 6)
    _PROTO_DOWNF_SYSMAC_MISMATCH    = (1 << 7)
    # delay bringup by a blanked X seconds
    _PROTO_DOWNF_INIT_DELAY = (1 << 8)
    _PROTO_DOWNF_PARTNER_MAC_MISMATCH = (1 << 9)
    _PROTO_DOWNF_DUPLICATE_PARTNER_MAC = (1 << 10)
    _PROTO_DOWNF_BRIDGE_PRIORITY_MISMATCH = (1 << 11)
    _PROTO_DOWNF_BRIDGE_STP_STATE_MISMATCH = (1 << 12)
    _PROTO_DOWNF_BRIDGE_STP_MODE_MISMATCH = (1 << 13)
    # During CSU keep the intf down
    _PROTO_DOWNF_CSU = (1 << 14)
    _PROTO_DOWNF_PEERLINK_NO_VLAN_AWARE_MASTER = (1 << 15)
    _PROTO_DOWNF_PEERLINK_MTU_MISMATCH = (1 << 16)
    _PROTO_DOWNF_PEERLINK_NO_PVLAN = (1 << 17)
    _PROTO_DOWNF_PEERLINK_PVLAN_MISMATCH = (1 << 18)
    _PROTO_DOWNF_PVLAN_MISMATCH = (1 << 19)
    _PROTO_DOWNF_INTF_BRIDGE_NO_MASTER = (1 << 20)
    _PROTO_DOWNF_MAX = (1 << 21)
    _PROTO_DOWNF_ALL = _PROTO_DOWNF_MAX - 1
    
    # some of the proto_down reason codes are ignored on the primary as
    # we want to keep the pimary bonds up and running most of the time
    _PROTO_DOWNF_USED_ON_PRIMARY = (_PROTO_DOWNF_INIT |\
                                    _PROTO_DOWNF_SHUTDOWN |\
                                    _PROTO_DOWNF_SINGLE_VXLAN |\
                                    _PROTO_DOWNF_NO_ANYCAST |\
                                    _PROTO_DOWNF_INIT_DELAY |\
                                    _PROTO_DOWNF_PARTNER_MAC_MISMATCH |\
                                    _PROTO_DOWNF_DUPLICATE_PARTNER_MAC | \
                                    _PROTO_DOWNF_PEERLINK_NO_VLAN_AWARE_MASTER | \
                                    _PROTO_DOWNF_INTF_BRIDGE_NO_MASTER | \
                                    _PROTO_DOWNF_CSU)

    # list of proto down events that are set for vxlan interface which
    # we will use to declare vxlan interface dual connected or not on secondary
    _PROTO_DOWNF_USED_FOR_VXLAN_INTF = (_PROTO_DOWNF_NO_ANYCAST |\
                                        _PROTO_DOWNF_ANYCAST_MISMATCH |\
                                        _PROTO_DOWNF_PEER_IP_MISMATCH |\
                                        _PROTO_DOWNF_BRIDGE_PRIORITY_MISMATCH |\
                                        _PROTO_DOWNF_PEERLINK_NO_VLAN_AWARE_MASTER |\
                                        _PROTO_DOWNF_PEERLINK_MTU_MISMATCH |\
                                        _PROTO_DOWNF_PEERLINK_NO_PVLAN |\
                                        _PROTO_DOWNF_PEERLINK_PVLAN_MISMATCH |\
                                        _PROTO_DOWNF_INTF_BRIDGE_NO_MASTER |\
                                        _PROTO_DOWNF_SYSMAC_MISMATCH)

    # following protodown flags are implemented on clag proto version 1.6.0
    # we will ensure that these proto down events are not handled when peer is
    # running clag version less than 1.6.0
    _PROTO_DOWNF_SKIP_PEER_CLAG_VER_UNSUPPORTED = (_PROTO_DOWNF_PEERLINK_NO_VLAN_AWARE_MASTER |\
                                                   _PROTO_DOWNF_PEERLINK_MTU_MISMATCH |\
                                                   _PROTO_DOWNF_PEERLINK_NO_PVLAN |\
                                                   _PROTO_DOWNF_PEERLINK_PVLAN_MISMATCH |\
                                                   _PROTO_DOWNF_PVLAN_MISMATCH |\
                                                   _PROTO_DOWNF_BRIDGE_STP_STATE_MISMATCH |\
                                                   _PROTO_DOWNF_BRIDGE_STP_MODE_MISMATCH |\
                                                   _PROTO_DOWNF_INTF_BRIDGE_NO_MASTER)
    _PROTO_DOWNF_ALLOWED_PEER_CLAG_VER_UNSUPPORTED = _PROTO_DOWNF_ALL & ~_PROTO_DOWNF_SKIP_PEER_CLAG_VER_UNSUPPORTED

    # We will skip the following proto down flags in case the clag-args is forced to not take disruptive actions
    _PROTO_DOWNF_SKIP_FORCE = _PROTO_DOWNF_SKIP_PEER_CLAG_VER_UNSUPPORTED

    def __init__(self, nlm, intfName, intfType, clagId = 0):
        self.intfName = intfName
        self.intfType = intfType
        self.master = None
        self.clagId = clagId
        self.bpduGuard = False
        self.peerBpduGuard = False
        self.peerStpSync = True
        self.restrictedRole = False
        self.peerRestrictedRole = False
        self.isPeerNoMaster = False
        self.conflicts = Parser._CLAG_INTF_CONFLICT_NONE
        self.conflicts_ignored = Parser._CLAG_INTF_CONFLICT_NONE
        self.protoDownFlags = 0 if Intf.initHandShakeDone or \
            (HealthCheck and HealthCheck.reloadExpired()) else self._PROTO_DOWNF_INIT

        # Do not set vxlan_single protodown flag for vxlan devices
        # after reload timeout when peerlink/backup links are down
        if self.intfType == Parser._CLAG_INTF_TYPE_VXLAN:
            if Intf.isPeerAlive() or not HealthCheck or \
               HealthCheck.GetBackupActive() or not HealthCheck.reloadExpired():
                self.protoDownFlags |= self._PROTO_DOWNF_SINGLE_VXLAN

        if self.intfType == Parser._CLAG_INTF_TYPE_BOND and\
           HealthCheck.initDelayBringup():
            self.protoDownFlags |= self._PROTO_DOWNF_INIT_DELAY

        if csu_client and self.intfType == Parser._CLAG_INTF_TYPE_BOND and \
            csu_client.isSlaveProtoDown() == True:
            self.protoDownFlags |= self._PROTO_DOWNF_CSU

        self.kernProtoDown = None

        # add clag bond to an id dict
        if self.clagId:
            Parser.bondClagIdDB[self.clagId] = self.intfName

        if self.intfType == Parser._CLAG_INTF_TYPE_BOND:
            if (Parser.args.backupIp and Intf.GetIfIndex(self.intfName) is not None) \
                    or (Intf.isPeerAlive() and not Parser.args.backupIp):
                Intf.SetBondSysMac(nlm, system_mac, self.intfName)
            self.SetClagIntfModeDormant(nlm, dormant=True)
        self.UpdateIntfProtoDown(nlm)

        # initHandShakeDone is set once the peer has sent his
        # peer db over or after reload timer has fired. Once
        # this attr is set we can start conflict evaluation
        # on clag bond add itself
        if Intf.ReadyToRunClagConflictChecks() and LacpSync and self.intfType == Parser._CLAG_INTF_TYPE_BOND:
            (setConflicts, ignoreConflicts) = LacpSync.EvaluateClagIntfConflicts(self.intfName, self.clagId)
            self.UpdateConflicts(setConflicts, 0, ignoreConflicts)

    def setIntfMaster(self, master):
        self.master = master
                
    def isPeerClagIntfBridgeMember(self):
        return False if self.isPeerNoMaster else True

    def isClagIntfBridgeMember(self):
        return True if (Intf and Intf.isBridgeMember(self.intfName)) else False

    def ignoreClagIntfCC(self):
        if ((not Intf.isPeerAlive()) or (not self.isClagIntfBridgeMember()) or (not self.isPeerClagIntfBridgeMember())):
            return True
        return False
        
    def CleanUp(self, nlm):
        if self.intfType == Parser._CLAG_INTF_TYPE_BOND:
            if self.clagId in Parser.bondClagIdDB:
                del Parser.bondClagIdDB[self.clagId]
            self.SetClagIntfModeDormant(nlm, dormant=False)

            Intf.SetBondSysMac(nlm, '00:00:00:00:00:00', self.intfName)
            self.SetIntfKernProtoDown(nlm, protoDown=0)

    def SetNewClagId(self, nlm, clagId):
        if self.clagId == clagId:
            return

        if self.clagId in Parser.bondClagIdDB:
            if VlanSync:
                # Copy the vlan map from old clag id to new clag id
                VlanSync.CopyOurVlanIntfDB(clagId, self.clagId)
                # Clear the vlan db information for the old clag id
                VlanSync.ClearOurVlanIntfDB(self.clagId)
                # Send the vlan info to peer for both old and new clag id
                VlanSync.SendVlanInfoToPeer(self.intfName, self.clagId)
                VlanSync.SendVlanInfoToPeer(self.intfName, clagId)
                # Copy the suspend vlan map from old clag id to new clag id
                VlanSync.CopyOurSuspendVlanIntfDB(clagId, self.clagId)
                # Delete the suspend vlan map for old clag id
                VlanSync.ClearOurSuspendVlanIntfDB(self.clagId)
            del Parser.bondClagIdDB[self.clagId]

        self.clagId = clagId

        # add clag bond to an id dict
        if self.clagId:
            Parser.bondClagIdDB[self.clagId] = self.intfName

        self.SetClagIntfModeDormant(nlm, dormant=True)
        self.UpdateIntfProtoDown(nlm)

        # Reset the conflicts
        if Intf.ReadyToRunClagConflictChecks() and LacpSync and self.intfType == Parser._CLAG_INTF_TYPE_BOND:
            (setConflicts, ignoreConflicts) = LacpSync.EvaluateClagIntfConflicts(self.intfName, self.clagId)
            self.UpdateConflicts(setConflicts, Parser._CLAG_INTF_CONFLICT_LACP_ALL & ~setConflicts, ignoreConflicts)

    def GetProtoDownRole(self):
        global HealthCheck
        if not Intf.isPeerAlive() and HealthCheck and HealthCheck.IsBackupRoleAvailable():
            role = HealthCheck.GetBackupRole()
        else:
            role = Parser.clagRole

        return role

    def GetProtoDown(self): 
        role = self.GetProtoDownRole()

        if role == Parser._ROLE_PRIMARY:
            stateFlags = (self.protoDownFlags & self._PROTO_DOWNF_USED_ON_PRIMARY)
        else:
            stateFlags = self.protoDownFlags
        return 1 if stateFlags else 0

    def GetProtoDownReasons(self):
        reasons = []
        role = self.GetProtoDownRole()
        if role == Parser._ROLE_PRIMARY:
            stateFlags = (self.protoDownFlags & self._PROTO_DOWNF_USED_ON_PRIMARY)
        else:
            stateFlags = self.protoDownFlags

        if stateFlags & self._PROTO_DOWNF_INIT:
            reasons.append('init')
        if stateFlags & self._PROTO_DOWNF_INIT_DELAY:
            reasons.append('init-delay')
        if stateFlags & self._PROTO_DOWNF_PEER_LINK_DOWN_BACKUP_ACTIVE:
            reasons.append('isl-down')
        if stateFlags & self._PROTO_DOWNF_SHUTDOWN:
            reasons.append('shut')
        if stateFlags & self._PROTO_DOWNF_SINGLE_VXLAN:
            reasons.append('vxlan-single')
        if stateFlags & self._PROTO_DOWNF_NO_ANYCAST:
            reasons.append('no-anycast-ip')
        if stateFlags & self._PROTO_DOWNF_ANYCAST_MISMATCH:
            reasons.append('anycast-ip-mismatch')
        if stateFlags & self._PROTO_DOWNF_PEER_IP_MISMATCH:
            reasons.append('peer-ip-mismatch')
        if stateFlags & self._PROTO_DOWNF_SYSMAC_MISMATCH:
            reasons.append('system-mac-mismatch')
        if stateFlags & self._PROTO_DOWNF_DUPLICATE_PARTNER_MAC:
            reasons.append('duplicate-partner-mac')
        if stateFlags & self._PROTO_DOWNF_PARTNER_MAC_MISMATCH:
            reasons.append('partner-mac-mismatch')
        if stateFlags & self._PROTO_DOWNF_BRIDGE_PRIORITY_MISMATCH:
            reasons.append('bridge-priority-mismatch')
        if stateFlags & self._PROTO_DOWNF_BRIDGE_STP_STATE_MISMATCH:
            reasons.append('bridge-stp-state-mismatch')
        if stateFlags & self._PROTO_DOWNF_BRIDGE_STP_MODE_MISMATCH:
            # Starting from Clag Data version 1.7.0 bridge-stp-mode-mismatch
            # refers to PVRST/RSTP mode mismatch. Earlier it used to mean
            # bridge-stp-state mismatch.
            reasons.append('bridge-stp-mode-mismatch')
        if stateFlags & self._PROTO_DOWNF_CSU:
            reasons.append('CSU-in-progress')            
        if stateFlags & self._PROTO_DOWNF_PEERLINK_NO_VLAN_AWARE_MASTER:
            reasons.append('peerlink-vlan-aware-master-missing')
        if stateFlags & self._PROTO_DOWNF_PEERLINK_MTU_MISMATCH:
            reasons.append('peerlink-mtu-mismatch')
        if stateFlags & self._PROTO_DOWNF_INTF_BRIDGE_NO_MASTER:
            reasons.append('intf-not-bridge-member')
        if stateFlags & self._PROTO_DOWNF_PEERLINK_NO_PVLAN:
            reasons.append('peerlink-no-pvlan')
        if stateFlags & self._PROTO_DOWNF_PEERLINK_PVLAN_MISMATCH:
            reasons.append('peerlink-pvlan-mismatch')
        if stateFlags & self._PROTO_DOWNF_PVLAN_MISMATCH:
            reasons.append('pvlan-mismatch')

        return reasons

    def UpdateProtoDownFlags(self, nlm, addProtoDownFlags, delProtoDownFlags, batch=False):
        retVal = bytes()
        self.protoDownFlags |= addProtoDownFlags
        self.protoDownFlags &= ~delProtoDownFlags

        if not Parser.isProtoDownFlagOnConflict():
            # If the user has configured the proto down flag to be disabled
            # we will no longer protodown the clag bonds if there is conflict
            self.protoDownFlags &= ~self._PROTO_DOWNF_SKIP_FORCE

        if not Parser.isPeerConsistencyCheckSupported():
            # If peer clag version is less than 1.6.0, we will not allow newly implemented
            # proto down flags to be configured
            self.protoDownFlags &= self._PROTO_DOWNF_ALLOWED_PEER_CLAG_VER_UNSUPPORTED

        newProtoDown = self.GetProtoDown()
        if self.kernProtoDown != newProtoDown:
            Log.log("UpdateProtoDownFlags Interface %s add flags 0x%d del flags 0x%x final flags 0x%x"
                % (self.intfName, addProtoDownFlags, delProtoDownFlags, self.protoDownFlags))
            retVal = self.SetIntfKernProtoDown(nlm, newProtoDown, batch=batch)
        return retVal

    def DoSetIntfKernProtoDown(self, nlm, intfName, newProtoDown, batch=False):
        retVal = bytes()
        self.kernProtoDown = newProtoDown
        oldProtoDown = Intf.GetLinkProtoDownFlags(intfName)
        if oldProtoDown != newProtoDown:
            Log.log_debug(Log._LOG_DEBUG_FSM, "Interface %s mbr %s proto_down change; from %d to %d" % (self.intfName, intfName, oldProtoDown, newProtoDown))
            retVal = Intf.SetLinkProtoDownFlags(nlm, intfName, newProtoDown, batch=batch)
        return retVal

    def SetIntfKernProtoDown(self, nlm, protoDown, batch=False):
        retVal = bytes()
        newProtoDown = 1 if protoDown else 0
        if self.intfType == Parser._CLAG_INTF_TYPE_BOND:
            for intfMbr in Intf.GetBaseInterfaces(self.intfName):
                retVal += self.DoSetIntfKernProtoDown(nlm, intfMbr, newProtoDown, batch=batch)
        else:
            retVal += self.DoSetIntfKernProtoDown(nlm, self.intfName, newProtoDown, batch=batch)
        return retVal

    def UpdateIntfProtoDown(self, nlm, batch=False):
        protoDown = self.GetProtoDown()
        #Log.log_debug(Log._LOG_DEBUG_FSM, "Interface %s protoDown %d" % (intf, protoDown))
        return self.SetIntfKernProtoDown(nlm, protoDown, batch=batch)

    def SetClagIntfModeDormant(self, nlm, dormant, retainProtoDown=False):
        # Check if dormant mode setting has been disabled
        if dormant:
            if Parser.args.dormantDisable:
                Log.log_debug(Log._LOG_DEBUG_FSM, "Interface %s dormant mode setting skipped" % (self.intfName))
                return
            if (self.protoDownFlags & self._PROTO_DOWNF_SHUTDOWN):
                # clagd is in the process of exiting; don't allow dormant
                # setting from the UpdateLacpConfig thread at this point
                Log.log_debug(Log._LOG_DEBUG_FSM,
                    "%s dormant mode skipped; clag exit in prog" % (self.intfName))
                return

        # check if the mode already matches
        oldMode = Intf.GetLinkMode(self.intfName)
        if oldMode == dormant:
            return

        # shut down the slaves first
        self.SetIntfKernProtoDown(nlm, protoDown=1)

        # set the mode
        modeStr = "dormant" if dormant else "default"
        oldModeStr = "dormant" if oldMode else "default"
        Log.log_debug(Log._LOG_DEBUG_FSM, "Interface %s mode changed from %s to %s" % (self.intfName, oldModeStr, modeStr))
        Intf.SetLinkMode(nlm, self.intfName, dormant)

        # change the mode to match local state
        if retainProtoDown:
            self.UpdateIntfProtoDown(nlm)

    def _clag_intf_cc_parent_conflict(self, conflict):
        parentConflict = Parser._clag_intf_cc_dependent_conflicts_map.get(conflict, None)
        if parentConflict:
            return True if (self.conflicts & parentConflict) else False
        return False

    def LogConflictSetFlags(self, flags):
        if (flags & Parser._CLAG_INTF_CONFLICT_NO_PEER):
            Log.log("Conflict (%s): matching clag-id (%d) not configured on peer" % (self.intfName, self.clagId))

        if (flags & Parser._CLAG_INTF_CONFLICT_LACP_PARTNER_MAC_MISMATCH):
            Log.log("Conflict (%s): LACP partner MAC mismatch" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_LACP_MAC_EQ_SYS_MAC):
            Log.log("Conflict (%s): LACP partner MAC is same as our CLAG sysmac" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_LACP_MAC_DUP):
            Log.log("Conflict (%s): duplicate LACP partner MAC" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_VLAN_MISMATCH):
            Log.log("Conflict (%s): Allowed vlan mismatch" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_PVLAN_MISMATCH):
            Log.log("Conflict (%s): Native vlan mismatch" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_VLAN_MISMATCH_ON_PEERLINK):
            Log.log("Conflict (%s): Some of the vlans are mismatched on the peerlink" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_MASTER_NOT_BRIDGE):
            Log.log("Conflict (%s): The interface is not a bridge member" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_MTU_MISMATCH):
            Log.log("Conflict (%s): mtu mismatch between clag peers" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_BRIDGE_LEARNING_MISMATCH):
            Log.log("Conflict (%s): bridge learning mismatch between clag peers" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_BPDU_GUARD_MISMATCH):
            Log.log("Conflict (%s): bpdu guard mismatch between clag peers" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_RESTRICTED_ROLE_MISMATCH):
            Log.log("Conflict (%s): restricted role mismatch between clag peers" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_VXLAN_VLAN_MISMATCH):
            Log.log("Conflict (%s): vni-vlan mapping mismatch between clag peers" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_VXLAN_VNI_NOT_CONFIGURED):
            Log.log("Conflict (%s): vni configuration mismatch on the clag peer" % (self.intfName))

    def LogConflictClearFlags(self, flags):
        if (flags & Parser._CLAG_INTF_CONFLICT_NO_PEER):
            Log.log("Conflict cleared (%s): matching clag-id (%d) detected on peer" % (self.intfName, self.clagId))

        if (flags & Parser._CLAG_INTF_CONFLICT_LACP_PARTNER_MAC_MISMATCH):
            Log.log("Conflict cleared (%s): LACP partner MAC is no longer mismatched" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_LACP_MAC_EQ_SYS_MAC):
            Log.log("Conflict cleared (%s): LACP partner MAC is no longer same as our CLAG sysmac" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_LACP_MAC_DUP):
            Log.log("Conflict cleared (%s): LACP partner MAC is no longer duplicate" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_VLAN_MISMATCH):
            Log.log("Conflict cleared (%s): Allowed vlan is no longer mismatched" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_PVLAN_MISMATCH):
            Log.log("Conflict cleared (%s): Native vlan is no longer mismatched" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_VLAN_MISMATCH_ON_PEERLINK):
            Log.log("Conflict cleared (%s): Vlans are no longer mismatched on the peerlink" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_MASTER_NOT_BRIDGE):
            Log.log("Conflict cleared (%s): The interface is now a bridge member" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_BPDU_GUARD_MISMATCH):
            Log.log("Conflict cleared (%s): bpdu guard no longer mismatched between clag peers" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_RESTRICTED_ROLE_MISMATCH):
            Log.log("Conflict cleared (%s): restricted role no longer mismatched between clag peers" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_MTU_MISMATCH):
            Log.log("Conflict cleared (%s): mtu is no longer mismatched between clag peers" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_BRIDGE_LEARNING_MISMATCH):
            Log.log("Conflict cleared (%s): bridge learning is no longer mismatched between clag peers" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_VXLAN_VLAN_MISMATCH):
            Log.log("Conflict cleared (%s): vni-vlan mapping is no longer mismatched between clag peers" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_VXLAN_VNI_NOT_CONFIGURED):
            Log.log("Conflict cleared (%s): vni is no longer mismatched on the clag peer" % (self.intfName))

    def LogConflictSkipFlags(self, flags):
        if (flags & Parser._CLAG_INTF_CONFLICT_PVLAN_MISMATCH):
            Log.log("Conflict ignored (%s): Native vlan mismatch" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_VLAN_MISMATCH_ON_PEERLINK):
            Log.log("Conflict ignored (%s): Vlans mismatch on the peerlink" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_MTU_MISMATCH):
            Log.log("Conflict ignored (%s): mtu mismatch between clag peers" % (self.intfName))

        if (flags & Parser._CLAG_INTF_CONFLICT_BRIDGE_LEARNING_MISMATCH):
            Log.log("Conflict ignored (%s): bridge learning mismatch between clag peers" % (self.intfName))

    def UpdateConflicts(self, setList, clear, ignore=0x0, logConflict=True):
        """
        Api updates the self.conflicts variable on conflict set/clear for a clag interface.
        Updates the self.conflicts_ignored variable if conflicts are ignored. 
        If api is called with set/clear conflicts, any stale state in the 
        conflicts_ignored is cleared before proceeding to update the conflicts.
        set/clear of a conflicts get preference over ignore conflicts.
        """
        if not ignore:
            self.conflicts_ignored &= ~setList if setList else ~clear
  
        setList &= ~self.conflicts
        clear &= self.conflicts
        ignore &= ~self.conflicts_ignored
        if not Parser.isPeerConsistencyCheckSupported():
            # We will only update intf conflicts in the allowed list
            # when peer version does not support CC
            setList &= Parser._CLAG_INTF_CONFLICT_ALLOWED_PEER_CLAG_VER_UNSUPPORTED
            clear &= Parser._CLAG_INTF_CONFLICT_ALLOWED_PEER_CLAG_VER_UNSUPPORTED

        if clear:
            self.conflicts &= ~clear
            if logConflict:
                self.LogConflictClearFlags(clear)

        if setList:
            self.conflicts |= setList
            if logConflict:
                self.LogConflictSetFlags(setList)

        if ignore:
            self.conflicts_ignored |= ignore
            self.LogConflictSkipFlags(ignore)

#-------------------------------------------------------------------------------
#
#   Command line parsing code
#
#-------------------------------------------------------------------------------

class ThrowingArgumentParser(argparse.ArgumentParser):
    def error(self, message):
        raise ArgumentParsingError(message)

class GlobalCC:
    # Consistency checks are run between the peer switches for global inconsistencies
    # If there is config/state inconsistency it is reported
    # as a conflict on the global system level via one of these reason codes.
    _CLAG_GLOBAL_CONFLICT_NONE = 0
    _CLAG_GLOBAL_CONFLICT_SYSTEM_MAC_MISMATCH = (1 << 0)
    _CLAG_GLOBAL_CONFLICT_BRIDGE_PRIORITY_MISMATCH = (1 << 1)
    _CLAG_GLOBAL_CONFLICT_CLAG_VERSION_MISMATCH = (1 << 2)
    _CLAG_GLOBAL_CONFLICT_CLAG_PACKAGE_VERSION_MISMATCH = (1 << 3)
    _CLAG_GLOBAL_CONFLICT_ANYCAST_IP_MISMATCH = (1 << 4)
    _CLAG_GLOBAL_CONFLICT_ANYCAST_IP_NOT_CONFIGURED = (1 << 5)
    _CLAG_GLOBAL_CONFLICT_PEER_IP_MISMATCH = (1 << 6)
    _CLAG_GLOBAL_CONFLICT_REDIRECT_ENABLE_MISMATCH = (1 << 7)
    _CLAG_GLOBAL_CONFLICT_PEERLINK_MTU_MISMATCH = (1 << 8)
    _CLAG_GLOBAL_CONFLICT_BRIDGE_TYPE_MISMATCH = (1 << 9)
    _CLAG_GLOBAL_CONFLICT_PEERLINK_MASTER_NOT_BRIDGE = (1 << 10)
    _CLAG_GLOBAL_CONFLICT_PEERLINK_VLAN_MISMATCH = (1 << 11)
    _CLAG_GLOBAL_CONFLICT_BRIDGE_STP_STATE_MISMATCH = (1 << 12)
    _CLAG_GLOBAL_CONFLICT_BRIDGE_STP_MODE_MISMATCH = (1 << 13)
    _CLAG_GLOBAL_CONFLICT_PEERLINK_PVLAN_MISMATCH = (1 << 14)
    _CLAG_GLOBAL_CONFLICT_PEERLINK_NO_PVLAN = (1 << 15)
    _CLAG_GLOBAL_CONFLICT_MAX = (1 << 16)

    _CLAG_GLOBAL_CONFLICT_ALL = (_CLAG_GLOBAL_CONFLICT_MAX -1)

    _clag_global_conflict_map = { 
            _CLAG_GLOBAL_CONFLICT_SYSTEM_MAC_MISMATCH: 'system mac mismatch between clag peers',
            _CLAG_GLOBAL_CONFLICT_BRIDGE_PRIORITY_MISMATCH: 'bridge stp priority mismatch between clag peers',
            _CLAG_GLOBAL_CONFLICT_CLAG_VERSION_MISMATCH: 'clag version mismatch between clag peers',
            _CLAG_GLOBAL_CONFLICT_CLAG_PACKAGE_VERSION_MISMATCH: 'clag package version mismatch between clag peers',
            _CLAG_GLOBAL_CONFLICT_ANYCAST_IP_MISMATCH: 'anycast ip mismatch between clag peers',
            _CLAG_GLOBAL_CONFLICT_ANYCAST_IP_NOT_CONFIGURED: 'anycast ip not configured on clag node',
            _CLAG_GLOBAL_CONFLICT_PEER_IP_MISMATCH: 'peer ip mismatch between clag peers',
            _CLAG_GLOBAL_CONFLICT_REDIRECT_ENABLE_MISMATCH: 'redirect enable flag mismatch between clag peers',
            _CLAG_GLOBAL_CONFLICT_PEERLINK_MTU_MISMATCH: 'Peerlink MTU mismatch between clag peers',
            _CLAG_GLOBAL_CONFLICT_BRIDGE_TYPE_MISMATCH: 'bridge type (vlan-aware, vlan-unaware) mismatch between clag peers',
            _CLAG_GLOBAL_CONFLICT_PEERLINK_MASTER_NOT_BRIDGE: 'Peerlink is not part of the bridge on the clag node',
            _CLAG_GLOBAL_CONFLICT_PEERLINK_VLAN_MISMATCH: 'Peerlink vlans mismatch between clag peers',
            _CLAG_GLOBAL_CONFLICT_BRIDGE_STP_STATE_MISMATCH: 'bridge-stp state mismatch between clag peers',
            _CLAG_GLOBAL_CONFLICT_BRIDGE_STP_MODE_MISMATCH: 'bridge-stp-mode mismatch between clag peers',
            _CLAG_GLOBAL_CONFLICT_PEERLINK_PVLAN_MISMATCH: 'native vlan on peerlink mismatch between clag peers',
            _CLAG_GLOBAL_CONFLICT_PEERLINK_NO_PVLAN: 'native vlan not configured on peerlink',
    }

    _clag_global_cc_name = {
            # Parameter: (Description, Conflicts to match)
            'system-mac' : ('System Mac configured on both clag peers', _CLAG_GLOBAL_CONFLICT_SYSTEM_MAC_MISMATCH),
            'clag-protocol-version' : ('CLAG internal protocol version on both clag peers', _CLAG_GLOBAL_CONFLICT_CLAG_VERSION_MISMATCH),
            'clag-pkg-version' : ('CLAG package version on both clag peers', _CLAG_GLOBAL_CONFLICT_CLAG_PACKAGE_VERSION_MISMATCH),
            'bridge-priority' : ('Bridge Priority of the bridge on both clag peers', _CLAG_GLOBAL_CONFLICT_BRIDGE_PRIORITY_MISMATCH),
            'anycast-ip' : ('Anycast IP configured on both clag peers', _CLAG_GLOBAL_CONFLICT_ANYCAST_IP_MISMATCH | _CLAG_GLOBAL_CONFLICT_ANYCAST_IP_NOT_CONFIGURED),
            'peer-ip' : ('Peer IP configured on both clag peers', _CLAG_GLOBAL_CONFLICT_PEER_IP_MISMATCH),
            'redirect2-enable' : ('Redirect enable flag configured on both clag peers', _CLAG_GLOBAL_CONFLICT_REDIRECT_ENABLE_MISMATCH),
            'peerlink-mtu' : ('peerlink mtu configured on both clag peers', _CLAG_GLOBAL_CONFLICT_PEERLINK_MTU_MISMATCH),
            'bridge-type' : ('bridge type (vlan-aware/vlan-unaware) configured on both clag peers', _CLAG_GLOBAL_CONFLICT_BRIDGE_TYPE_MISMATCH),
            'peerlink-bridge-member' : ('peerlink master configured on the clag node', _CLAG_GLOBAL_CONFLICT_PEERLINK_MASTER_NOT_BRIDGE),
            'peerlink-vlans' : ('Peerlink vlans configured on both clag peers', _CLAG_GLOBAL_CONFLICT_PEERLINK_VLAN_MISMATCH),
            'bridge-stp-state' : ('bridge-stp state configured on bridge of both clag peers', _CLAG_GLOBAL_CONFLICT_BRIDGE_STP_STATE_MISMATCH),
            'bridge-stp-mode' : ('bridge-stp mode (PVRST or RSTP) configured on both clag peers', _CLAG_GLOBAL_CONFLICT_BRIDGE_STP_MODE_MISMATCH),
            'peerlink-native-vlan' : ('native vlan configured on peerlink of both clag peers', _CLAG_GLOBAL_CONFLICT_PEERLINK_PVLAN_MISMATCH | _CLAG_GLOBAL_CONFLICT_PEERLINK_NO_PVLAN),
    }

    # List of CC params to show when the peer clag data version is not supported
    _clag_global_cc_name_with_peer_unsupported = {
            # Parameter: (Description, Conflicts to match)
            'system-mac' : ('System Mac configured on both clag peers', _CLAG_GLOBAL_CONFLICT_SYSTEM_MAC_MISMATCH),
            'clag-protocol-version' : ('CLAG internal protocol version on both clag peers', _CLAG_GLOBAL_CONFLICT_CLAG_VERSION_MISMATCH),
            'clag-pkg-version' : ('CLAG package version on both clag peers', _CLAG_GLOBAL_CONFLICT_CLAG_PACKAGE_VERSION_MISMATCH),
            'bridge-priority' : ('Bridge Priority of the bridge on both clag peers', _CLAG_GLOBAL_CONFLICT_BRIDGE_PRIORITY_MISMATCH),
            'anycast-ip' : ('Anycast IP configured on both clag peers', _CLAG_GLOBAL_CONFLICT_ANYCAST_IP_MISMATCH | _CLAG_GLOBAL_CONFLICT_ANYCAST_IP_NOT_CONFIGURED),
            'peer-ip' : ('Peer IP configured on both clag peers', _CLAG_GLOBAL_CONFLICT_PEER_IP_MISMATCH),
            'redirect2-enable' : ('Redirect enable flag configured on both clag peers', _CLAG_GLOBAL_CONFLICT_REDIRECT_ENABLE_MISMATCH),
    }

    # List of CC params which are dependent. 
    # The dictionary gives the mapping of each and their dependent
    # params.  
    _clag_global_cc_dependent_conflicts_map = {
        _CLAG_GLOBAL_CONFLICT_PEERLINK_MTU_MISMATCH : _CLAG_GLOBAL_CONFLICT_PEERLINK_MASTER_NOT_BRIDGE, 
        _CLAG_GLOBAL_CONFLICT_PEERLINK_VLAN_MISMATCH : 
        _CLAG_GLOBAL_CONFLICT_PEERLINK_MASTER_NOT_BRIDGE,
        _CLAG_GLOBAL_CONFLICT_PEERLINK_PVLAN_MISMATCH :
        _CLAG_GLOBAL_CONFLICT_PEERLINK_MASTER_NOT_BRIDGE,
        _CLAG_GLOBAL_CONFLICT_PEERLINK_NO_PVLAN :
        _CLAG_GLOBAL_CONFLICT_PEERLINK_MASTER_NOT_BRIDGE,
        _CLAG_GLOBAL_CONFLICT_CLAG_VERSION_MISMATCH:
        _CLAG_GLOBAL_CONFLICT_CLAG_PACKAGE_VERSION_MISMATCH,
        _CLAG_GLOBAL_CONFLICT_BRIDGE_PRIORITY_MISMATCH:
        _CLAG_GLOBAL_CONFLICT_BRIDGE_STP_MODE_MISMATCH
    }

    def __init__(self):
        # Global Consistency Conflict
        self.global_conflicts = self._CLAG_GLOBAL_CONFLICT_NONE
        self.global_conflicts_ignored = self._CLAG_GLOBAL_CONFLICT_NONE

    def get_global_cc_parent_conflict_status(self, conflict):
        parentConflict = self._clag_global_cc_dependent_conflicts_map.get(conflict, None)
        if parentConflict:
            return True if (self.global_conflicts & parentConflict) else False
        return False

    def get_global_cc_allowed_conflict_with_peer_unsupported(self):
        allowed_conflict = self._CLAG_GLOBAL_CONFLICT_NONE
        for _, val in self._clag_global_cc_name_with_peer_unsupported.items():
            allowed_conflict |= val[1]
        return allowed_conflict

    def LogGlobalConflictSetFlags(self, flags):
        if (flags & self._CLAG_GLOBAL_CONFLICT_SYSTEM_MAC_MISMATCH):
            Log.log("Conflict: system-mac configuration mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_BRIDGE_PRIORITY_MISMATCH):
            Log.log("Conflict: Bridge STP Priority mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_CLAG_VERSION_MISMATCH):
            Log.log("Conflict: Clag Version mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_CLAG_PACKAGE_VERSION_MISMATCH):
            Log.log("Conflict: Clag Package Version mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_ANYCAST_IP_MISMATCH):
            Log.log("Conflict: Anycast IP mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_ANYCAST_IP_NOT_CONFIGURED):
            Log.log("Conflict: Anycast IP not configured on clag node")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEER_IP_MISMATCH):
            Log.log("Conflict: Peer IP mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_REDIRECT_ENABLE_MISMATCH):
            Log.log("Conflict: Redirect enable flag mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEERLINK_MTU_MISMATCH):
            Log.log("Conflict: Peerlink MTU mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_BRIDGE_TYPE_MISMATCH):
            Log.log("Conflict: Bridge Vlan Aware mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEERLINK_MASTER_NOT_BRIDGE):
            Log.log("Conflict: Peerlink is not part of bridge")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEERLINK_VLAN_MISMATCH):
            Log.log("Conflict: Peerlink vlans mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_BRIDGE_STP_STATE_MISMATCH):
            Log.log("Conflict: Bridge Stp State mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_BRIDGE_STP_MODE_MISMATCH):
            Log.log("Conflict: Bridge Stp Mode mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEERLINK_NO_PVLAN):
            Log.log("Conflict: Native Vlan not configured on the peerlink")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEERLINK_PVLAN_MISMATCH):
            Log.log("Conflict: Native Vlan mismatch on peerlink between clag peers")

    def LogGlobalConflictClearFlags(self, flags):
        if (flags & self._CLAG_GLOBAL_CONFLICT_SYSTEM_MAC_MISMATCH):
            Log.log("Conflict cleared: system-mac configuration is no longer mismatched between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_BRIDGE_PRIORITY_MISMATCH):
            Log.log("Conflict cleared: Bridge STP Priority is no longer mismatched between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_CLAG_VERSION_MISMATCH):
            Log.log("Conflict cleared: Clag Version is no longer mismatched between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_CLAG_PACKAGE_VERSION_MISMATCH):
            Log.log("Conflict cleared: Clag Package Version is no longer mismatched between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_ANYCAST_IP_MISMATCH):
            Log.log("Conflict cleared: Anycast IP is no longer mismatched between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_ANYCAST_IP_NOT_CONFIGURED):
            Log.log("Conflict cleared: Anycast IP is now configured on clag node")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEER_IP_MISMATCH):
            Log.log("Conflict cleared: Peer IP is no longer mismatched between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_REDIRECT_ENABLE_MISMATCH):
            Log.log("Conflict cleared: Redirect enable flag is no longer mismatched between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEERLINK_MTU_MISMATCH):
            Log.log("Conflict cleared: Peerlink MTU is no longer mismatched between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_BRIDGE_TYPE_MISMATCH):
            Log.log("Conflict cleared: Bridge Vlan Aware is no longer mismatched between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEERLINK_MASTER_NOT_BRIDGE):
            Log.log("Conflict cleared: Peerlink is now part of bridge")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEERLINK_VLAN_MISMATCH):
            Log.log("Conflict cleared: Peerlink vlans no longer mismatched between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_BRIDGE_STP_STATE_MISMATCH):
            Log.log("Conflict cleared: Bridge Stp State no longer mismatched between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_BRIDGE_STP_MODE_MISMATCH):
            Log.log("Conflict cleared: Bridge Stp Mode no longer mismatched between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEERLINK_NO_PVLAN):
            Log.log("Conflict cleared: Native Vlan now configured on the peerlink")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEERLINK_PVLAN_MISMATCH):
            Log.log("Conflict cleared: Native Vlan no longer mismatched on peerlink between clag peers")

    def LogGlobalConflictSkipFlags(self, flags):
        if (flags & self._CLAG_GLOBAL_CONFLICT_CLAG_VERSION_MISMATCH):
            Log.log("Conflict ignored: Clag Version mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEERLINK_MTU_MISMATCH):
            Log.log("Conflict ignored: Peerlink MTU mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEERLINK_VLAN_MISMATCH):
            Log.log("Conflict ignored: Peerlink vlans mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEERLINK_NO_PVLAN):
            Log.log("Conflict ignored: Native Vlan not configured on the peerlink")

        if (flags & self._CLAG_GLOBAL_CONFLICT_BRIDGE_PRIORITY_MISMATCH):
            Log.log("Conflict ignored: Bridge STP Priority mismatch between clag peers")

        if (flags & self._CLAG_GLOBAL_CONFLICT_PEERLINK_PVLAN_MISMATCH):
            Log.log("Conflict ignored: Native Vlan mismatch on peerlink between clag peers")

    def UpdateGlobalConflicts(self, set, clear, ignore=0x0, logConflict=True):
        """
        Api updates the self.global_conflicts variable on conflict set/clear.
        Updates the self.global_conflicts_ignored variable if conflicts are ignored. 
        If api is called with set/clear conflicts, any stale state in the 
        global_conflicts_ignored is cleared before proceeding to update the global_conflicts.
        set/clear of a conflicts get preference over ignore conflicts.
        """
        if not ignore:
            self.global_conflicts_ignored &= ~set if set else ~clear

        set &= ~self.global_conflicts
        clear &= self.global_conflicts
        ignore &= ~self.global_conflicts_ignored

        if not Parser.isPeerConsistencyCheckSupported():
            # We will only update global conflicts in the allowed list
            # when peer version does not support CC
            set &= self.get_global_cc_allowed_conflict_with_peer_unsupported()
            clear &= self.get_global_cc_allowed_conflict_with_peer_unsupported()

        if clear:
            self.global_conflicts &= ~clear
            if logConflict:
                self.LogGlobalConflictClearFlags(clear)

        if set:
            self.global_conflicts |= set
            if logConflict:
                self.LogGlobalConflictSetFlags(set)

        if ignore:
            self.global_conflicts_ignored |= ignore
            if logConflict:
                self.LogGlobalConflictSkipFlags(ignore)

    def GetClagGlobalConflicts(self):
        return self.global_conflicts

    def GetClagGlobalConflictsIgnored(self):
        return self.global_conflicts_ignored

    def GetClagGlobalConflictStr(self, conflicts=None):
        conflictList = []
        if not conflicts:
            conflicts = self.GetClagGlobalConflicts()
        bits = Parser.GetNonZeroBits(conflicts)
        for bit in bits:
            if bit in Globcc._clag_global_conflict_map:
                bitStr = Globcc._clag_global_conflict_map[bit]
            else:
                bitStr = "0x%x" % bit
            conflictList.append(bitStr)
        return conflictList

    def getLocalPeerValueClagGlobalCC(self, ccName):
        valueDict = {}

        if ccName == "system-mac":
            valueDict['local'] = Parser.args.sysMac if Parser.args.sysMac else '-'
            valueDict['peer'] = Parser.peerSysMac if Parser.peerSysMac else '-'
        elif ccName == "clag-protocol-version":
            valueDict['local'] = Parser._ClagDataVersion if Parser._ClagDataVersion else '-'
            valueDict['peer'] = Parser.peerDataVersion if Parser.peerDataVersion else '-'
        elif ccName == "clag-pkg-version":
            valueDict['local'] = Parser.packageVersion if Parser.packageVersion else '-'
            valueDict['peer'] = Parser.peerPackageVersion if Parser.peerPackageVersion else '-'
        elif ccName == "bridge-priority":
            if StpSync.bridgePvrst and StpSync.isPeerPvrstCCSupported():
                valueDict['local'] = StpSync.normalize_bridge_prio_map(StpSync.bridgeStpPrio)
                valueDict['peer'] = StpSync.normalize_bridge_prio_map(StpSync.peerBridgeStpPrio)
            else:
                valueDict['local'] = str(Parser.bridgePriority) if Parser.bridgePriority else '-'
                valueDict['peer'] = str(Parser.peerBridgePriority) if Parser.peerBridgePriority else '-'
            # When we ignore the bridge-prio cc, show it as "-" for the peer
            if StpSync.ignoreBridgePrioCC():
                valueDict['peer'] = "-"
        elif ccName == "bridge-stp-state":
            valueDict['local'] = "on" if Parser.bridgeStpState else 'off'
            valueDict['peer'] = "on" if Parser.peerBridgeStpState else 'off'
        elif ccName == "bridge-stp-mode":
            valueDict['local'] = "pvrst" if StpSync.bridgePvrst else 'rstp'
            valueDict['peer'] = "pvrst" if StpSync.peerBridgePvrst else 'rstp'
        elif ccName == "anycast-ip":
            valueDict['local'] = Parser.args.vxlanAnycast if Parser.args.vxlanAnycast else '-'
            valueDict['peer'] = Parser.peerAnycastIp if Parser.peerAnycastIp else '-'
        elif ccName == "peer-ip":
            valueDict['local'] = Parser.remotePeerIp if Parser.remotePeerIp else '-'
            valueDict['peer'] = Parser.peerIpAddr if Parser.peerIpAddr else '-'
        elif ccName == "redirect2-enable":
            valueDict['local'] = 'yes' if Parser.args.redirect2Enable else 'no'
            valueDict['peer'] = 'yes' if Parser.peerRedirect2Enable else 'no'
        elif ccName == "peerlink-mtu":
            valueDict['local'] = str(Parser.peerlinkMtu) if Parser.peerlinkMtu else '-'
            valueDict['peer'] = str(Parser.peerPeerlinkMtu) if Parser.peerPeerlinkMtu else '-'
        elif ccName == "bridge-type":
            valueDict['local'] = Parser.bridgeType
            valueDict['peer'] = Parser.peerBridgeType
        elif ccName == "peerlink-vlans" and VlanSync and Parser:
            ourVlanMap = VlanSync.GetOurPeerlinkVlanMap()
            peerVlanMap = VlanSync.GetPeerPeerlinkVlanMap()
            valueDict['local'] = Parser.ranges_to_compact_str(VlanSync.GetVlanIdList(ourVlanMap))
            valueDict['peer'] = Parser.ranges_to_compact_str(VlanSync.GetVlanIdList(peerVlanMap))
        elif ccName == "peerlink-native-vlan" and VlanSync:
            valueDict['local'] = VlanSync.GetOurPeerlinkPvlan()
            valueDict['peer'] = VlanSync.GetPeerPeerlinkPvlan()
        else:
            valueDict['local'] = "-"
            valueDict['peer'] = "-"

        return valueDict

    def getCCDictClagGlobal(self, conflicts):
        ccList = []
        skip_cc_list = []
        if not Parser.isPeerConsistencyCheckSupported():
            skip_cc_list = list(set(Globcc._clag_global_cc_name.keys()) - set(Globcc._clag_global_cc_name_with_peer_unsupported.keys()))
        for ccName in Globcc._clag_global_cc_name:
            if ccName in skip_cc_list:
                # Skip all the global CC params in the skip_cc_list when peer clag data version is unsupported for CC
                continue
            if ccName in ['bridge-stp-mode'] and not StpSync.isPeerPvrstCCSupported():
                # Skip the CC if peer does not support
                continue
            ccDict = {}
            if ccName == 'peerlink-bridge-member':
                for peerlink in Parser.peerlinkMaster:
                    ccDict = {}
                    ccDict['name'] = 'peerlink-bridge-member'
                    if Parser.peerlinkMaster[peerlink][0]:
                        ccDict['description'] = '-'
                    else:
                        ccDict['description'] = ", ".join(self.GetClagGlobalConflictStr(self._CLAG_GLOBAL_CONFLICT_PEERLINK_MASTER_NOT_BRIDGE))
                    ccDict['local'] = "Yes" if Parser.peerlinkMaster[peerlink][0] else 'No'
                    ccDict['peer'] = Parser.peerPeerlinkBridgeMember
                    ccList.append(ccDict)
                continue

            if ccName == "bridge-priority" and StpSync.bridgePvrst and StpSync.isPeerPvrstCCSupported():
                # In PVRST mode we want to rename the CC parameter to add `per-vlan` string so that it is clear
                ccDict['name'] = "bridge-priority (per-vlan)"
            else:
                ccDict['name'] = ccName

            ccDict.update(self.getLocalPeerValueClagGlobalCC(ccName))

            ccConflict = Globcc._clag_global_cc_name[ccName][1]
            ccConflict_ignored = self.GetClagGlobalConflictsIgnored()
            confDescList = []
            if (ccConflict & conflicts):
                confDescList = self.GetClagGlobalConflictStr(ccConflict & conflicts)
            
            if ccDict['peer'] == Parser._VALUE_FIELD_NOT_SET[0]:
                confDescList.append(Parser._VALUE_FIELD_NOT_SET[1])

            if confDescList:
                ccDict['description'] = ", ".join(confDescList)
            elif (ccConflict_ignored & ccConflict):
                ccDict['description'] = "".join("Consistency-Check Ignored")
            else:
                ccDict['description'] = '-'

            ccList.append(ccDict)
        return ccList

    def getClagGlobalCCDict(self):
        ccParamDict = {}
        conflicts = self.GetClagGlobalConflicts()
        ccParamDict = self.getCCDictClagGlobal(conflicts)
        return ccParamDict

class ClagParser:

    #
    #   CLAG Version information
    #
    _ClagVersion            = "1.4.0"
    _ClagDataVersion        = "1.8.0"
    _ClagCmdVersion         = "1.1.0"
    _ClagHelloVersion       = "1.1.1"

    _ROLE_SECONDARY         = "secondary"
    _ROLE_PRIMARY           = "primary"

    _CLAG_ID_MAX            = 65535

    # tuple to be set for the consistency-check parameter if the peer does not have support
    _VALUE_FIELD_NOT_SET     = ("DATA NOT AVAILABLE", 'data not available from clag peer')

    # CLAG package version
    packageVersion            = None
    peerPackageVersion        = None

    #
    #   Default command line parameter values
    #
    default_lacp_poll       = 2
    default_peer_connect    = 1
    default_send_timeout    = 30
    default_send_bufsize    = 65536
    default_cmd_connect     = 1
    default_link_poll       = 1
    default_daemon          = False
    default_peer_port       = 5342
    default_backup_port     = 5342
    default_peer_timeout    = None
    default_role            = _ROLE_SECONDARY
    default_priority        = 32768
    default_switchd_timeout = 120
    default_reload_timer    = 300
    default_periodic_run    = 4
    default_init_delay      = 180
    default_force_dynamic   = False
    default_dormant_disable = True
    default_vxlan_anycast   = None
    default_redirect_enable = False
    default_graceful_cc     = False
    default_redirect2_enable = True
    default_neigh_sync      = True
    default_permanent_mac_sync = True
    default_single_vxlan_device = True
    default_peerlinklearning_enable = False
    default_dup_partner_mac = True
    default_protobuf = True
    default_csu_support = True
    default_mem_trace = False
    default_mem_snapshot_intv = 10
    default_start_up_sync_delay = True

    # fully resolved remote peer IP address 
    remotePeerIp            = None
    localVtepIp             = "0.0.0.0"

    # The following variable used to skip some steps on shutdown/reboot
    fastShutdown = True
    # The number of echo replies to ignore from the peer which is going down/rebooting
    backupFaultTolerance = 3

    # Keep a cached value of the system MAC (workaround for CM-21314)
    # CM-21321 will be the actual fix
    systemMac = None

    # bond to clagIntf association
    # {
    #     "bond1" : <clagIntf1>,
    #     "bond2" : <clagIntf2>
    # }
    # key=intfName : val=class ClagIntf
    clagIntfDB              = {}
    # lock for clagIntfDB and bondClagIdDb access
    clagIntfDBLock          = threading.Lock()

    # clag-id to bond association
    # {
    #     100 : "bond1",
    #     2 : "bond2"
    # }
    bondClagIdDB            = {}

    peerDataVersion             = "0.0.0"
    peerRedirect2Enable         = False

    peerProtoBuf                = default_protobuf
    # set once all the initial data has been replayed to peer. cleared when peer
    # connectivity is lost
    syncDoneToPeer          = False
    # set once the initial replay of data from peer is done (peer will send a 
    # sync_done notification). cleared when peer connectivity is lost.
    syncDoneFromPeer        = False

    # We wait for all the config to be replayed from ifupdown2 before starting
    # communication with the peer
    configReloadDone        = False

    #Flag to check clagD is in the process of exiting 
    sendGoodbye             = False

    peerTimeoutStart        = 0
    bridgePriority          = 0
    peerBridgePriority      = 0
    bridgeStpState          = False
    peerBridgeStpState      = False

    bridgePeerCCSuspended   = False
    bridgeLocalCCSuspended  = False

    peerlinkMtu        = 0
    peerPeerlinkMtu    = 0
    peerPeerlinkBridgeMember = ""

    bridgeType         = ''
    peerBridgeType     = ''

    peerIpAddr         = None
    peerSysMac         = None
    peerAnycastIp      = None

    # peerlinkMaster is a dictionary with key as peerlink interface
    # data is the master bridge for each peerlink interface and if the bridge is vlan-aware
    # eg: {'peerlink-1': ('bridge-1', True), 'peerlink-2': ('bridge-2', False)}
    peerlinkMaster     = {}

    vlan_aware_bridge  = []
    vlan_unaware_bridge = []
    peerlinkVlanAwareBridgeAdded = False

    #Flag to check if anycast Ip has been deleted by clag
    vxlanAnycastDeleted = True
    
    # Flag to enable/disable interaction with CSU Manager
    csuSupport              = default_csu_support

    # Consistency checks are run between the peer switches for all clag
    # interfaces/bonds. If there is config/state inconsistency it is reported
    # as a conflict on the on the interface via one of these reason codes.
    _CLAG_INTF_CONFLICT_NONE = 0
    _CLAG_INTF_CONFLICT_NO_PEER = (1 << 0)
    _CLAG_INTF_CONFLICT_LACP_PARTNER_MAC_MISMATCH = (1 << 1)
    _CLAG_INTF_CONFLICT_LACP_MAC_EQ_SYS_MAC = (1 << 2)
    _CLAG_INTF_CONFLICT_LACP_MAC_DUP = (1 << 3)
    _CLAG_INTF_CONFLICT_MTU_MISMATCH = (1 << 4)
    _CLAG_INTF_CONFLICT_BRIDGE_LEARNING_MISMATCH = (1 << 5)
    _CLAG_INTF_CONFLICT_LACP_MAX = (1 << 6)

    _CLAG_INTF_CONFLICT_VLAN_MISMATCH = (1 << 6)
    _CLAG_INTF_CONFLICT_VLAN_MISMATCH_ON_PEERLINK = (1 << 7)
    _CLAG_INTF_CONFLICT_MASTER_NOT_BRIDGE = (1 << 8)
    _CLAG_INTF_CONFLICT_VXLAN_VLAN_MISMATCH = (1 << 9)
    _CLAG_INTF_CONFLICT_VXLAN_VNI_NOT_CONFIGURED = (1 << 10)
    _CLAG_INTF_CONFLICT_VXLAN_INTF_MISMATCH = (1 << 11)
    _CLAG_INTF_CONFLICT_PVLAN_MISMATCH = (1 << 12)
    _CLAG_INTF_CONFLICT_BPDU_GUARD_MISMATCH = (1 << 13)
    _CLAG_INTF_CONFLICT_RESTRICTED_ROLE_MISMATCH = (1 << 14)
    _CLAG_INTF_CONFLICT_MAX = (1 << 15)

    _CLAG_INTF_CONFLICT_LACP_ALL = (_CLAG_INTF_CONFLICT_LACP_MAX -1)
    _CLAG_INTF_CONFLICT_ALL = (_CLAG_INTF_CONFLICT_MAX -1)
    _CLAG_INTF_CONFLICT_VXLAN_VNI_ALL = (_CLAG_INTF_CONFLICT_VXLAN_VLAN_MISMATCH | _CLAG_INTF_CONFLICT_VXLAN_VNI_NOT_CONFIGURED)

    # When peer clag version is less than 1.6.0, we will only mark
    # supported conflicts from the below list
    _CLAG_INTF_CONFLICT_ALLOWED_PEER_CLAG_VER_UNSUPPORTED = (_CLAG_INTF_CONFLICT_NO_PEER |\
                                                             _CLAG_INTF_CONFLICT_LACP_PARTNER_MAC_MISMATCH |\
                                                             _CLAG_INTF_CONFLICT_LACP_MAC_EQ_SYS_MAC |\
                                                             _CLAG_INTF_CONFLICT_LACP_MAC_DUP)
    _clag_intf_conflict_map = \
            {_CLAG_INTF_CONFLICT_NO_PEER : 'matching clagid not configured on peer',\
             _CLAG_INTF_CONFLICT_LACP_PARTNER_MAC_MISMATCH : 'lacp partner mac mismatch',
             _CLAG_INTF_CONFLICT_LACP_MAC_EQ_SYS_MAC : 'lacp partner mac is same as our clag sysmac',
             _CLAG_INTF_CONFLICT_VLAN_MISMATCH : 'vlan mismatch on clag interface between clag peers',
             _CLAG_INTF_CONFLICT_VLAN_MISMATCH_ON_PEERLINK: 'some allowed vlans on clag interface are not allowed on the peerlink',
             _CLAG_INTF_CONFLICT_MASTER_NOT_BRIDGE : 'clag interface is not a bridge member',
             _CLAG_INTF_CONFLICT_MTU_MISMATCH: 'mtu mismatch on clag interface between clag peers',
             _CLAG_INTF_CONFLICT_BRIDGE_LEARNING_MISMATCH: 'bridge learning mismatch on clag interface between clag peers',
             _CLAG_INTF_CONFLICT_BPDU_GUARD_MISMATCH: 'bpdu guard mismatch on clag interface between clag peers',
             _CLAG_INTF_CONFLICT_RESTRICTED_ROLE_MISMATCH: 'restricted role mismatch on clag interface between clag peers',
             _CLAG_INTF_CONFLICT_VXLAN_VLAN_MISMATCH: 'vni-vlan mapping mismatch between clag peers',
             _CLAG_INTF_CONFLICT_VXLAN_VNI_NOT_CONFIGURED: 'vni not configured on the clag peer',
             _CLAG_INTF_CONFLICT_VXLAN_INTF_MISMATCH: 'vxlan interface not configured on the clag peer',
             _CLAG_INTF_CONFLICT_PVLAN_MISMATCH: 'native vlan mismatch on clag interface between clag peers',
             _CLAG_INTF_CONFLICT_LACP_MAC_DUP : 'duplicate lacp partner mac'}

    _clag_intf_cc_name = {
            # Parameter: (Description, Conflicts to match)
            'clag-id' : ('clag ids of clag bonds on both clag peers', _CLAG_INTF_CONFLICT_NO_PEER),
            'lacp-partner-mac' : ('LACP partner MACs of clag bonds on both clag peers', _CLAG_INTF_CONFLICT_LACP_PARTNER_MAC_MISMATCH | _CLAG_INTF_CONFLICT_LACP_MAC_DUP),
            'lacp-actor-mac' : ('LACP system mac on both clag peers', _CLAG_INTF_CONFLICT_LACP_MAC_EQ_SYS_MAC),
            'vlan-id' : ('Allowed vlan list on both clag peers', _CLAG_INTF_CONFLICT_VLAN_MISMATCH | _CLAG_INTF_CONFLICT_VLAN_MISMATCH_ON_PEERLINK),
            'native-vlan' : ('Native vlan on both clag peers', _CLAG_INTF_CONFLICT_PVLAN_MISMATCH),
            'master' : ('master bridge intf on the local clag node', _CLAG_INTF_CONFLICT_MASTER_NOT_BRIDGE),
            'mtu' : ('MTU configured on both clag peers', _CLAG_INTF_CONFLICT_MTU_MISMATCH),
            'bridge-learning' : ('Bridge Learning configured on both clag peers', _CLAG_INTF_CONFLICT_BRIDGE_LEARNING_MISMATCH),
            'bpdu-guard' : ('Bpdu Guard configured on both clag peers', _CLAG_INTF_CONFLICT_BPDU_GUARD_MISMATCH),
            'restricted-role' : ('Bpdu Guard configured on both clag peers', _CLAG_INTF_CONFLICT_RESTRICTED_ROLE_MISMATCH),
    }

    _clag_vxlan_intf_cc_name = {
            # Parameter: (Description, Conflicts to match)
            'vxlan-intf' : ('vxlan interface configured on both clag peers', _CLAG_INTF_CONFLICT_VXLAN_INTF_MISMATCH),
            'vni' : ('vxlan vni configured on both clag peers', _CLAG_INTF_CONFLICT_VXLAN_VNI_NOT_CONFIGURED),
            'vlan' : ('vxlan vlan configured on both clag peers', _CLAG_INTF_CONFLICT_VXLAN_VLAN_MISMATCH),
            'master' : ('master bridge intf for the local vxlan interface', _CLAG_INTF_CONFLICT_MASTER_NOT_BRIDGE),
            'bpdu-guard' : ('Bpdu Guard configured on both clag peers', _CLAG_INTF_CONFLICT_BPDU_GUARD_MISMATCH),
            'restricted-role' : ('Bpdu Guard configured on both clag peers', _CLAG_INTF_CONFLICT_RESTRICTED_ROLE_MISMATCH),
    }

    _clag_intf_cc_dependent_conflicts_map = {
        _CLAG_INTF_CONFLICT_VLAN_MISMATCH : _CLAG_INTF_CONFLICT_MASTER_NOT_BRIDGE,
        _CLAG_INTF_CONFLICT_PVLAN_MISMATCH : _CLAG_INTF_CONFLICT_MASTER_NOT_BRIDGE,
        _CLAG_INTF_CONFLICT_BRIDGE_LEARNING_MISMATCH : _CLAG_INTF_CONFLICT_MASTER_NOT_BRIDGE
    }

    # When peer clag version is less than 1.6.0, we will only show
    # supported cc names from the below list in clagctl consistency-check CLI
    _clag_peer_version_unsupported_cc_name = {
            # Parameter: (Description, Conflicts to match)
            'clag-id' : ('clag ids of clag bonds on both clag peers', _CLAG_INTF_CONFLICT_NO_PEER),
            'lacp-partner-mac' : ('LACP partner MACs of clag bonds on both clag peers', _CLAG_INTF_CONFLICT_LACP_PARTNER_MAC_MISMATCH | _CLAG_INTF_CONFLICT_LACP_MAC_DUP),
            'lacp-actor-mac' : ('LACP system mac on both clag peers', _CLAG_INTF_CONFLICT_LACP_MAC_EQ_SYS_MAC),
    }

    _CLAG_INTF_TYPE_BOND  = 0
    _CLAG_INTF_TYPE_VXLAN = 1

    _SWITCHD_STARTING   = 1
    _SWITCHD_READY      = 2
    _SWITCHD_DEAD       = 3


    def __init__(self):
        '''
        Create the parser
        '''

        # Determine if we are running on a VM
        default_vm = False
        try:
            default_vm = (subprocess.check_output("/usr/sbin/virt-what").decode() != "")
        except (subprocess.CalledProcessError, OSError):
            pass

        self.parser = ThrowingArgumentParser(description="CLAG daemon, version %s" % (ClagParser._ClagVersion,))
        self.parser.add_argument("--lacpPoll", "-o", type=int, default=ClagParser.default_lacp_poll, 
                                 metavar="SECONDS",
                                 help="Seconds between obtaining local LACP information")
        self.parser.add_argument("--peerConnect", "-r", type=int, default=ClagParser.default_peer_connect,
                                 metavar="SECONDS",
                                 help="Seconds between trying to connect to peer.")
        self.parser.add_argument("--sendTimeout", type=int, default=ClagParser.default_send_timeout,
                                 metavar="SECONDS",
                                 help="Seconds until sending socket will timeout.")
        self.parser.add_argument("--sendBufSize", type=int, default=ClagParser.default_send_bufsize,
                                 metavar="BYTES",
                                 help="Bytes in the send socket buffer.")
        self.parser.add_argument("--cmdConnect", "-c", type=int, default=ClagParser.default_cmd_connect,
                                 metavar="SECONDS",
                                 help="Seconds between waiting for command connections.")
        self.parser.add_argument("--peerLinkPoll", type=int, default=ClagParser.default_link_poll,
                                 metavar="SECONDS",
                                 help="Seconds between checking if peer link is up.")
        self.parser.add_argument("--switchdReadyTimeout", type=int, default=ClagParser.default_switchd_timeout,
                                 metavar="SECONDS",
                                 help="Seconds clagd waits for switchd to be ready.")
        self.parser.add_argument("--reloadTimer", type=int, default=ClagParser.default_reload_timer,
                                 metavar="SECONDS",
                                 help="Seconds clagd waits for peer to become ready.")
        self.parser.add_argument("--priority", "-i", type=int, default=ClagParser.default_priority,
                                 help="The priority of this instance compared to the peer.")
        self.parser.add_argument("--periodicRun", type=int, default=ClagParser.default_periodic_run, 
                                 metavar="SECONDS",
                                 help="Seconds between running periodic processes.")
        self.parser.add_argument("--initDelay", type=int, default=ClagParser.default_init_delay,
                                 metavar="SECONDS",
                                 help="Seconds clagd delays the bringup of clag bond and anycastIp")
        self.parser.add_argument("--forceDynamic", "-f", action="store_true", 
                                 default=ClagParser.default_force_dynamic, 
                                 help=argparse.SUPPRESS)
        self.parser.add_argument("--dormantDisable", "-a",
                                 type=self.str2bool,
                                 default=ClagParser.default_dormant_disable, 
                                 help=argparse.SUPPRESS)
        self.parser.add_argument("--gracefulConsistencyCheck", action="store_true",
                                 default=ClagParser.default_graceful_cc,
                                 help=argparse.SUPPRESS)
        self.parser.add_argument("--redirectEnable", action="store_true",
                                 default=ClagParser.default_redirect_enable,
                                 help=argparse.SUPPRESS)
        self.parser.add_argument("--redirect2Enable", type=self.str2bool,
                                default=ClagParser.default_redirect2_enable,
                                help=argparse.SUPPRESS)
        self.parser.add_argument("--neighSync", type=self.str2bool,
                                 default=ClagParser.default_neigh_sync,
                                 help=argparse.SUPPRESS)
        self.parser.add_argument("--permanentMacSync", type=self.str2bool,
                                 default=ClagParser.default_permanent_mac_sync,
                                 help=argparse.SUPPRESS)
        self.parser.add_argument("--singleVxlanDevice", type=self.str2bool,
                                 default=ClagParser.default_single_vxlan_device,
                                 help=argparse.SUPPRESS)
        self.parser.add_argument("--vxlanAnycast", default=ClagParser.default_vxlan_anycast, 
                                 metavar="IPADDR",
                                 help="Anycast local IP address for dual connected VxLANs")
        self.parser.add_argument("--daemon", "-d", action="store_true", default=ClagParser.default_daemon,
                                 help="Run as a daemon.")
        self.parser.add_argument("--quiet", "-q", action="store_true", default=Logger.default_quiet,
                                 help="Don't log anything.")
        self.parser.add_argument("--debug", type=self.intBase, default=Logger.default_debug,
                                 metavar="DEBUG_CODE",
                                 help="Debugging output mask.")
        self.parser.add_argument("--verbose", "-v", action="store_true", default=Logger.default_verbose,
                                 help="Log with verbosity.")
        self.parser.add_argument("--log", "-l", "--logfile", type=self.logDest, default=Logger.default_handlerStr,
                                 help="Log output to stdout, syslog, or a file")
        self.parser.add_argument("--vm", "-m", action="store_true", dest='vm',
                                 help="Enable debugging on a virtual machine")
        self.parser.add_argument("--novm", action="store_false", dest='vm',
                                 help="Disable debugging on a virtual machine")
        self.parser.set_defaults(vm=default_vm)
        self.parser.add_argument("--backupPort", "-b", type=int, default=ClagParser.default_backup_port,
                                 help="UDP port used by peer to respond to backup health check requests.")
        self.parser.add_argument("--backupIp", "-s", default="",
                                 help="Backup IP address of peer")
        self.parser.add_argument("--backupVrf", default="",
                                 help="VRF for the backupIp address")
        self.parser.add_argument("--peerPort", "-p", type=int, default=ClagParser.default_peer_port,
                                 help="TCP/IP port used by peer to communicate topology info.")
        self.parser.add_argument("--peerTimeout", "-t", type=int, default=ClagParser.default_peer_timeout,
                                 help="Seconds before peer information is discarded.")
        self.parser.add_argument("peerIp", type=self.isValidPeerIp,
                                 help='Unique IP v4/v6 address of peer. "linklocal" connects to peers IPv6 link address')
        self.parser.add_argument("peerIf", type=self.isIfName,
                                 help="Name of the interface connected to peer")
        self.parser.add_argument("sysMac", type=self.isMacAddr,
                                 help="The system ID shared with the peer (xx:xx:xx:xx:xx:xx)")
        self.parser.add_argument("--peerlinkLearnEnable", type=self.str2bool,
                                 default=ClagParser.default_peerlinklearning_enable,
                                 help=argparse.SUPPRESS)
        self.parser.add_argument("--allowPartnerMacDup", type=self.str2bool,
                                 default=ClagParser.default_dup_partner_mac,
                                 help=argparse.SUPPRESS)
        self.parser.add_argument("--protobuf", type=self.str2bool,
                                 default=ClagParser.default_protobuf,
                                 help=argparse.SUPPRESS)
        self.parser.add_argument("--csuSupport", type=self.str2bool,
                                 default=ClagParser.default_csu_support,
                                 help=argparse.SUPPRESS)
        self.parser.add_argument("--memTrace", type=self.str2bool,
                                 default=ClagParser.default_mem_trace,  
                                 help=argparse.SUPPRESS)
        self.parser.add_argument("--memSnapshotIntv", type=int, default=ClagParser.default_mem_snapshot_intv,
                                 metavar="SECONDS",
                                 help="Seconds clagd waits between taking memory snapshot")
        self.parser.add_argument("--startUpSyncDelay", type=self.str2bool,
                                 default=ClagParser.default_start_up_sync_delay,
                                 help=argparse.SUPPRESS)
    
        #
        #   LACP Rate - The rate at which the peer will send information to us. Before
        #   we receive the first message from the peer, we don't know his rate and so
        #   this value will be None. After each message is received from the peer the
        #   included rate, in seconds, is saved here. If we lose communication to the
        #   peer, the value goes back to None.
        #
        self.peerLacpRateLock   = threading.Lock()
        self.peerLacpRate       = None

        self.peerPeerlinkIf     = None

        self.currLacpPoll       = ClagParser.default_lacp_poll
        #                                 [0] Priority,             [1] UniqueId,     
        self.clagId             = [ ClagParser.default_priority, "00:00:00:00:00:00" ]
        self.peerId             = [ ClagParser.default_priority, "00:00:00:00:00:00" ]
        self.clagRole           = ClagParser.default_role
        self.peerRole           = None
        self.systemRebootTriggered = False
        self.UpdateSwitchdState()

    def UpdateSwitchdState(self):
        systemdStateMap = {
            "inactive": ClagParser._SWITCHD_DEAD,
            "activating": ClagParser._SWITCHD_STARTING,
            "reloading": ClagParser._SWITCHD_READY,
            "active": ClagParser._SWITCHD_READY,
            "deactivating": ClagParser._SWITCHD_DEAD,
            "failed": ClagParser._SWITCHD_DEAD,
        }
        try:
            stateStrT = subprocess.check_output(
                ["/bin/systemctl", "is-active", "switchd.service"],
                timeout=2
            ).decode()
            stateStr = stateStrT.strip()
        except subprocess.CalledProcessError:
            stateStr = "failed"
        except Exception:
            stateStr = "failed"
        self.switchdState = systemdStateMap.get(stateStr, ClagParser._SWITCHD_DEAD)
        Log.log("Switchd state is now %d (%s)" % (self.switchdState, stateStr))

    def intBase(self, intStr):
        '''
        Allow an integer to be specified with standard base representations:
        37 = decimal, 0x25 = hex, 045 = octal, 0b100101 = binary
        '''
        try:
            intVal = int(intStr,0)
        except:
            msg = "%s is not a valid integer" % (intStr,)
            raise argparse.ArgumentTypeError(msg)
        return intVal

    def logDest(self, logDest):
        '''
        The log file can be the constant 'syslog', 'stdout', or the name of a file.
        '''
        if logDest not in ['syslog', 'stdout']:
            if os.path.isfile(logDest):
                if not os.access(logDest, os.W_OK):
                    msg = "%s is not a file that can be opened for writing" % (logDest,)
                    raise argparse.ArgumentTypeError(msg)
            else:
                if not os.access(os.path.dirname(os.path.realpath(logDest)), os.W_OK):
                    msg = "%s is not a file/directory that can be opened for writing" % (logDest,)
                    raise argparse.ArgumentTypeError(msg)

        return logDest

    def str2bool(self, v):
        if v.lower() in ('yes', 'true', 't', 'y', '1'):
            return True
        elif v.lower() in ('no', 'false', 'f', 'n', '0'):
            return False
        else:
            raise argparse.ArgumentTypeError('provide a boolean string; "true" or "false"')

    def memTraceSet(self, enable):
        global memTraceThreadT
        if enable: 
            if not Parser.args.memTrace:
                Parser.args.memTrace = True
                memTraceThreadT = threading.Thread(None, MemTraceT, "MemTraceT")
                memTraceThreadT.daemon = True
                memTraceThreadT.start()
        else:
            Parser.args.memTrace = False
            if memTraceThreadT:
                memTraceEvent.set()
                tracemalloc.stop() 
                memTraceThreadT.join()

    def isBridgeCCSuspended(self):
        return self.bridgePeerCCSuspended or self.bridgeLocalCCSuspended

    def getBridgeCount(self):
        return len(self.vlan_aware_bridge) + len(self.vlan_unaware_bridge)

    def resetPeerBridgeConsistencyCheckSupport(self, numOfBridges):
        if numOfBridges > 1:
            if not self.bridgePeerCCSuspended:
                Log.log_warn('We do not currently support consistency-check for bridgePriority and bridgeStpState when the clag peer is configured with more than one bridge')
                self.bridgePeerCCSuspended = True
        else:
            if self.bridgePeerCCSuspended:
                Log.log('clag peer is now configured with appropriate number of bridges to restore consistency-check for bridgePriority and bridgeStpState parameters')
                self.bridgePeerCCSuspended = False

    def resetLocalBridgeConsistencyCheckSupport(self):
        if self.getBridgeCount() > 1:
            if not self.bridgeLocalCCSuspended:
                Log.log_warn('We do not currently support consistency-check for bridgePriority and bridgeStpState when local clag node is configured with more than one bridge')
                self.bridgeLocalCCSuspended = True
        else:
            if self.bridgeLocalCCSuspended:
                Log.log('Local clag is now configured with appropriate number of bridges to restore consistency-check for bridgePriority and bridgeStpState parameters')
                self.bridgeLocalCCSuspended = False

    def setBridgeType(self, bridgeName, add, is_vlan_aware):
        if add:
            if is_vlan_aware:
                # Add bridge to vlan_aware_bridges
                if bridgeName not in self.vlan_aware_bridge:
                    self.vlan_aware_bridge.append(bridgeName)
                # Remove bridge from vlan_unaware_bridge list
                if bridgeName in self.vlan_unaware_bridge:
                    self.vlan_unaware_bridge.remove(bridgeName)
            else:
                # Remove bridge from vlan_aware_bridge list
                if bridgeName in self.vlan_aware_bridge:
                    self.vlan_aware_bridge.remove(bridgeName)
                # Add bridge to vlan_unaware_bridge list
                if bridgeName not in self.vlan_unaware_bridge:
                    self.vlan_unaware_bridge.append(bridgeName)
        else:
            if is_vlan_aware:
                if bridgeName in self.vlan_aware_bridge:
                    self.vlan_aware_bridge.remove(bridgeName)
            else:
                if bridgeName in self.vlan_unaware_bridge:
                    self.vlan_unaware_bridge.remove(bridgeName)

        curr_bridge_type_list = []
        if len(self.vlan_aware_bridge):
            curr_bridge_type_list.append('vlan-aware')
        if len(self.vlan_unaware_bridge):
            curr_bridge_type_list.append('vlan-unaware')
        self.bridgeType = ','.join(list(set(curr_bridge_type_list)))
         
    def getDataVer(self, verStr):
        (majVer, minVer, subVer) = verStr.split(".")
        return (int(majVer), int(minVer))

    def getPeerDataVer(self):
        return self.getDataVer(Parser.peerDataVersion)

    def getMyDataVer(self):
        return self.getDataVer(Parser._ClagDataVersion)

    def isPeerConsistencyCheckSupported(self):
        # New consistency-check params for global consistencies and
        # interface level consistencies were supported in 1.6.0
        # This checks if the version is greater than or equal to 1.6.0
        (majVer, minVer) = self.getPeerDataVer()
        if (majVer > 1):
            return True
        if majVer == 1 and minVer > 5:
            return True
        return False

    def readyToAcceptConnFromPeer(self):
        srvConn = (serverSock, serverSockPair)
        lenAdj = 2 if set(srvConn).issubset(set(inputSocks)) else 0
        if (len(inputSocks) - lenAdj) == 0:
            return True
        else:
            return False

    def isPeerStaticClagStateAware(self):
        # dormant and protoDown states were introduced in in 1.1.0
        # This checks if the version is greater than or equal to 1.1.0
        (majVer, minVer) = self.getPeerDataVer()
        if (majVer > 1):
            return True
        if majVer == 1 and minVer > 0:
            return True
        return False

    def isPeerVlanSyncOptimized(self):
        # An optimized version of the vlan sync was introduced in in 1.3.0
        # This checks if the version is greater than or equal to 1.3.0
        (majVer, minVer) = self.getPeerDataVer()
        if (majVer > 1):
            return True
        if majVer == 1 and minVer > 2:
            return True
        return False

    def isPeerVxLanSupportDelMsg(self):
        # VxLAN Support for parsing vxlan.op=del for vxlan sync was introduced in in 1.6.0
        # This checks if the version is greater than or equal to 1.6.0
        (majVer, minVer) = self.getPeerDataVer()
        if (majVer > 1):
            return True
        if majVer == 1 and minVer > 5:
            return True
        return False

    def isPeerVxLanStateAware(self):
        # VxLAN Active-Active was introduced in in 1.2.0
        # This checks if the version is greater than or equal to 1.2.0
        (majVer, minVer) = self.getPeerDataVer()
        if (majVer > 1):
            return True
        if majVer == 1 and minVer > 1:
            return True
        return False

    def isPeerRedirect2Capable(self):
        (majVer, minVer) = self.getPeerDataVer()
        if (majVer > 1):
            return True
        if majVer == 1 and minVer > 3:
            return True
        return False

    def isValidPeerIp(self, peerIp):
        '''
        Make sure the peerIp specification is a valid IP v4/v6 address or "linklocal".
        '''
        if peerIp == 'linklocal':
            return peerIp
        try:
            remoteIp = ipaddress.ip_address(peerIp)
        except ValueError as msg:
            raise argparse.ArgumentTypeError(msg)
        return str(remoteIp)
    
    def isPeerlinkBridgeMbr(self):
        if self.bridgeType == "vlan-aware":
            return True if self.peerlinkVlanAwareBridgeAdded else False
        return True

    def isPeerPeerlinkBridgeMbr(self):
        return False if self.peerPeerlinkBridgeMember.__eq__("No") else True

    def ignorePeerlinkCC(self):
        if (not Intf.isPeerAlive()) or (not self.isPeerlinkBridgeMbr()) or \
            (not self.isPeerPeerlinkBridgeMbr()):
            return True
        return False

    def isIfName(self, ifName):
        '''
        Determine if the agrument passed in is the name of an interface. Used 
        for argparse type checking.
        '''
        if not Intf.isIfName(ifName):
            msg = "%s is not the name of an interface" % (ifName,)
            raise argparse.ArgumentTypeError(msg)
        return ifName

    def isClagBond(self, bond):
        return self.GetBondClagId(bond)

    def isValidClagId(self, clagId):
        return 0 <= clagId <= Parser._CLAG_ID_MAX

    def GetClagIntf(self, bond):
        clagIntf = None
        self.clagIntfDBLock.acquire()
        if bond in self.clagIntfDB:
            clagIntf = self.clagIntfDB[bond]
        self.clagIntfDBLock.release()
        return clagIntf

    def GetBondClagId(self, bond):
        clagId = 0
        self.clagIntfDBLock.acquire()
        if bond in self.clagIntfDB:
            clagIntf = self.clagIntfDB[bond]
            clagId = clagIntf.clagId
        self.clagIntfDBLock.release()
        return clagId

    def GetBondFromClagId(self, clagId):
        bond = None
        self.clagIntfDBLock.acquire()
        if clagId in self.bondClagIdDB:
            bond = self.bondClagIdDB[clagId]
        self.clagIntfDBLock.release()
        return bond

    def GetVxLanInfo(self, vxlan):
        vxinfo = None
        if VxLanSync:
            vxinfo = VxLanSync.GetOurVxLanInfo(vxlan)
        return vxinfo

    def GetClagIntfDB(self, ignoreLocks=False):
        # Return a list of current CLAG interfaces
        if not ignoreLocks:
            self.clagIntfDBLock.acquire()
        clagIntfs = copy.copy(self.clagIntfDB)
        if not ignoreLocks:
            self.clagIntfDBLock.release()
        return clagIntfs

    def GetClagIntfMaster(self, intfName):
        master = None
        intf = self.GetClagIntf(intfName)
        if intf:
            master = intf.master
        return master

    def SetClagIntfMaster(self, intfName, master):
        intf = self.GetClagIntf(intfName)
        if intf:
            intf.setIntfMaster(master)

    def GetClagBondDB(self, ignoreLocks=False):
        # Return a list of current CLAG bond 
        clagBonds = { k:v for k,v in list(self.GetClagIntfDB(ignoreLocks).items()) if v.intfType == Parser._CLAG_INTF_TYPE_BOND }
        return clagBonds

    def GetPackageVersion(self):
        '''
        Returns the CLAG package version.
        '''
        Parser.packageVersion = None
        try:
            pkgStr = subprocess.check_output([ "dpkg-query", "-W", "-f='${Version}\n'", "python3-clag" ])
        except subprocess.CalledProcessError as e:
            pkgStr = e.output
        Parser.packageVersion = pkgStr.decode("utf-8").strip("'").strip()
        return Parser.packageVersion

    def SetPackageVersionMismatch(self, peerPackageVersion):
        global packageVersionMismatch
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_CLAG_PACKAGE_VERSION_MISMATCH
        if not packageVersionMismatch:
            packageVersionMismatch = True
            # Update the global conflict
            Globcc.UpdateGlobalConflicts(conflict, 0)

            Log.log_error("The peer CLAG package version does not match ours. My package: %s, Peer package: %s" \
                          % (Parser.packageVersion, "None" if not peerPackageVersion else peerPackageVersion))
            syslog.syslog("The peer CLAG package version does not match ours. My package: %s, Peer package: %s" \
                          % (Parser.packageVersion, "None" if not peerPackageVersion else peerPackageVersion))

    def ClearPackageVersionMismatch(self, peerPackageVersion):
        global packageVersionMismatch
        if packageVersionMismatch:
            packageVersionMismatch = False
            # Update the global conflict
            conflict = Globcc._CLAG_GLOBAL_CONFLICT_CLAG_PACKAGE_VERSION_MISMATCH
            Globcc.UpdateGlobalConflicts(0, conflict)

            if peerPackageVersion:
                Log.log("The peer CLAG package version %s is now matching ours %s" % (peerPackageVersion, Parser.packageVersion))
                syslog.syslog("The peer CLAG package version %s is now matching ours %s" % (peerPackageVersion, Parser.packageVersion))

    def SetAnycastMismatch(self, nlm, peerAnycast):
        global vxlanAnycastMismatch
        global peerConfigMismatch
        if Parser.clagRole == Parser._ROLE_PRIMARY:
            peerConfigMismatch = True
        if not vxlanAnycastMismatch:
            vxlanAnycastMismatch = True
            # Update the global conflict
            conflict = Globcc._CLAG_GLOBAL_CONFLICT_ANYCAST_IP_MISMATCH
            Globcc.UpdateGlobalConflicts(conflict, 0)

            Log.log_error("The vxlanAnycast IP of the peer does not match our vxlanAnycast IP. Peer = %s, Our = %s" % (str(peerAnycast), Parser.args.vxlanAnycast))
            if Parser.clagRole != Parser._ROLE_PRIMARY:
                Intf.protoDownLock.acquire()
                nlmsgs = bytes()
                for intf in list(Parser.GetClagIntfDB().values()):
                    Log.log_error("Anycast IP mismatch, setting proto down for interface %s " % (intf.intfName,))
                    nlmsgs += intf.UpdateProtoDownFlags(nlm, intf._PROTO_DOWNF_ANYCAST_MISMATCH, 0, batch=True)
                if nlmsgs:
                    nlm.tx_nlpacket_raw(nlmsgs)
                Intf.protoDownLock.release()
                if VxLanSync:
                    VxLanSync.newVxLanDataEvent.set()
                Parser.ListenersStateUpdate("down")
                if MrouteSync:
                    MrouteSync.ZebraNotifyClagStatusUpdate()

    def ClearAnycastMismatch(self, nlm):
        global vxlanAnycastMismatch
        if vxlanAnycastMismatch:
            vxlanAnycastMismatch = False
            Parser.ClearPeerConfigMismatch()
            # Update the global conflict
            conflict = Globcc._CLAG_GLOBAL_CONFLICT_ANYCAST_IP_MISMATCH
            Globcc.UpdateGlobalConflicts(0, conflict)

            nlmsgs = bytes()
            Intf.protoDownLock.acquire()
            for intf in list(Parser.GetClagIntfDB().values()):
                nlmsgs += intf.UpdateProtoDownFlags(nlm, 0, intf._PROTO_DOWNF_ANYCAST_MISMATCH, batch=True)
            if nlmsgs:
                nlm.tx_nlpacket_raw(nlmsgs)
            Intf.protoDownLock.release()
            if VxLanSync:
                VxLanSync.newVxLanDataEvent.set()
            Parser.ListenersStateUpdate("up")
            if MrouteSync:
                MrouteSync.ZebraNotifyClagStatusUpdate()

    def SetSysMacMismatch(self, nlm, sysId):
        global sysMacMismatch
        global peerConfigMismatch
        if Parser.clagRole == Parser._ROLE_PRIMARY:
            peerConfigMismatch = True
        if not sysMacMismatch:
            sysMacMismatch = True
            Log.log_error("The system ID of the peer does not match our system ID. Peer = %s, Our = %s" % ("None" if not sysId else sysId, Parser.args.sysMac))

            # Update the global conflict
            conflict = Globcc._CLAG_GLOBAL_CONFLICT_SYSTEM_MAC_MISMATCH
            Globcc.UpdateGlobalConflicts(conflict, 0)

            if Parser.clagRole != Parser._ROLE_PRIMARY:
                Intf.protoDownLock.acquire()
                nlmsgs = bytes()
                for intf in list(Parser.GetClagIntfDB().values()):
                    Log.log_error("System ID  mismatch, setting proto down for interface %s " % (intf.intfName,))
                    nlmsgs += intf.UpdateProtoDownFlags(nlm, intf._PROTO_DOWNF_SYSMAC_MISMATCH, 0, batch=True)
                if nlmsgs:
                    nlm.tx_nlpacket_raw(nlmsgs)
                Intf.protoDownLock.release()
                Parser.ListenersStateUpdate("down")
                if MrouteSync:
                    MrouteSync.ZebraNotifyClagStatusUpdate()

    def ClearSysMacMismatch(self, nlm):
        global sysMacMismatch
        if sysMacMismatch:
            sysMacMismatch = False
            Parser.ClearPeerConfigMismatch()

            # Update the global conflict
            conflict = Globcc._CLAG_GLOBAL_CONFLICT_SYSTEM_MAC_MISMATCH
            Globcc.UpdateGlobalConflicts(0, conflict)

            nlmsgs = bytes()
            Intf.protoDownLock.acquire()
            for intf in list(Parser.GetClagIntfDB().values()):
                nlmsgs += intf.UpdateProtoDownFlags(nlm, 0, intf._PROTO_DOWNF_SYSMAC_MISMATCH, batch=True)
            if nlmsgs:
                nlm.tx_nlpacket_raw(nlmsgs)
            Intf.protoDownLock.release()
            Parser.ListenersStateUpdate("up")
            if MrouteSync:
                MrouteSync.ZebraNotifyClagStatusUpdate()

    def SetBridgePriorityMismatch(self, nlm, peerBridgePriority):
        global bridgePriorityMismatch
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_BRIDGE_PRIORITY_MISMATCH

        if (StpSync and StpSync.ignoreBridgePrioCC()):
            if bridgePriorityMismatch:
                self.ClearBridgePriorityMismatch(nlm)
            Globcc.UpdateGlobalConflicts(0, 0, conflict)
            return

        global peerConfigMismatch

        if Parser.clagRole == Parser._ROLE_PRIMARY:
            peerConfigMismatch = True

        if not bridgePriorityMismatch:
            bridgePriorityMismatch = True
            Log.log_error("The peer bridge priority %s does not match ours %s" % (str(peerBridgePriority), Parser.bridgePriority))
            # Update the global conflict
            Globcc.UpdateGlobalConflicts(conflict, 0)
            if Parser.clagRole != Parser._ROLE_PRIMARY:
                Intf.protoDownLock.acquire()
                nlmsgs = bytes()
                for intf in Parser.GetClagIntfDB().values():
                    Log.log_error("Bridge Priority mismatch, setting proto down for interface %s " % (intf.intfName,))
                    nlmsgs += intf.UpdateProtoDownFlags(nlm, intf._PROTO_DOWNF_BRIDGE_PRIORITY_MISMATCH, 0, batch=True)
                if nlmsgs:
                    nlm.tx_nlpacket_raw(nlmsgs)
                Intf.protoDownLock.release()
                Parser.ListenersStateUpdate("down")
                if MrouteSync:
                    MrouteSync.ZebraNotifyClagStatusUpdate()
                if VxLanSync:
                    VxLanSync.newVxLanDataEvent.set()

    def ClearBridgePriorityMismatch(self, nlm):
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_BRIDGE_PRIORITY_MISMATCH
        # Clear the conflict if it is in ignored list
        if not StpSync.ignoreBridgePrioCC() and (Globcc.GetClagGlobalConflictsIgnored() & conflict):
            Globcc.UpdateGlobalConflicts(0, conflict)
            return

        global bridgePriorityMismatch
        if bridgePriorityMismatch:
            bridgePriorityMismatch = False
            Parser.ClearPeerConfigMismatch()

            # Update the global conflict
            Globcc.UpdateGlobalConflicts(0, conflict)

            nlmsgs = bytes()
            Intf.protoDownLock.acquire()
            for intf in Parser.GetClagIntfDB().values():
                nlmsgs += intf.UpdateProtoDownFlags(nlm, 0, intf._PROTO_DOWNF_BRIDGE_PRIORITY_MISMATCH, batch=True)
            if nlmsgs:
                nlm.tx_nlpacket_raw(nlmsgs)
            Intf.protoDownLock.release()
            Parser.ListenersStateUpdate("up")
            if MrouteSync:
                MrouteSync.ZebraNotifyClagStatusUpdate()
            if VxLanSync:
                VxLanSync.newVxLanDataEvent.set()

        #  Add the conflict to ignore list if it needs to be ignored
        if (StpSync and StpSync.ignoreBridgePrioCC()):
            Globcc.UpdateGlobalConflicts(0, 0, conflict)
            return

    def SetPeerlinkPvlanNotConfigured(self, nlm):
        # Update the global conflict
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_PEERLINK_NO_PVLAN
        Globcc.UpdateGlobalConflicts(conflict, 0)

        Intf.protoDownLock.acquire()
        nlmsgs = bytes()
        for intf in Parser.GetClagIntfDB().values():
            if Parser.clagRole == Parser._ROLE_SECONDARY:
                Log.log_error("Native vlan not configured on peerlink, setting proto down for interface %s " % (intf.intfName,))
            nlmsgs += intf.UpdateProtoDownFlags(nlm, intf._PROTO_DOWNF_PEERLINK_NO_PVLAN, 0, batch=True)
        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)
        Intf.protoDownLock.release()
        if VxLanSync:
            VxLanSync.newVxLanDataEvent.set()

    def ClearPeerlinkPvlanNotConfigured(self, nlm):
        # Update the global conflict
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_PEERLINK_NO_PVLAN
        Globcc.UpdateGlobalConflicts(0, conflict)

        nlmsgs = bytes()
        Intf.protoDownLock.acquire()
        for intf in Parser.GetClagIntfDB().values():
            nlmsgs += intf.UpdateProtoDownFlags(nlm, 0, intf._PROTO_DOWNF_PEERLINK_NO_PVLAN, batch=True)
        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)
        Intf.protoDownLock.release()
        if VxLanSync:
            VxLanSync.newVxLanDataEvent.set()

    def SetPeerlinkVlanMismatch(self, ourVlanMap, peerVlanMap):
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_PEERLINK_VLAN_MISMATCH
        if VlanSync and not (conflict & Globcc.global_conflicts):
            mismatchVlanMap = VlanSync.GetVlanMapMismatchWithSecondVlanMap(ourVlanMap, peerVlanMap)
            mismatchVlanStr = self.ranges_to_compact_str(VlanSync.GetVlanIdList(mismatchVlanMap))
            if mismatchVlanStr != "None":
                Log.log_error("Peerlink vlans (%s) mismatch between clag peers" % (mismatchVlanStr,))
        Globcc.UpdateGlobalConflicts(conflict, 0)

    def ClearPeerlinkVlanMismatch(self):
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_PEERLINK_VLAN_MISMATCH
        Globcc.UpdateGlobalConflicts(0, conflict)

    def SetPeerlinkPvlanMismatch(self, nlm):
        Log.log_error("The peerlink native vlan configured mismatch between clag peers")
        # Update the global conflict
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_PEERLINK_PVLAN_MISMATCH
        Globcc.UpdateGlobalConflicts(conflict, 0)

        Intf.protoDownLock.acquire()
        nlmsgs = bytes()
        for intf in Parser.GetClagIntfDB().values():
            if Parser.clagRole == Parser._ROLE_SECONDARY:
                Log.log_error("Native vlan configured on peerlink mismatch between clag peers, setting proto down for interface %s " % (intf.intfName,))
            nlmsgs += intf.UpdateProtoDownFlags(nlm, intf._PROTO_DOWNF_PEERLINK_PVLAN_MISMATCH, 0, batch=True)
        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)
        Intf.protoDownLock.release()
        if VxLanSync:
            VxLanSync.newVxLanDataEvent.set()

    def ClearPeerlinkPvlanMismatch(self, nlm):
        # Update the global conflict
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_PEERLINK_PVLAN_MISMATCH
        Globcc.UpdateGlobalConflicts(0, conflict)

        nlmsgs = bytes()
        Intf.protoDownLock.acquire()
        for intf in Parser.GetClagIntfDB().values():
            nlmsgs += intf.UpdateProtoDownFlags(nlm, 0, intf._PROTO_DOWNF_PEERLINK_PVLAN_MISMATCH, batch=True)
        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)
        Intf.protoDownLock.release()
        if VxLanSync:
            VxLanSync.newVxLanDataEvent.set()

    def SetBridgeStpStateMismatch(self, nlm, peerBridgeStpState):
        global bridgeStpStateMismatch
        global peerConfigMismatch
        if Parser.clagRole == Parser._ROLE_PRIMARY:
            peerConfigMismatch = True
        if not bridgeStpStateMismatch:
            bridgeStpStateMismatch = True
            # Update the global conflict
            conflict = Globcc._CLAG_GLOBAL_CONFLICT_BRIDGE_STP_STATE_MISMATCH
            Globcc.UpdateGlobalConflicts(conflict, 0)

            # Bring down the clag bonds here
            if Parser.clagRole != Parser._ROLE_PRIMARY:
                Intf.protoDownLock.acquire()
                nlmsgs = bytes()
                for intf in Parser.GetClagBondDB().values():
                    nlmsgs += intf.UpdateProtoDownFlags(nlm, intf._PROTO_DOWNF_BRIDGE_STP_STATE_MISMATCH, 0, batch=True)
                if nlmsgs:
                    nlm.tx_nlpacket_raw(nlmsgs)
                Intf.protoDownLock.release()
                Parser.ListenersStateUpdate("down")
                if MrouteSync:
                    MrouteSync.ZebraNotifyClagStatusUpdate()

    def ClearBridgeStpStateMismatch(self, nlm):
        global bridgeStpStateMismatch
        if bridgeStpStateMismatch:
            bridgeStpStateMismatch = False
            Parser.ClearPeerConfigMismatch()

            # Update the global conflict
            conflict = Globcc._CLAG_GLOBAL_CONFLICT_BRIDGE_STP_STATE_MISMATCH
            Globcc.UpdateGlobalConflicts(0, conflict)

            # Bring up the clag bonds here
            nlmsgs = bytes()
            Intf.protoDownLock.acquire()
            for intf in Parser.GetClagBondDB().values():
                nlmsgs += intf.UpdateProtoDownFlags(nlm, 0, intf._PROTO_DOWNF_BRIDGE_STP_STATE_MISMATCH, batch=True)
            if nlmsgs:
                nlm.tx_nlpacket_raw(nlmsgs)
            Intf.protoDownLock.release()
            Parser.ListenersStateUpdate("up")
            if MrouteSync:
                MrouteSync.ZebraNotifyClagStatusUpdate()

    def SetPeerIpMismatch(self, nlm, peerIpAddr, ourPeerIp):
        global peerIpMismatch
        if not peerIpMismatch:
            # Update the global conflict
            conflict = Globcc._CLAG_GLOBAL_CONFLICT_PEER_IP_MISMATCH
            Globcc.UpdateGlobalConflicts(conflict, 0)

            Log.log_error("The peer's IP address supplied when starting clagd (%s) is not the IP address the peer is using (%s)" % (Parser.remotePeerIp, peerIpAddr))

            if peerIpAddr == ourPeerIp:
                Log.log_error("The peer's IP address supplied when starting clagd (%s) is the same as our IP" % Parser.remotePeerIp)

            if (Parser.args.peerIp == "linklocal") and not (peerIpAddr == ourPeerIp):
                try:
                    neighbors = subprocess.check_output(['/bin/ip', '-6', 'n', 's', 'dev', Parser.args.peerIf]).decode()
                    remotePeerIps = re.findall(r'^(fe80:\S+)', neighbors, re.MULTILINE)
                except:
                    remotePeerIps = []
                if peerIpAddr in remotePeerIps:
                    Parser.remotePeerIp = peerIpAddr
                    Log.log_error("Reconnecting with the new PeerIp %s" % (Parser.remotePeerIp))
                    Parser.CloseClientSock()
            else:
                if HealthCheck:
                    # notify peer-ip mismatch via backup until conflict is resolved
                    HealthCheck.HelloSendPeerIpMismatch()
                Log.log_error("Restart clagd with the correct peerIp address (%s)" % (peerIpAddr,))
                Intf.SetClagRole(nlm, Parser._ROLE_SECONDARY, "peer-ip mismatch")
                Intf.protoDownLock.acquire()
                nlmsgs = bytes()
                for intf in list(Parser.GetClagIntfDB().values()):
                    Log.log_error("Peer IP mismatch, setting proto down for interface %s " % (intf.intfName,))
                    nlmsgs += intf.UpdateProtoDownFlags(nlm, intf._PROTO_DOWNF_PEER_IP_MISMATCH, 0, batch=True)
                if nlmsgs:
                    nlm.tx_nlpacket_raw(nlmsgs)
                Intf.protoDownLock.release()
                peerIpMismatch = True
                Parser.ListenersStateUpdate("down")
                if MrouteSync:
                    MrouteSync.ZebraNotifyClagStatusUpdate()

    def ClearPeerIpMismatch(self, nlm):
        global peerIpMismatch
        if peerIpMismatch:
            # Update the global conflict
            conflict = Globcc._CLAG_GLOBAL_CONFLICT_PEER_IP_MISMATCH
            Globcc.UpdateGlobalConflicts(0, conflict)

            Intf.protoDownLock.acquire()
            nlmsgs = bytes()
            for intf in list(Parser.GetClagIntfDB().values()):
                nlmsgs += intf.UpdateProtoDownFlags(nlm, 0, intf._PROTO_DOWNF_PEER_IP_MISMATCH, batch=True)
            if nlmsgs:
                nlm.tx_nlpacket_raw(nlmsgs)
            Intf.protoDownLock.release()
            peerIpMismatch = False
            Parser.ListenersStateUpdate("up")
            if MrouteSync:
                MrouteSync.ZebraNotifyClagStatusUpdate()

    def SetPeerlinkMasterNoBridge(self, nlm):
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_PEERLINK_MASTER_NOT_BRIDGE
        Globcc.UpdateGlobalConflicts(conflict, 0)

        Intf.protoDownLock.acquire()
        nlmsgs = bytes()
        for intf in list(Parser.GetClagIntfDB().values()):
            nlmsgs += intf.UpdateProtoDownFlags(nlm, intf._PROTO_DOWNF_PEERLINK_NO_VLAN_AWARE_MASTER, 0, batch=True)
        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)
        Intf.protoDownLock.release()
        if VxLanSync:
            VxLanSync.newVxLanDataEvent.set()
        if VlanSync:
            VlanSync.setVlanCCInterfaceAll()
            VlanSync.compareVlanMapEvent.set()

    def ClearPeerlinkMasterNoBridge(self, nlm):
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_PEERLINK_MASTER_NOT_BRIDGE
        Globcc.UpdateGlobalConflicts(0, conflict)

        Intf.protoDownLock.acquire()
        nlmsgs = bytes()
        for intf in list(Parser.GetClagIntfDB().values()):
            nlmsgs += intf.UpdateProtoDownFlags(nlm, 0, intf._PROTO_DOWNF_PEERLINK_NO_VLAN_AWARE_MASTER, batch=True)
        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)
        Intf.protoDownLock.release()
        if VxLanSync:
            VxLanSync.newVxLanDataEvent.set()
        if VlanSync:
            VlanSync.setVlanCCInterfaceAll()
            VlanSync.compareVlanMapEvent.set()

    def SetPeerlinkMTUMismatch(self, nlm):
        global peerlinkMTUMismatch
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_PEERLINK_MTU_MISMATCH

        if (self.isPeerConsistencyCheckSupported() and Parser.ignorePeerlinkCC()):
            if peerlinkMTUMismatch:
                self.ClearPeerlinkMTUMismatch(nlm)
            Globcc.UpdateGlobalConflicts(0, 0, conflict)
            return 

        Globcc.UpdateGlobalConflicts(conflict, 0)
        if not peerlinkMTUMismatch:
            Intf.protoDownLock.acquire()
            nlmsgs = bytes()
            for intf in list(Parser.GetClagIntfDB().values()):
                nlmsgs += intf.UpdateProtoDownFlags(nlm, intf._PROTO_DOWNF_PEERLINK_MTU_MISMATCH, 0, batch=True)
            if nlmsgs:
                nlm.tx_nlpacket_raw(nlmsgs)
            Intf.protoDownLock.release()
            peerlinkMTUMismatch = True
            if VxLanSync:
                VxLanSync.newVxLanDataEvent.set()

    def ClearPeerlinkMTUMismatch(self, nlm):
        global peerlinkMTUMismatch
        conflict = Globcc._CLAG_GLOBAL_CONFLICT_PEERLINK_MTU_MISMATCH
        Globcc.UpdateGlobalConflicts(0, conflict)
        if peerlinkMTUMismatch:
            Intf.protoDownLock.acquire()
            nlmsgs = bytes()
            for intf in list(Parser.GetClagIntfDB().values()):
                nlmsgs += intf.UpdateProtoDownFlags(nlm, 0, intf._PROTO_DOWNF_PEERLINK_MTU_MISMATCH, batch=True)
            if nlmsgs:
                nlm.tx_nlpacket_raw(nlmsgs)
            Intf.protoDownLock.release()
            peerlinkMTUMismatch = False
            if VxLanSync:
                VxLanSync.newVxLanDataEvent.set()

    def SetRemoteIpMismatchOnPeer(self):
        global remoteIpMismatchOnPeer
        remoteIpMismatchOnPeer = True
        
    def ClearRemoteIpMismatchOnPeer(self):
        global remoteIpMismatchOnPeer
        remoteIpMismatchOnPeer = False

    def SetPeerConfigMismatch(self):
        global peerConfigMismatch
        peerConfigMismatch = True

    def ClearPeerConfigMismatch(self):
        global peerConfigMismatch
        if vxlanAnycastMismatch or sysMacMismatch or remoteIpMismatchOnPeer or \
                bridgePriorityMismatch or bridgeStpStateMismatch:
            return 
        peerConfigMismatch = False

    def GetMismatchState(self):
        return True if peerIpMismatch or vxlanAnycastMismatch or \
                       peerConfigMismatch or sysMacMismatch or \
                       bridgePriorityMismatch or bridgeStpStateMismatch else False

    def ClearConfigMismatch(self, nlm):
        '''
        reset any related configuration mismatch when the peer sends a goodbye
        message or when the peer is not reachable via peerlink
        '''
        global peerConfigMismatch
        if vxlanAnycastMismatch or peerIpMismatch or sysMacMismatch or \
                bridgePriorityMismatch or bridgeStpStateMismatch or \
                peerlinkMTUMismatch or packageVersionMismatch:
            Log.log("Clearing peer config mismatches since peer is not reachable")
        if vxlanAnycastMismatch:
            Parser.ClearAnycastMismatch(nlm)
        if peerIpMismatch:
            Parser.ClearPeerIpMismatch(nlm)
        if sysMacMismatch:
            Parser.ClearSysMacMismatch(nlm)
        if bridgePriorityMismatch:
            Parser.ClearBridgePriorityMismatch(nlm)
        if bridgeStpStateMismatch:
            Parser.ClearBridgeStpStateMismatch(nlm)
        if peerlinkMTUMismatch:
            Parser.ClearPeerlinkMTUMismatch(nlm)
        if packageVersionMismatch:
            Parser.ClearPackageVersionMismatch("")
        peerConfigMismatch = False

    def ListenersStateUpdate(self, state):
        global mstpClagState
        if peerConfigMismatch or not Intf.isPeerAlive():
            state = "down"
        if Cmd and state != mstpClagState:
            Log.log("toListeners: add clag-state = %s" % state)
            Cmd.CmdGetAllOutput("add clag-state = %s" % state)
            mstpClagState = state

    def GetMstpClagState(self):
        global mstpClagState
        return mstpClagState

    def ListenersRoleUpdate(self):
        role = self.GetCombinedRole()
        if Cmd:
            Log.log_debug(Log._LOG_DEBUG_FSM, "toListeners: add clag-role = %s" % (str(role)))
            Cmd.CmdGetAllOutput("add clag-role = %s" % (str(role),))

    def GetCombinedRole(self):
        if not Intf.isPeerAlive() and HealthCheck and\
                     HealthCheck.IsBackupRoleAvailable():
            role = HealthCheck.GetBackupRole()
        else:
            role = self.clagRole
        return role

    def delClagBond(self, nlm, bond):
        self.clagIntfDBLock.acquire()
        clagIntf = self.clagIntfDB.get(bond)
        if clagIntf:
            clagIntf.CleanUp(nlm)
            del self.clagIntfDB[bond]
        self.clagIntfDBLock.release()

    def addClagBond(self, nlm, bond, clagId):
        self.clagIntfDBLock.acquire()
        if bond in self.clagIntfDB:
            clagIntf = self.clagIntfDB[bond]
            clagIntf.SetNewClagId(nlm, clagId)
        else:
            self.clagIntfDB[bond] = ClagIntf(nlm, bond, intfType=Parser._CLAG_INTF_TYPE_BOND, clagId=clagId)
        intf = self.clagIntfDB[bond]
        self.clagIntfDBLock.release()
        if LacpSync:
            LacpSync.EvaluateProtoDownBondConflict(nlm, intf)

    def delClagVxLan(self, nlm, vxlan):
        self.clagIntfDBLock.acquire()
        clagIntf = self.clagIntfDB.get(vxlan)
        if clagIntf:
            clagIntf.CleanUp(nlm)
            del self.clagIntfDB[vxlan]
        self.clagIntfDBLock.release()

    def addClagVxLan(self, nlm, vxlan):
        self.clagIntfDBLock.acquire()
        if vxlan not in self.clagIntfDB:
            self.clagIntfDB[vxlan] = ClagIntf(nlm, vxlan, intfType=Parser._CLAG_INTF_TYPE_VXLAN)
        self.clagIntfDBLock.release()

    def UpdateClagConflicts(self, bond, set, clear, ignore=0x0):
        self.clagIntfDBLock.acquire()
        if bond in self.clagIntfDB:
            self.clagIntfDB[bond].UpdateConflicts(set, clear, ignore)
        self.clagIntfDBLock.release()

    def ResetClagConflicts(self):
        self.clagIntfDBLock.acquire()
        resetDone = False
        for intfName in self.clagIntfDB:
            if self.clagIntfDB[intfName].conflicts:
                resetDone = True
                self.clagIntfDB[intfName].UpdateConflicts(0, self._CLAG_INTF_CONFLICT_ALL, logConflict=False)
        if resetDone:
            Log.log("Conflict reset (all-interfaces): peer connectivity lost")
        self.clagIntfDBLock.release()
        
    def SetSyncDoneFromPeer(self, done):
        self.syncDoneFromPeer = done
        if self.syncDoneFromPeer:
            if LacpSync:
                LacpSync.EvaluateAndUpdateConflicts()
            if VxLanSync:
                VxLanSync.newVxLanDataEvent.set()
            if VlanSync:
                VlanSync.setVlanCCInterfaceAll()
                VlanSync.compareVlanMapEvent.set()
        else:
            self.ResetClagConflicts()

    def GetClagIntfConflicts(self, intfName):
        conflicts = 0
        self.clagIntfDBLock.acquire()
        conflicts = self.clagIntfDB[intfName].conflicts if intfName in self.clagIntfDB else 0
        self.clagIntfDBLock.release()
        return conflicts
        
    def GetClagIntfConflictsAll(self, intfName):
        conflicts = 0
        self.clagIntfDBLock.acquire()
        (conflicts, conflicts_ignored) = (self.clagIntfDB[intfName].conflicts, self.clagIntfDB[intfName].conflicts_ignored) \
             if intfName in self.clagIntfDB else (0,0)
        self.clagIntfDBLock.release()
        return (conflicts, conflicts_ignored)

    def setPeerClagIntfNoMaster(self, intfName, noPeerMaster):
        self.clagIntfDBLock.acquire()
        if intfName in self.clagIntfDB:
            if self.clagIntfDB[intfName].isPeerNoMaster != noPeerMaster:
                self.clagIntfDB[intfName].isPeerNoMaster = noPeerMaster
                if VlanSync:
                    VlanSync.addVlanCCInterface(intfName)
                    VlanSync.compareVlanMapEvent.set()
        self.clagIntfDBLock.release()

    # XXX - shoudl really sit in an util module
    def GetNonZeroBits(self, bits):
        while bits:
            # get the first non-zero bit
            nzBit = bits & (~bits + 1)    
            # generator for nz bit value
            yield nzBit
            # clear the bit that we just returned
            bits &= ~nzBit

    def GetClagIntfConflictStr(self, intfName, conflicts=None):
        conflictList = []
        if not conflicts:
            conflicts = self.GetClagIntfConflicts(intfName)
        bits = self.GetNonZeroBits(conflicts)
        for bit in bits:
            if bit in Parser._clag_intf_conflict_map:
                bitStr = Parser._clag_intf_conflict_map[bit]
            else:
                bitStr = "0x%x" % bit
            conflictList.append(bitStr)
        return conflictList

    def setReloadDone(self):
        Log.log("Initial config loaded")
        # XXX - need to use this as trigger to start communicating with the
        # peer
        self.configReloadDone = True
        return ""

    def setBondClagId(self, nlm, bond, clagId):
        Log.log_debug(Log._LOG_DEBUG_PARSER, "Set %s clag_id %d" % (bond, clagId))
        msg = ""

        clagBondSet = set(Intf.GetSubIfsOfMaster(bond))
        clagBondSet |= set([bond])

        if clagId == 0:
            oldClagId = self.GetBondClagId(bond)
            if oldClagId and VlanSync and Intf:
                # Clear the vlan db information for the old clag id
                VlanSync.ClearOurVlanIntfDB(oldClagId)
                # Send the vlan info to peer for old clag id
                VlanSync.SendVlanInfoToPeer(bond, oldClagId)

                # clear the existing conflicts and reset suspended vlans for oldClagId
                # Need to do these operations here itself and not in a different thread.
                # Since we are going to delete the bond from the clagInfDB, we need to
                # deterministically clear the mismatch state for the existing intf before
                # bond is deleted from the clagIntfDB
                Intf.ClearClagVlanIdMismatch(bond)
                Intf.ClearClagPvlanIdMismatch(bond, nlm)
                VlanSync.clearSuspendVlanMap(bond, oldClagId, nlm)

            self.delClagBond(nlm, bond)
            Intf.bridgeAttrLock.acquire()
            Intf.SetBridgeBackupPort(nlm, clagBondSet, False)
            Intf.bridgeAttrLock.release()
            return msg

        if Intf.GetMasterOfSubIf(self.args.peerIf) == bond:
            return "ERROR %s is a peer interface and cannot be assigned a clag-id (%s)" % (bond, clagId)

        if not self.isValidClagId(clagId):
            return "ERROR %s is not in the clag id range of <0-%d>." % (clagId, Parser._CLAG_ID_MAX)

        # duplicate config
        oldClagId = self.GetBondClagId(bond)
        if clagId == oldClagId:
            return msg

        # check if the clagId is being used by a different bond
        oldBond = self.GetBondFromClagId(clagId)
        if oldBond:
            return "ERROR %d is already in use for bond %s" % (clagId, oldBond)

        # set clag id
        self.addClagBond(nlm, bond, clagId)

        Intf.bridgeAttrLock.acquire()
        Intf.SetBridgeBackupPort(nlm, clagBondSet, True)
        Intf.bridgeAttrLock.release()

        if VlanSync:
            # Trigger the vlan map comparision for the clag id
            VlanSync.addVlanCCInterface(bond)
            VlanSync.compareVlanMapEvent.set()

        return msg

    def setAnycastIp(self, nlm, address):
        msg = ""
        if self.args.vxlanAnycast != address:
            Log.log_debug(Log._LOG_DEBUG_PARSER, "Set VXLAN Anycast IP to %s" % (address))
            if VxLanSync:
                if self.args.vxlanAnycast:
                    vxlanDB = VxLanSync.GetOurVxLanDB()
                    Parser.vxlanAnycastDeleted = True
                    Intf.DelIpAddress(nlm, "lo", str(self.args.vxlanAnycast), 32)
                    Intf.SetVxLanLocalIp(nlm, [(k,v[1]) for k,v in list(vxlanDB.items()) if v[1]])
                self.args.vxlanAnycast = address
                VxLanSync.anycastIpEvent = True
                VxLanSync.newVxLanDataEvent.set()
        return msg

    def setBackupIp(self, nlm, address, vrf=None):
        msg = ""
        self.args.backupIp = address
        self.args.backupVrf = vrf
        addrFamily = HealthCheck.GetAddressFamily(address)
        if not addrFamily:
            return "ERROR %s is not a valid IP address" % address

        # if address family has changed, close the TX/RX sockets
        if HealthCheck.addrFamily != addrFamily:
            HealthCheck.addrFamily = addrFamily
            HealthCheck.helloTxSock.close()
            HealthCheck.CloseRxSocket()
            HealthCheck.CreateTxSocket()
        else:
            if not vrf:
                HealthCheck.backupVrfUnbind = True
            HealthCheck.backupVrfBindSuccess = False
            HealthCheck.AttemptVrfBind()

        HealthCheck.HelloBackupReset(nlm)
        return msg

    def CloseClientSock(self):
        global clientSock
        global sendMsgSeq
        if Parser:
            Parser.peerProtoBuf = Parser.args.protobuf
        sendMsgSeq = 0
        myClientSock = clientSock
        if clientSock:
            clientSock = None
            myClientSock.close()
            self.NotifyPeerCommChange()

    def isClientSockOpen(self):
        return clientSock is not None

    def isVxlanAnycastIPMismatch(self):
        return vxlanAnycastMismatch

    def NotifyPeerCommChange(self):
        if VxLanSync:
            VxLanSync.newVxLanDataEvent.set()
        if LacpSync:
            LacpSync.newLacpDataEvent.set()

    def isMacAddr(self, macAddr):
        '''
        Determine if the agrument passed in is a valid mac address, of the form
        xx:xx:xx:xx:xx:xx, where x is a hexidecimal digit.
        '''
        macAddr = macAddr.lower()
        if re.match("[0-9a-f]{1,2}([-:])[0-9a-f]{1,2}(\\1[0-9a-f]{1,2}){4}$", macAddr):
            macAddr = macAddr.replace("-", ":") 
            macAddr = ':'.join(x.zfill(2) for x in macAddr.split(":"))
            Log.log("macAddr = %s" % macAddr)
        else:
            msg = "%s is not a valid mac address " % (macAddr,)
            raise argparse.ArgumentTypeError(msg)
        return macAddr

    def isProtoDownFlagOnConflict(self):
        return not Parser.args.gracefulConsistencyCheck

    def isSuspendVlanFlagOnConflict(self):
        return not Parser.args.gracefulConsistencyCheck

    def isRedirectEnabled(self):
        if Parser.args.redirectEnable:
            return True
        return Parser.args.redirect2Enable and Parser.peerRedirect2Enable

    def ParseCmdLine(self):
        '''
        Parse the command line parameters
        '''
        try:
            self.args, unknown = self.parser.parse_known_args()
        except ArgumentParsingError as e:
            Log.log_error("There was an error in the command line parameters.")
            Log.log_error(str(e))
            Log.log_error("exit with status -1")
            sys.exit(-1)
        if self.args.initDelay == 0:
            Log.log_error("initDelay value of 0 is not supported. Please configure non-zero init-delay value")
            Log.log_error("exit with status -1")    
            sys.exit(-1)    

        for arg in unknown:
            Log.log_warn("The command line argument '%s' is unrecognized and will be ignored." % (arg,))

    def getCfgLacpPoll(self):
        return self.args.lacpPoll

    def getCurrLacpPoll(self):
        return self.currLacpPoll

    def setPeerRate(self, rate):
        self.peerLacpRateLock.acquire()
        self.peerLacpRate = rate
        try:
            self.currLacpPoll = max(self.peerLacpRate, self.args.lacpPoll)
        except:
            self.currLacpPoll = self.args.lacpPoll
        self.peerLacpRateLock.release()

    def getPeerTimeout(self):
        if self.args.peerTimeout:
            return self.args.peerTimeout
        return self.getCurrLacpPoll() * 10

    def setPeerTimeout(self, timeout):
        self.args.peerTimeout = timeout

    def getPeerLinkPoll(self):
        return self.args.peerLinkPoll

    def setPeerLinkPoll(self, timeout):
        self.args.peerLinkPoll = timeout

    def getPeriodicRun(self):
        return self.args.periodicRun

    def setPeriodicRun(self, timeout):
        self.args.periodicRun = timeout

    def getSocketInfo(self):
        sockInfo = {}
        sockInfo["socketToPeer"] = "Not Connected" if clientSock is None or clientSockInit else "Connected"
        lenAdj = 1 if serverSock in inputSocks else 0
        if serverSockPair in inputSocks:
            lenAdj += 1
        sockInfo["socketFromPeer"] = "Not Connected" if len(inputSocks) - lenAdj == 0 else "Connected"
        sockInfo["serverSocket"] = "Not Listening" if serverSock is None else "Listening"
        return sockInfo

    def getInterestingObjectsSize(self):
        objSize = {}
        objSize['bondClagIdDB'] = util.getSize(self.bondClagIdDB)
        self.clagIntfDBLock.acquire()
        objSize['clagIntfDB'] = util.getSize(self.clagIntfDB)
        self.clagIntfDBLock.release()
        Intf.ifInfoLock.acquire()
        objSize['ifNameToIndex'] = util.getSize(Intf.ifNameToIndex)
        objSize['ifInfoByIndex'] = util.getSize(Intf.ifInfoByIndex)
        Intf.ifInfoLock.release()
        for sync in DataSyncs:
            objSize.update(sync.GetObjectSize())
        return objSize
    
    def getTid(self):
        try:
            if platform.system().startswith('Linux'):
                syscalls = {
                'i386':   224,   # unistd_32.h: #define __NR_gettid 224
                'x86_64': 186,   # unistd_64.h: #define __NR_gettid 186
                }
                tid = ctypes.CDLL('libc.so.6').syscall(syscalls[platform.machine()])
        except:
            tid = -1
        return tid

    def clearConflictState(self, nlm, bonds):
        nlmsgs = bytes()
        if not bonds:
            bonds = Intf.GetAllClagBonds()
        for intf in list(Parser.GetClagBondDB().values()):
            protoDown = intf._PROTO_DOWNF_PARTNER_MAC_MISMATCH | intf._PROTO_DOWNF_DUPLICATE_PARTNER_MAC
            if intf.intfName in bonds and intf.protoDownFlags & protoDown:
                Intf.protoDownLock.acquire()
                nlmsgs += intf.UpdateProtoDownFlags(nlm, 0, protoDown, batch=True)
                Intf.protoDownLock.release()
        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)
        return ""

    # Given
    # (3,4,7,10,11,12,17,22,4001,4002,4003,7777,8000,8001,8002,8003,8004)
    #
    # Return:
    #  3->4, 7, 10->12, 17, 22, 4001->4003, 7777, 8000->8004
    #
    def ranges_to_compact_str(self, ids, makeUnique=True, sortItems=True):

        # Anything to process?
        if len(ids) == 0:
            return "None"

        # Use a set to eliminate duplicate elements
        if makeUnique:
            ids=set(ids)

        # If the list wasn't sorted, put everything in order
        if sortItems:
            ids=sorted(ids)

        # Handle the first element
        outStr = "%d" % (ids[0],)
        startRange = ids[0]
        prevElem = ids[0]

        # Handle the elements in the middle
        for currElem in ids[1:-1]:
            if currElem != prevElem+1:
                if prevElem == startRange:
                    outStr += ", %d" % (currElem,)
                else:
                    outStr += "->%d, %d" % (prevElem, currElem)
                startRange = currElem
            prevElem = currElem

        # Handle the last element
        if ids[0] != ids[-1]:
            if ids[-1] != prevElem+1:
                if prevElem == startRange:
                    outStr += ", %d" % (ids[-1],)
                else:
                    outStr += "->%d, %d" % (prevElem, ids[-1])
            else:
                outStr += "->%d" % (ids[-1],)
        return outStr

    def getLocalPeerValueClagIntfCC(self, ccName, bond):
        valueDict = {}
        peerLacpInfo = None
        ourLacpInfo = None
        clagId = self.GetBondClagId(bond)
        peerIntf = Intf.getDualIfName(bond)
        if LacpSync:
            ourLacpInfo = LacpSync.GetOurLacpInfo(bond)
            peerLacpInfo = LacpSync.GetPeerLacpInfoByClagId(clagId)

        if ccName == "clag-id":
            valueDict['local'] = str(ourLacpInfo[2]) if ourLacpInfo else '-'
            valueDict['peer'] = str(peerLacpInfo[2]) if peerLacpInfo else '-'
        elif ccName == "lacp-partner-mac":
            valueDict['local'] = ourLacpInfo[0] if ourLacpInfo else '-'
            valueDict['peer'] = peerLacpInfo[0] if peerLacpInfo else '-'
        elif ccName == "lacp-actor-mac":
            valueDict['local'] = Parser.args.sysMac
            valueDict['peer'] = Parser.peerSysMac
        elif ccName == "master":
            master_name = Parser.GetClagIntfMaster(bond)
            valueDict['local'] = master_name if master_name else '-'
            valueDict['peer'] = "NOT-SYNCED"
        elif ccName == "vlan-id" and VlanSync:
            ourVlanMap = VlanSync.GetOurVlanIntfDB(clagId)
            peerVlanMap = VlanSync.GetPeerVlanIntfDB(clagId)
            valueDict['local'] = self.ranges_to_compact_str(VlanSync.GetVlanIdList(ourVlanMap[VlanSync.VLAN_DB_VLAN_MAP]))
            valueDict['peer'] = self.ranges_to_compact_str(VlanSync.GetVlanIdList(peerVlanMap[VlanSync.VLAN_DB_VLAN_MAP]))
        elif ccName == "native-vlan" and VlanSync:
            ourPvlanDb = VlanSync.GetOurVlanIntfDB(clagId)
            peerPvlanDb = VlanSync.GetPeerVlanIntfDB(clagId)
            valueDict['local'] = ourPvlanDb[VlanSync.VLAN_DB_PVLAN_ID]
            valueDict['peer'] = peerPvlanDb[VlanSync.VLAN_DB_PVLAN_ID]
        elif ccName == "mtu":
            valueDict['local'] = str(ourLacpInfo[6]) if ourLacpInfo else '-'
            valueDict['peer'] = str(peerLacpInfo[6]) if peerLacpInfo else '-'
        elif ccName == "bpdu-guard":
            valueDict['local'] = 'on' if StpSync.GetBpduGuard(bond) else 'off'
            valueDict['peer'] = 'on' if StpSync.GetPeerBpduGuard(bond) else 'off'
        elif ccName == "restricted-role":
            valueDict['local'] = 'yes' if StpSync.GetRestrictedRole(bond) else 'no'
            valueDict['peer'] = 'yes' if StpSync.GetPeerRestrictedRole(bond) else 'no'
        elif ccName == "bridge-learning":
            valueDict['local'] = 'yes' if ourLacpInfo and (ourLacpInfo[4] & LacpSync._LACP_DB_FLAGS_BR_LEARN) else 'no'
            valueDict['peer'] = 'yes' if peerLacpInfo and (peerLacpInfo[4] & LacpSync._LACP_DB_FLAGS_BR_LEARN) else 'no'
        else:
            valueDict['local'] = "-"
            valueDict['peer'] = "-"

        return valueDict

    def getLocalPeerValueVxlanIntfCC(self, ccName, vxlanIntf, ourVxlanInfo):
        valueDict = {}
        ourVniDict = None
        peerVniDict = None
        peerVxlanDB = None
        peerVniDB = None
        if VxLanSync:
            ourVniDict = VxLanSync.GetOurVniDB()
            peerVniDict = VxLanSync.GetPeerVniDB()
            peerVxlanDB = VxLanSync.GetPeerVxLanDB()
        if peerVxlanDB:
            peerVniDB = {v[0]: k for k, v in list(peerVxlanDB.items())}

        if ccName == "vxlan-intf":
            valueDict['local'] = vxlanIntf
            valueDict['peer'] = peerVniDB.get(ourVxlanInfo[0], '-') if (peerVniDB and ourVxlanInfo) else '-'
        elif ccName == "vni":
            if ourVniDict:
                ourVnis = [k for k, v in list(ourVniDict.items()) if v[VxLanSync.VNI_DB_VXLAN_DEV] == vxlanIntf]
            else:
                ourVnis = [ourVxlanInfo[0]]
            if peerVniDict:
                peerVnis = list(peerVniDict.keys())
            else:
                peerVnis = [ourVxlanInfo[0]] if (peerVniDB and ourVxlanInfo[0] in peerVniDB) else []
            valueDict['local'] = self.ranges_to_compact_str(ourVnis)
            valueDict['peer'] = self.ranges_to_compact_str(peerVnis)
        elif ccName == "vlan":
            if ourVniDict:
                ourVlans = [v[VxLanSync.VNI_DB_VLAN_ID] for _, v in list(ourVniDict.items()) if v[VxLanSync.VNI_DB_VXLAN_DEV] == vxlanIntf]
            else:
                ourVlans = [ourVxlanInfo[5]]
            if peerVniDict:
                peerVlans = [v[VxLanSync.VNI_DB_VLAN_ID] for _, v in list(peerVniDict.items())]
            else:
                peerVxlanIntf = peerVniDB.get(ourVxlanInfo[0], None) if peerVniDB else '-'
                peerVlans = [peerVxlanDB[peerVxlanIntf][4]] if (peerVxlanDB and peerVxlanIntf and peerVxlanIntf in peerVxlanDB) else []
            valueDict['local'] = self.ranges_to_compact_str(ourVlans)
            valueDict['peer'] = self.ranges_to_compact_str(peerVlans)
        elif ccName == "master":
            master_name = Parser.GetClagIntfMaster(vxlanIntf)
            valueDict['local'] = master_name if master_name else '-'
            valueDict['peer'] = "NOT-SYNCED"
        elif ccName == "bpdu-guard":
            valueDict['local'] = 'on' if StpSync.GetBpduGuard(vxlanIntf) else 'off'
            valueDict['peer'] = 'on' if StpSync.GetPeerBpduGuard(vxlanIntf) else 'off'
        elif ccName == "restricted-role":
            valueDict['local'] = 'yes' if StpSync.GetRestrictedRole(vxlanIntf) else 'no'
            valueDict['peer'] = 'yes' if StpSync.GetPeerRestrictedRole(vxlanIntf) else 'no'
        else:
            valueDict['local'] = "-"
            valueDict['peer'] = "-"

        return valueDict

    def getCCDictClagBond(self, bond, conflicts, conflicts_ignored):
        ccList = []
        skip_cc_list = []
        if not Parser.isPeerConsistencyCheckSupported():
            skip_cc_list = list(set(Parser._clag_intf_cc_name.keys()) - set(Parser._clag_peer_version_unsupported_cc_name.keys()))

        if not StpSync.isPeerPvrstCCSupported():
            skip_cc_list.append("bpdu-guard")
            skip_cc_list.append("restricted-role")

        for ccName in Parser._clag_intf_cc_name:
            if ccName in skip_cc_list:
                # Skip all the CC params in the skip_cc_list when peer clag data version is unsupported for CC
                continue

            ccDict = {}
            ccDict['name'] = ccName

            ccConflict = Parser._clag_intf_cc_name[ccName][1]
            if (ccConflict & conflicts):
                confDescList = self.GetClagIntfConflictStr(bond, ccConflict & conflicts)
                ccDict['description'] = ", ".join(confDescList)
            else:
                ccDict['description'] = '-'

            if (ccConflict & conflicts_ignored):
                ccDict['description'] = "Consistency-Check Ignored"
            ccDict.update(self.getLocalPeerValueClagIntfCC(ccName, bond))

            ccList.append(ccDict)
        return ccList

    def getCCDictVxlanIntf(self, vxlan_intf, vxlan_info, conflicts):
        ccList = []
        skip_cc_list = []
        if not Parser.isPeerConsistencyCheckSupported():
            skip_cc_list = list(set(Parser._clag_vxlan_intf_cc_name.keys()) - set(Parser._clag_peer_version_unsupported_cc_name.keys()))
        if not StpSync.isPeerPvrstCCSupported():
            skip_cc_list.append("bpdu-guard")
            skip_cc_list.append("restricted-role")

        for ccName in Parser._clag_vxlan_intf_cc_name:
            if ccName in skip_cc_list:
                # Skip all the CC params in the skip_cc_list when peer clag data version is unsupported for CC
                continue
            ccDict = {}
            ccDict['name'] = ccName

            ccConflict = Parser._clag_vxlan_intf_cc_name[ccName][1]
            if (ccConflict & conflicts):
                confDescList = self.GetClagIntfConflictStr(vxlan_intf, ccConflict & conflicts)
                ccDict['description'] = ", ".join(confDescList)
            else:
                ccDict['description'] = '-'

            ccDict.update(self.getLocalPeerValueVxlanIntfCC(ccName, vxlan_intf, vxlan_info))

            ccList.append(ccDict)
        return ccList

    def getClagBondsCCDict(self):
        ccParamDict = {}
        for bond in list(Parser.GetClagBondDB().keys()):
            (conflicts, conflicts_ignored) = self.GetClagIntfConflictsAll(bond)
            ccParamDict[bond] = self.getCCDictClagBond(bond, conflicts, conflicts_ignored)
        ourVxlanDB = VxLanSync.GetOurVxLanDB()
        for intf in list(ourVxlanDB.keys()):
            conflicts = self.GetClagIntfConflicts(intf)
            ccParamDict[intf] = self.getCCDictVxlanIntf(intf,ourVxlanDB[intf],conflicts)
        return ccParamDict
    def requestResyncFromPeer(self):
        if Intf.isPeerAlive() and Parser and Parser.peerProtoBuf:
            msgHdr = peerXchange.Header()
            msgHdr.type = peerXchange.Header.MessageType.Value('CLAG_PERIODIC_SYNC')
            msgData = peerXchange.ClagPeerSync()
            msgData.version = Parser._ClagDataVersion
            msgData.resync = True
            msgHdr.data = msgData.SerializeToString()
            peerSendQueue.put((msgHdr, None))
            Log.log("Resync Requested From Peer")

    def resyncClagDataToPeer(self):
        if Intf.isPeerAlive() and Parser and Parser.peerProtoBuf:
            for sync in DataSyncs:
                sync.MakeProtoBufMessage(True)
            Log.log("Resync Sent To Peer")

    def DumpParameters(self):
        '''
        Print out all of the command line parameter values to the debug log.
        '''
        Log.log_debug(Log._LOG_DEBUG_PARSER, "The parameters are:")
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    lacpPoll = %d" % (self.args.lacpPoll,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    peerConnect = %d" % (self.args.peerConnect,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    sendTimeout = %d" % (self.args.sendTimeout,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    sendBufSize = %d" % (self.args.sendBufSize,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    cmdConnect = %d" % (self.args.cmdConnect,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    peerLinkPoll = %d" % (self.args.peerLinkPoll,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    switchdReadyTimeout = %d" % (self.args.switchdReadyTimeout,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    reloadTimer = %d" % (self.args.reloadTimer,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    periodicRun = %d" % (self.args.periodicRun,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    priority = %d" % (self.args.priority,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    quiet = %s" % (self.args.quiet,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    debug = 0x%X" % (self.args.debug,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    verbose = %s" % (self.args.verbose,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    log = %s" % (self.args.log,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    vm = %s" % (self.args.vm,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    peerPort = %d" % (self.args.peerPort,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    peerTimeout = %s" % (self.args.peerTimeout,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    peerIp = %s" % (self.args.peerIp,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    peerIf = %s" % (self.args.peerIf,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    backupIp = %s" % (self.args.backupIp,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    backupVrf = %s" % (self.args.backupVrf,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    backupPort = %d" % (self.args.backupPort,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    sysMac = %s" % (self.args.sysMac,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    forceDynamic = %s" % (self.args.forceDynamic,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    vxlanAnycast = %s" % (self.args.vxlanAnycast,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    dormantDisable = %s" % (self.args.dormantDisable,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    redirectEnable = %s" % (self.args.redirectEnable,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    redirect2Enable = %s" % (self.args.redirect2Enable,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    gracefulConsistencyCheck = %s" % (self.args.gracefulConsistencyCheck,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    neighSync = %s" % (self.args.neighSync,))
        Log.log_debug(Log._LOG_DEBUG_PARSER, "    peerlinkLearnEnable = %s" % (self.args.peerlinkLearnEnable,))


#-------------------------------------------------------------------------------
#
#   Linux interface utility routines
#
#-------------------------------------------------------------------------------

class IntfSupport:

    # Device flags are stored with this attribute ID
    IFLA_FLAGS         = -1
    CLAG_ID_MATCHIF_IDX_RIF = 0
    CLAG_ID_MATCHIF_IDX_RIF_STATE = 1

    def __init__(self, nlm):
        #
        #   Dual Connected Interfaces - This is a dictionary which contains our  
        #   interface names of the current dual connected bonds as the keys and the
        #   peer switch's corresponding interface names as the values:
        #   {
        #       "bond1" : "bond4",
        #       "bond3" : "bond3"
        #   }
        #
        self.dualIfsLock = threading.Lock()
        self.dualIfs     = {}

        # key: l_if, data: (CLAG_ID_MATCHIF_IDX_XXX,....)
        self.clagIdMatchIfs = {}
        self.peerAlive   = False
        self.firstInitHandShakeDone = False
        self.initHandShakeDone = False
        # multiple threads can trigger peer connectivity shutdown
        self.peerDownLock = threading.Lock()
        self.peerDeathPending = False
        self.peerDeathPendingEvent = False
        self.protoDownLock = threading.Lock()
        self.bridgeAttrLock = threading.Lock()
        self.ifInfoLock = threading.Lock()
        self.ifInfoByIndex = {}
        self.ifNameToIndex = {}
        self.vlan_id_to_ifname = {}
        self.waitAllBondDownEvent = clag.clagthread.Event()
        self.allClagBondDownEvent = True

        self.anycastIpAddDelayed = False

        # Override the nlmanager's mac_int_to_str function to print the MACs
        # like xx:xx:xx:xx:xx:xx instead of xxxx.xxxx.xxxx
        nlmanager.nlpacket.mac_int_to_str = self.mac_int_to_str

        # Dump the interface table and load up the ifInfoByIndex database
        debug = nlmanager.nlpacket.RTM_GETLINK in nlm.debug
        for msg in nlm.request_dump(nlmanager.nlpacket.RTM_GETLINK, socket.AF_UNSPEC, debug):
            self.AddInterface(msg)
        for msg in nlm.request_dump(nlmanager.nlpacket.RTM_GETLINK, socket.AF_BRIDGE, debug):
            self.AddInterface(msg)

    def mac_int_to_str(self, mac_int):
        """
        Return an integer in MAC string format: xx:xx:xx:xx:xx:xx
        """
        return ':'.join(("%012x" % mac_int)[i:i+2] for i in range(0, 12, 2))

    def CombineDicts(self, dest, source):
        """
        Take all of the items in the source dictionary whose values are not
        None, and add/replace them in the dest dictionary. Nested dictionaries
        are handled recursively.
        """
        for k,v in list(source.items()):
            if v is not None:
                if isinstance(v, dict):
                    self.CombineDicts(dest.setdefault(k,{}), v)
                else:
                    dest[k] = v

    def SetClagMasterConflict(self, intfName, nlm):
        if Parser:
            conflict = Parser._CLAG_INTF_CONFLICT_MASTER_NOT_BRIDGE
            Parser.UpdateClagConflicts(intfName, conflict, 0)

            intf = Parser.GetClagIntf(intfName)
            if not intf:
                return
            self.protoDownLock.acquire()
            nlmsg = intf.UpdateProtoDownFlags(nlm, intf._PROTO_DOWNF_INTF_BRIDGE_NO_MASTER, 0, batch=True)
            if nlmsg:
                nlm.tx_nlpacket_raw(nlmsg)
            self.protoDownLock.release()

            if VxLanSync and intf.intfType == Parser._CLAG_INTF_TYPE_VXLAN:
                VxLanSync.newVxLanDataEvent.set()
            if VlanSync and intf.intfType == Parser._CLAG_INTF_TYPE_BOND:
                VlanSync.addVlanCCInterface(intfName)
                VlanSync.compareVlanMapEvent.set()

    def ClearClagMasterConflict(self, intfName, nlm):
        if Parser:
            conflict = Parser._CLAG_INTF_CONFLICT_MASTER_NOT_BRIDGE
            Parser.UpdateClagConflicts(intfName, 0, conflict)

            intf = Parser.GetClagIntf(intfName)
            if not intf:
                return
            self.protoDownLock.acquire()
            nlmsg = intf.UpdateProtoDownFlags(nlm, 0, intf._PROTO_DOWNF_INTF_BRIDGE_NO_MASTER, batch=True)
            if nlmsg:
                nlm.tx_nlpacket_raw(nlmsg)
            self.protoDownLock.release()

            if VxLanSync and intf.intfType == Parser._CLAG_INTF_TYPE_VXLAN:
                VxLanSync.newVxLanDataEvent.set()
            if VlanSync and intf.intfType == Parser._CLAG_INTF_TYPE_BOND:
                VlanSync.addVlanCCInterface(intfName)
                VlanSync.compareVlanMapEvent.set()

    def isVxlanIntfProtoDownFlagSetSecondary(self, intfName):
        if not Parser:
            return False
        intf = Parser.GetClagIntf(intfName)
        if not intf:
            return False
        if intf.intfType != Parser._CLAG_INTF_TYPE_VXLAN:
            return False
        if Parser.clagRole != Parser._ROLE_SECONDARY:
            return False
        if not (intf.protoDownFlags & intf._PROTO_DOWNF_USED_FOR_VXLAN_INTF):
            return False
        return True

    def SetClagVlanIdMismatch(self, intfName):
        if Parser:
            conflict = Parser._CLAG_INTF_CONFLICT_VLAN_MISMATCH
            Parser.UpdateClagConflicts(intfName, conflict, 0)

    def ClearClagVlanIdMismatch(self, intfName):
        if Parser:
            conflict = Parser._CLAG_INTF_CONFLICT_VLAN_MISMATCH
            Parser.UpdateClagConflicts(intfName, 0, conflict)

    def SetClagPvlanIdMismatch(self, intfName, nlm):
        if Parser:
            conflict = Parser._CLAG_INTF_CONFLICT_PVLAN_MISMATCH
            Parser.UpdateClagConflicts(intfName, conflict, 0)

            intf = Parser.GetClagIntf(intfName)
            # Occassionally we see traceback in clagd logs during cleanup sequence while
            # networking restart or clagd restart that we are unable to fetch the intf
            # for which we get conflict in vlan-cc. Safe check here to prevent such traceback
            if not intf:
                return
            protoDown = intf._PROTO_DOWNF_PVLAN_MISMATCH
            self.protoDownLock.acquire()
            nlmsg = intf.UpdateProtoDownFlags(nlm, protoDown, 0, batch=True)
            if nlmsg:
                nlm.tx_nlpacket_raw(nlmsg)
            self.protoDownLock.release()

    def ClearClagPvlanIdMismatch(self, intfName, nlm):
        if Parser:
            conflict = Parser._CLAG_INTF_CONFLICT_PVLAN_MISMATCH
            Parser.UpdateClagConflicts(intfName, 0, conflict)

            intf = Parser.GetClagIntf(intfName)
            # Occassionally we see traceback in clagd logs during cleanup sequence while
            # networking restart or clagd restart that we are unable to fetch the intf
            # for which we get conflict in vlan-cc. Safe check here to prevent such traceback
            if not intf:
                return
            protoDown = intf._PROTO_DOWNF_PVLAN_MISMATCH
            self.protoDownLock.acquire()
            nlmsg = intf.UpdateProtoDownFlags(nlm, 0, protoDown, batch=True)
            if nlmsg:
                nlm.tx_nlpacket_raw(nlmsg)
            self.protoDownLock.release()

    def SetClagVlanIdMismatchOnPeerlink(self, intfName):
        conflict = Parser._CLAG_INTF_CONFLICT_VLAN_MISMATCH_ON_PEERLINK
        Parser.UpdateClagConflicts(intfName, conflict, 0)

    def ClearClagVlanIdMismatchOnPeerlink(self, intfName):
        conflict = Parser._CLAG_INTF_CONFLICT_VLAN_MISMATCH_ON_PEERLINK
        Parser.UpdateClagConflicts(intfName, 0, conflict)

    def DeleteAdPartnerMacInDict(self, idx):
        self.ifInfoLock.acquire()
        ifDict = self.ifInfoByIndex.get(idx, {})
        link = nlmanager.nlmanager.Link.IFLA_LINKINFO
        data = nlmanager.nlmanager.Link.IFLA_INFO_DATA
        adInfo = nlmanager.nlpacket.Link.IFLA_BOND_AD_INFO
        adPartnerMac = nlmanager.nlpacket.Link.IFLA_BOND_AD_INFO_PARTNER_MAC
        if link in ifDict:
            if data in ifDict[link]:
                if adInfo in ifDict[link][data]:
                    if adPartnerMac in ifDict[link][data][adInfo]:
                        del ifDict[link][data][adInfo][adPartnerMac]
        self.ifInfoLock.release()

    def DelVlan(self, msg):
        idx = msg.ifindex
        nlm = global_nlm
        vlanInfo = msg.get_attribute_value(msg.BRIDGE_VLANDB_ENTRY, {})
        if Intf and vlanInfo:
            Intf.DelBridgeVlan(nlm, idx, vlanInfo)

    def AddInterface(self, msg):
        """
        Called by the netlink code when an interface is added or updated. This
        code adds/updates the ifInfoByIndex and ifNameToIndex databases and
        calls any other code which needs to know about new interfaces.
        """
        idx = msg.ifindex
         #Ignore netlink updates for docker interfaces before acquiring lock
        name = msg.get_attribute_value(nlmanager.nlpacket.Link.IFLA_IFNAME)
        if name and name.startswith('docker'):
            Log.log("Ignoring docker interface %s ADD" % (name))
            return

        flags = msg.flags
        nlm = global_nlm


        linkIdx = msg.get_attribute_value(msg.IFLA_LINK)

        # Pair down the dictionary to include only the information which clagd needs.
        srcLinkInfo = msg.get_attribute_value(msg.IFLA_LINKINFO, {})
        kind = srcLinkInfo.get(msg.IFLA_INFO_KIND)
        srcLinkData = srcLinkInfo.get(msg.IFLA_INFO_DATA, {})
        srcSlaveData = None
        if srcLinkInfo.get(msg.IFLA_INFO_SLAVE_KIND) == "bond":
            srcSlaveData = srcLinkInfo.get(msg.IFLA_INFO_SLAVE_DATA)
            if srcSlaveData:
                slaveByPass = srcSlaveData.get(msg.IFLA_BOND_SLAVE_AD_RX_BYPASS)

        adPartnerMac = None
        if kind == 'bond':
            srcAdInfo = srcLinkData.get(msg.IFLA_BOND_AD_INFO, {})
            if srcAdInfo:
                adPartnerMac = srcAdInfo.get(msg.IFLA_BOND_AD_INFO_PARTNER_MAC)
        if kind == 'vxlan':
            ip = srcLinkData.get(msg.IFLA_VXLAN_LOCAL)
            if ip is not None:
                srcLinkData[msg.IFLA_VXLAN_LOCAL] = str(ip)
        dataKeys = {
            'vlan'   : (msg.IFLA_VLAN_ID,),
            'bond'   : (msg.IFLA_BOND_MODE, msg.IFLA_BOND_AD_ACTOR_SYSTEM),
            'bridge' : (msg.IFLA_BR_AGEING_TIME, msg.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
                        msg.IFLA_BR_MCAST_QUERIER_INTVL, msg.IFLA_BR_VLAN_FILTERING,
                        msg.IFLA_BR_STP_STATE, msg.IFLA_BR_PRIORITY),
            'vxlan'  : (msg.IFLA_VXLAN_ID, msg.IFLA_VXLAN_LOCAL)
        }
        linkData = { k:srcLinkData.get(k) for k in dataKeys.get(kind, ()) }
        if kind == 'bond' and adPartnerMac:
            adInfo = {
                msg.IFLA_BOND_AD_INFO_PARTNER_MAC : adPartnerMac
            }
            linkData[msg.IFLA_BOND_AD_INFO] = adInfo

        slaveData = None
        if srcSlaveData:
            slaveData = {
                msg.IFLA_BOND_SLAVE_AD_RX_BYPASS : slaveByPass
            }

        linkInfo = {
            msg.IFLA_INFO_KIND : kind,
            msg.IFLA_INFO_DATA : linkData,
            msg.IFLA_INFO_SLAVE_DATA : slaveData
        }

        srcAfSpec = msg.get_attribute_value(msg.IFLA_AF_SPEC, {})
        afSpec = {
            msg.IFLA_BRIDGE_MODE : srcAfSpec.get(msg.IFLA_BRIDGE_MODE),
            msg.IFLA_BRIDGE_VLAN_INFO : srcAfSpec.get(msg.IFLA_BRIDGE_VLAN_INFO),
            msg.IFLA_BRIDGE_VLAN_TUNNEL_INFO : srcAfSpec.get(msg.IFLA_BRIDGE_VLAN_TUNNEL_INFO)
        } if msg.family == socket.AF_BRIDGE else {}

        srcProtInfo = msg.get_attribute_value(msg.IFLA_PROTINFO, {})
        protInfo = {
            msg.IFLA_BRPORT_STATE     : srcProtInfo.get(msg.IFLA_BRPORT_STATE),
            msg.IFLA_BRPORT_LEARNING  : srcProtInfo.get(msg.IFLA_BRPORT_LEARNING),
            msg.IFLA_BRPORT_VLAN_TUNNEL : srcProtInfo.get(msg.IFLA_BRPORT_VLAN_TUNNEL),
            msg.IFLA_BRPORT_PEER_LINK : srcProtInfo.get(msg.IFLA_BRPORT_PEER_LINK),
            msg.IFLA_BRPORT_DUAL_LINK : srcProtInfo.get(msg.IFLA_BRPORT_DUAL_LINK),
            msg.IFLA_BRPORT_BACKUP_PORT  : srcProtInfo.get(msg.IFLA_BRPORT_BACKUP_PORT),
            msg.IFLA_BRPORT_MULTICAST_ROUTER : srcProtInfo.get(msg.IFLA_BRPORT_MULTICAST_ROUTER)
        }

        nlDict = {
            msg.IFLA_IFNAME     : msg.get_attribute_value(msg.IFLA_IFNAME),
            msg.IFLA_MTU     : msg.get_attribute_value(msg.IFLA_MTU),
            msg.IFLA_PROTO_DOWN : msg.get_attribute_value(msg.IFLA_PROTO_DOWN),
            msg.IFLA_LINKMODE   : msg.get_attribute_value(msg.IFLA_LINKMODE),
            msg.IFLA_MASTER     : msg.get_attribute_value(msg.IFLA_MASTER),
            msg.IFLA_OPERSTATE  : msg.get_attribute_value(msg.IFLA_OPERSTATE),
            msg.IFLA_LINK       : msg.get_attribute_value(msg.IFLA_LINK),
            msg.IFLA_ADDRESS    : msg.get_attribute_value(msg.IFLA_ADDRESS),
            msg.IFLA_LINKINFO   : linkInfo,
            msg.IFLA_AF_SPEC    : afSpec,
            msg.IFLA_PROTINFO   : protInfo
        }

        self.ifInfoLock.acquire()

        ifDict = self.ifInfoByIndex.setdefault(idx, {})
        new_add = False
        if not ifDict:
            new_add = True

        prevOperstate = ifDict.get(nlmanager.nlpacket.Link.IFLA_OPERSTATE)
        prevMac = ifDict.get(nlmanager.nlpacket.Link.IFLA_ADDRESS)
        prevLinkInfo = ifDict.get(nlmanager.nlmanager.Link.IFLA_LINKINFO, {})
        prevLinkData = prevLinkInfo.get(nlmanager.nlmanager.Link.IFLA_INFO_DATA, {})
        prevAdInfo = prevLinkData.get(nlmanager.nlpacket.Link.IFLA_BOND_AD_INFO, {})
        prevAdPartnerMac = prevAdInfo.get(nlmanager.nlpacket.Link.IFLA_BOND_AD_INFO_PARTNER_MAC)

        # If nl overflow happened, clear ifDict and combine again with nlDict
        if 'stale' in ifDict and ifDict['stale'] is True:
            ifDict.clear()

        ifDict[IntfSupport.IFLA_FLAGS] = flags

        #Master if None is not updated in CombineDicts sequence since only non-zero values gets updated.
        ifDict[nlmanager.nlpacket.Link.IFLA_MASTER] = nlDict[msg.IFLA_MASTER]

        self.CombineDicts(ifDict, nlDict)

        name            = ifDict.get(nlmanager.nlpacket.Link.IFLA_IFNAME)

        master          = ifDict.get(nlmanager.nlpacket.Link.IFLA_MASTER)
        master_name     = self.ifInfoByIndex.get(master, {}).get(nlmanager.nlpacket.Link.IFLA_IFNAME)
        operstate       = ifDict.get(nlmanager.nlpacket.Link.IFLA_OPERSTATE)
        mac             = ifDict.get(nlmanager.nlpacket.Link.IFLA_ADDRESS)
        ifInfo          = ifDict.get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        kind            = ifInfo.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND)
        ifData          = ifInfo.get(nlmanager.nlpacket.Link.IFLA_INFO_DATA, {})
        vlanId          = ifData.get(nlmanager.nlpacket.Link.IFLA_VLAN_ID, {})
        adInfo          = ifData.get(nlmanager.nlpacket.Link.IFLA_BOND_AD_INFO, {})
        adPartnerMac    = adInfo.get(nlmanager.nlpacket.Link.IFLA_BOND_AD_INFO_PARTNER_MAC)
        vni             = ifData.get(nlmanager.nlpacket.Link.IFLA_VXLAN_ID)
        localip         = ifData.get(nlmanager.nlpacket.Link.IFLA_VXLAN_LOCAL)
        ifProtInfo      = ifDict.get(nlmanager.nlpacket.Link.IFLA_PROTINFO, {})
        brPortState     = ifProtInfo.get(nlmanager.nlpacket.Link.IFLA_BRPORT_STATE)
        brPortVlanTunnel = ifProtInfo.get(nlmanager.nlpacket.Link.IFLA_BRPORT_VLAN_TUNNEL)
        ifAfSpec        = ifDict.get(nlmanager.nlpacket.Link.IFLA_AF_SPEC, {})
        brVlanTunnelInfo = ifAfSpec.get(nlmanager.nlmanager.Link.IFLA_BRIDGE_VLAN_TUNNEL_INFO, {})

        if name is not None:
            self.ifNameToIndex[name] = idx

        self.ifInfoLock.release()
        Log.log_debug(Log._LOG_DEBUG_INTF, "Adding interface %s" % (name,))
        #Log.log_debug(Log._LOG_DEBUG_INTF, "Adding interface %s : %s" % (name, str(self.ifInfoByIndex[idx])))

        peerMasters = []
        if Intf:
            peerMasters = Intf.GetLogicalMastersOfSubIf(Parser.args.peerIf)
        if Parser and not peerMasters:
            peerMasters = [Parser.args.peerIf]

        # For the given interface, compare the vlan map list
        prevVlanMapList = [0x00000000]*128
        clagInterface = None
        curVlanMapList = [0x00000000]*128
        oldPvid = '-'
        currPvid = '-'
        clagId = None
        linkName = self.GetIfName(linkIdx)

        if kind == "vlan" and master and linkName and vlanId and VlanSync and not self.isVlanFilteringBridge(self.GetBridgeOfMember(linkName)):
            # Update the local vlan db for vlan-unaware bridge topo
            clagId = Parser.GetBondClagId(linkName)
            if clagId:
                ourVlanMap = VlanSync.GetOurVlanIntfDB(clagId)
                curVlanMapList = ourVlanMap[VlanSync.VLAN_DB_VLAN_MAP]
                prevVlanMapList = copy.deepcopy(curVlanMapList)
                curVlanMapList[vlanId // 32] |= 1 << (vlanId % 32)
                clagInterface = linkName
            elif linkName in peerMasters:
                curVlanMapList = VlanSync.GetOurPeerlinkVlanMap()
                prevVlanMapList = copy.deepcopy(curVlanMapList)
                curVlanMapList[vlanId // 32] |= 1 << (vlanId % 32)
                clagInterface = linkName
            else:
                clagInterface = None

        vlanMapList = self.GetIfVlanMap(name, srcAfSpec.get(msg.IFLA_BRIDGE_VLAN_INFO))
        if not vlanMapList:
            # For empty list, initialize the vlanMap
            vlanMapList = [0x00000000]*128
        if kind != "vlan" and VlanSync and Intf and Parser and self.isVlanFilteringBridge(self.GetBridgeOfMember(name)):
            clagId = Parser.GetBondClagId(name)

            currPvid = Intf.GetPvid(name, afspec=ifAfSpec)
            curVlanMapList = vlanMapList
            clagInterface = name
            if not currPvid:
                currPvid = '-'
            else:
                currPvid = str(currPvid)

            if clagId:
                if clagId not in VlanSync.GetOurVlanDB():
                    # Take care of empty/no vlanMap configured for the clag bond
                    oldPvid = None
                    prevVlanMapList = None
                else:
                    ourVlanMap = VlanSync.GetOurVlanIntfDB(clagId)
                    oldPvid = ourVlanMap[VlanSync.VLAN_DB_PVLAN_ID]
                    prevVlanMapList = ourVlanMap[VlanSync.VLAN_DB_VLAN_MAP]
            elif name in peerMasters:
                if VlanSync.ourPeerlinkVlanMapSetFlag:
                    prevVlanMapList = VlanSync.GetOurPeerlinkVlanMap()
                else:
                    # Take care of empty/no vlanMap configured for peerlink
                    prevVlanMapList = None
                if VlanSync.ourPeerlinkPvlanSetFlag:
                    oldPvid = VlanSync.GetOurPeerlinkPvlan()
                else:
                    oldPvid = None
            else:
                currPvid = '-'
                curVlanMapList = [0x00000000]*128
                clagInterface = None

        # Inform whomever wants to know about this new/modified interface
        if Parser is not None and prevOperstate != operstate and \
           name in [Parser.args.peerIf, self.GetMasterOfSubIf(Parser.args.peerIf)]:
            peerlinkEvent.set()

        if Parser is not None and prevOperstate != operstate and name == "lo":      
            anycastIp = Parser.args.vxlanAnycast
            if (anycastIp and \
                (self.GetOperStateFromSysFs("lo") != "down") and Parser.vxlanAnycastDeleted == False):
                Log.log_error("Anycast Ip Deleted on lo by external app. Re-adding the anycast-ip on lo")
                self.AddIpAddress(nlm, "lo", str(Parser.args.vxlanAnycast), 32)

        if Parser is not None and Parser.isClagBond(name):
            if Parser.args.backupIp and new_add and name is not None:
                Intf.SetBondSysMac(nlm, system_mac, name)
            if prevOperstate != operstate:
                if prevOperstate == nlmanager.nlpacket.Link.IF_OPER_DOWN and operstate == nlmanager.nlpacket.Link.IF_OPER_UP and LacpSync:
                    Log.log("%s moved from down to up" % (name))
                    LacpSync.CollectLocalInfo(nlm)
                    LacpSync.newLacpDataEvent.set()
                elif operstate == nlmanager.nlpacket.Link.IF_OPER_DORMANT:
                    Log.log_debug(Log._LOG_DEBUG_INTF, '%s oper state changed to dormant' % name)
                    if LacpSync:
                        lacpBypass = 0
                        for slave in self.GetMembersOfBond(name):
                            lacpBypass |= self.GetLacpBypass(slave)
                        lacpInfo = LacpSync.GetOurLacpInfo(name)
                        if lacpInfo and lacpBypass != lacpInfo[5]:
                            LacpSync.CollectLocalInfo(nlm)
                    self.SetDormantBondOperState(nlm, name)

            # always check for if_oper_down because partner mac could still
            # be here on first nl notification
            if operstate == nlmanager.nlpacket.Link.IF_OPER_DOWN and LacpSync:
                self.DeleteAdPartnerMacInDict(idx)
                if prevOperstate != operstate:
                    LacpSync.CollectLocalInfo(nlm, name)
                    LacpSync.FlushMacsOnDownIf(name)
                    if self.allClagBondDownEvent:
                        Log.log( "Last Clag Bond is Down.")
                        # If node is primary with peerlink down send HealthFail and make self secondary
                        # Sometimes all clag bonds are down and then peerlink goes down. 
                        # Send the health fail event when switchd is dead.
                        # This is a race condition seen when switchd is killed. Peerlink goes down
                        # after all clag bonds are down so we are not sending the health fail event in this case.
                        # resulting is secondary node bringing all clag bonds down due to isl-down event later.
                        switchdActive = True  # Default to True if Parser not initialized
                        if Parser:
                            Parser.UpdateSwitchdState()
                            switchdActive = True if Parser.switchdState == ClagParser._SWITCHD_READY else False
                            
                        if (Intf.isPeerLinkDown() or not switchdActive) and \
                           (Parser and Parser.clagRole == Parser._ROLE_PRIMARY) and \
                           HealthCheck and not HealthCheck.exitInProcess and HealthCheck.GetBackupActive() and \
                           not HealthCheck.healthFailRecv:
                            Intf.SetClagRole(nlm, Parser._ROLE_SECONDARY, "health failed/switchd dead", doBackupElection=False)
                            with Intf.protoDownLock:
                                nlmsgs = bytes()
                                for intf in list(Parser.GetClagIntfDB().values()):
                                    Log.log_error("Peer IP Health failed , setting proto down for interface %s " % (intf.intfName,))
                                    nlmsgs += intf.UpdateProtoDownFlags(nlm,  intf._PROTO_DOWNF_PEER_LINK_DOWN_BACKUP_ACTIVE, 0, batch=True)
                                if nlmsgs:
                                    nlm.tx_nlpacket_raw(nlmsgs)
                            HealthCheck.HelloSendHealthFail()
                        peerlinkEvent.set()

            elif prevAdPartnerMac != adPartnerMac and adPartnerMac and LacpSync:
                Log.log_debug(Log._LOG_DEBUG_INTF, "%s partner mac changed from %r to %r" % (name, prevAdPartnerMac, adPartnerMac))
                intf = Parser.GetClagIntf(name)
                if intf:
                    LacpSync.EvaluateProtoDownBondConflict(nlm, intf)
                LacpSync.CollectLocalInfo(nlm, name)

        elif VxLanSync is not None and kind == 'vxlan':
            operstatestr = self.OperStateToStr(operstate)
            if operstate == nlmanager.nlpacket.Link.IF_OPER_DOWN and not flags & nlmanager.nlpacket.Link.IFF_UP:
                operstatestr = "admin-down"
            if brPortVlanTunnel and brVlanTunnelInfo and Parser.args.singleVxlanDevice:
                VxLanSync.UpdateVniSingleVxLan(nlm, name, brVlanTunnelInfo)

            VxLanSync.AddVxLanInterface(nlm, name, vni, str(localip), operstatestr, brPortState)

        if keep_going and NeighSync is not None and operstate == nlmanager.nlpacket.Link.IF_OPER_UP and \
           self.isSubIfName(name) and not self.isVlanFilteringBridge(self.GetBridgeOfMember(name)):
            if brPortState == 3:
                if self.isBridgePeerLink(name):
                    bridge_master = self.GetBridgeOfMember(name)
                    Log.log_debug(Log._LOG_DEBUG_INTF,"%s peerlink bond is in forwarding Syncing %s" % (name,bridge_master))
                    NeighSync.SyncOnIfaceUp(bridge_master, False)
            else :
                Log.log_debug(Log._LOG_DEBUG_INTF,"%s kind bond is not in forwarding" % (name))

        # Special handling for neighbor sync when bridge interface comes up last
        #
        # In general, neighbor sync is done for the bridge when peerlink comes up.
        # However, if the bridge interface is operationally down when the peerlink
        # gets UP, the neighbor sync will not happen. This code handles the case
        # where the bridge comes up after the peerlink is already operational.
        #
        # Conditions: Bridge interface + CLAG running + NeighSync available + Bridge UP transition
        #             + VLAN-unaware bridge + Peer_up

        if (kind == 'bridge' and keep_going and NeighSync is not None and
            operstate == nlmanager.nlpacket.Link.IF_OPER_UP and
            prevOperstate != operstate and not self.isVlanFilteringBridge(name)):

            peerlinkBridgeMap = self.GetPeerlinksOfBridgeMap()
            peerlink_member = peerlinkBridgeMap.get(name)
            peerlink_found = peerlink_member is not None
            peerlink_vid = None
            if peerlink_found:
                # Check if peerlink_member is operationally up
                peerlink_operstate = self.GetOperState(peerlink_member)
                if peerlink_operstate == "up" and self.isSubIfName(peerlink_member):
                    # Peerlink is up and is a sub-interface, proceed with neighbor sync
                    peerlink_vid = self.GetVlanIdOfSubIf(peerlink_member)

                    # Create brPeerVlanMap for the bridge with peerlink VID
                    brPeerVlanMap = {}
                    if peerlink_vid is not None:
                        brPeerVlanMap[name] = peerlink_vid

                    Log.log_debug(
                        Log._LOG_DEBUG_INTF,
                        "%s bridge interface transitioned to up with peerlink %s, triggering NeighSync.SyncOnIfaceUp vid=%s"
                        % (name, peerlink_member, peerlink_vid)
                    )
                    NeighSync.SyncOnIfaceUp(name, False, brPeerVlanMap)

        if kind == 'vlan':
            parentIntf = self.GetMasterOfSubIf(name)
            if parentIntf and self.isBridgeName(parentIntf) and self.isVlanFilteringBridge(parentIntf):
                if NeighSync and prevOperstate != operstate and operstate == nlmanager.nlpacket.Link.IF_OPER_UP:
                    NeighSync.SyncOnIfaceUp(name, True)

                self.vlan_id_to_ifname[vlanId] = name
                if operstate == nlmanager.nlpacket.Link.IF_OPER_UP:
                    if prevMac and prevMac != mac:
                        if Parser and Parser.args.permanentMacSync and FdbSync:
                            FdbSync.fdbWorkQueue.put((FdbSync._WORK_QUEUE_PERM_DELETE, (prevMac, vlanId, name, None, None, name, None)))
                        if NeighSync:
                            NeighSync.SyncOnIfaceMacChange(vlanId, mac)
                    if Parser and Parser.args.permanentMacSync and FdbSync:
                        FdbSync.fdbWorkQueue.put((FdbSync._WORK_QUEUE_PERM_ADD, (mac, vlanId, name, None, None, name, None)))
                else:
                    if Parser and Parser.args.permanentMacSync and FdbSync:
                        FdbSync.fdbWorkQueue.put((FdbSync._WORK_QUEUE_PERM_DELETE, (mac, vlanId, name, None, None, name, None)))
                    if prevMac and prevMac != mac:
                        if NeighSync:
                            NeighSync.SyncOnIfaceMacChange(vlanId, mac)

                if FdbSync:
                    FdbSync.pendingPermanentMacDBLock.acquire()
                    [p_mac, p_peerIf] = FdbSync.pendingPermanentMacDB.pop(vlanId, [None, None])
                    FdbSync.pendingPermanentMacDBLock.release()
                    if p_mac and p_peerIf:
                        FdbSync.HwWorkQueuePut((FdbSync._MAC_STATIC_ADD, p_mac, vlanId, p_peerIf, None, None))

        if Parser and kind == 'bridge':
            Parser.bridgePriority = ifData.get(nlmanager.nlpacket.Link.IFLA_BR_PRIORITY)
            Parser.bridgeStpState = True if ifData.get(nlmanager.nlpacket.Link.IFLA_BR_STP_STATE) else False
            is_vlan_aware = ifData.get(nlmanager.nlpacket.Link.IFLA_BR_VLAN_FILTERING, 0) == 1
            Parser.setBridgeType(name, True, is_vlan_aware)
            Parser.resetLocalBridgeConsistencyCheckSupport()

        if name in peerMasters:
            # Get the MTU of the peerlink only if the master is the vlan-aware bridge
            if Parser and master_name:
                Parser.peerlinkMaster[name] = (master_name, self.isVlanFilteringBridge(master_name))
                if self.isVlanFilteringBridge(master_name):
                    #Clear the peerlink master no bridge inconsistency protodown clag bond here
                    Parser.ClearPeerlinkMasterNoBridge(nlm)
                    Parser.peerlinkMtu = int(self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_MTU, 0))

                    Parser.peerlinkVlanAwareBridgeAdded = True
            elif Parser:
                Parser.peerlinkMaster[name] = (master_name, False)

        if (curVlanMapList != prevVlanMapList or oldPvid != currPvid) and Parser and clagInterface and VlanSync:
            if clagInterface in peerMasters:
                VlanSync.SetOurPeerlinkVlanMap(curVlanMapList)
                VlanSync.SetOurPeerlinkPvlan(currPvid)
            elif clagId:
                vlanDB = {clagId: (curVlanMapList, currPvid, clagInterface)}
                VlanSync.UpdateOurVlanDB(vlanDB)
            # Set the interface as clagInterface to compare the vlanMap between local and peer
            VlanSync.addVlanCCInterface(clagInterface)
            VlanSync.compareVlanMapEvent.set()
            # Send the vlan map info to the peer
            VlanSync.SendVlanInfoToPeer(clagInterface)

        if Parser and not self.isBridgePeerLink(name):
            Parser.SetClagIntfMaster(name, master_name)
            if not master:
                self.SetClagMasterConflict(name, nlm)
            else:
                self.ClearClagMasterConflict(name, nlm)

        if Parser and len(Parser.vlan_aware_bridge) and not Parser.peerlinkVlanAwareBridgeAdded:
            #Bring down the clag bonds since peerlink is not part of the vlan-aware bridge
            Parser.SetPeerlinkMasterNoBridge(nlm)

    def DelInterface(self, idx, family):
        """
        Called by the netlink code when an interface is deleted. This code
        removes the entries in the ifInfoByIndex and ifNameToIndex databases and
        calls any other code which needs to know that the interface no longer
        exists.
        """
        nlm = global_nlm

        self.ifInfoLock.acquire()
        ifDict = self.ifInfoByIndex.get(idx, {})
        name = ifDict.get(nlmanager.nlpacket.Link.IFLA_IFNAME)
        mac  = ifDict.get(nlmanager.nlpacket.Link.IFLA_ADDRESS)

        ifInfo = ifDict.get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        kind = ifInfo.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND)
        data = ifInfo.get(nlmanager.nlpacket.Link.IFLA_INFO_DATA, {})
        vlanId = data.get(nlmanager.nlpacket.Link.IFLA_VLAN_ID)
        parent_idx = ifDict.get(nlmanager.nlpacket.Link.IFLA_LINK, None)
        parent_dict = self.ifInfoByIndex.get(parent_idx, {})
        parent = parent_dict.get(nlmanager.nlpacket.Link.IFLA_IFNAME, None)
        parent_info = parent_dict.get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        parent_kind = parent_info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND, None)
        parent_data = parent_info.get(nlmanager.nlpacket.Link.IFLA_INFO_DATA, {})
        parent_vlan_aware = parent_data.get(nlmanager.nlpacket.Link.IFLA_BR_VLAN_FILTERING)

        curVlanMapList = [0x00000000]*128
        prevVlanMapList = [0x00000000]*128
        curPvid = '-'
        prevPvid = '-'
        isPeerIf = False

        if Parser and name in Parser.peerlinkMaster:
            # Update the peerlinkMTU for the peerlink if the master bridge was vlan-aware
            bridgeInfo = Parser.peerlinkMaster[name]
            is_vlan_aware = bridgeInfo[1]
            if is_vlan_aware:
                Parser.peerlinkMtu = 0

                # Update the peerlink vlan-aware bridge master flag to False
                # since the peerlink is either deleted or removed from the vlan-aware bridge
                Parser.peerlinkVlanAwareBridgeAdded = False

        if family == socket.AF_BRIDGE:
            ifDict.pop(nlmanager.nlpacket.Link.IFLA_PROTINFO, None)
            ifDict.pop(nlmanager.nlpacket.Link.IFLA_AF_SPEC, None)
            self.ifInfoLock.release()
            Log.log_debug(Log._LOG_DEBUG_INTF, "Removing bridge information from interface %s : %s" % (name, str(ifDict)))

            if kind == 'vlan' and parent and vlanId and VlanSync and Parser:
                # For traditional bridge topo, when subinterface is removed from the bridge, remove the vlan from the parent
                clagId = Parser.GetBondClagId(parent)
                if clagId:
                    isPeerIf = False
                    ourVlanMap = VlanSync.GetOurVlanIntfDB(clagId)
                    curVlanMapList = ourVlanMap[VlanSync.VLAN_DB_VLAN_MAP]
                    prevVlanMapList = copy.deepcopy(curVlanMapList)
                    curVlanMapList[vlanId // 32] &= ~(1 << (vlanId % 32))
                elif parent in Parser.peerlinkMaster:
                    isPeerIf = True
                    curVlanMapList = VlanSync.GetOurPeerlinkVlanMap()
                    prevVlanMapList = copy.deepcopy(curVlanMapList)
                    curVlanMapList[vlanId // 32] &= ~(1 << (vlanId % 32))
                self.InitiateVlanComparision(parent, curVlanMapList, prevVlanMapList, clagId, isPeerIf, curPvid, prevPvid)
            elif VlanSync and Parser:
                # Remove the vlan info from the local vlan DB and do vlan cc again
                clagId = Parser.GetBondClagId(name)
                if clagId:
                    isPeerIf = False
                    ourVlanMap = VlanSync.GetOurVlanIntfDB(clagId)
                    prevVlanMapList = ourVlanMap[VlanSync.VLAN_DB_VLAN_MAP]
                    prevPvid = ourVlanMap[VlanSync.VLAN_DB_PVLAN_ID]
                elif name in Parser.peerlinkMaster:
                    isPeerIf = True
                    prevVlanMapList = VlanSync.GetOurPeerlinkVlanMap()
                    prevPvid = VlanSync.GetOurPeerlinkPvlan()
                self.InitiateVlanComparision(name, curVlanMapList, prevVlanMapList, clagId, isPeerIf, curPvid, prevPvid)

            # Remove the master bridge from the peerlink in Parser.peerlinkMaster dict
            if Parser and name in Parser.peerlinkMaster:
                Parser.peerlinkMaster[name] = (None, False)

            # Reset the master bridge for the clag interface and mark the conflict
            if Parser:
                Parser.SetClagIntfMaster(name, None)
                self.SetClagMasterConflict(name, nlm)

        else:
            if kind == 'vlan':
                if parent_kind == "bridge" and parent_vlan_aware == 1 and vlanId:
                    self.vlan_id_to_ifname.pop(vlanId)

            self.ifInfoByIndex.pop(idx, None)
            self.ifNameToIndex.pop(name, None)
            self.ifInfoLock.release()
            Log.log_debug(Log._LOG_DEBUG_INTF, "Deleting interface %s : %s" % (name, str(ifDict)))

            if Parser and kind == 'bridge':
                data = ifInfo.get(nlmanager.nlpacket.Link.IFLA_INFO_DATA, {})
                is_vlan_aware = data.get(nlmanager.nlpacket.Link.IFLA_BR_VLAN_FILTERING, 0) == 1
                Parser.setBridgeType(name, False, is_vlan_aware)
                Parser.resetLocalBridgeConsistencyCheckSupport()

            # Reset the master bridge for the clag interface
            if Parser:
                Parser.SetClagIntfMaster(name, None)
                self.ClearClagMasterConflict(name, nlm)

            # Handle the delete nlm notification for subinterfaces in vlan-unaware topology.
            # Need to reset the vlan from the vlanList of the master interface and send update to the peer
            if kind == 'vlan' and parent and vlanId and not self.isVlanFilteringBridge(self.GetBridgeOfMember(parent)) and VlanSync and Parser:
                clagId = Parser.GetBondClagId(parent)
                if clagId:
                    # Update the local vlan db for vlan-unaware bridge topo
                    isPeerIf = False
                    ourVlanMap = VlanSync.GetOurVlanIntfDB(clagId)
                    curVlanMapList = ourVlanMap[VlanSync.VLAN_DB_VLAN_MAP]
                    prevVlanMapList = copy.deepcopy(curVlanMapList)
                    curVlanMapList[vlanId // 32] &= ~(1 << (vlanId % 32))
                elif name in Parser.peerlinkMaster:
                    isPeerIf = True
                    curVlanMapList = VlanSync.GetOurPeerlinkVlanMap()
                    prevVlanMapList = copy.deepcopy(curVlanMapList)
                    curVlanMapList[vlanId // 32] &= ~(1 << (vlanId % 32))
                self.InitiateVlanComparision(parent, curVlanMapList, prevVlanMapList, clagId, isPeerIf, curPvid, prevPvid)

            if NeighSync and vlanId:
                NeighSync.DelNeighEntries(name, vlanId)

            if Parser and Parser.args.permanentMacSync and FdbSync and vlanId:
                master = self.GetMasterOfSubIf(name)
                FdbSync.fdbWorkQueue.put((FdbSync._WORK_QUEUE_PERM_DELETE, (mac, vlanId, name, None, None, name, None)))

            # Remove the peerlink from Parser.peerlinkMaster dict
            if Parser and name in Parser.peerlinkMaster:
                Parser.peerlinkMaster.pop(name, None)

            # Inform whomever wants to know about this removed interface
            if VxLanSync is not None and kind == 'vxlan':
                VxLanSync.DelVxLanInterface(nlm, name)

        if Parser and not len(Parser.vlan_aware_bridge):
            #Clear the peerlink master no bridge inconsistency protodown when all vlan-aware bridges are deleted
            Parser.ClearPeerlinkMasterNoBridge(nlm)

        if Parser and not Parser.peerlinkVlanAwareBridgeAdded and len(Parser.vlan_aware_bridge):
            #Bring down the clag bonds since peerlink is not part of the vlan-aware bridge
            Parser.SetPeerlinkMasterNoBridge(nlm)

    def InitiateVlanComparision(self, name, curVlanMapList, prevVlanMapList, clagId=None, peerIf=False, currPvid='-', prevPvid='-'):
        if not VlanSync:
            return

        if curVlanMapList != prevVlanMapList or currPvid != prevPvid:
            if clagId:
                vlanDB = {clagId: (curVlanMapList, currPvid, name)}
                VlanSync.UpdateOurVlanDB(vlanDB)
            elif peerIf:
                VlanSync.SetOurPeerlinkVlanMap(curVlanMapList)
                VlanSync.SetOurPeerlinkPvlan(currPvid)

            VlanSync.addVlanCCInterface(name)
            VlanSync.compareVlanMapEvent.set()
            # Send the vlan map info to the peer
            VlanSync.SendVlanInfoToPeer(name)

    def GetIfIndex(self, ifName):
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(ifName, None)
        self.ifInfoLock.release()
        return idx

    def GetIfName(self, ifIndex):
        self.ifInfoLock.acquire()
        ifName = self.ifInfoByIndex.get(ifIndex, {}).get(nlmanager.nlpacket.Link.IFLA_IFNAME)
        self.ifInfoLock.release()
        return ifName

    def GetLacpBypass(self, ifName):
        self.ifInfoLock.acquire()
        lacpBypass = 0
        idx = self.ifNameToIndex.get(ifName)
        nlmLink = nlmanager.nlpacket.Link
        lacpBypass = self.ifInfoByIndex.get(idx, {}).\
                        get(nlmLink.IFLA_LINKINFO, {}).\
                        get(nlmLink.IFLA_INFO_SLAVE_DATA, {}).\
                        get(nlmLink.IFLA_BOND_SLAVE_AD_RX_BYPASS, 0)
        self.ifInfoLock.release()
        return lacpBypass

    def GetLinkAdminState(self, ifName):
        # return the admin state as non-zero (IFF_UP) or 0
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(ifName)
        flags = self.ifInfoByIndex.get(idx, {}).get(IntfSupport.IFLA_FLAGS, 0)
        self.ifInfoLock.release()
        return flags & nlmanager.nlpacket.Link.IFF_UP

    def SetLinkAdminState(self, nlm, ifName, state):
        if_flags = nlmanager.nlpacket.Link.IFF_UP if state else 0
        if_change = nlmanager.nlpacket.Link.IFF_UP
        debug = nlmanager.nlpacket.RTM_NEWLINK in nlm.debug
        self.ifInfoLock.acquire()
        if_idx = self.ifNameToIndex.get(ifName, 0)
        self.ifInfoLock.release()

        link = nlmanager.nlpacket.Link(nlmanager.nlpacket.RTM_NEWLINK, debug, Log.logger, Log.getHandlerStr() != "syslog")
        link.flags = nlmanager.nlpacket.NLM_F_REQUEST
        link.body = struct.pack('=BxxxiLL', socket.AF_UNSPEC, if_idx, if_flags, if_change)
        link.build_message(next(nlm.sequence), nlm.pid)
        Log.log_debug(Log._LOG_DEBUG_INTF, "(%s): Setting link admin state to %s" % (ifName, str(state)))
        return nlm.tx_nlpacket_raw(link.message)

    def GetLinkProtoDownFlags(self, ifName):
        # return the protodown state as 0 or 1
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(ifName)
        protodown = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_PROTO_DOWN, -1)
        self.ifInfoLock.release()
        return protodown

    def SetLinkProtoDownFlags(self, nlm, ifName, protoDown, batch=False):
        retVal = bytes()
        debug = nlmanager.nlpacket.RTM_NEWLINK in nlm.debug
        self.ifInfoLock.acquire()
        if_idx = self.ifNameToIndex.get(ifName)
        if if_idx is not None:
            self.ifInfoByIndex[if_idx][nlmanager.nlpacket.Link.IFLA_PROTO_DOWN] = protoDown
        self.ifInfoLock.release()
        if protoDown:
            reasonValue = 0x1
            maskValue   = 0x1
        else:
            reasonValue = 0x0
            maskValue = 0x1
        protodownReason = { nlmanager.nlpacket.Link.IFLA_PROTO_DOWN_REASON_VALUE : reasonValue, 
                           nlmanager.nlpacket.Link.IFLA_PROTO_DOWN_REASON_MASK : maskValue }

        if if_idx is not None:
            link = nlmanager.nlpacket.Link(nlmanager.nlpacket.RTM_NEWLINK, debug, Log.logger, Log.getHandlerStr() != "syslog")
            link.flags = nlmanager.nlpacket.NLM_F_REQUEST
            link.body = struct.pack('=BxxxiLL', socket.AF_UNSPEC, if_idx, 0, 0)
            link.add_attribute(nlmanager.nlpacket.Link.IFLA_PROTO_DOWN_REASON, protodownReason)
            link.add_attribute(nlmanager.nlpacket.Link.IFLA_PROTO_DOWN, protoDown)
            link.build_message(next(nlm.sequence), nlm.pid)
            if batch:
                return link.message
            Log.log_debug(Log._LOG_DEBUG_INTF, "(%s): Setting protodown flag to %s" % (ifName, str(protoDown)))
            nlm.tx_nlpacket_raw(link.message)
        return retVal

    def GetLinkMode(self, ifName):
        # return the link mode
        linkmode = 0
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(ifName)
        mode = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKMODE, 0)
        self.ifInfoLock.release()
        return mode

    def SetLinkMode(self, nlm, ifName, dormant):
        debug = nlmanager.nlpacket.RTM_NEWLINK in nlm.debug
        self.ifInfoLock.acquire()
        if_idx = self.ifNameToIndex.get(ifName)
        self.ifInfoLock.release()

        if if_idx is not None:
            link = nlmanager.nlpacket.Link(nlmanager.nlpacket.RTM_NEWLINK, debug, Log.logger, Log.getHandlerStr() != "syslog")
            link.flags = nlmanager.nlpacket.NLM_F_REQUEST
            link.body = struct.pack('=BxxxiLL', socket.AF_UNSPEC, if_idx, 0, 0)
            dormant_val = 1 if dormant else 0
            link.add_attribute(nlmanager.nlpacket.Link.IFLA_LINKMODE, dormant_val)
            link.build_message(next(nlm.sequence), nlm.pid)
            Log.log_debug(Log._LOG_DEBUG_INTF, "(%s): Setting link mode to %s" % (ifName, str(dormant_val)))
            nlm.tx_nlpacket_raw(link.message)

    def CheckAndUpdateIntfModeDormant(self, nlm):
        '''
        The protoDown state in interface driver could have fallen out of sync
        with clagd
        '''
        if Parser.args.dormantDisable:
            return

        clagIntfs = list(Parser.GetClagBondDB().values()) 
        self.protoDownLock.acquire()
        for intf in clagIntfs:
            intf.SetClagIntfModeDormant(nlm, dormant=True, retainProtoDown=True)
        self.protoDownLock.release()

    def SetProtoDownOnExit(self, nlm, ignoreLocks=False):
        nlmsgs = bytes()
        for intf in list(Parser.GetClagIntfDB(ignoreLocks).values()):
            intf.protoDownFlags |= intf._PROTO_DOWNF_SHUTDOWN
            if intf.intfType == Parser._CLAG_INTF_TYPE_BOND:
                for intfMbr in self.GetBaseInterfaces(intf.intfName):
                    nlmsgs += self.SetLinkProtoDownFlags(nlm, intfMbr, 1, True)
                    Log.log_debug(Log._LOG_DEBUG_INTF, "(%s): Setting protodown flag (on exit) to 1" % (intfMbr,))
            else:
                nlmsgs += self.SetLinkProtoDownFlags(nlm, intf.intfName, 1, True)
                Log.log_debug(Log._LOG_DEBUG_INTF, "(%s): Setting protodown flag (on exit) to 1" % (intf.intfName,))
        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)

    def ClearDormantMode(self, nlm, ignoreLocks=False):
        for intf in Parser.GetClagBondDB(ignoreLocks):
            self.SetLinkMode(nlm, intf, 0)

    def GetBackupRole(self): 
        if HealthCheck and HealthCheck.IsBackupRoleAvailable():
            role = HealthCheck.GetBackupRole()
        else:
            role = Parser.clagRole

        return role

    def SetClagRole(self, nlm, newRole, reasonStr, doBackupElection=True):
        if Parser.clagRole == newRole or Parser.GetMismatchState() or StpSync.GetMismatchState():
            return

        Parser.clagRole = newRole
        if newRole == Parser._ROLE_PRIMARY:
            Log.log("Peerlink: role is now primary; %s" % reasonStr)
        else:
            Parser.clagRole = Parser._ROLE_SECONDARY
            Log.log("Peerlink: role is now secondary; %s" % reasonStr)

        # role over ISL can influence the role over backup
        if doBackupElection and HealthCheck and HealthCheck.GetBackupActive():
            HealthCheck.RunBackupElection(nlm)
        Parser.ListenersRoleUpdate()
        if MrouteSync:
            MrouteSync.ZebraNotifyClagStatusUpdate()

        # For clag role change, sometimes the role changes are triggered
        # too fast (before getPeriodicRun timer expiry) for UpdateVxLanConfig
        # thread to ignore the change. If the currRole in VxlanSync is same as the
        # newRole set in clag, it is most likely that we have hit the scenario of
        # clagRole changing from X -> Y -> X within getPeriodicRun timer.
        # To safeguard against this, we will trigger
        # vxlanDataEvent with newRole flag set when clag role changes so that
        # vxlan interface programmings can be taken care.
        if VxLanSync and VxLanSync.currRole == newRole:
            Log.log_debug(Log._LOG_DEBUG_VXLAN_SYNC, "Trigger vxlansync newRole change after role change triggered too fast")
            VxLanSync.newRole = True
            VxLanSync.newVxLanDataEvent.set()

    def GetKCacheLinkCopy(self):
        self.ifInfoLock.acquire()
        ifInfo = copy.deepcopy(self.ifInfoByIndex)
        self.ifInfoLock.release()
        return ifInfo

    def SetDormantBondOperState(self, nlm, bond):
        if LacpSync:
            LacpSync.SetDormantBondOperState(nlm, bond)
        
    def UpdatePendingDormantBonds(self, nlm):
        if not LacpSync:
            return
        clagBonds = Parser.GetClagBondDB()
        for bond in clagBonds:
            self.ifInfoLock.acquire()
            idx = self.ifNameToIndex.get(bond)
            operstate = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_OPERSTATE, 0)
            self.ifInfoLock.release()
            if operstate == nlmanager.nlpacket.Link.IF_OPER_DORMANT:
                LacpSync.SetDormantBondOperState(nlm, bond)

    def SetInitHandShakeDone(self, nlm):
        if not self.initHandShakeDone:
            Log.log("Initial handshake done.")
            self.firstInitHandShakeDone = True
            self.initHandShakeDone = True
            nlmsgs = bytes()
            self.protoDownLock.acquire()
            for intf in list(Parser.GetClagIntfDB().values()):
                if (intf.protoDownFlags & intf._PROTO_DOWNF_INIT):
                    nlmsgs += intf.UpdateProtoDownFlags(nlm, 0, intf._PROTO_DOWNF_INIT, batch=True)
            if nlmsgs:
                nlm.tx_nlpacket_raw(nlmsgs)
            self.protoDownLock.release()

    def ClearInitHandShake(self):
        self.initHandShakeDone = False

    def SetProtoPeerOrBackupState(self, nlm):
        nlmsgs = bytes()
        if not self.isPeerAlive() and HealthCheck and HealthCheck.GetBackupActive() and not HealthCheck.peerInitState:
            if Parser.clagRole == Parser._ROLE_PRIMARY and HealthCheck.IsBackupRoleAvailable() and HealthCheck.GetBackupRole() == "secondary":
                Intf.SetClagRole(nlm, Parser._ROLE_SECONDARY, "backup connectivity restored while the main connection over the peerlink was down", False)

            for intf in list(Parser.GetClagIntfDB().values()):
                if not (intf.protoDownFlags & intf._PROTO_DOWNF_PEER_LINK_DOWN_BACKUP_ACTIVE):
                    if intf.intfType == Parser._CLAG_INTF_TYPE_VXLAN and self.waitAllBondDownEvent.is_set():
                        continue

                    nlmsgs += intf.UpdateProtoDownFlags(nlm, intf._PROTO_DOWNF_PEER_LINK_DOWN_BACKUP_ACTIVE, 0, batch=True)
            #Since we are bringing down the vnis Delete the Ip Address on the lo
            if VxLanSync and Parser.args.vxlanAnycast and HealthCheck.IsBackupRoleAvailable() and \
                    HealthCheck.GetBackupRole() == "secondary" and not Parser.vxlanAnycastDeleted:
                Parser.vxlanAnycastDeleted = True
                Intf.DelIpAddress(nlm, "lo", Parser.args.vxlanAnycast, 32)
        else:
            #To handle the case when backup goes down, intermittently after peerlink failure 
            if self.waitAllBondDownEvent.is_set():
                self.waitAllBondDownEvent.clear()
            setProtoDnInitDelay = False
            if HealthCheck and HealthCheck.initDelayTimerStartEvent.is_set() and \
               HealthCheck.initDelayBringup() and (Parser.clagRole == Parser._ROLE_SECONDARY and \
                    HealthCheck.IsBackupRoleAvailable() and HealthCheck.GetBackupRole() == "secondary"):
                setProtoDnInitDelay = True
            for intf in list(Parser.GetClagIntfDB().values()):
                delFlags = intf._PROTO_DOWNF_PEER_LINK_DOWN_BACKUP_ACTIVE | intf._PROTO_DOWNF_SINGLE_VXLAN \
                            if (intf.intfType == Parser._CLAG_INTF_TYPE_VXLAN and self.isMlagInSplitBrain()) \
                                else intf._PROTO_DOWNF_PEER_LINK_DOWN_BACKUP_ACTIVE
                addFlags = intf._PROTO_DOWNF_INIT_DELAY if intf.intfType == Parser._CLAG_INTF_TYPE_BOND and \
                            setProtoDnInitDelay else intf._PROTO_DOWNF_NONE
                Log.log_debug(Log._LOG_DEBUG_INTF, "addFlags = %d, delFlags = %d" % (addFlags, delFlags))
                if (intf.protoDownFlags & intf._PROTO_DOWNF_PEER_LINK_DOWN_BACKUP_ACTIVE) or \
                   setProtoDnInitDelay and not (intf.protoDownFlags & intf._PROTO_DOWNF_INIT_DELAY):
                    nlmsgs += intf.UpdateProtoDownFlags(nlm, addFlags, delFlags, batch=True)

        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)

    def isPeerLinkDown(self):
        '''
        Determine if communication with the peer should be possible because the
        interfaces to the peer are all up. This typically involves checking the
        peerlink SVI and the peerlink bond.
        '''
        # Determine if the interfaces to the peer are down
        peerIfDown = self.GetOperStateFromSysFs(Parser.args.peerIf) != "up"
        if self.isSubIfName(Parser.args.peerIf):
            peerIfDown |= self.GetOperStateFromSysFs(self.GetMasterOfSubIf(Parser.args.peerIf)) != "up"
        peerIfs = self.GetLogicalMastersOfSubIf(Parser.args.peerIf)
        for peerIf in peerIfs:
            peerIfDown |= self.GetOperStateFromSysFs(peerIf) != "up"
        return peerIfDown

    #
    #   Bridge interface routines
    #
    def isBridgeName(self, bridgeName):
        '''
        Determine if the agrument passed in is the name of a bridge.
        '''
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bridgeName)
        info = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        isBridge = info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bridge"
        self.ifInfoLock.release()
        return isBridge

    def isBridgeMember(self, bridgeMemberName):
        '''
        Determines if the specified interface is a member of a bridge.
        '''
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bridgeMemberName)
        master = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_MASTER)
        info = self.ifInfoByIndex.get(master, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        isBridgeMem = info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bridge"
        self.ifInfoLock.release()
        return isBridgeMem

    def GetBridgeOfMember(self, bridgeMemberName):
        '''
        Returns the name of the bridge of which the specified interface is a member.
        '''
        bridge = None
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bridgeMemberName)
        master = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_MASTER)
        info = self.ifInfoByIndex.get(master, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bridge":
            bridge = self.ifInfoByIndex.get(master, {}).get(nlmanager.nlpacket.Link.IFLA_IFNAME)
        self.ifInfoLock.release()
        return bridge

    def GetMembersOfBridge(self, bridgeName):
        '''
        Returns a list of interfaces which are members of the specified bridge.
        '''
        bridgeMembers = []
        self.ifInfoLock.acquire()
        bridgeIdx = self.ifNameToIndex.get(bridgeName)
        info = self.ifInfoByIndex.get(bridgeIdx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bridge":
            for ifDict in list(self.ifInfoByIndex.values()):
                if ifDict.get(nlmanager.nlpacket.Link.IFLA_MASTER) == bridgeIdx:
                    ifName = ifDict.get(nlmanager.nlpacket.Link.IFLA_IFNAME)
                    if ifName is not None:
                        bridgeMembers.append(ifName)
        self.ifInfoLock.release()
        return bridgeMembers

    def GetAllBridges(self):
        '''
        Returns a list of all bridges in the system
        '''
        bridgeNames = []
        self.ifInfoLock.acquire()
        for ifDict in list(self.ifInfoByIndex.values()):
            info = ifDict.get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
            if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bridge":
                brName = ifDict.get(nlmanager.nlpacket.Link.IFLA_IFNAME)
                if brName is not None:
                    bridgeNames.append(brName)
        self.ifInfoLock.release()
        return bridgeNames

    def isVlanFilteringBridge(self, bridgeName):
        '''
        Return True if bridge is VLAN filtering or False otherwise
        '''
        vlanFiltering = False
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bridgeName)
        info = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bridge":
            data = info.get(nlmanager.nlpacket.Link.IFLA_INFO_DATA, {})
            vlanFiltering = data.get(nlmanager.nlpacket.Link.IFLA_BR_VLAN_FILTERING) == 1
        self.ifInfoLock.release()
        return vlanFiltering

    def GetBridgePeerVlanMap(self):
        '''
        Returns a dictionary whose keys are bridge names and data is the VLAN ID
        of the peerlink in that bridge. Will only include vlan unaware bridges.
        '''
        peerVlanMap = {}
        subIfVlanMap = self.GetSubIfsVlanMap()
        peerMIf = self.GetMasterOfSubIf(Parser.args.peerIf)
        peerIfs = set(self.GetSubIfsOfMaster(peerMIf)) | set([peerMIf])
        for bridge in self.GetAllBridges():
            if not self.isVlanFilteringBridge(bridge):
                for intf in set(self.GetMembersOfBridge(bridge)) & peerIfs:
                    peerVlanMap[bridge] = subIfVlanMap.get(intf, (None,0))[1]
        return peerVlanMap

    def GetPeerBridgeVlanMap(self):
        '''
        Returns a dictionary whose keys are vlan IDs of the sub interfaces of 
        the peerlink which are members of bridges. The data is a dictionary.
        The keys of the dictionary are the "base" interface names of members
        of the bridge, and the data is the vlan IDs of the base interface in
        the bridge.
        '''
        vlanMap = {}
        masterVlanMap = self.GetMasterIfVlanMap()
        vlanIdMap = self.GetSubIfsVlanMap()
        peerMIf = self.GetMasterOfSubIf(Parser.args.peerIf)
        peerIfs = set(self.GetSubIfsOfMaster(peerMIf)) | set([peerMIf])
        for peerIf in peerIfs:
            peerVlanId = vlanIdMap.get(peerIf, (None, 0))[1]
            bridgeName = self.GetBridgeOfMember(peerIf)
            for member in self.GetMembersOfBridge(bridgeName):
                (intfBase, intfVlanId) = vlanIdMap.get(member, (None, 0))
                if not vlanMap.get(peerVlanId):
                    vlanMap[peerVlanId] = {}
                vlanMap[peerVlanId][intfBase] = intfVlanId
        return vlanMap

    def GetPeerlinksOfBridgeMap(self):
        '''
        Returns a dictionary whose keys are the bridge names and the data
        is the peerlink interface configured for the bridge.
        '''
        peerMIf = self.GetMasterOfSubIf(Parser.args.peerIf)
        peerIfs = set(self.GetSubIfsOfMaster(peerMIf)) | set([peerMIf])
        peerlinkBridgeMap = {}
        for peerIf in peerIfs:
            bridge = Intf.GetBridgeOfMember(peerIf) 
            peerlinkBridgeMap[bridge] = peerIf
        return peerlinkBridgeMap

    def GetBridgeAgeingTime(self, bridgeName):
        '''
        Returns the number of seconds after which MAC addresses will be aged
        '''
        ageTimer = None
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bridgeName)
        info = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bridge":
            data = info.get(nlmanager.nlpacket.Link.IFLA_INFO_DATA, {})
            ageTimer = data.get(nlmanager.nlpacket.Link.IFLA_BR_AGEING_TIME)
            # Value in ticks: CLOCKS_PER_SEC = USER_HZ = 100
            ageTimer = int(ageTimer) / 100
        self.ifInfoLock.release()
        return ageTimer

    def GetBridgeMemInterval(self, bridgeName):
        '''
        Returns the multicast group membership interval for a bridge.
        '''
        memIntval = None
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bridgeName)
        info = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bridge":
            data = info.get(nlmanager.nlpacket.Link.IFLA_INFO_DATA, {})
            memIntval = data.get(nlmanager.nlpacket.Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL)
            # Value in ticks: CLOCKS_PER_SEC = USER_HZ = 100
            memIntval = int(memIntval) / 100
        self.ifInfoLock.release()
        return memIntval

    def GetBridgeQuerierInterval(self, bridgeName):
        '''
        Returns the multicast querier interval for a bridge.
        '''
        querierIntval = None
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bridgeName)
        info = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bridge":
            data = info.get(nlmanager.nlpacket.Link.IFLA_INFO_DATA, {})
            querierIntval = data.get(nlmanager.nlpacket.Link.IFLA_BR_MCAST_QUERIER_INTVL)
            # Value in ticks: CLOCKS_PER_SEC = USER_HZ = 100
            querierIntval = int(querierIntval) / 100
        self.ifInfoLock.release()
        return querierIntval

    def VlanGet(self, nlm, filter_ifindex=None, filter_vlanid=None, compress_vlans=None):
        return nlm.vlan_get(filter_ifindex, filter_vlanid, compress_vlans)

    def GetVlanOfVxlanIntf(self, nlm, ifname):
        '''
        Returns vlan mapped to vni corresponding to the vxlan interface passed.
        This function can be used in general to return PVID of a bridge port.
        '''
        pvid = None
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(ifname)
        vlanInfoDict = self.VlanGet(nlm, [idx])
        vlanInfo = vlanInfoDict[ifname]["vlans"]
        for vlan_id, vlan_flag in  vlanInfo:
            if (vlan_flag & nlmanager.nlpacket.Link.BRIDGE_VLAN_INFO_PVID):
                pvid = vlan_id
                break
        self.ifInfoLock.release()
        return pvid

    def GetPvid(self, intf, afspec=None):
        '''
        Returns the PVID of a bridge port, or None if there is none, as in the
        case of the old bridge driver
        '''
        #This will not return pvid if called for Vxlan interfaces at init time.
        pvid = None
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(intf)
        if not afspec:
            afspec = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_AF_SPEC, {})
        vlanInfo = afspec.get(nlmanager.nlpacket.Link.IFLA_BRIDGE_VLAN_INFO, [])
        for flags, vlanId in vlanInfo:
            if flags & nlmanager.nlpacket.Link.BRIDGE_VLAN_INFO_PVID:
                pvid = vlanId
                break
        self.ifInfoLock.release()
        return pvid

    def GetUntaggedVlan(self, intf):
        '''
        Returns the untagged vlan of a bridge port, or None if there is none, as in the
        case of the old bridge driver
        '''
        #This will not return untagged vlan if called for Vxlan interfaces at init time.
        nativeVlan = None
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(intf)
        afspec = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_AF_SPEC, {})
        vlanInfo = afspec.get(nlmanager.nlpacket.Link.IFLA_BRIDGE_VLAN_INFO, [])
        for flags, vlanId in vlanInfo:
            if flags & nlmanager.nlpacket.Link.BRIDGE_VLAN_INFO_UNTAGGED:
                nativeVlan = vlanId
                break
        self.ifInfoLock.release()
        return nativeVlan

    def GetPvidMap(self):
        '''
        Returns a dictionary: keys = bridge interface names, values = PVID of the bridge port
        '''
        pvidMap = {}
        self.ifInfoLock.acquire()
        for ifDict in list(self.ifInfoByIndex.values()):
            afspec = ifDict.get(nlmanager.nlpacket.Link.IFLA_AF_SPEC, {})
            vlanInfo = afspec.get(nlmanager.nlpacket.Link.IFLA_BRIDGE_VLAN_INFO, [])
            for flags, vlanId in vlanInfo:
                ifName = ifDict.get(nlmanager.nlpacket.Link.IFLA_IFNAME)
                if ifName is not None and flags & nlmanager.nlpacket.Link.BRIDGE_VLAN_INFO_PVID:
                    pvidMap[ifName] = vlanId
                    break
        self.ifInfoLock.release()
        return pvidMap

    def isBridgeLearning(self, bridgeMemberName):
        """
        Returns true if learning is enabled on a bridge member.
        """
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bridgeMemberName)
        protinfo = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_PROTINFO, {})
        learning = protinfo.get(nlmanager.nlpacket.Link.IFLA_BRPORT_LEARNING, False)
        self.ifInfoLock.release()
        return learning

    def isBridgeDualLink(self, bridgeMemberName):
        """
        Returns true if dual link attribute is enabled on a bridge member.
        """
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bridgeMemberName)
        protinfo = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_PROTINFO, {})
        duallink = protinfo.get(nlmanager.nlpacket.Link.IFLA_BRPORT_DUAL_LINK, False)
        self.ifInfoLock.release()
        return duallink

    def getBridgeMtu(self, ifName):
        """
        Returns the MTU of the bridge member
        """
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(ifName)
        mtu = int(self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_MTU, 0))
        self.ifInfoLock.release()
        return mtu

    def isBridgePeerLink(self, bridgeMemberName):
        """
        Returns true if peer link attribute is enabled on a bridge member.
        """
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bridgeMemberName)
        protinfo = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_PROTINFO, {})
        peerlink = protinfo.get(nlmanager.nlpacket.Link.IFLA_BRPORT_PEER_LINK, False)
        self.ifInfoLock.release()
        return peerlink

    def isBridgeBackupPort(self, bridgeMemberName):
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bridgeMemberName)
        protinfo = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_PROTINFO, {})
        backup_port = protinfo.get(nlmanager.nlpacket.Link.IFLA_BRPORT_BACKUP_PORT, False)
        self.ifInfoLock.release()
        peerlink = self.GetMasterOfSubIf(Parser.args.peerIf)
        return backup_port if backup_port == self.GetIfIndex(peerlink) else 0

    def isBridgeVlanTunnel(self, bridgeMemberName):
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bridgeMemberName)
        protinfo = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_PROTINFO, {})
        vlanTunnel = protinfo.get(nlmanager.nlpacket.Link.IFLA_BRPORT_VLAN_TUNNEL, False)
        self.ifInfoLock.release()
        return vlanTunnel

    def getBridgeMcastRtrType(self, bridgeMemberName):
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bridgeMemberName)
        protinfo = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_PROTINFO, {})
        mcast_rport_type = protinfo.get(nlmanager.nlpacket.Link.IFLA_BRPORT_MULTICAST_ROUTER, False)
        self.ifInfoLock.release()
        return mcast_rport_type

    def SetBridgeDualLink(self, nlm, ifList, enable):
        """
        Enables or disables the bridge port duallink attribute on a list of 
        interface names.
        """
        nlmsgs = bytes()
        duallink = 1 if enable else 0
        protinfo = {
            nlmanager.nlpacket.Link.IFLA_BRPORT_DUAL_LINK : duallink
        }
        for ifName in ifList:
            debug = nlmanager.nlpacket.RTM_SETLINK in nlm.debug
            self.ifInfoLock.acquire()
            if_idx = self.ifNameToIndex.get(ifName, 0)
            self.ifInfoLock.release()

            link = nlmanager.nlpacket.Link(nlmanager.nlpacket.RTM_SETLINK, debug, Log.logger, Log.getHandlerStr() != "syslog")
            link.flags = nlmanager.nlpacket.NLM_F_REQUEST
            link.family = socket.AF_BRIDGE
            link.body = struct.pack('=BxxxiLL', socket.AF_BRIDGE, if_idx, 0, 0)
            link.add_attribute(nlmanager.nlpacket.Link.IFLA_PROTINFO | nlmanager.nlpacket.NLA_F_NESTED, protinfo)
            link.build_message(next(nlm.sequence), nlm.pid)
            Log.log_debug(Log._LOG_DEBUG_INTF, "(%s): Setting duallink to %d" % (ifName, duallink))
            nlmsgs += link.message
        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)

    def SetBridgePeerLink(self, nlm, ifList, enable):
        """
        Enables or disables the bridge port peerlink attribute on a list of 
        interface names.
        """
        nlmsgs = bytes()
        peerlink = 1 if enable else 0
        protinfo = {
            nlmanager.nlpacket.Link.IFLA_BRPORT_PEER_LINK : peerlink
        }
        for ifName in ifList:
            debug = nlmanager.nlpacket.RTM_SETLINK in nlm.debug
            self.ifInfoLock.acquire()
            if_idx = self.ifNameToIndex.get(ifName, 0)
            self.ifInfoLock.release()

            link = nlmanager.nlpacket.Link(nlmanager.nlpacket.RTM_SETLINK, debug, Log.logger, Log.getHandlerStr() != "syslog")
            link.flags = nlmanager.nlpacket.NLM_F_REQUEST
            link.family = socket.AF_BRIDGE
            link.body = struct.pack('=BxxxiLL', socket.AF_BRIDGE, if_idx, 0, 0)
            link.add_attribute(nlmanager.nlpacket.Link.IFLA_PROTINFO | nlmanager.nlpacket.NLA_F_NESTED, protinfo)
            link.build_message(next(nlm.sequence), nlm.pid)
            Log.log_debug(Log._LOG_DEBUG_INTF, "(%s): Setting peerlink to %d" % (ifName, peerlink))
            nlmsgs += link.message
        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)

    def SetBridgeMulticastRouter(self, nlm, ifList, rtrType):
        """
        Enables or disables the bridge port multicast router attribute on a list of 
        interface names.
        """
        nlmsgs = bytes()
        protinfo = {
            nlmanager.nlpacket.Link.IFLA_BRPORT_MULTICAST_ROUTER : rtrType
        }
        for ifIdx in ifList:
            debug = nlmanager.nlpacket.RTM_SETLINK in nlm.debug

            link = nlmanager.nlpacket.Link(nlmanager.nlpacket.RTM_SETLINK, debug, Log.logger, Log.getHandlerStr() != "syslog")
            link.flags = nlmanager.nlpacket.NLM_F_REQUEST
            link.family = socket.AF_BRIDGE
            link.body = struct.pack('=BxxxiLL', socket.AF_BRIDGE, ifIdx, 0, 0)
            link.add_attribute(nlmanager.nlpacket.Link.IFLA_PROTINFO | nlmanager.nlpacket.NLA_F_NESTED, protinfo)
            link.build_message(next(nlm.sequence), nlm.pid)
            Log.log_debug(Log._LOG_DEBUG_INTF, "RtrPort Config to %d Type %d" % (ifIdx, rtrType))
            nlmsgs += link.message
        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)

    def SetBridgeLearning(self, nlm, ifList, enable):
        """
        Enables or disables the bridge port learning attribute on a list of 
        interface names.
        """
        nlmsgs = bytes()
        learning = 1 if enable else 0
        protinfo = {
            nlmanager.nlpacket.Link.IFLA_BRPORT_LEARNING : learning
        }
        for ifName in ifList:
            debug = nlmanager.nlpacket.RTM_SETLINK in nlm.debug
            self.ifInfoLock.acquire()
            if_idx = self.ifNameToIndex.get(ifName, 0)
            self.ifInfoLock.release()

            link = nlmanager.nlpacket.Link(nlmanager.nlpacket.RTM_SETLINK, debug, Log.logger, Log.getHandlerStr() != "syslog")
            link.flags = nlmanager.nlpacket.NLM_F_REQUEST
            link.family = socket.AF_BRIDGE
            link.body = struct.pack('=BxxxiLL', socket.AF_BRIDGE, if_idx, 0, 0)
            link.add_attribute(nlmanager.nlpacket.Link.IFLA_PROTINFO | nlmanager.nlpacket.NLA_F_NESTED, protinfo)
            link.build_message(next(nlm.sequence), nlm.pid)
            Log.log_debug(Log._LOG_DEBUG_INTF, "(%s): Setting learning to %d" % (ifName, learning))
            nlmsgs += link.message
        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)

    def SetBridgeBackupPort(self, nlm, ifList, enable):
        """
        Enables (sets to the peerlink idx) or disables (sets to 0) the bridge
        port backup_port attribute on a list of interface names.
        """
        if not Parser.isRedirectEnabled():
            return

        peerlinkBrMap = self.GetPeerlinksOfBridgeMap()
        peerlink_default = self.GetMasterOfSubIf(Parser.args.peerIf)
        nlmsgs = bytes()

        for ifName in ifList:
            br = self.GetBridgeOfMember(ifName)
            if br is not None:
                peerlink = peerlinkBrMap.get(br, peerlink_default)
            else:
                peerlink = peerlink_default

            if not self.GetIfIndex(peerlink):
                Log.log_warn("Cannot get ifindex for peerlink interface")
                return
            backup_port = self.GetIfIndex(peerlink) if enable else 0
            protinfo = {
                nlmanager.nlpacket.Link.IFLA_BRPORT_BACKUP_PORT : backup_port
            }

            debug = nlmanager.nlpacket.RTM_SETLINK in nlm.debug
            self.ifInfoLock.acquire()
            if_idx = self.ifNameToIndex.get(ifName, 0)
            self.ifInfoLock.release()

            link = nlmanager.nlpacket.Link(nlmanager.nlpacket.RTM_SETLINK, debug, Log.logger, Log.getHandlerStr() != "syslog")
            link.flags = nlmanager.nlpacket.NLM_F_REQUEST
            link.family = socket.AF_BRIDGE
            link.body = struct.pack('=BxxxiLL', socket.AF_BRIDGE, if_idx, 0, 0)
            link.add_attribute(nlmanager.nlpacket.Link.IFLA_PROTINFO | nlmanager.nlpacket.NLA_F_NESTED, protinfo)
            link.build_message(next(nlm.sequence), nlm.pid)
            Log.log_debug(Log._LOG_DEBUG_INTF, "(%s): Setting backup port to %d (%s)" % (ifName, backup_port, peerlink))
            nlmsgs += link.message
        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)

    def DelBridgeVlan(self, nlm, if_idx, vlanInfo):
        vlan_info_flags = 0
        ifname = self.GetIfName(if_idx)

        if not (Parser and Parser.isClagBond(ifname)):
            # RM-3035716: Currently this is implemented to force a RTM_DELVLAN only for clag bonds
            # to overcome a timing issue where the vlans are suspended on the clag bond due to
            # conflict with peerlink AFTER the vlans are deleted on the clag bond. This creates
            # a stale state on the bond which needs to be cleared to get the nlm notifications when
            # the vlans are added back
            return

        debug = nlmanager.nlpacket.RTM_DELVLAN in nlm.debug
        vlan = nlmanager.nlpacket.Vlan(nlmanager.nlpacket.RTM_DELVLAN, debug, Log.logger, Log.getHandlerStr() != "syslog")
        vlan.family = socket.AF_BRIDGE
        vlan.flags = nlmanager.nlpacket.NLM_F_REQUEST
        vlan.body = struct.pack('=Bxxxi', socket.AF_BRIDGE, if_idx)
        vlan.add_attribute(nlmanager.nlpacket.Vlan.BRIDGE_VLANDB_ENTRY, vlanInfo)

        vlan.build_message(next(nlm.sequence), nlm.pid)
        Log.log_debug(Log._LOG_DEBUG_INTF, "({},{}): Deleting vlanInfo {}".format(ifname, if_idx, vlanInfo))
        return nlm.tx_nlpacket_raw(vlan.message)

    def SetBridgeVlanState(self, nlm, if_idx, vid, state):
        ifname = self.GetIfName(if_idx)
        stp_suspend = 1 if state == 4 else 0
        debug = nlmanager.nlpacket.RTM_NEWVLAN in nlm.debug
        vlan = nlmanager.nlpacket.Vlan(nlmanager.nlpacket.RTM_NEWVLAN, debug, Log.logger, Log.getHandlerStr() != "syslog")
        vlan.family = socket.AF_BRIDGE
        vlan.flags = nlmanager.nlpacket.NLM_F_REQUEST
        vlan_info_flags = nlmanager.nlpacket.Link.BRIDGE_VLAN_INFO_ONLY_OPTS
        vlan_info = {
            nlmanager.nlpacket.Vlan.BRIDGE_VLANDB_ENTRY_STATE : state,
            nlmanager.nlpacket.Vlan.BRIDGE_VLANDB_ENTRY_STP_SUSPEND : stp_suspend,
            nlmanager.nlpacket.Vlan.BRIDGE_VLANDB_ENTRY_INFO : (vlan_info_flags, vid)
        }
        vlan.body = struct.pack('=Bxxxi', socket.AF_BRIDGE, if_idx)
        vlan.add_attribute(nlmanager.nlpacket.Vlan.BRIDGE_VLANDB_ENTRY, vlan_info)

        vlan.build_message(next(nlm.sequence), nlm.pid)
        Log.log_debug(Log._LOG_DEBUG_INTF, "(%s,%d): Setting vlan %d state to %d" % (ifname, if_idx, vid, state))
        return nlm.tx_nlpacket_raw(vlan.message)

    #
    #   VLAN Subinterface utility routines
    #
    def isSubIfName(self, subInterfaceName):
        '''
        Determine if the argument passed in is the name of a VLAN subinterface.
        '''
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(subInterfaceName)
        info = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        isSubIf = info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "vlan"
        self.ifInfoLock.release()
        return isSubIf

    def GetMasterOfSubIf(self, subInterfaceName):
        '''
        Returns the name of the interface which is the "master" of the given
        VLAN subinterface
        '''
        ifName = None
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(subInterfaceName)
        info = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "vlan":
            master = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_LINK)
            ifName = self.ifInfoByIndex.get(master, {}).get(nlmanager.nlpacket.Link.IFLA_IFNAME)
        self.ifInfoLock.release()
        return ifName

    def GetLogicalMastersOfSubIf(self, subInterfaceName):
        '''
        Returns the name of the interface which is the "master" of the given
        VLAN subinterface. If the VLAN subinterface is from a bridge, then
        the bridge members with the same VLAN membership are returned.
        '''
        masterIfs = []
        directMaster = self.GetMasterOfSubIf(subInterfaceName)
        if directMaster:
            masterIfs = [directMaster]
            if self.isBridgeName(directMaster):
                vlanId = self.GetVlanIdOfSubIf(subInterfaceName)
                brifs = set(self.GetMembersOfBridge(directMaster))
                vlanifs = set(self.GetMembersOfVlan(vlanId))
                masterIfs = [i for i in brifs & vlanifs]
        return masterIfs

    def GetVlanIdOfSubIf(self, subInterfaceName):
        '''
        Returns the VLAN ID of the VLAN subinterface
        '''
        vlanId = None
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(subInterfaceName)
        info = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "vlan":
            data = info.get(nlmanager.nlpacket.Link.IFLA_INFO_DATA, {})
            vlanId = data.get(nlmanager.nlpacket.Link.IFLA_VLAN_ID)
        self.ifInfoLock.release()
        return vlanId

    def GetSubIfBasedOnVlanId(self, vlanId):
        '''
        Returns the name of the VLAN subinterface for a specific VLAN ID
        '''
        self.ifInfoLock.acquire()
        if_name = self.vlan_id_to_ifname.get(vlanId, None)
        self.ifInfoLock.release()
        return if_name

    def GetSubIfsOfMaster(self, masterIf):
        '''
        Returns a list of VLAN sub-interfaces of the supplied interface
        '''
        subIfs = []
        self.ifInfoLock.acquire()
        masterIdx = self.ifNameToIndex.get(masterIf)
        if masterIdx is not None:
            for ifDict in list(self.ifInfoByIndex.values()):
                if masterIdx == ifDict.get(nlmanager.nlpacket.Link.IFLA_LINK):
                    ifName = ifDict.get(nlmanager.nlpacket.Link.IFLA_IFNAME)
                    if ifName is not None:
                        subIfs.append(ifName)
        self.ifInfoLock.release()
        return subIfs

    def GetSubIfsVlanMap(self):
        '''
        Returns a dictionary which allows mapping of VLAN sub-interface name
        (the key) to the raw interface name and VLAN ID (the data, a tuple).
        '''
        vlanMap = {}
        self.ifInfoLock.acquire()
        for ifDict in list(self.ifInfoByIndex.values()):
            ifName = ifDict.get(nlmanager.nlpacket.Link.IFLA_IFNAME)
            if ifName is not None:
                info = ifDict.get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
                if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "vlan":
                    data = info.get(nlmanager.nlpacket.Link.IFLA_INFO_DATA, {})
                    vlanId = data.get(nlmanager.nlpacket.Link.IFLA_VLAN_ID)
                    linkIdx = ifDict.get(nlmanager.nlpacket.Link.IFLA_LINK)
                    linkName = self.ifInfoByIndex.get(linkIdx,{}).get(nlmanager.nlpacket.Link.IFLA_IFNAME)
                    if vlanId is not None and linkName is not None:
                        vlanMap[ifName] = (linkName, vlanId)
        self.ifInfoLock.release()
        return vlanMap

    def GetIntfVlanMap(self, intf):
        '''
        Returns a list of 128 32-bit integers which are bitmaps of the VLANs
        enabled on an interface. It first tries to get the vlan map of the
        interface in vlan-aware topo. If it is empty, then it tries to get
        from vlan-unaware topo
        '''
        masterIfVlanMap = self.GetMasterIfVlanMap()
        vlanMap = self.GetIfVlanMap(intf)
        if len(vlanMap) == 0:
            vlanMap = [0x00000000] * 128
            for vlanId in masterIfVlanMap.get(intf, {}):
                vlanMap[vlanId // 32] |= 1 << (vlanId % 32)
        return vlanMap

    def GetMasterIfVlanMap(self):
        '''
        Returns a dictionary which allows mapping of a master interface name
        (the key) to a dictionary of VLAN sub-interfaces on that interface (the
        data). That dictionary contains VLAN IDs (the key) and VLAN sub-interface
        names (the data).
        '''
        vlanMap = {}
        self.ifInfoLock.acquire()
        for ifDict in list(self.ifInfoByIndex.values()):
            ifName = ifDict.get(nlmanager.nlpacket.Link.IFLA_IFNAME)
            if ifName is not None:
                info = ifDict.get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
                if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "vlan":
                    data = info.get(nlmanager.nlpacket.Link.IFLA_INFO_DATA, {})
                    vlanId = data.get(nlmanager.nlpacket.Link.IFLA_VLAN_ID)
                    linkIdx = ifDict.get(nlmanager.nlpacket.Link.IFLA_LINK)
                    linkName = self.ifInfoByIndex.get(linkIdx,{}).get(nlmanager.nlpacket.Link.IFLA_IFNAME)
                    if vlanId is not None and linkName is not None:
                        if linkName not in vlanMap:
                            vlanMap[linkName] = {}
                        vlanMap[linkName][vlanId] = ifName
        self.ifInfoLock.release()
        return vlanMap

    def GetIfVlanMap(self, intf, vlan_info = None):
        '''
        Returns a list of 128 32-bit integers which are bitmaps of the VLANs 
        enabled on an interface, or the empty list [] if the new bridge driver
        is not being used.
        '''
        vlanMapList = []
        if not vlan_info:
            self.ifInfoLock.acquire()
            idx = self.ifNameToIndex.get(intf)
            afspec = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_AF_SPEC, {})
            vlanInfo = afspec.get(nlmanager.nlpacket.Link.IFLA_BRIDGE_VLAN_INFO, [])
        else:
            vlanInfo = vlan_info
        startId = None
        for (flags, vlanId) in vlanInfo:
            if not vlanMapList:
                vlanMapList = [0x00000000] * 128
            if flags & nlmanager.nlpacket.Link.BRIDGE_VLAN_INFO_RANGE_BEGIN:
                startId = vlanId
            elif flags & nlmanager.nlpacket.Link.BRIDGE_VLAN_INFO_RANGE_END:
                if startId is not None:
                    for vid in range(startId, vlanId+1):
                        vlanMapList[vid // 32] |= 1 << (vid % 32)
                    startId = None
            else:
                vlanMapList[vlanId // 32] |= 1 << (vlanId % 32)
        if not vlan_info:
            self.ifInfoLock.release()
        return vlanMapList

    def GetMembersOfVlan(self, vlanId):
        '''
        Returns a list of interface names which are members of the specified
        VLAN ID, or the empty list if the new bridge driver is not being used.
        '''
        ifs = []
        self.ifInfoLock.acquire()
        for ifDict in list(self.ifInfoByIndex.values()):
            ifName = ifDict.get(nlmanager.nlpacket.Link.IFLA_IFNAME)
            if ifName is not None:
                afspec = ifDict.get(nlmanager.nlpacket.Link.IFLA_AF_SPEC, {})
                vlanInfo = afspec.get(nlmanager.nlpacket.Link.IFLA_BRIDGE_VLAN_INFO, [])
                startId = None
                for (flags, vid) in vlanInfo:
                    if flags & nlmanager.nlpacket.Link.BRIDGE_VLAN_INFO_RANGE_BEGIN:
                        startId = vid
                    elif flags & nlmanager.nlpacket.Link.BRIDGE_VLAN_INFO_RANGE_END:
                        if startId is not None:
                            if startId <= vlanId <= vid:
                                ifs.append(ifName)
                                break
                            startId = None
                    else:
                        if vid == vlanId:
                            ifs.append(ifName)
                            break
        self.ifInfoLock.release()
        return ifs

    def GetVxlanVniDB(self):
        if VxLanSync:
            return VxLanSync.GetOurVniDB()
        return None

    def GetVniToVlanMap(self, vni):
        if VxLanSync:
            vniDB = VxLanSync.GetOurVniDB().get(vni)
            if vniDB:
                return vniDB[VxLanSync.VNI_DB_VLAN_ID]
        return None

    def GetLocalIfBasedOnPeerVlanId(self, vlanId, brPeerVlanMap=None):
        if brPeerVlanMap is None:
            brPeerVlanMap = self.GetBridgePeerVlanMap()

        # assuming vlan unaware bridge
        if brPeerVlanMap != {}:
            for bif, vid in list(brPeerVlanMap.items()):
                if vid == vlanId:
                    return bif

        # assuming vlan aware bridge
        return self.GetSubIfBasedOnVlanId(vlanId)

    #
    #   Bond utility routines
    #
    def isBondName(self, bondName):
        '''
        Determine if the argument passed in is the name of a bond
        '''
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bondName)
        info = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        isBond = info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bond"
        self.ifInfoLock.release()
        return isBond

    def isBondMember(self, bondMemberName):
        '''
        Determines if the specified interface is a member of a bond.
        '''
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bondMemberName)
        master = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_MASTER)
        info = self.ifInfoByIndex.get(master, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        isBondMem = info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bond"
        self.ifInfoLock.release()
        return isBondMem

    def GetBondOfMember(self, bondMemberName):
        '''
        Returns the name of the bond of which the specified interface is a member.
        '''
        bond = None
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(bondMemberName)
        master = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_MASTER)
        info = self.ifInfoByIndex.get(master, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bond":
            bond = self.ifInfoByIndex.get(master, {}).get(nlmanager.nlpacket.Link.IFLA_IFNAME)
        self.ifInfoLock.release()
        return bond

    def GetMembersOfBond(self, bondName):
        '''
        Returns a list of the interfaces which are members fo the specified bond.
        '''
        bondMembers = []
        self.ifInfoLock.acquire()
        bondIdx = self.ifNameToIndex.get(bondName)
        info = self.ifInfoByIndex.get(bondIdx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bond":
            for ifDict in list(self.ifInfoByIndex.values()):
                if ifDict.get(nlmanager.nlpacket.Link.IFLA_MASTER) == bondIdx:
                    ifName = ifDict.get(nlmanager.nlpacket.Link.IFLA_IFNAME)
                    if ifName is not None:
                        bondMembers.append(ifName)
        self.ifInfoLock.release()
        return bondMembers

    def GetBondMode(self, bondName):
        '''
        Returns the numeric mode of the bond, or None.
        '''
        bondMode = None
        self.ifInfoLock.acquire()
        bondIdx = self.ifNameToIndex.get(bondName)
        info = self.ifInfoByIndex.get(bondIdx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bond":
            data = info.get(nlmanager.nlpacket.Link.IFLA_INFO_DATA, {})
            bondMode = data.get(nlmanager.nlpacket.Link.IFLA_BOND_MODE)
        self.ifInfoLock.release()
        return bondMode

    def GetIntfBondNames(self, intfName):
        '''
        Given the name of an interface, returns the name of the bond on that
        interface, or [] if there is no bond. If the interface is a bond,
        then simply return that name. If the interface is a bond member then
        return the interface's "master". And if the interface is a VLAN sub-
        interface of a bond, then return the master of the subinterface. And
        if the interface is a VLAN sub-interface of a bridge, then return all
        bonds in the bridge which are part of the same VLAN.
        '''
        bondNames = []
        if self.isBondName(intfName):
            bondNames = [intfName]
        elif self.isBondMember(intfName):
            bondNames = [self.GetBondOfMember(intfName)]
        elif self.isSubIfName(intfName) and self.isBondName(self.GetMasterOfSubIf(intfName)):
            bondNames = [self.GetMasterOfSubIf(intfName)]
        elif self.isSubIfName(intfName) and self.isBridgeName(self.GetMasterOfSubIf(intfName)):
            vlanId = self.GetVlanIdOfSubIf(intfName)
            brName = self.GetMasterOfSubIf(intfName)
            brifs = set(self.GetMembersOfBridge(brName))
            vlanifs = set(self.GetMembersOfVlan(vlanId))
            bondNames = [i for i in brifs & vlanifs if self.isBondName(i)]
        return bondNames

    def GetAllBonds(self, excludeIfs=[]):
        bonds = []
        self.ifInfoLock.acquire()
        for ifDict in list(self.ifInfoByIndex.values()):
            info = ifDict.get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
            if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bond":
                bondName = ifDict.get(nlmanager.nlpacket.Link.IFLA_IFNAME)
                if bondName and bondName not in excludeIfs:
                    bonds.append(bondName)
        self.ifInfoLock.release()
        return bonds

    def GetAllClagBonds(self, excludeIfs=[]):
        bonds = self.GetAllBonds(excludeIfs)
        clagBonds = [bond for bond in bonds if Parser.GetBondClagId(bond) or Parser.args.forceDynamic]
        return clagBonds

    def SetBondSysMac(self, nlm, sys_mac, bondName, dupChecks=False):
        '''
        Sets the ad_actor_system value for a bond
        '''
        debug = nlmanager.nlpacket.RTM_NEWLINK in nlm.debug
        self.ifInfoLock.acquire()
        if_idx = self.ifNameToIndex.get(bondName)
        self.ifInfoLock.release()

        if if_idx is not None and self.GetBondMode(bondName) == 4:
            if sys_mac == "00:00:00:00:00:00":
                sys_mac = self.GetIfMac(bondName)
            initState = self.GetLinkAdminState(bondName)
            if initState:
                self.SetLinkAdminState(nlm, bondName, 0)

            link = nlmanager.nlpacket.Link(nlmanager.nlpacket.RTM_NEWLINK, debug, Log.logger, Log.getHandlerStr() != "syslog")
            link.flags = nlmanager.nlpacket.NLM_F_REQUEST | nlmanager.nlpacket.NLM_F_ACK
            link.body = struct.pack('=Bxxxiii', socket.AF_UNSPEC, if_idx, 0, 0)
            info = {
                nlmanager.nlpacket.Link.IFLA_INFO_KIND : "bond",
                nlmanager.nlpacket.Link.IFLA_INFO_DATA : {
                    nlmanager.nlpacket.Link.IFLA_BOND_AD_ACTOR_SYSTEM : sys_mac
                }
            }
            link.add_attribute(nlmanager.nlpacket.Link.IFLA_LINKINFO, info)
            link.build_message(next(nlm.sequence), nlm.pid)
            Log.log("(%s): Setting ad_actor_system to %s" % (bondName, sys_mac))
            #retry on exception
            tries = 2
            for i in range(tries):
                try:
                    nlm.tx_nlpacket_get_response(link)
                except (nlmanager.nlmanager.NetlinkError, OSError) as error:
                    if i < tries - 1:
                        continue
                    else:
                        Log.log_error("(%s): Failed to set ad_actor_system to %s with reason %s" % (bondName, sys_mac, str(error)))

            if initState:
                self.SetLinkAdminState(nlm, bondName, 1)

    def SetBondsSysMac(self, nlm, sys_mac, bonds, excludeIfs=[]):
        for bond in bonds:
            if bond not in excludeIfs:
                self.SetBondSysMac(nlm, sys_mac, bond)

    def SetAllClagBondsSysMac(self, nlm, sys_mac, excludeIfs=[]):
        global system_mac
        bonds = self.GetAllClagBonds(excludeIfs)
        if sys_mac != system_mac:
            self.SetBondsSysMac(nlm, sys_mac, bonds, excludeIfs)
            system_mac = sys_mac

    def GetBondSysMac(self, bondName):
        '''
        Returns the ad_actor_system of a bond
        '''
        sysMac = None
        self.ifInfoLock.acquire()
        bondIdx = self.ifNameToIndex.get(bondName)
        info = self.ifInfoByIndex.get(bondIdx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        if info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND) == "bond":
            data = info.get(nlmanager.nlpacket.Link.IFLA_INFO_DATA, {})
            sysMac = data.get(nlmanager.nlpacket.Link.IFLA_BOND_AD_ACTOR_SYSTEM)
        self.ifInfoLock.release()
        return sysMac

    def GetBondPartnerMac(self, bondName):
        '''
        Returns the ad_partner_mac of a bond.
        '''
        partnerMac = None
        partner_mac_file = _InterfacePath + str(bondName) + "/bonding/ad_partner_mac"
        try:
            with open(partner_mac_file) as f:
                partnerMac = f.readline().strip()
        except IOError:
            partnerMac = None
        return partnerMac

    #
    #   VxLan Interface routines
    #
    def SetVxLanLocalIp(self, nlm, vxlanInfo):
        """
        Set the local IP attribute for a list of VxLAN interfaces. The vxlanInfo
        parameter is a list of tuples. Each tuple contains two items: the name
        of the VxLAN interface and the local IP address.
        """
        nlmsgs = bytes()
        for vxlanName, localIp in vxlanInfo:
            if localIp == 'None':
                Log.log_error("Interface %s: No local Tunnel IP address configured" % (vxlanName, ))
            else:
                debug = nlmanager.nlpacket.RTM_NEWLINK in nlm.debug
                self.ifInfoLock.acquire()
                if_idx = self.ifNameToIndex.get(vxlanName, 0)
                self.ifInfoLock.release()

                ifInfo = {
                    nlmanager.nlpacket.Link.IFLA_INFO_KIND : "vxlan",
                    nlmanager.nlpacket.Link.IFLA_INFO_DATA : {
                        nlmanager.nlpacket.Link.IFLA_VXLAN_LOCAL : ipaddress.ip_address(localIp)
                    }
                }
                link = nlmanager.nlpacket.Link(nlmanager.nlpacket.RTM_NEWLINK, debug, Log.logger, Log.getHandlerStr() != "syslog")
                link.flags = nlmanager.nlpacket.NLM_F_REQUEST
                link.body = struct.pack('=BxxxiLL', socket.AF_UNSPEC, if_idx, 0, 0)
                link.add_attribute(nlmanager.nlpacket.Link.IFLA_LINKINFO, ifInfo)
                link.build_message(next(nlm.sequence), nlm.pid)
                Log.log("(%s): Setting localIp to %s" % (vxlanName, localIp))
                nlmsgs += link.message
        if nlmsgs:
            nlm.tx_nlpacket_raw(nlmsgs)

    def GetVxlanLocalIp(self, vxlanIntf):
        Intf.ifInfoLock.acquire()
        idx = Intf.ifNameToIndex.get(vxlanIntf)
        ifDict = Intf.ifInfoByIndex.get(idx, {})
        ifInfo = ifDict.get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        data = ifInfo.get(nlmanager.nlpacket.Link.IFLA_INFO_DATA, {})
        localip = data.get(nlmanager.nlpacket.Link.IFLA_VXLAN_LOCAL)
        Intf.ifInfoLock.release()
        return localip

    #
    #   Dual Connected Interfaces - Sort of specific to CLAG
    #
    def GetDualIfs(self):
        self.dualIfsLock.acquire()
        dualIfs = copy.deepcopy(self.dualIfs)
        self.dualIfsLock.release()
        return dualIfs

    def SetDualIfs(self, dualIfs):
        self.dualIfsLock.acquire()
        self.dualIfs = copy.deepcopy(dualIfs)
        self.dualIfsLock.release()

    def AddDualIfs(self, dualIfs):
        self.dualIfsLock.acquire()
        self.dualIfs.update(dualIfs)
        self.dualIfsLock.release()

    def DelDualIfs(self, dualIfs):
        self.dualIfsLock.acquire()
        for dualIf in dualIfs:
            self.dualIfs.pop(dualIf, None)
        self.dualIfsLock.release()

    def isDualIf(self, intf):
        self.dualIfsLock.acquire()
        isDual = intf in self.dualIfs
        self.dualIfsLock.release()
        return isDual

    # Get the corresponding clag peer interface name
    def getDualIfName(self, intfName):
        return self.GetDualIfs().get(intfName, None)

    # Get the clag local interface name corresponding to peer interface
    def getLocalDualIfName(self, peerIntfName):
        dualIfs = self.GetDualIfs()
        revDualIfs = { v:k for k,v in list(dualIfs.items()) }
        return revDualIfs.get(peerIntfName, None)

    # ClagId matching Interfaces - dict of interfaces with matching clagId or
    # vni (doesn't include op-state or partner-mac comparison)
    def GetClagIdMatchIfs(self):
        self.dualIfsLock.acquire()
        clagIdMatchIfs =\
               {iface:self.clagIdMatchIfs[iface][self.CLAG_ID_MATCHIF_IDX_RIF]\
                for iface in self.clagIdMatchIfs}
        self.dualIfsLock.release()
        return clagIdMatchIfs

    def GetClagIdMatchInfo(self):
        self.dualIfsLock.acquire()
        clagIdMatchIfs = copy.deepcopy(self.clagIdMatchIfs)
        self.dualIfsLock.release()
        return clagIdMatchIfs

    def AddClagIdMatchIfs(self, clagIdMatchIfs):
        self.dualIfsLock.acquire()
        self.clagIdMatchIfs.update(clagIdMatchIfs)
        self.dualIfsLock.release()

    def DelClagIdMatchIfs(self, clagIdMatchIfs):
        self.dualIfsLock.acquire()
        for clagIf in clagIdMatchIfs:
            self.clagIdMatchIfs.pop(clagIf, None)
        self.dualIfsLock.release()

    def SetPeerAlive(self, nlm, alive):
        self.protoDownLock.acquire()
        if Parser.args.initDelay and HealthCheck and not HealthCheck.initState and not \
           HealthCheck.peerInitState  and \
            (Parser.clagRole == Parser._ROLE_SECONDARY and HealthCheck.GetBackupActive() and \
            HealthCheck.IsBackupRoleAvailable() and HealthCheck.GetBackupRole() == "secondary") \
            and not Parser.sendGoodbye:
            if alive:
                HealthCheck.initDelayEvent.clear()
                HealthCheck.initDelayTimerStartEvent.set()
            else:
                if not self.allClagBondDownEvent:
                    Log.log("Setting Wait on All Clag Bonds Down Event")
                    self.waitAllBondDownEvent.set()
        self.peerAlive = alive
        self.SetProtoPeerOrBackupState(nlm)
        self.protoDownLock.release()
        if self.peerAlive:
            if LacpSync:
                LacpSync.EvaluateAndUpdateConflicts()
        else:
            Parser.ResetClagConflicts() 
        Parser.NotifyPeerCommChange()

    def SetPeerDeathPendingEvent(self):
        self.peerDeathPendingEvent = True

    def ClearPeerDeathPendingEvent(self):
        self.peerDeathPendingEvent = False

    def GetPeerDeathPendingEvent(self):
        return self.peerDeathPendingEvent

    def SetPeerDeathPending(self, pending):
        if pending == True:
            Log.log("Peer link is down; checking if the peer switch is alive.")
        self.peerDeathPending = pending

    def GetPeerDeathPending(self):
        return self.peerDeathPending

    def ClearPeerDeathPending(self, nlm):
        if self.peerDeathPending:
            # Instead of triggering PeerIsNotAlive from here, just set the flag
            # and let the function to bring down peer be triggered from another
            # independent thread. peerDeathPending flag is also cleared after
            # peerlink is brought down
            self.SetPeerDeathPendingEvent()
            peerlinkEvent.set()

    def isPeerAlive(self):
        return self.peerAlive

    def isBackupActive(self):
        if HealthCheck and HealthCheck.GetBackupActive():
            return True
        return False

    def isMlagInSplitBrain(self):
        if (not self.isPeerAlive() and not self.isBackupActive()):
            return True
        return False

    #
    #   Other interface utility routines
    #
    def isPortName(self, portName):
        '''
        Determine if the argument passed in is the name is a "base" interface
        (not a bond, VLAN subinterface, bridge, etc...)
        '''
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(portName)
        info = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_LINKINFO, {})
        kind = info.get(nlmanager.nlpacket.Link.IFLA_INFO_KIND)
        self.ifInfoLock.release()

        return kind in ('bcm_knet', 'tuntap')

    def isIfName(self, ifName):
        '''
        Determine if the argument passed in is the name of an interface
        '''
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(ifName)
        self.ifInfoLock.release()
        return idx is not None

    def AnycastIpAddOk(self):
        # If HealthCheck is not yet initialized and 
        # init Delay is configured then do not add anycast Ip
        if Parser.args.initDelay and not HealthCheck:
            return False
        if HealthCheck and HealthCheck.initDelayBringup():
            self.anycastIpAddDelayed = True
            return False
        return True

    def SetInitDelayTimeoutEvents(self):
        # Api to handle any required events when init Delay 
        # Expires
        # Recalculate the mac ownership
        FdbSync.ownershipUpdCount = 0
        FdbSync.ownershipUpdDone = False

    def AddIpAddress(self, nlm, ifName, ipAddressStr, prefix):
        """
        Add an IP address to an interface
        """
        debug = nlmanager.nlpacket.RTM_NEWADDR in nlm.debug
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(ifName)
        self.ifInfoLock.release()
        if ipAddressStr:
            ipAddress = ipaddress.ip_address(ipAddressStr)

        if idx is not None:
            addr = nlmanager.nlpacket.Address(nlmanager.nlpacket.RTM_NEWADDR, debug, Log.logger, Log.getHandlerStr() != "syslog")
            addr.flags = nlmanager.nlpacket.NLM_F_CREATE | nlmanager.nlpacket.NLM_F_REPLACE | nlmanager.nlpacket.NLM_F_REQUEST
            addr.family = socket.AF_INET
            addr.body = struct.pack('=4Bi', socket.AF_INET, prefix, 0, 0, idx)
            addr.add_attribute(nlmanager.nlpacket.Address.IFA_LOCAL, ipAddress)
            addr.add_attribute(nlmanager.nlpacket.Address.IFA_ADDRESS, ipAddress)
            addr.build_message(next(nlm.sequence), nlm.pid)
            Log.log("(%s): Adding IP address %s/%d" % (ifName, ipAddress, prefix))
            nlm.tx_nlpacket_raw(addr.message)

    def DelIpAddress(self, nlm, ifName, ipAddressStr, prefix):
        """
        Remove an IP address from an interface
        """
        debug = nlmanager.nlpacket.RTM_DELADDR in nlm.debug
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(ifName)
        self.ifInfoLock.release()
        if ipAddressStr:
            ipAddress = ipaddress.ip_address(ipAddressStr)

        if idx is not None:
            addr = nlmanager.nlpacket.Address(nlmanager.nlpacket.RTM_DELADDR, debug, Log.logger, Log.getHandlerStr() != "syslog")
            addr.flags = nlmanager.nlpacket.NLM_F_REQUEST
            addr.family = socket.AF_INET
            addr.body = struct.pack('=4Bi', socket.AF_INET, prefix, 0, 0, idx)
            addr.add_attribute(nlmanager.nlpacket.Address.IFA_LOCAL, ipAddress)
            addr.add_attribute(nlmanager.nlpacket.Address.IFA_ADDRESS, ipAddress)
            addr.build_message(next(nlm.sequence), nlm.pid)
            Log.log("(%s): Deleting IP address %s/%d" % (ifName, ipAddress, prefix))
            nlm.tx_nlpacket_raw(addr.message)

    def GetIpAddress(self, ifName):
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifreq = struct.pack('16sH14s', ifName, socket.AF_INET, '\x00'*14)
        try:
            res = fcntl.ioctl(s.fileno(), 0x8915, ifreq)
        except:
            return None
        ip = struct.unpack('16sH2x4s8x', res)[2]
        return socket.inet_ntoa(ip)

    def GetIpAddressInSameNet(self, ifName, ipAddr):
        v6Family = False
        try:
            ipAddrByte = subprocess.check_output([ "/bin/ip", "addr", "show", ifName ])
        except subprocess.CalledProcessError as e:
            ipAddrByte = e.output
        ipAddrStr = ipAddrByte.decode("utf-8")
        if ipAddr == 'linklocal' or (ipAddr.count(":") > 0):
            if ipAddr == 'linklocal': 
                ipAddr = 'fe80::0'
            v6Family = True
        for ifIpStr in re.findall(r'\s+inet6?\s+(\S+/\d+)\s+', ipAddrStr):
            if ipaddress.ip_address(ipAddr) in ipaddress.ip_network(ifIpStr, False):
                return str(ipaddress.IPv6Interface(ifIpStr).ip)  if v6Family else \
                            str(ipaddress.IPv4Interface(ifIpStr).ip)
        return None

    def GetBaseInterfaces(self, ifName):
        '''
        Given an interface, returns an unordered set of "base" interfaces which
        are controlled by that interface. A "base" interface is one which does
        not control other (lower) interfaces. Specifically:
           1. For bridges this means the "base" interface(s) of all bridge 
              member interface(s).
           2. For bonds, this means the "base" interface(s) of all enslaved
              interface(s).
           3. For VLAN sub-interfaces, this means the "base" interface(s) of
              the interface from which the VLAN was created.
           4. All other interfaces are base interfaces.
        '''
        if self.isBridgeName(ifName):
            baseIfs = set()
            for bintf in self.GetMembersOfBridge(ifName):
                baseIfs |= self.GetBaseInterfaces(bintf)
            return baseIfs
        elif self.isBondName(ifName):
            baseIfs = set()
            for bintf in self.GetMembersOfBond(ifName):
                baseIfs |= self.GetBaseInterfaces(bintf)
            return baseIfs
        elif self.isSubIfName(ifName):
            baseIfs = set()
            for bintf in self.GetLogicalMastersOfSubIf(ifName):
                baseIfs |= self.GetBaseInterfaces(bintf)
            return baseIfs
        else:
            return {ifName}

    def GetIfMac(self, ifName):
        '''
        Given an interface name, return the MAC address of that interface.
        '''
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(ifName)
        mac = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_ADDRESS)
        self.ifInfoLock.release()
        return mac

    def OperStateToStr(self, operstate):
        """
        Convert an operstate numeric value to a string
        """
        operstate_str = {
            nlmanager.nlpacket.Link.IF_OPER_UNKNOWN        : "unknown",
            nlmanager.nlpacket.Link.IF_OPER_NOTPRESENT     : "notpresent",
            nlmanager.nlpacket.Link.IF_OPER_DOWN           : "down",
            nlmanager.nlpacket.Link.IF_OPER_LOWERLAYERDOWN : "lowerlayerdown",
            nlmanager.nlpacket.Link.IF_OPER_TESTING        : "testing",
            nlmanager.nlpacket.Link.IF_OPER_DORMANT        : "dormant",
            nlmanager.nlpacket.Link.IF_OPER_UP             : "up"
        }
        return operstate_str.get(operstate, "unknown")

    def GetOperState(self, ifName):
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(ifName)
        operstate = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_OPERSTATE)
        self.ifInfoLock.release()
        operstatestr = self.OperStateToStr(operstate)
        if operstate is None:
            operstatestr = self.GetOperStateFromSysFs(ifName)
        return operstatestr

    def GetOperStateFromKCache(self, ifName):
        self.ifInfoLock.acquire()
        idx = self.ifNameToIndex.get(ifName)
        operstate = self.ifInfoByIndex.get(idx, {}).get(nlmanager.nlpacket.Link.IFLA_OPERSTATE)
        self.ifInfoLock.release()
        return self.OperStateToStr(operstate)

    def GetOperStateFromSysFs(self, ifName):
        '''
        Given an interface name, return the operational state
        '''
        if self.isIfName(ifName):
            try:
                for line in open(_InterfacePath + str(ifName) + "/operstate"):
                    return line.strip()
            except IOError:
                pass
        return "unknown"

    def SetOperStateUp(self, nlm, ifName):
        # we only change the oper state if it is dormant
        oldState = self.GetOperStateFromSysFs(ifName)
        if oldState == 'dormant':
            debug = nlmanager.nlpacket.RTM_NEWLINK in nlm.debug
            self.ifInfoLock.acquire()
            if_idx = self.ifNameToIndex.get(ifName)
            self.ifInfoLock.release()

            if if_idx is not None:
                link = nlmanager.nlpacket.Link(nlmanager.nlpacket.RTM_NEWLINK, debug, Log.logger, Log.getHandlerStr() != "syslog")
                link.flags = nlmanager.nlpacket.NLM_F_REQUEST
                link.body = struct.pack('=BxxxiLL', socket.AF_UNSPEC, if_idx, 0, 0)
                link.add_attribute(nlmanager.nlpacket.Link.IFLA_OPERSTATE, nlmanager.nlpacket.Link.IF_OPER_UP)
                link.build_message(next(nlm.sequence), nlm.pid)
                Log.log_debug(Log._LOG_DEBUG_INTF, "(%s): Setting operstate to up" % (ifName,))
                nlm.tx_nlpacket_raw(link.message)

    def GetOperSpeedFromSysFs(self, ifName):
        '''
        Given an interface name, return the operational speed
        '''
        if self.isIfName(ifName):
            try:
                for line in open(_InterfacePath + str(ifName) + "/speed"):
                    return line.strip()
            except:
                pass
        return 0

    def ReadyToRunClagConflictChecks(self):
        return self.isPeerAlive() and Parser.syncDoneFromPeer

    def PeerLinkChangeDelayedUpdates(self, nlm):
        Log.log("PeerLinkChange delayed updates")
        if not self.waitAllBondDownEvent.is_set():
            return
        self.waitAllBondDownEvent.clear()
        self.SetProtoPeerOrBackupState(nlm)
        if LacpSync:
            LacpSync.newLacpDataEvent.set()
        if VxLanSync:
            VxLanSync.newVxLanDataEvent.set()
        Parser.ListenersStateUpdate("down")

#-------------------------------------------------------------------------------
#
#   The init code
#
#-------------------------------------------------------------------------------

def ClagInit():
    # Create a log for our output
    global Log
    Log = Logger()

    ThreadMonitor = clag.threadmonitor.ThreadMonitor(Log)
    monitoring_thread = ThreadMonitor.start_monitoring(seconds_frozen=7, test_interval=100)

    Log.log("Beginning execution of clagd version %s" % (ClagParser._ClagVersion,))
    Log.log("Invoked with: " + " ".join(sys.argv))
    util.queueAddNameMap(peerSendQueue, 'peerSendQueue')

    # Create the daemon
    global Daemon
    Daemon = ClagDaemon()

    # Interface utility routines
    nlm = nlmanager.nlmanager.NetlinkManager(util.CLAG_NLM_INIT_ID)
    global Intf
    Intf = IntfSupport(nlm)

    # Global Consistency Check parameters
    global Globcc
    Globcc = GlobalCC()

    # Parse the command line parameters
    global Parser
    Parser = ClagParser()
    Parser.ParseCmdLine()
    Parser.DumpParameters()

    # Update the peerProtoBuf value with the passed arg value
    Parser.peerProtoBuf = Parser.args.protobuf

    global system_mac
    system_mac = Parser.args.sysMac

    # Make sure script is invoked with root privledges
    if os.getuid() != 0:
        Log.log_error("You must be root to run this command.")
        print("You must be root to run this command.")
        sys.exit(-1)

    if not Parser.args.backupIp:
        Log.log("Backup IP has not been configured")

def ClagReloadConfig(nlm):
    clagIdCfg, stpModeIsPvrst = reloadconfig.getClagIdConfig(Log)
    for bond in clagIdCfg:
        clagIdInfo = clagIdCfg[bond]
        Parser.setBondClagId(nlm, bond, clagIdInfo.get('clagId'))
        if Intf.GetOperState(bond) == nlmanager.nlpacket.Link.IFF_DORMANT:
            Intf.SetDormantBondOperState(nlm, bond)
    if StpSync:
        StpSync.bridgePvrst = stpModeIsPvrst
    Parser.setReloadDone()


#-------------------------------------------------------------------------------
#
#   The main runtime code
#
#-------------------------------------------------------------------------------

def ClagRun(nlm):
    try:
        if Parser.args.memTrace:
            Log.log("Starting Memory Tracer")
            global memTraceThreadT
            memTraceThreadT = threading.Thread(None, MemTraceT, "MemTraceT")
            memTraceThreadT.daemon = True
            memTraceThreadT.start()
    except:
        pass
    Daemon.start()

    # Perform operations based on the command line parameter settings
    if Parser.args.allowPartnerMacDup:
        Log.log_warn("Allowing duplicate LACP partner MACs")
    Log.log("Role is now %s" % (Parser.clagRole,))
    Log.setQuiet(Parser.args.quiet)
    Log.setDebug(Parser.args.debug)
    Log.setVerbose(Parser.args.verbose)
    Log.setHandler(Parser.args.log)

    if Parser.args.csuSupport:
        global csu_client
        csu_client = clagdcsu.CSUClient(Log, Intf, Parser)

    Parser.clagId[0] = Parser.args.priority
    Parser.clagId[1] = Intf.GetIfMac(Parser.args.peerIf)
    Parser.currLacpPoll = Parser.getCfgLacpPoll()
    ConfigurePeerLearning(nlm, True)

    Parser.GetPackageVersion()

    global Cmd
    global StpSync
    global FdbSync
    global LacpSync
    global VxLanSync
    global NeighSync
    global HealthCheck
    global ClagNetLink
    global MrouteSync
    global MdbSync
    global VlanSync
    global DataSyncs
    global exitEvent
    ClagNetLink = clag.clagnetlink.ClagNl(Daemon, Log, Intf, Parser)
    MrouteSync = clag.mroutesync.mroutesync(Daemon, Intf, Parser, Log, peerSendQueue)
    VlanSync = clag.vlansync.vlansync(Daemon, Intf, Parser, Globcc, Log, peerSendQueue)
    VxLanSync = clag.vxlansync.vxlansync(Daemon, Intf, Parser, Globcc, Log, MrouteSync, peerSendQueue, ClagNetLink)
    FdbSync  = clag.fdbsync.fdbsync(Daemon, Intf, Parser, Log, peerSendQueue, ClagNetLink, VxLanSync)
    MdbSync  = clag.mdbsync.mdbsync(Daemon, Intf, Parser, Log, peerSendQueue, ClagNetLink)
    if Parser.args.neighSync:
        NeighSync = clag.neighsync.neighsync(Daemon, Intf, Parser, Log, peerSendQueue, ClagNetLink)
    LacpSync = clag.lacpsync.lacpsync(Daemon, Intf, Parser, Log, peerSendQueue, FdbSync)
    StpSync = clag.stpsync.stpsync(Log, Parser, Intf, VlanSync, VxLanSync, Globcc, MrouteSync, peerSendQueue)
    DataSyncs = [LacpSync, VxLanSync, FdbSync, MdbSync, VlanSync, MrouteSync, StpSync]
    if NeighSync:
        DataSyncs.append(NeighSync)
    LacpSync.DataSyncs = DataSyncs
    VxLanSync.DataSyncs = DataSyncs

    exitEvent = clag.clagthread.Event()
    HealthCheck = clag.healthcheck.HealthCheck(Daemon, Parser, Log, Intf, VxLanSync, ClagNetLink)
    Cmd = clag.cmdsrv.cmdsrv(Daemon, Intf, Parser, Globcc, Log, LacpSync, VxLanSync, FdbSync, MdbSync, NeighSync, VlanSync, MrouteSync, HealthCheck, StpSync)
    LacpSync.Cmd = Cmd
    VxLanSync.Cmd = Cmd

    ClagReloadConfig(nlm)
    if Parser.isRedirectEnabled():
        ConfigureBridgeBackupPort(nlm, True)

    # Create the threads which do the work
    global Threads
    for threadName in Threads:
        Threads[threadName][1] = threading.Thread(None, Threads[threadName][0], threadName)
        Threads[threadName][1].daemon = True

    # Start the threads
    for threadName in Threads:
        Threads[threadName][1].start()

    # Tell systemd that we are initialized and ready
    clagSdnotify.sd_notify(0, bytes("READY=1", "utf-8"))

    # Take the day off
    while not exitEvent.wait(60*60*24):
        pass


#-------------------------------------------------------------------------------
#
#   Main program entry point
#
#-------------------------------------------------------------------------------

def main():
    status = 0
    try:
        nlm = nlmanager.nlmanager.NetlinkManager(util.CLAG_NLM_MAIN_ID)
        ClagRun(nlm)
    except RuntimeError as e:
        if Log:
            Log.log_error(str(e))
        else:
            print((str(e)))
        status = 2
    except KeyboardInterrupt:
        pass
    except Exception:
        (exc_type, exc_value, exc_traceback) = sys.exc_info()
        err = "".join(traceback.format_exception(exc_type, exc_value,
                                                 exc_traceback))
        if "release unlocked lock" not in err:
           if Log:
               Log.log_error("unhandled exception: %s" % (err,))
           else:
               print(("unhandled exception: %s" % (err,)))
           status = 3

    do_exit(nlm, status)


#-------------------------------------------------------------------------------
#
#   Are we being executed or imported?
#
#-------------------------------------------------------------------------------

if __name__ == '__main__':

    ClagInit()

    if Parser.args.daemon:
        if Daemon.isClagAlreadyRunning():
            Log.log_error("Unable to start clagd - clagd is already running.")
            sys.exit(-1) 
        try:
            with daemon.DaemonContext(
                    prevent_core = False,
                    signal_map={
                        signal.SIGHUP:  signal_handler,
                        signal.SIGTERM: signal_handler,
                        signal.SIGRTMIN: signal_handler,
                        signal.SIGRTMIN+1: signal_handler,
                        signal.SIGRTMIN+2: signal_handler,
                        signal.SIGRTMIN+3: signal_handler
                    },
                    umask = 0o022):
                main()
        except Exception as e:
            Log.log("Failed to create the daemon[%s]. Disabling daemon and starting clagd" % str(e))
    signal.signal(signal.SIGHUP, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)
    signal.signal(signal.SIGRTMIN, signal_handler)
    signal.signal(signal.SIGRTMIN+1, signal_handler)
    signal.signal(signal.SIGRTMIN+2, signal_handler)
    signal.signal(signal.SIGRTMIN+3, signal_handler)
    main()

