#!/usr/bin/python

#-------------------------------------------------------------------------------
#
# Copyright 2014 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, 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 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 "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 "sendTimeout = %s" % (params.get('sendTimeout'))
    print "sendBufSize = %s" % (params.get('sendBufSize'))
    print "forceDynamic = %s" % (params.get('forceDynamic'))
    print "dormantDisable = %s" % (params.get('dormantDisable'))
    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 "cmdLine = %s" % (" ".join(params.get('cmdLine')))
    return 0

def HandleGarbage():
    cmdStr = ClagCmd.run("CollectGarbage")
    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 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"
            if ourRole == "primary":
                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 "    Peer Priority, ID, and Role: %d %s %s" % (status.get('peerPriority', 0), status.get('peerId'), status.get('peerRole'))
                print "     Our Priority, ID, and Role: %d %s %s" % (status.get('ourPriority', 0), status.get('ourId'), ourRole)
        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" % (status.get('peerIf'), status.get('peerIp'))
        if status.get('vxlanAnycast'):
            print "               VxLAN Anycast IP: %s" % (status.get("vxlanAnycast"),)
        backupVrf = status.get('backupVrf')
        backupIp = status.get('backupIp')
        if backupVrf:
            backupIp = '%s vrf %s' % (backupIp, backupVrf)
        print "                      Backup IP: %s (%s)" % (backupIp, 'active' if status.get('backupActive', 0) else 'inactive')
        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
        HandleDumpOurMcast(jsonOut)
        if not Parser.args.json:
            print
        HandleDumpPeerMcast(jsonOut)
        if not Parser.args.json:
            print
        HandleDumpOurRport(jsonOut)
        if not Parser.args.json:
            print
        HandleDumpPeerRport(jsonOut)
        if not Parser.args.json:
            print

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

        renderedVlanDb = {}
        for iface in vlanDb:
            renderedVlanDb[iface] = GetVlanIdList(vlanDb[iface])
        if Parser.args.json:
            jsonOut['ourVlans'] = renderedVlanDb
        else:
            print "Our VLAN Information"
            print "Our Interface      VLAN Id"
            print "----------------   -------"
            for iface in renderedVlanDb:
                vlanIdStr = ranges_to_compact_str(renderedVlanDb[iface])
                print "%-16s   %s" % (iface, vlanIdStr)
            print

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

        renderedVlanDb = {}
        for iface in vlanDb:
            renderedVlanDb[iface] = GetVlanIdList(vlanDb[iface])
        if Parser.args.json:
            jsonOut['peerVlans'] = renderedVlanDb
        else:
            print "Peer VLAN Information"
            print "Peer Interface     VLAN Id"
            print "----------------   -------"
            for iface in renderedVlanDb:
                vlanIdStr = ranges_to_compact_str(renderedVlanDb[iface])
                print "%-16s   %s" % (iface, vlanIdStr)
            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 GetVlanIdList(vlanMapsList):
    vlanBitConvert = {
        0x00000001 :  0, 0x00000003 :  1, 0x00000007 :  2, 0x0000000F :  3,
        0x0000001F :  4, 0x0000003F :  5, 0x0000007F :  6, 0x000000FF :  7,
        0x000001FF :  8, 0x000003FF :  9, 0x000007FF : 10, 0x00000FFF : 11,
        0x00001FFF : 12, 0x00003FFF : 13, 0x00007FFF : 14, 0x0000FFFF : 15,
        0x0001FFFF : 16, 0x0003FFFF : 17, 0x0007FFFF : 18, 0x000FFFFF : 19,
        0x001FFFFF : 20, 0x003FFFFF : 21, 0x007FFFFF : 22, 0x00FFFFFF : 23,
        0x01FFFFFF : 24, 0x03FFFFFF : 25, 0x07FFFFFF : 26, 0x0FFFFFFF : 27,
        0x1FFFFFFF : 28, 0x3FFFFFFF : 29, 0x7FFFFFFF : 30, 0xFFFFFFFF : 31
    }
    vlanIds = []
    vlanBase = 0
    for vlanMap in vlanMapsList:
        while vlanMap:
            vlanIds.append(vlanBase + vlanBitConvert[vlanMap ^ (vlanMap - 1)])
            vlanMap &= vlanMap - 1
        vlanBase += 32
    return vlanIds

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}

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

    for iface in vlanDb:
        ourVlanDB[iface] = GetVlanIdList(vlanDb[iface])

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

    for iface in vlanDb:
        peerVlanDB[iface] = GetVlanIdList(vlanDb[iface])

    retCode = 0
    if Parser.args.verbose:
        print "Our Bond Interface   VlanId   Peer Bond Interface"
        print "------------------   ------   -------------------"
    for ourIntf in dualIfs:
        peerIntf = dualIfs[ourIntf]
        for vlanId in ourVlanDB.get(ourIntf,[]):
            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 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')
    if backupVrf:
        backupIp = '%s vrf %s' % (backupIp, backupVrf)
    print "IP: %s; State: %s; Role: %s" %\
         (backupIp,\
         'active' if backupInfo.get("backupActive") else 'inactive',\
         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")
    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 = '0x%x' % linkInfo.get('flags')
        print "%-16s   %-10s   %s" % (ifName, flags, linkInfo.get('operState'))
    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():
    cmdStr = ClagCmd.run("GetQInfo")
    if Parser.args.json:
        print cmdStr
        return 0

    try:
        qDb = json.loads(cmdStr)
    except ValueError as e:
        print "Invalid qInfo output: %s\n" % e
        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 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 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 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      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     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 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    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   Device             Age"
        print "----------------   ----------------   ---"
    for mcastInfo in mcastDb:
        print "%-16s   %-16s   %d" %\
             (mcastInfo.get('port'), mcastInfo.get('bridge'), mcastInfo.get('age', 0))
    return 0

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


#-------------------------------------------------------------------------------
#
#   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),
    "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),
    "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),
    "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),
    "dumpnllink"        : ( HandleDumpNlLink, "Displays kernel link cache", True, True),
    "dumpintinfo"       : ( HandleDumpIntInfo, "Displays CLAG internal info", True, True),
    "dumpqinfo"         : ( HandleDumpQInfo, "Displays the CLAG bonds configured on this switch", True, True),
    "profile"           : ( HandleProfile, "Profile clagd: params on, of, dump", False, True)
}


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

class ClagCtlParser:

    def __init__(self):
        '''
        Create the parser
        '''
        epilogStr = "The commands are:\n"
        for cmd in sorted(clagCtlCmds.iterkeys()):
            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=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 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:
        print "Unable to communicate with clagd. Is it running?"
        return -1
    except ValueError as e:
        print "Unable to communicate with clagd. Is it running?"
        return -1

    # Execute the command
    try:
        return clagCtlCmds[Parser.args.command][0]()
    except IOError as e:
        print "*** Command interrupted (%s). Output incomplete." % (e,)
        ClagCmd.close()
    except KeyboardInterrupt:
        print "*** Command interrupted. Output incomplete."
        ClagCmd.close()

    return -1



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

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