#!/usr/bin/python3

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

#
#   Import the necessary modules
#
try:
    import argparse
    import clag.clagdcmd
    import signal
    import sys
    import textwrap
    import json
except ImportError as e:
    raise ImportError (str(e) + "- required module not found")
except KeyboardInterrupt:
    exit(-1)

#
#   Define constants
#
_ClagCtlVersion         = "0.1.0"
_ClagCmdVersion         = "0.1.0"


#
#   Global Variables
#
Parser  = None
ClagCmd = None


#-------------------------------------------------------------------------------
#
#   Command Handlers
#
#-------------------------------------------------------------------------------

def HandleEcho():
    cmdStr = ClagCmd.run("echo " + " ".join(Parser.args.args))
    print(("clagd echoed: " + cmdStr))
    return 0

def HandleDebug():
    cmdStr = ClagCmd.run("debug " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleReloadDone():
    cmdStr = ClagCmd.run("reloaddone " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleBondClagId():
    cmdStr = ClagCmd.run("setclagid " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleSetAnycastIp():
    cmdStr = ClagCmd.run("setanycastip " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleSetBackupIp():
    cmdStr = ClagCmd.run("setbackupip " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleVerbose():
    cmdStr = ClagCmd.run("SetVerbose " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleQuiet():
    cmdStr = ClagCmd.run("SetQuiet " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleLogFile():
    cmdStr = ClagCmd.run("SetLogFile " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleLacpPoll():
    cmdStr = ClagCmd.run("SetLacpPoll " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandlePeerTimeout():
    cmdStr = ClagCmd.run("SetPeerTimeout " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleSendTimeout():
    cmdStr = ClagCmd.run("SetSendTimeout " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleSendBufSize():
    cmdStr = ClagCmd.run("SetSendBufSize " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleLinkPoll():
    cmdStr = ClagCmd.run("SetPeerLinkPoll " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandlePriority():
    cmdStr = ClagCmd.run("SetPriority " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandlePeerLacpRate():
    cmdStr = ClagCmd.run("GetPeerLacpRate")
    print(cmdStr)
    return 0

def HandleLogMessage():
    cmdStr = ClagCmd.run("logmsg " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleParams():
    cmdStr = ClagCmd.run("Params")
    if Parser.args.json:
        print(cmdStr)
        return 0

    try:
        params = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid output: %s\n" % e))
        return 0

    print(("clagVersion = %s" % (params.get('clagVersion'))))
    print(("clagDataVersion = %s" % (params.get('clagDataVersion'))))
    print(("clagCmdVersion = %s" % (params.get('clagCmdVersion'))))
    print(("peerIp = %s" % (params.get('peerIp'))))
    print(("peerIf = %s" % (params.get('peerIf'))))
    print(("sysMac = %s" % (params.get('sysMac'))))
    print(("lacpPoll = %d" % (params.get('lacpPoll', 0))))
    print(("currLacpPoll = %d" % (params.get('currLacpPoll', 0))))
    print(("peerConnect = %d" % (params.get('peerConnect', 0))))
    print(("cmdConnect = %d" % (params.get('cmdConnect', 0))))
    print(("peerLinkPoll = %d" % (params.get('peerLinkPoll', 0))))
    print(("switchdReadyTimeout = %d" % (params.get('switchdReadyTimeout', 0))))
    print(("reloadTimer = %d" % (params.get('reloadTimer', 0))))
    print(("periodicRun = %d" % (params.get('periodicRun', 0))))
    print(("priority = %d" % (params.get('priority', 0))))
    print(("quiet = %s" % (params.get('quiet'))))
    print(("debug = 0x%X" % (params.get('debug', 0))))
    print(("verbose = %s" % (params.get('verbose'))))
    print(("log = %s" % (params.get('log'))))
    print(("vm = %s" % (params.get('vm'))))
    print(("peerPort = %d" % (params.get('peerPort', 0))))
    print(("peerTimeout = %s" % (params.get('peerTimeout'))))
    print(("initDelay = %s" % (params.get('initDelay'))))
    print(("sendTimeout = %s" % (params.get('sendTimeout'))))
    print(("sendBufSize = %s" % (params.get('sendBufSize'))))
    print(("forceDynamic = %s" % (params.get('forceDynamic'))))
    print(("dormantDisable = %s" % (params.get('dormantDisable'))))
    print(("redirectEnable = %s" % (params.get('redirectEnable'))))
    print(("redirect2Enable = %s" % (params.get('redirect2Enable'))))
    print(("backupIp = %s" % (params.get('backupIp'))))
    print(("backupVrf = %s" % (params.get('backupVrf'))))
    print(("backupPort = %d" % (params.get('backupPort'))))
    print(("vxlanAnycast = %s" % (params.get('vxlanAnycast'))))
    print(("neighSync = %s" % (params.get('neighSync'))))
    print(("permanentMacSync = %s" % (params.get('permanentMacSync'))))
    print(("cmdLine = %s" % (" ".join(params.get('cmdLine')))))
    print(("peerlinkLearnEnable = %s" % (params.get('peerlinkLearnEnable'))))
    print(("backupFaultTolerance = %s" % (params.get('backupFaultTolerance'))))
    return 0

def HandleGarbage():
    cmdStr = ClagCmd.run("CollectGarbage")
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleCollectObjectCount():
    cmdStr = ClagCmd.run("CollectObject")
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleUncollectableObject():
    cmdStr = ClagCmd.run("CollectUnCollectableObject")
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def LacpInfoFlagsToStr(flags):
    flagStr = ""
    for flag in flags:
        if flag is "dual":
            flagStr += "D"
    if not flagStr:
        flagStr = "-"

def HandleClearConflictState():
    cmdStr = ClagCmd.run("ClearConflictState " +  " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def ProtoDownReasonsToStr(reasons):
    return ",".join(reasons) if reasons else "-"

def ClagIntfConflictsToStr(conflicts):
    return ",".join(conflicts) if conflicts else "-"

def HandleStatus():
    jsonOut = {}
    cmdStr = ClagCmd.run("GetSummary")
    try:
        status = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid status output: %s\n" % e))
        return 0

    if Parser.args.json:
        jsonOut['status'] = status
    else:
        ourRole = status.get('ourRole')
        if status.get('peerAlive', 0):
            print("The peer is alive")
            print(("     Our Priority, ID, and Role: %d %s %s" % (status.get('ourPriority', 0), status.get('ourId'), ourRole)))
            print(("    Peer Priority, ID, and Role: %d %s %s" % (status.get('peerPriority', 0), status.get('peerId'), status.get('peerRole'))))
        else:
            print("The peer is not alive")
            print(("     Our Priority, ID, and Role: %d %s %s" % (status.get('ourPriority', 0), status.get('ourId'), ourRole)))

        print(("          Peer Interface and IP: %s %s %s" % (status.get('peerIf'), status.get('peerIp'), "(linklocal)" if status.get('linklocal') else "")))
        if status.get('vxlanAnycast'):
            print(("               VxLAN Anycast IP: %s" % (status.get("vxlanAnycast"),)))
        backupVrf = status.get('backupVrf')
        backupIp = status.get('backupIp')
        backupReason = status.get('backupReason')
        backupActiveStr = 'active' if status.get('backupActive', 0) else 'inactive' if not backupReason else 'inactive: %s' % (backupReason,)
        if backupVrf:
            backupIp = '%s vrf %s' % (backupIp, backupVrf)
        print(("                      Backup IP: %s (%s)" % (backupIp, backupActiveStr)))
        print(("                     System MAC: %s" % (status.get("sysMac"),)))
        print()

    cmdStr = ClagCmd.run("GetClagIntfDB")
    try:
        intfs = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid clagIntfDB output: %s\n" % e))
        return 0

    if Parser.args.json:
        jsonOut['clagIntfs'] = intfs
    else:
        if intfs:
            print("CLAG Interfaces")
            print("Our Interface      Peer Interface     CLAG Id   Conflicts              Proto-Down Reason")
            print("----------------   ----------------   -------   --------------------   -----------------")
            # This is the length of the string before the Conflicts column
            leadStr=48*" "
            for intf in intfs:
                intfInfo = intfs[intf]
                conflictStr = ClagIntfConflictsToStr(intfInfo.get('conflicts', []))
                # limit the conflict reason display to 15 characters per-line
                conflictStr = textwrap.wrap(conflictStr, 20)
                peerIf = intfInfo.get('peerIf')
                if not peerIf:
                    peerIf = '-'
                clagId = intfInfo.get('clagId')
                if not clagId:
                    clagId = '-'

                print(("%16s   %-16s   %-7s   %-20s   %-15s" % (intf, peerIf, clagId, conflictStr[0], ProtoDownReasonsToStr(intfInfo.get('protoDown', [])))))
                DumpRemainingLines(leadStr, conflictStr)
            print()

    if Parser.args.verbose:
        cmdStr = ClagCmd.run("GetOurLacpDB")
        try:
            lacpDb = json.loads(cmdStr)
        except ValueError as e:
            print(("Invalid lacpDb output: %s\n" % e))
            return 0

        if Parser.args.json:
            jsonOut['ourLacpInfo'] = lacpDb
        else:
            print("Our LACP Information")
            print("Our Interface      Partner MAC         CIST PortId   CLAG Id   Oper St   Flags")
            print("----------------   -----------------   -----------   -------   -------   -----")
            for bond in lacpDb:
                lacpInfo = lacpDb[bond]
                print(("%-16s   %-17s   %-10s    %-7d   %-7s   %-5s" % \
                    (bond, lacpInfo.get('partnerMac'),\
                     lacpInfo.get('cistPortId'), lacpInfo.get('clagId', 0),\
                     lacpInfo.get('opertState'),
                     LacpInfoFlagsToStr(lacpInfo.get('flags')))))
            print()

        cmdStr = ClagCmd.run("GetPeerLacpDB")
        try:
            lacpDb = json.loads(cmdStr)
        except ValueError as e:
            print(("Invalid peerLacpDb output: %s\n" % e))
            return 0

        if Parser.args.json:
            jsonOut['peerLacpInfo'] = lacpDb
        else:
            print("Peer LACP Information")
            print("Peer Interface     Partner MAC         CIST PortId   CLAG Id   Oper St   Flags")
            print("----------------   -----------------   -----------   -------   -------   -----")
            for bond in lacpDb:
                lacpInfo = lacpDb[bond]
                print(("%-16s   %-17s   %-10s    %-7d   %-7s   %-5s" % \
                    (bond, lacpInfo.get('partnerMac'),\
                     lacpInfo.get('cistPortId'), lacpInfo.get('clagId', 0),\
                     lacpInfo.get('opertState'),
                     LacpInfoFlagsToStr(lacpInfo.get('flags')))))
            print()

        HandleShowBackup(jsonOut)
        if not Parser.args.json:
            print()
        HandleDumpOurMacs(jsonOut)
        if not Parser.args.json:
            print()
        HandleDumpPeerMacs(jsonOut)
        if not Parser.args.json:
            print()
        HandleDumpPermanentMacs(jsonOut)
        if not Parser.args.json:
            print()
        HandleDumpPermanentNeighs(jsonOut)
        if not Parser.args.json:
            print()
        HandleDumpOurMcast(jsonOut)
        if not Parser.args.json:
            print()
        HandleDumpPeerMcast(jsonOut)
        if not Parser.args.json:
            print()
        HandleDumpNeighs(jsonOut)
        if not Parser.args.json:
            print()
        HandleDumpOurRport(jsonOut)
        if not Parser.args.json:
            print()
        HandleDumpPeerRport(jsonOut)
        if not Parser.args.json:
            print()
        HandleDumpOurMroute(jsonOut)
        if not Parser.args.json:
            print()
        HandleDumpPeerMroute(jsonOut)
        if not Parser.args.json:
            print() 
        HandleConnState(jsonOut)
        if not Parser.args.json:
            print()
        HandleDumpCheckSum(jsonOut)
        if not Parser.args.json:
            print()
        HandleShowTimers(jsonOut)
        if not Parser.args.json:
            print()
        HandleDumpQInfo(jsonOut)
        if not Parser.args.json:
            print()
        HandleZebraStatus(jsonOut)
        if not Parser.args.json:
            print() 
        HandleDumpOurVlans(jsonOut)
        if not Parser.args.json:
            print()
        HandleDumpPeerVlans(jsonOut)
        if not Parser.args.json:
            print()

    if Parser.args.json:
        print((json.dumps(jsonOut, sort_keys=True, indent=4)))

    return 0

# 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(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 HandleVerifyVlans():
    peerAliveStr = ClagCmd.run("GetPeerAlive")
    if peerAliveStr != "True":
        print("The peer is not alive")
        return 1

    cmdStr = ClagCmd.run("GetDualIfs")
    try:
        dualIfDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid dualIfs output: %s\n" % e))
        return 0

    dualIfs = {dualIf:dualIfDb[dualIf].get('peerIf') for dualIf in dualIfDb}

    intfDBStr = ClagCmd.run("GetClagIntfDB")
    try:
        intfs = json.loads(intfDBStr)
    except ValueError as e:
        print(("Invalid dualIfs output: %s\n" % e))
        return 0

    allClagIntfs = list(intfs.keys())

    ourVlanDB = {}
    cmdStr = ClagCmd.run("GetOurVlanDB")
    try:
        ourVlanDB = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid ourVlanDb output: %s\n" % e))
        return 0

    peerlinkIntf = set(list(ourVlanDB.keys()))-set(allClagIntfs)
    if peerlinkIntf:
        peerlinkIntf = list(peerlinkIntf)[0]

    peerlinkVlans = ourVlanDB.pop(peerlinkIntf, None)

    peerVlanDB = {}
    cmdStr = ClagCmd.run("GetPeerVlanDB")
    try:
        peerVlanDB = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid peerVlanDb output: %s\n" % e))
        return 0

    retCode = 0
    if Parser.args.verbose:
        print("Vlan Database")
        print("Our Bond Interface   VlanId   Peer Bond Interface")
        print("------------------   ------   -------------------")
    for ourIntf in allClagIntfs:
        peerIntf = dualIfs.get(ourIntf, None)
        for vlanId in ourVlanDB.get(ourIntf, None):
            if peerlinkVlans:
                if vlanId not in peerlinkVlans:
                    print("VlanId %s is not allowed on peerlink Interface %s" % (vlanId, peerlinkIntf))

            if not peerIntf:
                continue

            if vlanId in peerVlanDB.get(peerIntf,[]):
                if Parser.args.verbose:
                    print(("%-18s   %6s   %-19s" % (ourIntf, vlanId, peerIntf)))
                peerVlanDB[peerIntf].remove(vlanId)
            else:
                print(("The peer switch interface %s does not have VLAN %s configured but our switch does." % (peerIntf, vlanId)))
                retCode = 1
        for vlanId in peerVlanDB.get(peerIntf,[]):
            print(("Our switch interface %s does not have VLAN %s configured but the peer switch does." % (ourIntf, vlanId)))
            retCode = 1

    return retCode

def HandleDumpOurMacs(jsonOut=None):
    cmdStr = ClagCmd.run("GetOurMacDB")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        macDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid macDb output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['ourMacs'] = macDb
        return 0

    if Parser.args.verbose:
        print("Our MAC Database")
        print("Our Interface      Dynamic MAC         VLAN Id")
        print("----------------   -----------------   -------")
    for macInfo in macDb:
        print(("%-16s   %-17s   %d" % (macInfo.get('outIf'), macInfo.get('mac'), macInfo.get('vlanId', 0))))
    return 0

def DumpRemainingLines(leadStr, lines):
    if len(lines) < 2:
        return
    lineCnt=0
    for line in lines:
        if lineCnt:
            print(("%s%s" % (leadStr,line)))
        lineCnt += 1

def HandleShowClagId(jsonOut=None):
    intfDBStr = ClagCmd.run("GetClagIntfDB")
    if Parser.args.json and not jsonOut:
        print(intfDBStr)
        return 0

    try:
        intfs = json.loads(intfDBStr)
    except ValueError as e:
        print(("Invalid output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['clagIntfs'] = intfs
        return 0

    print("CLAG Interfaces")
    print("Our Interface      CLAG Id   Conflicts              Proto-Down     ")
    print("----------------   -------   --------------------   ---------------")
    # This is the length of the string before the Conflicts column
    leadStr=29*" "
    for intf in intfs:
        intfInfo = intfs[intf]
        conflictStr = ClagIntfConflictsToStr(intfInfo.get('conflicts', []))
        # limit the conflict reason display to 15 characters per-line
        conflictStr = textwrap.wrap(conflictStr, 20)
        print(("%-16s   %-7d   %-20s   %-15s" % (intf, intfInfo.get('clagId', 0), conflictStr[0], ProtoDownReasonsToStr(intfInfo.get('protoDown', [])))))
        DumpRemainingLines(leadStr, conflictStr)
    return 0

def HandleShowBackup(jsonOut=None):
    cmdStr = ClagCmd.run("GetBackupInfo")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        backupInfo = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid backupInfo output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['backupInfo'] = backupInfo
        return 0

    print("Backup info:")
    backupVrf = backupInfo.get('backupVrf')
    backupIp = backupInfo.get('backupIp')
    backupReason = backupInfo.get('backupReason')
    if backupVrf:
        backupIp = '%s vrf %s' % (backupIp, backupVrf)
    print(("IP: %s; State: %s; Role: %s" %\
         (backupIp,\
         'active' if backupInfo.get("backupActive") else \
         'inactive' if not backupReason else 'inactive (%s)' % (backupReason,),\
         backupInfo.get("backupRole"))))
    print(("Peer priority and id: %d %s; Peer role: %s" %\
         (backupInfo.get("peerPriority", 0), backupInfo.get("peerId"),\
         backupInfo.get("peerRole"))))

def HandleDumpNlLink():
    cmdStr = ClagCmd.run("GetNlLinkCache " + " ".join(Parser.args.args))
    if Parser.args.json:
        print(cmdStr)
        return 0

    try:
        linkCache = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid linkCache output: %s\n" % e))
        return 0

    print("Iface Name         Flags        Oper state")
    print("----------------   ----------   ----------")
    for ifName in linkCache:
        linkInfo = linkCache[ifName]
        flags = linkInfo.get('flags')
        operState = linkInfo.get('operState')
        print(("%-16s   %-10s   %s" % (ifName, flags, operState)))
    return 0

def HandleDumpIdMatchIfs():
    cmdStr = ClagCmd.run("GetIdMatchIfs")
    print(cmdStr)
    return 0


def HandleDumpIntInfo():
    cmdStr = ClagCmd.run("GetIntInfo")
    if Parser.args.json:
        print(cmdStr)
        return 0

    try:
        intInfo = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid intInfo output: %s\n" % e))
        return 0

    print(("Peer Data Version: %s; Peer sync done: %s; Config reload done: %s" %\
            (intInfo.get('peerDataVersion'),\
            'y' if intInfo.get('syncDoneFromPeer') else 'n',\
            'y' if intInfo.get('configReloadDone') else 'n')))

    return 0

def HandleDumpQInfo(jsonOut=None):
    cmdStr = ClagCmd.run("GetQInfo")

    try:
        qDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid qInfo output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['queueInfo'] = qDb
        return 0

    print("Queue Name                 len       maxLen")
    print("------------------------   -------   -------")
    for key in qDb:
        qInfo = qDb[key]

        print(("%-24s   %-6d    %-6d" %\
             (key, qInfo.get('currLen', 0), qInfo.get('maxLen', 0))))
    return 0

def HandleDumpObjectInfo():
    cmdStr = ClagCmd.run("GetObjectInfo")
    if Parser.args.json:
        print(cmdStr)
        return 0

    try:
        sizeInfo = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid sizeInfo output: %s\n" % e))
        return 0

    print("Python Object              size   ")
    print("------------------------   -------")
    for key, value in list(sizeInfo.items()):
        print(("%-24s   %-6d" % (key, value)))
    return 0

def HandleDumpDebugAll():
    jsonOut = {}

    cmdStr = ClagCmd.run("GetQInfo")
    info = json.loads(cmdStr)
    jsonOut['clagQInfo'] = info

    cmdStr = ClagCmd.run("GetIdMatchIfs")
    info = json.loads(cmdStr)
    jsonOut['clagIdMatchIfs'] = info

    cmdStr = ClagCmd.run("GetIntInfo")
    info = json.loads(cmdStr)
    jsonOut['clagIntInfo'] = info

    cmdStr = ClagCmd.run("GetNlLinkCache")
    info = json.loads(cmdStr)
    jsonOut['clagNlLinks'] = info

    cmdStr = ClagCmd.run("GetObjectInfo")
    info = json.loads(cmdStr)
    jsonOut['ObjectInfo'] = info

    cmdStr = ClagCmd.run("CollectObject")
    info = json.loads(cmdStr)
    jsonOut['CollectObjectCount'] = info

    cmdStr = ClagCmd.run("CollectUnCollectableObject")
    info = json.loads(cmdStr)
    jsonOut['CollectUnCollectableObject'] = info

    print((json.dumps(jsonOut, sort_keys=True, indent=4)))
    return 0

def HandleDumpPeerMacs(jsonOut=None):
    cmdStr = ClagCmd.run("GetPeerMacDB")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        macDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid peerMac output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['peerMacs'] = macDb
        return 0

    if Parser.args.verbose:
        print("Peer MAC Database")
        print("Peer Interface     Dynamic MAC         VLAN Id")
        print("----------------   -----------------   -------")
    for macInfo in macDb:
        print(("%-16s   %-17s   %d" %\
             (macInfo.get('outIf'), macInfo.get('mac'), macInfo.get('vlanId', 0))))
    return 0

def HandleDumpPermanentMacs(jsonOut=None):
    cmdStr = ClagCmd.run("GetPermanentMacDB")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        macDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid permanentMac output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['permanentMacs'] = macDb
        return 0

    if Parser.args.verbose:
        print("Permanent MAC Database")
        print("Interface          MAC Address         VLAN Id   State    ")
        print("----------------   -----------------   -------   ---------")
    for macInfo in macDb:
        if macInfo.get('permanent') and macInfo.get('static'):
            # do not show permanent vrr mac
            continue
        if macInfo.get('permanent'):
            state = 'permanent'
        elif macInfo.get('static'):
            state = 'static'
        else:
            state = 'unknown'
        print(("%-16s   %-17s   %-7d   %s" %\
             (macInfo.get('iface'), macInfo.get('mac'), macInfo.get('vlanId', 0),
              state)))
    return 0

def HandleDumpPermanentNeighs(jsonOut=None):
    cmdStr = ClagCmd.run("GetPermanentNeighDB")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        neighDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid permanentNeigh output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['permanentNeighs'] = neighDb
        return 0

    if Parser.args.verbose:
        print("Permanent Neighbor Database")
        print("IP Address                  Our Interface      Dynamic MAC         VLAN Id   Owner")
        print("-------------------------   ----------------   -----------------   -------   ----------")
    for neighInfo in neighDb:
        owner = "local" if neighInfo.get('local') else "peer"
        print(("%-25s   %-16s   %-17s   %-7d   %s" %\
             (neighInfo.get('addr'), neighInfo.get('dev'), neighInfo.get('mac'),
              neighInfo.get('vid', 0), owner)))
    return 0

def HandleDumpOurMcast(jsonOut=None):
    cmdStr = ClagCmd.run("GetOurMcastDB")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        mcastDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid ourMcast output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['ourMcastGroups'] = mcastDb
        return 0

    if Parser.args.verbose:
        print("Our Multicast Group Database")
        print("Our Multicast Group      Port               VLAN Id   Device             Age")
        print("----------------------   ----------------   -------   ----------------   ---")
    for mcastInfo in mcastDb:
        print(("%-22s   %-16s   %-7s   %-16s   %d" %\
                 (mcastInfo.get('group'), mcastInfo.get('port'),\
                  str(mcastInfo.get('vlanId', 0)), mcastInfo.get('bridge'), \
                  mcastInfo.get('age', 0))))
    return 0

def HandleDumpPeerMcast(jsonOut=None):
    cmdStr = ClagCmd.run("GetPeerMcastDB")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        mcastDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid peerMcast output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['peerMcastGroups'] = mcastDb
        return 0

    if Parser.args.verbose:
        print("Peer Multicast Group Database")
        print("Peer Multicast Group     Port               VLAN Id   Device             Age")
        print("----------------------   ----------------   -------   ----------------   ---")
    for mcastInfo in mcastDb:
        print(("%-22s   %-16s   %-7s   %-16s   %d" %\
                 (mcastInfo.get('group'), mcastInfo.get('port'),\
                  str(mcastInfo.get('vlanId', 0)), mcastInfo.get('bridge'), \
                  mcastInfo.get('age', 0))))
    return 0

def HandleDumpNeighs(jsonOut=None):
    cmdStr = ClagCmd.run("GetNeighDB")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        neighDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid neigh output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['neighs'] = neighDb
        return 0

    if Parser.args.verbose:
        print("Neighbor Database")
        print("Destination                 Our Interface      Dynamic MAC         VLAN Id   Ownership")
        print("-------------------------   ----------------   -----------------   -------   ----------")
    for neighInfo in neighDb:
        owners = []
        if neighInfo.get('ourNeigh'):
            owners.append("local")
        if neighInfo.get('peerNeigh'):
            owners.append("peer")
        ownership = ",".join(owners) if owners else "-"
        print(("%-25s   %-16s   %-17s   %-7d   %s" %\
             (neighInfo.get('dst'), neighInfo.get('dev'), neighInfo.get('mac'),
              neighInfo.get('vid', 0), ownership)))
    return 0

def HandleDumpKernelNeigh(jsonOut=None):
    cmdStr = ClagCmd.run("GetKernelNeighDB")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        kernNeighDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid kernNeighDB output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['kernNeigh'] = kernNeighDb
        return 0

    print("Destination                 AF_FAMILY   VLAN       Dynamic MAC          STATE      FLAGS")
    print("-------------------------   ---------   --------   ------------------   --------   --------")
    for kernNeighInfo in kernNeighDb:
        family = '0x%x' % kernNeighInfo.get('family')
        state = '0x%x' % kernNeighInfo.get('state')
        flags = '0x%x' % kernNeighInfo.get('flags')
        print(("%-25s   %-9s   %-8d   %-18s   %-8s   %s" %\
             (kernNeighInfo.get('dst'), family, kernNeighInfo.get('idx'),
              kernNeighInfo.get('mac'), state, flags)))
    return 0

def HandleDumpOurRport(jsonOut=None):
    cmdStr = ClagCmd.run("GetOurRportDB")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        mcastDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid ourRPort output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['ourRouterPorts'] = mcastDb
        return 0

    if Parser.args.verbose:
        print("Our Router Port Database")
        print("Our Router Port    Device             Age")
        print("----------------   ----------------   ---")
    for mcastInfo in mcastDb:
        print(("%-16s   %-16s   %d" %\
             (mcastInfo.get('port'), mcastInfo.get('bridge'), mcastInfo.get('age', 0))))
    return 0

def HandleDumpPeerRport(jsonOut=None):
    cmdStr = ClagCmd.run("GetPeerRportDB")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        mcastDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid peerRPort output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['peerRouterPorts'] = mcastDb
        return 0

    if Parser.args.verbose:
        print("Peer Router Port Database")
        print("Peer Router Port   Device             Age")
        print("----------------   ----------------   ---")
    for mcastInfo in mcastDb:
        print(("%-16s   %-16s   %d" %\
             (mcastInfo.get('port'), mcastInfo.get('bridge'), mcastInfo.get('age', 0))))
    return 0

def HandleDumpCheckSum(jsonOut=None):
    cmdStr = ClagCmd.run("GetDBCheckSum")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        checkSumDict = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid checksum output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['dataBaseCheckSum'] = checkSumDict
        return 0

    if Parser.args.verbose:
        print("Database Hash")
    print("Database           md5 hash")
    print("----------------   --------------------------------")
    for db, checksum in list(checkSumDict.items()):
        print(("%-16s   %s" % (db, checksum)))
    return 0

def HandleDumpOurMroute(jsonOut=None):
    cmdStr = ClagCmd.run("GetOurMrouteDB")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        ourMrouteDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid ourMroute output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['ourMrouteDB'] = ourMrouteDb
        return 0

    if Parser.args.verbose:
        print("Our Multicast Route Database")
    print("Vrf               Intf                    Source              Group               Vni             Cost To RP    is DR   isDualActive")
    print("----------------  ----------------------  ------------------  ------------------  --------------  ------------  ------  ------------")
    for mroute in ourMrouteDb:
        print(("%-16s  %-22s  %-18s  %-18s  %-14s  %-12d  %-6s  %-10s" %\
            (mroute.get('vrf_name'), mroute.get('intf_name'), mroute.get('source_ip'), mroute.get('group_ip'), \
            mroute.get('vni_id'), mroute.get('cost_to_rp'), mroute.get('am_i_DR'), \
            mroute.get('am_i_Dual_active'))))
    return 0

def HandleDumpPeerMroute(jsonOut=None):
    cmdStr = ClagCmd.run("GetPeerMrouteDB")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        peerMrouteDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid ourMroute output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['peerMrouteDB'] = peerMrouteDb
        return 0

    if Parser.args.verbose:
        print("Peer Multicast Route Database")
    print("Vrf               Intf                    Source              Group               Vni             Cost To RP    is DR   isDualActive")        
    print("----------------  ----------------------  ------------------  ------------------  --------------  ------------  ------  ------------")
    for mroute in peerMrouteDb:
        print(("%-16s  %-22s  %-18s  %-18s  %-14s  %-12d  %-6s  %-10s" %\
            (mroute.get('vrf_name'), mroute.get('intf_name'), mroute.get('source_ip'), mroute.get('group_ip'), \
            mroute.get('vni_id'), mroute.get('cost_to_rp'), mroute.get('am_i_DR'), \
            mroute.get('am_i_Dual_active'))))
    return 0

def HandleDumpOurVnis(jsonOut=None):
    cmdStr = ClagCmd.run("GetOurVniDB")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        vniDb = json.loads(cmdStr)
    except ValueError as e:
        print("Invalid vniDb output: %s\n" % e)
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['OurVnis'] = vniDb
        return 0

    if Parser.args.verbose:
        print("Our VNI Database")
        print("Our VNIs           Vxlan Device        VLAN Id")
        print("----------------   -----------------   -------")
    for vniInfo in vniDb:
        print("%-16d   %-17s   %d" % (vniInfo.get('vni'), vniInfo.get('name'), vniInfo.get('vlan', 0)))
    return 0

def HandleDumpPeerVnis(jsonOut=None):
    cmdStr = ClagCmd.run("GetPeerVniDB")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        vniDb = json.loads(cmdStr)
    except ValueError as e:
        print("Invalid vniDb output: %s\n" % e)
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['PeerVnis'] = vniDb
        return 0

    if Parser.args.verbose:
        print("Peer VNI Database")
        print("Peer VNIs          Vxlan Device        VLAN Id")
        print("----------------   -----------------   -------")
    for vniInfo in vniDb:
        print("%-16d   %-17s   %d" % (vniInfo.get('vni'), vniInfo.get('name'), vniInfo.get('vlan', 0)))
    return 0

def HandleDumpOurVlans(jsonOut=None):
    cmdStr = ClagCmd.run("GetOurVlanDB")
    try:
        vlanDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid vlanDb output: %s\n" % e))
        return 0

    if Parser.args.json:
        jsonOut['ourVlans'] = vlanDb
        return 0

    if Parser.args.verbose:
        print("Our VLAN Information")
        print("Our Interface      VLAN Id")
        print("----------------   -------")
        for iface in vlanDb:
            vlanIdStr = ranges_to_compact_str(vlanDb[iface])
            print(("%-16s   %s" % (iface, vlanIdStr)))
        print()
        
def HandleDumpPeerVlans(jsonOut=None):
    cmdStr = ClagCmd.run("GetPeerVlanDB")
    try:
        vlanDb = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid vlanDb output: %s\n" % e))
        return 0

    if Parser.args.json:
        jsonOut['ourVlans'] = vlanDb
        return 0

    if Parser.args.verbose:
        print("Peer VLAN Information")
        print("Peer Interface      VLAN Id")
        print("----------------    -------")
        for iface in vlanDb:
            vlanIdStr = ranges_to_compact_str(vlanDb[iface])
            print(("%-16s   %s" % (iface, vlanIdStr)))
        print()
    
def HandleZebraStatus(jsonOut=None):
    cmdStr = ClagCmd.run("GetZebraStatus")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        zebraStatus = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid zebra status %s \n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['zebraStatus'] = zebraStatus
        return 0

    print("ZebraStatus")
    print("Local              Peer")
    print("----------------   ----------------")
    print(("%-16s  %-22s" % (zebraStatus.get('local'), zebraStatus.get('remote'))))

def HandleShowTimers(jsonOut=None):
    cmdStr = ClagCmd.run("GetTimers")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        timers = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid timers output: %s\n" % e))
        return 0
    if Parser.args.json and jsonOut:
        jsonOut['timers'] = timers
        return 0

    if Parser.args.verbose:
        print("Timer Information")
    print("Timers             Value     Time remaining")
    print("----------------   -------   --------------")
    for timer, timerInfo in list(timers.items()):
        print(("%-16s   %-7s   %s" % (timer, timerInfo['value'], timerInfo['remaining'])))
    return 0

def HandleShowProtoDown():
    cmdStr = ClagCmd.run("GetClagProtoIntfDB")
    try:
        protoIntfs = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid clagIntfDB output: %s\n" % e))
        return 0

    if Parser.args.json:
        print((json.dumps(protoIntfs, sort_keys=True, indent=4)))
        return 0

    print("CLAG Interfaces in protodown")
    print("Our Interface      CLAG Id   Proto-Down Reason")
    print("----------------   -------   -----------------")
    for intf in protoIntfs:
        intfInfo = protoIntfs[intf]
        protodown = intfInfo.get('protoDown', [])
        print(("%-16s   %-7d   %-15s" % (intf, intfInfo.get('clagId', 0),  ProtoDownReasonsToStr(protodown))))

def HandleProfile():
    if len(Parser.args.args) != 1:
        print("This command requires exactly one argument: start, stop, dump")
        return 1
    opstr = Parser.args.args[0]
    if opstr == "start":
        cmdStr = ClagCmd.run("startprofile")
    elif opstr == "stop":
        cmdStr = ClagCmd.run("stopprofile")
    elif opstr == "dump":
        cmdStr = ClagCmd.run("dumpprofile")
    else:
        print("This command requires exactly one argument: start, stop, dump")
        return 1
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleConnState(jsonOut=None):
    cmdStr = ClagCmd.run("GetSocketInfo")
    if Parser.args.json and not jsonOut:
        print(cmdStr)
        return 0

    try:
        sockInfo = json.loads(cmdStr)
    except ValueError as e:
        print(("Invalid GetSocketInfo output: %s\n" % e))
        return 0

    if Parser.args.json and jsonOut:
        jsonOut['socketState'] = sockInfo
        return 0

    if Parser.args.verbose:
        print("Socket Information")
        print("Socket             State           ")
        print("----------------   ----------------")
    for sock in sockInfo:
        print(("%-16s   %-16s" %\
             (sock, sockInfo[sock])))
    return 0

def HandleShowDebugFlags():
    cmdStr = ClagCmd.run("getdebugflags")
    if cmdStr:
        print(("Debug flags: %s" % cmdStr))
        return 1
    return 0

def HandleSetDebugFlags():
    cmdStr = ClagCmd.run("setdebugflags " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleClearDebugFlags():
    cmdStr = ClagCmd.run("cleardebugflags " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleSetFastShutDown():
    cmdStr = ClagCmd.run("setfastshutdown")
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleSetFaultTolerance():
    cmdStr = ClagCmd.run("backup-fault-tolerance "+" ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

def HandleSetVxlanLocalIp():
    cmdStr = ClagCmd.run("setvxlanlocalip " + " ".join(Parser.args.args))
    if cmdStr:
        print(cmdStr)
        return 1
    return 0

#-------------------------------------------------------------------------------
#
#   Command Dictionary - This dictionary lists all of the command strings as
#   the dictionary keys, and the function which handles the execution of the
#   command as the value.
#   Commands that are setup as "Internal" are not displayed in the help menu.
#   Internal commands can be deprecated and output/display format can be 
#   changed without notice.
#-------------------------------------------------------------------------------
# hidden commands - will not be displayed in the help menu and no bash 
# completion. These commands can be deprecated and output/display format can be
# changed without notice.

clagCtlCmds = {
    #Command            : ( Function, Help, jsonSupport, Internal)
    "echo"              : ( HandleEcho, "Echo back the supplied string", False, False),
    "setclagid"         : ( HandleBondClagId, "Associates a bond with a clag id", False, False),
    "reloaddone"        : ( HandleReloadDone, "Config reload done", False, False),
    "showclagid"        : ( HandleShowClagId, "Displays the CLAG bonds configured on this switch", True, False),
    "setanycastip"      : ( HandleSetAnycastIp, "Sets the VXLAN anycast IP address", False, False),
    "setbackupip"       : ( HandleSetBackupIp, "Sets the backup IP address", False, False),
    "showbackupip"      : ( HandleShowBackup, "Displays backup link info", True, False),
    "debug"             : ( HandleDebug, "Sets the debugging level", False, False),
    "params"            : ( HandleParams, "Display the parameters in use by clagd", True, False),
    "collectgarbage"    : ( HandleGarbage, "Causes clagd to run python's garbage collection", False, False),
    "status"            : ( HandleStatus, "Display the status of the clagd daemon", True, False),
    "verifyvlans"       : ( HandleVerifyVlans, "Verifies VLAN configuration with the peer", False, False),
    "verbose"           : ( HandleVerbose, "Enables additional output in log file", False, False),
    "quiet"             : ( HandleQuiet, "Prevents output in the log file", False, False),
    "logfile"           : ( HandleLogFile, "Sets the name of the log file", False, False),
    "lacppoll"          : ( HandleLacpPoll, "The frequency clagd collects information and sends to peer", False, False),
    "peertimeout"       : ( HandlePeerTimeout, "The time clagd expects a message from the peer", False, False),
    "sendtimeout"       : ( HandleSendTimeout, "The time clagd send socket waits to enqueue data", False, False),
    "sendbufsize"       : ( HandleSendBufSize, "The size of the socket send buffer, in bytes", False, False),
    "peerlinkpoll"      : ( HandleLinkPoll, "The frequency clagd polls the status of the peer interface", False, False),
    "priority"          : ( HandlePriority, "Sets the priority of clagd", False, False),
    "peerlacprate"      : ( HandlePeerLacpRate, "Displays the peer's polling rate", False, False),
    "logmsg"            : ( HandleLogMessage, "Outputs a message to the log file", False, False),
    "dumpourmacs"       : ( HandleDumpOurMacs, "Displays the MACs learned on this switch", True, False),
    "dumppeermacs"      : ( HandleDumpPeerMacs, "Displays the MACs learned on the peer switch", True, False),
    "dumppermanentmacs" : ( HandleDumpPermanentMacs, "Displays the permanent MACs from this switch", True, False),
    "dumppermanentneighs" : ( HandleDumpPermanentNeighs, "Displays the permanent Neighs from this switch", True, False),
    "dumpourmcast"      : ( HandleDumpOurMcast, "Displays the multicast entries learned on this switch", True, False),
    "dumppeermcast"     : ( HandleDumpPeerMcast, "Displays the multicast entries learned on the peer switch", True, False),
    "dumpneighs"        : ( HandleDumpNeighs, "Displays the neighs learned on this switch", True, False),
    "dumpkernelneigh"   : ( HandleDumpKernelNeigh, "Displays the kernNeighDB", True, True),
    "dumpourrport"      : ( HandleDumpOurRport, "Displays the multicast router ports learned on this switch", True, False),
    "dumppeerrport"     : ( HandleDumpPeerRport, "Displays the multicast router ports learned on the peer switch", True, False),
    "dumpourmroute"     : ( HandleDumpOurMroute, "Displays the mroute entries learned on this switch", True, False),
    "dumppeermroute"    : ( HandleDumpPeerMroute, "Displays the mroute entries learned on peer switch", True, False),
    "dumpourvnis"       : ( HandleDumpOurVnis, "Displays our vni/vlan map when using single vxlan device", True, False),
    "dumppeervnis"      : ( HandleDumpPeerVnis, "Displays the peer vni/vlan map when using single vxlan device", True, False),
    "zebrastatus"       : ( HandleZebraStatus, "Displays the local and peer zebra status", True, False),
    "dumpnllink"        : ( HandleDumpNlLink, "Displays kernel link cache", True, True),
    "dumpclagidifs"     : ( HandleDumpIdMatchIfs, "Displays id based matches", True, True),
    "dumpintinfo"       : ( HandleDumpIntInfo, "Displays CLAG internal info", True, True),
    "dumpqinfo"         : ( HandleDumpQInfo, "Displays the CLAG bonds configured on this switch", True, True),
    "dumpdebugall"      : ( HandleDumpDebugAll, "Displays all info needed for debugging", True, True),
    "dumpcsum"          : ( HandleDumpCheckSum, "Computes and displays the md5 checksum of the clag databases", True, True),
    "dumpobjectinfo"    : ( HandleDumpObjectInfo, "Displays the size of interesting python objects", True, True),
    "showtimers"        : ( HandleShowTimers, "Displays CLAG related timers", True, False),
    "profile"           : ( HandleProfile, "Profile clagd: params on, of, dump", False, True),
    "connstate"         : ( HandleConnState, "Display socket connection state with peer", True, False),
    "showdebugflags"    : ( HandleShowDebugFlags, "Shows the debug logging flags", False, False),
    "setdebugflags"     : ( HandleSetDebugFlags, "Sets the debug logging flags", False, False),
    "cleardebugflags"   : ( HandleClearDebugFlags, "Removes or clears the debug logging flags", False, False),
    "setfastshutdown"   : ( HandleSetFastShutDown, "Skip some steps on reboot/shutdown", False, True),
    "collectobject"     : ( HandleCollectObjectCount, "Causes clagd to run python's get_objects() count", False, False),
    "uncollectableobject" : ( HandleUncollectableObject, "Causes clagd to run python's gc.garbage", False, False),
    "clearconflictstate"   : ( HandleClearConflictState, "Clears the protodown flags due to conflict (lacp patner mismatch/duplicate)", False, False),
    "showprotodown"     : ( HandleShowProtoDown, "Shows the clag interfaces in protodown", True, False),
    "setvxlanlocalip"   : ( HandleSetVxlanLocalIp, "Sets the new VxLan local IP", False, True),
    "backup-fault-tolerance" : ( HandleSetFaultTolerance, "Number of echo replies to ignore on the backup link from the peer which is going down/rebooting", False, False)
}


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

class ClagCtlParser:

    def __init__(self):
        '''
        Create the parser
        '''
        epilogStr = "The commands are:\n"
        for cmd in sorted(clagCtlCmds.keys()):
            if not clagCtlCmds[cmd][3]:
                epilogStr += "%-20s  %s\n" % (cmd, clagCtlCmds[cmd][1])
        epilogStr += "\nSee the clagctl man page for more information"
        self.parser = argparse.ArgumentParser(description="CLAG daemon control interface, version %s" % (_ClagCtlVersion,),
                                              usage='%(prog)s [-h] [-j] [-v] [command [args]]', epilog=epilogStr, 
                                              formatter_class=argparse.RawDescriptionHelpFormatter)
        self.parser.add_argument("command", choices=list(clagCtlCmds.keys()), default="status", nargs='?',
                                 metavar="command", help="Command to execute, default is 'status'")
        self.parser.add_argument("-v", "--verbose", action="store_true", default=False,
                                 help="Increase the amount of output.")
        self.parser.add_argument("-j", "--json", action="store_true", default=False,
                                 help="json output.")
        self.parser.add_argument("args", nargs=argparse.REMAINDER, help="Additional command parameters")

    def ParseCmdLine(self):
        '''
        Parse the command line parameters
        '''
        try:
            self.args = self.parser.parse_args()
        except:
            sys.exit(-1)


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

def main():

    # Parse the command line parameters
    global Parser
    Parser = ClagCtlParser()
    Parser.ParseCmdLine()
    if Parser.args.json and Parser.args.command in clagCtlCmds and\
             not clagCtlCmds[Parser.args.command][2]:
        print((json.dumps({"errorMsg" : "json output is not supported for %s" % Parser.args.command})))
        return 0

    # Handle broken pipe problems
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)

    # Create a connection to clagd
    global ClagCmd
    try:
        ClagCmd = clag.clagdcmd.clagdcmd()
    except IOError as e:
        errMsg = "Unable to communicate with clagd. Is it running?"
        if Parser.args.json:
            print((json.dumps({"errorMsg" : errMsg})))
        else:
            print(errMsg)
        return -1
    except ValueError as e:
        errMsg = "Unable to communicate with clagd. Is it running?"
        if Parser.args.json:
            print((json.dumps({"errorMsg" : errMsg})))
        else:
            print(errMsg)
        return -1

    # Execute the command
    try:
        return clagCtlCmds[Parser.args.command][0]()
    except IOError as e:
        errMsg = "*** Command interrupted (%s). Output incomplete." % (e,)
        if Parser.args.json:
            print((json.dumps({"errorMsg" : errMsg})))
        else:
            print(errMsg)
        ClagCmd.close()
    except KeyboardInterrupt:
        errMsg = "*** Command interrupted. Output incomplete."
        if Parser.args.json:
            print((json.dumps({"errorMsg" : errMsg})))
        else:
            print(errMsg)
        ClagCmd.close()

    return -1



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

if __name__ == '__main__':
    rc = main()
    sys.exit(rc)
