8 __author__ = "Jan Medved"
9 __copyright__ = "Copyright(c) 2014, Cisco Systems, Inc."
10 __license__ = "New-style BSD"
11 __email__ = "jmedved@cisco.com"
14 class InventoryCrawler(object):
19 INVENTORY_URL = "restconf/%s/opendaylight-inventory:nodes"
20 hdr = {"Accept": "application/json"}
22 table_stats_unavailable = 0
23 table_stats_fails = []
25 def __init__(self, host, port, plevel, datastore, auth, debug):
26 self.url = "http://" + host + ":" + port + "/" + self.INVENTORY_URL % datastore
31 def crawl_flows(self, flows):
33 Collects and prints summary information for all flows in a table
35 self.found_flows += len(flows)
37 print(" Flows found: %d\n" % len(flows))
40 s = json.dumps(f, sort_keys=True, indent=4, separators=(",", ": "))
41 # s = s.replace('{\n', '')
42 # s = s.replace('}', '')
46 s = s.replace("\n", "\n ")
48 print(" Flow %s:" % (f["id"]))
51 def crawl_table(self, table):
53 Collects and prints summary statistics information about a single table. Depending on the print level
54 (plevel), it also invokes the crawl_flows
57 stats = table["opendaylight-flow-table-statistics:flow-table-statistics"]
58 active_flows = int(stats["active-flows"])
61 self.reported_flows += active_flows
63 print(" Table %s:" % table["id"])
65 stats, sort_keys=True, indent=12, separators=(",", ": ")
67 s = s.replace("{\n", "")
68 s = s.replace("}", "")
72 print(" Stats for Table '%s' not available." % (table["id"]))
73 self.table_stats_unavailable += 1
77 flows_in_table = table["flow"]
78 self.crawl_flows(flows_in_table)
82 def crawl_node(self, node):
84 Collects and prints summary information about a single node
86 self.table_stats_unavailable = 0
90 print("\nNode '%s':" % ((node["id"])))
92 print("%s" % ((node["id"])))
95 tables = node["flow-node-inventory:table"]
97 print(" Tables: %d" % len(tables))
102 if self.table_stats_unavailable > 0:
103 self.table_stats_fails.append(node["id"])
107 print(" Data for tables not available.")
109 def crawl_inventory(self):
111 Collects and prints summary information about all openflow nodes in a data store (either operational or config)
115 self.reported_flows = 0
116 self.table_stats_unavailable = 0
117 self.table_stats_fails = []
119 s = requests.Session()
121 r = s.get(self.url, headers=self.hdr, stream=False)
123 r = s.get(self.url, headers=self.hdr, stream=False, auth=("admin", "admin"))
125 if r.status_code == 200:
127 inv = json.loads(r.content)["nodes"]["node"]
129 for n in range(len(inv)):
130 if re.search("openflow", inv[n]["id"]) is not None:
133 sinv = sorted(sinv, key=lambda k: int(re.findall(r"\d+", k["id"])[0]))
135 for n in range(len(sinv)):
137 self.crawl_node(sinv[n])
139 print("Can not crawl %s" % sinv[n]["id"])
142 print("Could not retrieve inventory, response not in JSON format")
144 print("Could not retrieve inventory, HTTP error %d" % r.status_code)
148 def set_plevel(self, plevel):
152 if __name__ == "__main__":
153 parser = argparse.ArgumentParser(description="Restconf test program")
157 help="host where " "the controller is running; default 127.0.0.1",
162 help="port on " "which odl's RESTCONF is listening; default 8181",
168 help="Print Level: 0 - Summary (stats only); 1 - Node names; 2 - Node details;"
173 choices=["operational", "config"],
174 default="operational",
175 help="Which data store to crawl; default operational",
180 action="store_false",
182 help="Do not use authenticated access to REST (default)",
188 help="Use authenticated access to REST (username: 'admin', password: 'admin').",
195 help="List nodes that have not provided proper statistics data",
198 in_args = parser.parse_args()
200 ic = InventoryCrawler(
209 print("Crawling '%s'" % (ic.url))
213 print(" Nodes: %d" % ic.nodes)
214 print(" Reported flows: %d" % ic.reported_flows)
215 print(" Found flows: %d" % ic.found_flows)
218 n_missing = len(ic.table_stats_fails)
220 print("\nMissing table stats (%d nodes):" % n_missing)
221 print("%s\n" % (", ".join([x for x in ic.table_stats_fails])))