#!/usr/bin/env python import urllib2, base64, json, sys, optparse # globals CONST_DEFAULT_DEBUG=0 options = None state = None jsonTopologyNodes = [] jsonInventoryNodes = [] flowInfoNodes = {} nodeIdToDpidCache = {} CONST_OPERATIONAL = 'operational' CONST_CONFIG = 'config' CONST_NET_TOPOLOGY = 'network-topology' CONST_TOPOLOGY = 'topology' CONST_TP_OF_INTERNAL = 65534 CONST_ALIASES = ['alpha', 'bravo', 'charlie', 'delta', 'echo', 'foxtrot', 'golf', 'hotel', 'india', 'juliet', 'kilo', 'lima', 'mike', 'november', 'oscar', 'papa', 'quebec', 'romeo', 'sierra', 'tango', 'uniform', 'victor', 'whiskey', 'xray', 'yankee', 'zulu'] class State: def __init__(self): self.nextAliasIndex = 0 self.nextAliasWrap = 0 self.nodeIdToAlias = {} self.bridgeNodes = {} self.ovsdbNodes = {} self.ofLinks = {} def __repr__(self): return 'State {}:{} {}:{} {}:{} {}:{} {}:{}'.format( 'nextAliasIndex', self.nextAliasIndex, 'nextAliasWrap', self.nextAliasWrap, 'bridgeNodes_ids', self.bridgeNodes.keys(), 'ovsdbNodes_ids', self.ovsdbNodes.keys(), 'nodeIdToAlias', self.nodeIdToAlias) def registerBridgeNode(self, bridgeNode): self.bridgeNodes[bridgeNode.nodeId] = bridgeNode def registerOvsdbNode(self, ovsdbNode): self.ovsdbNodes[ovsdbNode.nodeId] = ovsdbNode def getNextAlias(self, nodeId): result = CONST_ALIASES[ self.nextAliasIndex ] if self.nextAliasWrap > 0: result += '_' + str(self.nextAliasWrap) if CONST_ALIASES[ self.nextAliasIndex ] == CONST_ALIASES[-1]: self.nextAliasIndex = 0 self.nextAliasWrap += 1 else: self.nextAliasIndex += 1 self.nodeIdToAlias[ nodeId ] = result return result # -- class TerminationPoint: def __init__(self, name, ofPort, tpType, mac='', ifaceId=''): self.name = name self.ofPort = ofPort self.tpType = tpType self.mac = mac self.ifaceId = ifaceId def __repr__(self): result = '{} {}:{}'.format(self.name, 'of', self.ofPort) if self.tpType != '': result += ' {}:{}'.format('type', self.tpType) if self.mac != '': result += ' {}:{}'.format('mac', self.mac) if self.ifaceId != '': result += ' {}:{}'.format('ifaceId', self.ifaceId) return '{' + result + '}' # -- class BridgeNode: def __init__(self, nodeId, dpId, name, controllerTarget, controllerConnected): global state self.alias = state.getNextAlias(nodeId) self.nodeId = nodeId self.dpId = dpId self.name = name self.controllerTarget = controllerTarget self.controllerConnected = controllerConnected self.tps = [] def getOpenflowName(self): if self.dpId is None: return self.nodeId return dataPathIdToOfFormat(self.dpId) def addTerminationPoint(self, terminationPoint): self.tps.append(terminationPoint) def __repr__(self): return 'BridgeNode {}:{} {}:{} {}:{} {}:{} {}:{} {}:{} {}:{} {}:{}'.format( 'alias', self.alias, 'nodeId', self.nodeId, 'dpId', self.dpId, 'openflowName', self.getOpenflowName(), 'name', self.name, 'controllerTarget', self.controllerTarget, 'controllerConnected', self.controllerConnected, 'tps', self.tps) # -- class OvsdbNode: def __init__(self, nodeId, inetMgr, inetNode, otherLocalIp, ovsVersion): global state if inetNode != '': self.alias = inetNode else: self.alias = nodeId self.nodeId = nodeId self.inetMgr = inetMgr self.inetNode = inetNode self.otherLocalIp = otherLocalIp self.ovsVersion = ovsVersion def __repr__(self): return 'OvsdbNode {}:{} {}:{} {}:{} {}:{} {}:{} {}:{}'.format( 'alias', self.alias, 'nodeId', self.nodeId, 'inetMgr', self.inetMgr, 'inetNode', self.inetNode, 'otherLocalIp', self.otherLocalIp, 'ovsVersion', self.ovsVersion) # ====================================================================== def make_it_a_string(param): result = "" try: result = str( param ) except: pass return result # ====================================================================== def printError(msg): sys.stderr.write(msg) # ====================================================================== def prt(msg, logLevel=0): prtCommon(msg, logLevel) def prtLn(msg, logLevel=0): prtCommon('{}\n'.format(msg), logLevel) def prtCommon(msg, logLevel): if options.debug >= logLevel: sys.stdout.write(msg) # ====================================================================== def getMdsalTreeType(): if options.useConfigTree: return CONST_CONFIG return CONST_OPERATIONAL # -- def grabJson(url): try: request = urllib2.Request(url) # You need the replace to handle encodestring adding a trailing newline # (https://docs.python.org/2/library/base64.html#base64.encodestring) base64string = base64.encodestring('{}:{}'.format(options.odlUsername, options.odlPassword)).replace('\n', '') request.add_header('Authorization', 'Basic {}'.format(base64string)) result = urllib2.urlopen(request) except urllib2.URLError, e: printError('Unable to send request: {}\n'.format(e)) sys.exit(1) if (result.code != 200): printError( '{}\n{}\n\nError: unexpected code: {}\n'.format(result.info(), result.read(), result.code) ) sys.exit(1) data = json.load(result) prtLn(data, 4) return data # -- def grabInventoryJson(mdsalTreeType): global jsonInventoryNodes url = 'http://{}:{}/restconf/{}/opendaylight-inventory:nodes/'.format(options.odlIp, options.odlPort, mdsalTreeType) data = grabJson(url) if not 'nodes' in data: printError( '{}\n\nError: did not find nodes in {}'.format(data, url) ) sys.exit(1) data2 = data['nodes'] if not 'node' in data2: printError( '{}\n\nError: did not find node in {}'.format(data2, url) ) sys.exit(1) jsonInventoryNodes = data2['node'] # -- def parseInventoryJson(mdsalTreeType): global jsonInventoryNodes global flowInfoNodes for nodeDict in jsonInventoryNodes: if not 'id' in nodeDict: continue bridgeOfId = nodeDict.get('id') prtLn('inventory node {} has keys {}'.format(bridgeOfId, nodeDict.keys()), 3) # locate bridge Node bridgeNodeId = None bridgeNode = None for currNodeId in state.bridgeNodes.keys(): if state.bridgeNodes[currNodeId].getOpenflowName() == bridgeOfId: bridgeNodeId = currNodeId bridgeNode = state.bridgeNodes[currNodeId] break if bridgeNodeId is None: prtLn('inventory node {}'.format(bridgeOfId), 1) else: prtLn('inventory node {}, aka {}, aka {}'.format(bridgeOfId, bridgeNodeId, showPrettyName(bridgeNodeId)), 1) flowInfoNode = {} indent = ' ' * 2 prtLn('{}features: {}'.format(indent, nodeDict.get('flow-node-inventory:switch-features', {})), 2) prt('{}sw: {}'.format(indent, nodeDict.get('flow-node-inventory:software')), 2) prt('{}hw: {}'.format(indent, nodeDict.get('flow-node-inventory:hardware')), 2) prt('{}manuf: {}'.format(indent, nodeDict.get('flow-node-inventory:manufacturer')), 2) prtLn('{}ip: {}'.format(indent, nodeDict.get('flow-node-inventory:ip-address')), 2) for inventoryEntry in nodeDict.get('flow-node-inventory:table', []): if 'id' in inventoryEntry: currTableId = inventoryEntry.get('id') for currFlow in inventoryEntry.get('flow', []): prtLn('{}table {}: {}'.format(indent, currTableId, currFlow.get('id')), 1) prtLn('{}{}'.format(indent * 2, currFlow), 2) if currTableId in flowInfoNode: flowInfoNode[ currTableId ].append( currFlow.get('id') ) else: flowInfoNode[ currTableId ] = [ currFlow.get('id') ] prtLn('', 1) for currTableId in flowInfoNode.keys(): flowInfoNode[currTableId].sort() # store info collected in flowInfoNodes flowInfoNodes[bridgeOfId] = flowInfoNode # -- def grabTopologyJson(mdsalTreeType): global jsonTopologyNodes url = 'http://{}:{}/restconf/{}/network-topology:network-topology/'.format(options.odlIp, options.odlPort, mdsalTreeType) data = grabJson(url) if not CONST_NET_TOPOLOGY in data: printError( '{}\n\nError: did not find {} in data'.format(data, CONST_NET_TOPOLOGY) ) sys.exit(1) data2 = data[CONST_NET_TOPOLOGY] if not CONST_TOPOLOGY in data2: printError( '{}\n\nError: did not find {} in data2'.format(data2, CONST_TOPOLOGY) ) sys.exit(1) jsonTopologyNodes = data2[CONST_TOPOLOGY] # -- def buildDpidCache(): global jsonTopologyNodes global nodeIdToDpidCache # only needed if not parsing operational tree if getMdsalTreeType() == CONST_OPERATIONAL: return jsonTopologyNodesSave = jsonTopologyNodes grabTopologyJson(CONST_OPERATIONAL) jsonTopologyNodesLocal = jsonTopologyNodes jsonTopologyNodes = jsonTopologyNodesSave for nodeDict in jsonTopologyNodesLocal: if nodeDict.get('topology-id') != 'ovsdb:1': continue for node in nodeDict.get('node', []): if node.get('node-id') is None or node.get('ovsdb:datapath-id') is None: continue nodeIdToDpidCache[ node.get('node-id') ] = node.get('ovsdb:datapath-id') # -- def parseTopologyJson(mdsalTreeType): for nodeDict in jsonTopologyNodes: if not 'topology-id' in nodeDict: continue prtLn('{} {} keys are: {}'.format(mdsalTreeType, nodeDict['topology-id'], nodeDict.keys()), 3) if 'node' in nodeDict: nodeIndex = 0 for currNode in nodeDict['node']: parseTopologyJsonNode('', mdsalTreeType, nodeDict['topology-id'], nodeIndex, currNode) nodeIndex += 1 prtLn('', 2) if (mdsalTreeType == CONST_OPERATIONAL) and (nodeDict['topology-id'] == 'flow:1') and ('link' in nodeDict): parseTopologyJsonFlowLink(nodeDict['link']) prtLn('', 1) # -- def parseTopologyJsonNode(indent, mdsalTreeType, topologyId, nodeIndex, node): if node.get('node-id') is None: printError( 'Warning: unexpected node: {}\n'.format(node) ) return prt('{} {} node[{}] {} '.format(indent + mdsalTreeType, topologyId, nodeIndex, node.get('node-id')), 2) if 'ovsdb:bridge-name' in node: prtLn('', 2) parseTopologyJsonNodeBridge(indent + ' ', mdsalTreeType, topologyId, nodeIndex, node) elif 'ovsdb:connection-info' in node: prtLn('', 2) parseTopologyJsonNodeOvsdb(indent + ' ', mdsalTreeType, topologyId, nodeIndex, node) else: prtLn('keys: {}'.format(node.keys()), 2) # -- def parseTopologyJsonNodeOvsdb(indent, mdsalTreeType, topologyId, nodeIndex, node): keys = node.keys() keys.sort() for k in keys: prtLn('{}{} : {}'.format(indent, k, node[k]), 2) connectionInfoRaw = node.get('ovsdb:connection-info') connectionInfo = {} if type(connectionInfoRaw) is dict: connectionInfo['inetMgr'] = make_it_a_string(connectionInfoRaw.get('local-ip')) + ':' + make_it_a_string(connectionInfoRaw.get('local-port')) connectionInfo['inetNode'] = make_it_a_string(connectionInfoRaw.get('remote-ip')) + ':' + make_it_a_string(connectionInfoRaw.get('remote-port')) otherConfigsRaw = node.get('ovsdb:openvswitch-other-configs') otherLocalIp = '' if type(otherConfigsRaw) is list: for currOtherConfig in otherConfigsRaw: if type(currOtherConfig) is dict and \ currOtherConfig.get('other-config-key') == 'local_ip': otherLocalIp = currOtherConfig.get('other-config-value') break ovsdbNode = OvsdbNode(node.get('node-id'), connectionInfo.get('inetMgr'), connectionInfo.get('inetNode'), otherLocalIp, node.get('ovsdb:ovs-version')) state.registerOvsdbNode(ovsdbNode) prtLn('Added {}'.format(ovsdbNode), 1) # -- def parseTopologyJsonNodeBridge(indent, mdsalTreeType, topologyId, nodeIndex, node): controllerTarget = None controllerConnected = None controllerEntries = node.get('ovsdb:controller-entry') if type(controllerEntries) is list: for currControllerEntry in controllerEntries: if type(currControllerEntry) is dict: controllerTarget = currControllerEntry.get('target') controllerConnected = currControllerEntry.get('is-connected') break nodeId = node.get('node-id') dpId = node.get('ovsdb:datapath-id', nodeIdToDpidCache.get(nodeId)) bridgeNode = BridgeNode(nodeId, dpId, node.get('ovsdb:bridge-name'), controllerTarget, controllerConnected) keys = node.keys() keys.sort() for k in keys: if k == 'termination-point' and len(node[k]) > 0: tpIndex = 0 for tp in node[k]: terminationPoint = parseTopologyJsonNodeBridgeTerminationPoint('%stermination-point[%d] :' % (indent, tpIndex), mdsalTreeType, topologyId, nodeIndex, node, tp) # skip boring tps if terminationPoint.ofPort == CONST_TP_OF_INTERNAL and \ (terminationPoint.name == 'br-ex' or terminationPoint.name == 'br-int'): pass else: bridgeNode.addTerminationPoint(terminationPoint) tpIndex += 1 else: prtLn('{}{} : {}'.format(indent, k, node[k]), 2) state.registerBridgeNode(bridgeNode) prtLn('Added {}'.format(bridgeNode), 1) # -- def parseTopologyJsonNodeBridgeTerminationPoint(indent, mdsalTreeType, topologyId, nodeIndex, node, tp): attachedMac = '' ifaceId = '' keys = tp.keys() keys.sort() for k in keys: if (k == 'ovsdb:port-external-ids' or k == 'ovsdb:interface-external-ids') and len(tp[k]) > 0: extIdIndex = 0 for extId in tp[k]: prtLn('{} {}[{}] {} : {}'.format(indent, k, extIdIndex, extId.get('external-id-key'), extId.get('external-id-value')), 2) extIdIndex += 1 if extId.get('external-id-key') == 'attached-mac': attachedMac = extId.get('external-id-value') if extId.get('external-id-key') == 'iface-id': ifaceId = extId.get('external-id-value') else: prtLn('{} {} : {}'.format(indent, k, tp[k]), 2) return TerminationPoint(tp.get('ovsdb:name'), tp.get('ovsdb:ofport'), tp.get('ovsdb:interface-type', '').split('-')[-1], attachedMac, ifaceId) # -- def parseTopologyJsonFlowLink(link): linkCount = 0 spc = ' ' * 2 for currLinkDict in link: linkCount += 1 linkId = currLinkDict.get('link-id') linkDest = currLinkDict.get('destination', {}).get('dest-tp') linkSrc = currLinkDict.get('source', {}).get('source-tp') linkDestNode = currLinkDict.get('destination', {}).get('dest-node') linkSrcNode = currLinkDict.get('source', {}).get('source-node') prtLn('{} {} {} => {}:{} -> {}:{}'.format(spc, linkCount, linkId, linkSrcNode, linkSrc.split(':')[-1], linkDestNode, linkDest.split(':')[-1]), 3) if linkId != linkSrc: printError('Warning: ignoring link with unexpected id: %s != %s\n' % (linkId, linkSrc)) continue else: state.ofLinks[linkSrc] = linkDest # -- def showPrettyNamesMap(): spc = ' ' * 2 if not options.useAlias or len(state.bridgeNodes) == 0: return prtLn('aliasMap:', 0) resultMap = {} for bridge in state.bridgeNodes.values(): resultMap[ bridge.alias ] = '{0: <25} {1: <7} {2}'.format(bridge.getOpenflowName(), bridge.name, bridge.dpId) for resultMapKey in sorted(resultMap): prtLn('{0}{1: <10} -> {2}'.format(spc, resultMapKey, resultMap[resultMapKey]), 0) prtLn('', 0) # -- def showNodesPretty(): if len(state.ovsdbNodes) == 0: showBridgeOnlyNodes() return aliasDict = { state.ovsdbNodes[nodeId].alias : nodeId for nodeId in state.ovsdbNodes.keys() } aliasDictKeys = aliasDict.keys() aliasDictKeys.sort() for ovsAlias in aliasDictKeys: ovsdbNode = state.ovsdbNodes[ aliasDict[ovsAlias] ] prt('ovsdbNode:{} mgr:{} version:{}'.format(ovsAlias, ovsdbNode.inetMgr, ovsdbNode.ovsVersion), 0) if ovsdbNode.inetNode.split(':')[0] != ovsdbNode.otherLocalIp: prt(' **localIp:{}'.format(ovsdbNode.otherLocalIp), 0) prtLn('', 0) showPrettyBridgeNodes(' ', getNodeBridgeIds(ovsdbNode.nodeId), ovsdbNode) showBridgeOnlyNodes(True) prtLn('', 0) # -- def showFlowInfoPretty(): global flowInfoNodes spc = ' ' * 2 if not options.showFlows: return if len(flowInfoNodes) == 0: prtLn('no flowInfo found\n', 0) return # translate flowKeys (openflow:123124) into their alias format # then sort it and translate back, so we list them in the order flowInfoNodeKeysDict = {} for flowInfoNodeKey in flowInfoNodes.keys(): flowInfoNodeKeysDict[ showPrettyName(flowInfoNodeKey) ] = flowInfoNodeKey flowInfoNodeKeysKeys = flowInfoNodeKeysDict.keys() flowInfoNodeKeysKeys.sort() flowInfoNodesKeys = [ flowInfoNodeKeysDict[ x ] for x in flowInfoNodeKeysKeys ] nodeIdToDpidCacheReverse = {dataPathIdToOfFormat(v): k for k, v in nodeIdToDpidCache.items()} nodesVisited = 0 for flowInfoNodeKey in flowInfoNodesKeys: if nodesVisited > 0: prtLn('', 0) nodeName = showPrettyName(flowInfoNodeKey) if nodeName == flowInfoNodeKey: nodeName += ' ( {} )'.format( nodeIdToDpidCacheReverse.get(flowInfoNodeKey, 'node_not_in_topology') ) prtLn('{} tree flows at {}'.format(getMdsalTreeType(), nodeName), 0) flowInfoNode = flowInfoNodes[flowInfoNodeKey] flowInfoTables = flowInfoNode.keys() flowInfoTables.sort() for flowInfoTable in flowInfoTables: for rule in flowInfoNode[flowInfoTable]: prtLn('{}table {}: {}'.format(spc, flowInfoTable, rule), 0) nodesVisited += 1 prtLn('', 0) # -- def getNodeBridgeIds(nodeIdFilter = None): resultMap = {} for bridge in state.bridgeNodes.values(): if nodeIdFilter is None or nodeIdFilter in bridge.nodeId: resultMap[ bridge.alias ] = bridge.nodeId resultMapKeys = resultMap.keys() resultMapKeys.sort() return [ resultMap[x] for x in resultMapKeys ] # -- def showPrettyBridgeNodes(indent, bridgeNodeIds, ovsdbNode = None): if bridgeNodeIds is None: return for nodeId in bridgeNodeIds: bridgeNode = state.bridgeNodes[nodeId] prt('{}{}:{}'.format(indent, showPrettyName(nodeId), bridgeNode.name), 0) if ovsdbNode is None or \ bridgeNode.controllerTarget is None or \ bridgeNode.controllerTarget == '' or \ ovsdbNode.inetMgr.split(':')[0] != bridgeNode.controllerTarget.split(':')[-2] or \ bridgeNode.controllerConnected != True: prt(' controller:{}'.format(bridgeNode.controllerTarget), 0) prt(' connected:{}'.format(bridgeNode.controllerConnected), 0) prtLn('', 0) showPrettyTerminationPoints(indent + ' ', bridgeNode.tps) # -- def showBridgeOnlyNodes(showOrphansOnly = False): if len(state.bridgeNodes) == 0: return # group bridges by nodeId prefix resultMap = {} for bridge in state.bridgeNodes.values(): nodePrefix = bridge.nodeId.split('/bridge/')[0] if showOrphansOnly and nodePrefix in state.ovsdbNodes: continue if nodePrefix in resultMap: resultMap[nodePrefix][bridge.alias] = bridge.nodeId else: resultMap[nodePrefix] = { bridge.alias: bridge.nodeId } resultMapKeys = resultMap.keys() resultMapKeys.sort() if len(resultMapKeys) == 0: return #noop for nodePrefix in resultMapKeys: nodePrefixEntriesKeys = resultMap[nodePrefix].keys() nodePrefixEntriesKeys.sort() # prtLn('Bridges in {}: {}'.format(nodePrefix, nodePrefixEntriesKeys), 0) prtLn('Bridges in {}'.format(nodePrefix), 0) nodeIds = [ resultMap[nodePrefix][nodePrefixEntry] for nodePrefixEntry in nodePrefixEntriesKeys ] showPrettyBridgeNodes(' ', nodeIds) prtLn('', 0) # -- def showPrettyTerminationPoints(indent, tps): tpsDict = {} for tp in tps: tpsDict[ tp.ofPort ] = tp tpDictKeys = tpsDict.keys() tpDictKeys.sort() for tpKey in tpDictKeys: tp = tpsDict[tpKey] prt('{}of:{} {}'.format(indent, tp.ofPort, tp.name), 0) if tp.mac != '': prt(' {}:{}'.format('mac', tp.mac), 0) if tp.ifaceId != '': prt(' {}:{}'.format('ifaceId', tp.ifaceId), 0) prtLn('', 0) # -- def dataPathIdToOfFormat(dpId): return 'openflow:' + str( int('0x' + dpId.replace(':',''), 16) ) # -- def showPrettyName(name): if not options.useAlias: return name # handle both openflow:138604958315853:2 and openflow:138604958315853 (aka dpid) # also handle ovsdb://uuid/5c72ec51-1e71-4a04-ab0b-b044fb5f4dc0/bridge/br-int (aka nodeId) # nameSplit = name.split(':') ofName = ':'.join(nameSplit[:2]) ofPart = '' if len(nameSplit) > 2: ofPart = ':' + ':'.join(nameSplit[2:]) for bridge in state.bridgeNodes.values(): if bridge.getOpenflowName() == ofName or bridge.nodeId == name: return '{}{}'.format(bridge.alias, ofPart) # not found, return paramIn return name # -- def showOfLinks(): spc = ' ' * 2 ofLinksKeys = state.ofLinks.keys() ofLinksKeys.sort() ofLinksKeysVisited = set() if len(ofLinksKeys) == 0: # prtLn('no ofLinks found\n', 0) return prtLn('ofLinks (discover via lldp):', 0) for ofLinkKey in ofLinksKeys: if ofLinkKey in ofLinksKeysVisited: continue if state.ofLinks.get( state.ofLinks[ofLinkKey] ) == ofLinkKey: prtLn('{}{} <-> {}'.format(spc, showPrettyName(ofLinkKey), showPrettyName(state.ofLinks[ofLinkKey])), 0) ofLinksKeysVisited.add(state.ofLinks[ofLinkKey]) else: prtLn('{}{} -> {}'.format(spc, showPrettyName(ofLinkKey), showPrettyName(state.ofLinks[ofLinkKey])), 0) ofLinksKeysVisited.add(ofLinkKey) prtLn('', 0) # -- def parseArgv(): global options parser = optparse.OptionParser(version="0.1") parser.add_option("-d", "--debug", action="count", dest="debug", default=CONST_DEFAULT_DEBUG, help="Verbosity. Can be provided multiple times for more debug.") parser.add_option("-n", "--noalias", action="store_false", dest="useAlias", default=True, help="Do not map nodeId of bridges to an alias") parser.add_option("-i", "--ip", action="store", type="string", dest="odlIp", default="localhost", help="opendaylights ip address") parser.add_option("-t", "--port", action="store", type="string", dest="odlPort", default="8080", help="opendaylights listening tcp port on restconf northbound") parser.add_option("-u", "--user", action="store", type="string", dest="odlUsername", default="admin", help="opendaylight restconf username") parser.add_option("-p", "--password", action="store", type="string", dest="odlPassword", default="admin", help="opendaylight restconf password") parser.add_option("-c", "--config", action="store_true", dest="useConfigTree", default=False, help="parse mdsal restconf config tree instead of operational tree") parser.add_option("-f", "--hide-flows", action="store_false", dest="showFlows", default=True, help="hide flows") (options, args) = parser.parse_args(sys.argv) prtLn('argv options:{} args:{}'.format(options, args), 2) # -- def doMain(): global state state = State() parseArgv() buildDpidCache() grabTopologyJson(getMdsalTreeType()) grabInventoryJson(getMdsalTreeType()) parseTopologyJson(getMdsalTreeType()) parseInventoryJson(getMdsalTreeType()) showPrettyNamesMap() showNodesPretty() showFlowInfoPretty() showOfLinks() # -- if __name__ == "__main__": doMain() sys.exit(0)