#!/usr/bin/python import argparse import requests import re import json __author__ = "Jan Medved" __copyright__ = "Copyright(c) 2014, Cisco Systems, Inc." __license__ = "New-style BSD" __email__ = "jmedved@cisco.com" class InventoryCrawler(object): reported_flows = 0 found_flows = 0 nodes = 0 INVENTORY_URL = "restconf/%s/opendaylight-inventory:nodes" hdr = {"Accept": "application/json"} OK, ERROR = range(2) table_stats_unavailable = 0 table_stats_fails = [] def __init__(self, host, port, plevel, datastore, auth, debug): self.url = "http://" + host + ":" + port + "/" + self.INVENTORY_URL % datastore self.plevel = plevel self.auth = auth self.debug = debug def crawl_flows(self, flows): """ Collects and prints summary information for all flows in a table """ self.found_flows += len(flows) if self.plevel > 1: print(" Flows found: %d\n" % len(flows)) if self.plevel > 2: for f in flows: s = json.dumps(f, sort_keys=True, indent=4, separators=(",", ": ")) # s = s.replace('{\n', '') # s = s.replace('}', '') s = s.strip() s = s.lstrip("{") s = s.rstrip("}") s = s.replace("\n", "\n ") s = s.lstrip("\n") print(" Flow %s:" % (f["id"])) print(s) def crawl_table(self, table): """ Collects and prints summary statistics information about a single table. Depending on the print level (plevel), it also invokes the crawl_flows """ try: stats = table["opendaylight-flow-table-statistics:flow-table-statistics"] active_flows = int(stats["active-flows"]) if active_flows > 0: self.reported_flows += active_flows if self.plevel > 1: print(" Table %s:" % table["id"]) s = json.dumps( stats, sort_keys=True, indent=12, separators=(",", ": ") ) s = s.replace("{\n", "") s = s.replace("}", "") print(s) except KeyError: if self.plevel > 1: print(" Stats for Table '%s' not available." % (table["id"])) self.table_stats_unavailable += 1 pass try: flows_in_table = table["flow"] self.crawl_flows(flows_in_table) except KeyError: pass def crawl_node(self, node): """ Collects and prints summary information about a single node """ self.table_stats_unavailable = 0 self.nodes += 1 if self.plevel > 1: print("\nNode '%s':" % ((node["id"]))) elif self.plevel > 0: print("%s" % ((node["id"]))) try: tables = node["flow-node-inventory:table"] if self.plevel > 1: print(" Tables: %d" % len(tables)) for t in tables: self.crawl_table(t) if self.table_stats_unavailable > 0: self.table_stats_fails.append(node["id"]) except KeyError: if self.plevel > 1: print(" Data for tables not available.") def crawl_inventory(self): """ Collects and prints summary information about all openflow nodes in a data store (either operational or config) """ self.nodes = 0 self.found_flows = 0 self.reported_flows = 0 self.table_stats_unavailable = 0 self.table_stats_fails = [] s = requests.Session() if not self.auth: r = s.get(self.url, headers=self.hdr, stream=False) else: r = s.get(self.url, headers=self.hdr, stream=False, auth=("admin", "admin")) if r.status_code == 200: try: inv = json.loads(r.content)["nodes"]["node"] sinv = [] for n in range(len(inv)): if re.search("openflow", inv[n]["id"]) is not None: sinv.append(inv[n]) sinv = sorted(sinv, key=lambda k: int(re.findall(r"\d+", k["id"])[0])) for n in range(len(sinv)): try: self.crawl_node(sinv[n]) except Exception: print("Can not crawl %s" % sinv[n]["id"]) except KeyError: print("Could not retrieve inventory, response not in JSON format") else: print("Could not retrieve inventory, HTTP error %d" % r.status_code) s.close() def set_plevel(self, plevel): self.plevel = plevel if __name__ == "__main__": parser = argparse.ArgumentParser(description="Restconf test program") parser.add_argument( "--host", default="127.0.0.1", help="host where " "the controller is running; default 127.0.0.1", ) parser.add_argument( "--port", default="8181", help="port on " "which odl's RESTCONF is listening; default 8181", ) parser.add_argument( "--plevel", type=int, default=0, help="Print Level: 0 - Summary (stats only); 1 - Node names; 2 - Node details;" "3 - Flow details", ) parser.add_argument( "--datastore", choices=["operational", "config"], default="operational", help="Which data store to crawl; default operational", ) parser.add_argument( "--no-auth", dest="auth", action="store_false", default=False, help="Do not use authenticated access to REST (default)", ) parser.add_argument( "--auth", dest="auth", action="store_true", help="Use authenticated access to REST (username: 'admin', password: 'admin').", ) parser.add_argument( "--debug", dest="debug", action="store_true", default=False, help="List nodes that have not provided proper statistics data", ) in_args = parser.parse_args() ic = InventoryCrawler( in_args.host, in_args.port, in_args.plevel, in_args.datastore, in_args.auth, in_args.debug, ) print("Crawling '%s'" % (ic.url)) ic.crawl_inventory() print("\nTotals:") print(" Nodes: %d" % ic.nodes) print(" Reported flows: %d" % ic.reported_flows) print(" Found flows: %d" % ic.found_flows) if in_args.debug: n_missing = len(ic.table_stats_fails) if n_missing > 0: print("\nMissing table stats (%d nodes):" % n_missing) print("%s\n" % (", ".join([x for x in ic.table_stats_fails])))