Added shard performance tests (shard_perf_test.py and shard_multi_test.sh)
[integration/test.git] / test / tools / odl-mdsal-clustering-tests / clustering-performance-test / inventory_crawler.py
1 #!/usr/bin/python
2 __author__ = "Jan Medved"
3 __copyright__ = "Copyright(c) 2014, Cisco Systems, Inc."
4 __license__ = "New-style BSD"
5 __email__ = "jmedved@cisco.com"
6
7 import argparse
8 import requests
9 import re
10 import json
11
12
13 class InventoryCrawler(object):
14     reported_flows = 0
15     found_flows = 0
16     nodes = 0
17
18     INVENTORY_URL = 'restconf/%s/opendaylight-inventory:nodes'
19     hdr = {'Accept': 'application/json'}
20     OK, ERROR = range(2)
21     table_stats_unavailable = 0
22     table_stats_fails = []
23
24     def __init__(self, host, port, plevel, datastore, auth, debug):
25         self.url = 'http://' + host + ":" + port + '/' + self.INVENTORY_URL % datastore
26         self.plevel = plevel
27         self.auth = auth
28         self.debug = debug
29
30
31     def crawl_flows(self, flows):
32         """
33         Collects and prints summary information for all flows in a table
34         """
35         self.found_flows += len(flows)
36         if self.plevel > 1:
37             print '             Flows found: %d\n' % len(flows)
38             if self.plevel > 2:
39                 for f in flows:
40                     s = json.dumps(f, sort_keys=True, indent=4, separators=(',', ': '))
41                     # s = s.replace('{\n', '')
42                     # s = s.replace('}', '')
43                     s = s.strip()
44                     s = s.lstrip('{')
45                     s = s.rstrip('}')
46                     s = s.replace('\n', '\n            ')
47                     s = s.lstrip('\n')
48                     print "             Flow %s:" % f['id']
49                     print s
50
51
52     def crawl_table(self, table):
53         """
54         Collects and prints summary statistics information about a single table. Depending on the print level
55         (plevel), it also invokes the crawl_flows
56         """
57         try:
58             stats = table['opendaylight-flow-table-statistics:flow-table-statistics']
59             active_flows = int(stats['active-flows'])
60
61             if active_flows > 0:
62                 self.reported_flows += active_flows
63                 if self.plevel > 1:
64                     print '        Table %s:' % table['id']
65                     s = json.dumps(stats, sort_keys=True, indent=12, separators=(',', ': '))
66                     s = s.replace('{\n', '')
67                     s = s.replace('}', '')
68                     print s
69         except KeyError:
70             if self.plevel > 1:
71                 print "        Stats for Table '%s' not available." % table['id']
72             self.table_stats_unavailable += 1
73             pass
74
75         try:
76             flows_in_table = table['flow']
77             self.crawl_flows(flows_in_table)
78         except KeyError:
79             pass
80
81
82     def crawl_node(self, node):
83         """
84         Collects and prints summary information about a single node
85         """
86         self.table_stats_unavailable = 0
87         self.nodes += 1
88
89         if self.plevel > 1:
90             print "\nNode '%s':" % (node['id'])
91         elif self.plevel > 0:
92             print "%s" % (node['id'])
93
94         try:
95             tables = node['flow-node-inventory:table']
96             if self.plevel > 1:
97                 print '    Tables: %d' % len(tables)
98
99             for t in tables:
100                 self.crawl_table(t)
101
102             if self.table_stats_unavailable > 0:
103                 self.table_stats_fails.append(node['id'])
104
105         except KeyError:
106             if self.plevel > 1:
107                 print '    Data for tables not available.'
108
109
110     def crawl_inventory(self):
111         """
112         Collects and prints summary information about all openflow nodes in a data store (either operational or config)
113         """
114         self.found_flows = 0
115         self.reported_flows = 0
116         self.table_stats_unavailable = 0
117         self.table_stats_fails = []
118
119         s = requests.Session()
120         if not self.auth:
121             r = s.get(self.url, headers=self.hdr, stream=False)
122         else:
123             r = s.get(self.url, headers=self.hdr, stream=False, auth=('admin', 'admin'))
124
125         if r.status_code == 200:
126             try:
127                 inv = json.loads(r.content)['nodes']['node']
128                 sinv = []
129                 for n in range(len(inv)):
130                     if re.search('openflow', inv[n]['id']) is not None:
131                         sinv.append(inv[n])
132
133                 sinv = sorted(sinv, key=lambda k: int(re.findall('\d+', k['id'])[0]))
134
135                 for n in range(len(sinv)):
136                     try:
137                         self.crawl_node(sinv[n])
138                     except:
139                         print 'Can not crawl %s' % sinv[n]['id']
140
141             except KeyError:
142                 print 'Could not retrieve inventory, response not in JSON format'
143         else:
144             print 'Could not retrieve inventory, HTTP error %d' % r.status_code
145
146         s.close()
147
148
149     def set_plevel(self, plevel):
150         self.plevel = plevel
151
152
153
154
155 if __name__ == "__main__":
156     parser = argparse.ArgumentParser(description='Restconf test program')
157     parser.add_argument('--host', default='127.0.0.1', help='host where '
158                                                                'the controller is running; default 127.0.0.1')
159     parser.add_argument('--port', default='8181', help='port on '
160                                                           'which odl\'s RESTCONF is listening; default 8181')
161     parser.add_argument('--plevel', type=int, default=0,
162                         help='Print Level: 0 - Summary (stats only); 1 - Node names; 2 - Node details;'
163                              '3 - Flow details')
164     parser.add_argument('--datastore', choices=['operational', 'config'],
165                         default='operational', help='Which data store to crawl; default operational')
166     parser.add_argument('--no-auth', dest='auth', action='store_false', default=False,
167                         help="Do not use authenticated access to REST (default)")
168     parser.add_argument('--auth', dest='auth', action='store_true',
169                         help="Use authenticated access to REST (username: 'admin', password: 'admin').")
170     parser.add_argument('--debug', dest='debug', action='store_true', default=False,
171                         help="List nodes that have not provided proper statistics data")
172
173     in_args = parser.parse_args()
174
175     ic = InventoryCrawler(in_args.host, in_args.port, in_args.plevel, in_args.datastore, in_args.auth,
176                           in_args.debug)
177
178     print "Crawling '%s'" % ic.url
179     ic.crawl_inventory()
180
181     print '\nTotals:'
182     print '    Nodes:          %d' % ic.nodes
183     print '    Reported flows: %d' % ic.reported_flows
184     print '    Found flows:    %d' % ic.found_flows
185
186     if in_args.debug:
187         n_missing = len(ic.table_stats_fails)
188         if n_missing > 0:
189             print '\nMissing table stats (%d nodes):' % n_missing
190             print "%s\n" % ", ".join([x for x in ic.table_stats_fails])
191
192