96613a2bc1357a6ddb0c5f2749beea9d1ec15a54
[integration/test.git] / tools / odl-mdsal-clustering-tests / clustering-performance-test / onos_stats.py
1 import requests
2 import json
3 import argparse
4 import sys
5 import netaddr
6 import threading
7 import Queue
8 import random  # noqa  # FIXME: This script seems to be unfinished!
9 import copy
10 import time
11
12
13 flow_template = {
14     "appId": 10,
15     "priority": 40000,
16     "timeout": 0,
17     "isPermanent": True,
18     "deviceId": "of:0000000000000001",
19     "treatment": {
20         "instructions": [
21             {
22                 "type": "NOACTION"
23             }
24         ],
25         "deferred": []
26     },
27     "selector": {
28         "criteria": [
29             {
30                 "type": "ETH_TYPE",
31                 "ethType": 2048
32             },
33             {
34                 "type": "IPV4_DST",
35                 "ip": "10.0.0.0/32"
36             }
37         ]
38     }
39 }
40
41
42 class Timer(object):
43     def __init__(self, verbose=False):
44         self.verbose = verbose
45
46     def __enter__(self):
47         self.start = time.time()
48         return self
49
50     def __exit__(self, *args):
51         self.end = time.time()
52         self.secs = self.end - self.start
53         self.msecs = self.secs * 1000  # millisecs
54         if self.verbose:
55             print("elapsed time: %f ms" % self.msecs)
56
57
58 class Counter(object):
59     def __init__(self, start=0):
60         self.lock = threading.Lock()
61         self.value = start
62
63     def increment(self, value=1):
64         self.lock.acquire()
65         val = self.value
66         try:
67             self.value += value
68         finally:
69             self.lock.release()
70         return val
71
72
73 def _prepare_post(cntl, method, flows, template=None):
74     """Creates a POST http requests to configure a flow in configuration datastore.
75
76     Args:
77         :param cntl: controller's ip address or hostname
78
79         :param method: determines http request method
80
81         :param flows: list of flow details
82
83         :param template: flow template to be to be filled
84
85     Returns:
86         :returns req: http request object
87     """
88     fl1 = flows[0]
89     dev_id, ip = fl1
90     url = 'http://' + cntl + ':' + '8181/onos/v1/flows/' + dev_id
91     flow = copy.deepcopy(template)
92     flow["deviceId"] = dev_id
93     flow["selector"]["criteria"][1]["ip"] = '%s/32' % str(netaddr.IPAddress(ip))
94     req_data = json.dumps(flow)
95     req = requests.Request(method, url, headers={'Content-Type': 'application/json'},
96                            data=req_data, auth=('onos', 'rocks'))
97     return req
98
99
100 def _prepare_delete(cntl, method, flows, template=None):
101     """Creates a DELETE http requests to configure a flow in configuration datastore.
102
103     Args:
104         :param cntl: controller's ip address or hostname
105
106         :param method: determines http request method
107
108         :param flows: list of flow details
109
110         :param template: flow template to be to be filled
111
112     Returns:
113         :returns req: http request object
114     """
115     fl1 = flows[0]
116     dev_id, flow_id = fl1
117     url = 'http://' + cntl + ':' + '8181/onos/v1/flows/' + dev_id + '/' + flow_id
118     req = requests.Request(method, url, auth=('onos', 'rocks'))
119     return req
120
121
122 def _wt_request_sender(thread_id, preparefnc, inqueue=None, exitevent=None, controllers=[], restport='',
123                        template=None, outqueue=None, method=None):
124     """The funcion sends http requests.
125
126     Runs in the working thread. It reads out flow details from the queue and sends apropriate http requests
127     to the controller
128
129     Args:
130         :param thread_id: thread id
131
132         :param preparefnc: function to preparesthe http request
133
134         :param inqueue: input queue, flow details are comming from here
135
136         :param exitevent: event to notify working thread that parent (task executor) stopped filling the input queue
137
138         :param controllers: a list of controllers' ip addresses or hostnames
139
140         :param restport: restconf port
141
142         :param template: flow template used for creating flow content
143
144         :param outqueue: queue where the results should be put
145
146         :param method: method derermines the type of http request
147
148     Returns:
149         nothing, results must be put into the output queue
150     """
151     ses = requests.Session()
152     cntl = controllers[0]
153     counter = [0 for i in range(600)]
154     loop = True
155
156     while loop:
157         try:
158             flowlist = inqueue.get(timeout=1)
159         except Queue.Empty:
160             if exitevent.is_set() and inqueue.empty():
161                 loop = False
162             continue
163         req = preparefnc(cntl, method, flowlist, template=template)
164         # prep = ses.prepare_request(req)
165         prep = req.prepare()
166         try:
167             rsp = ses.send(prep, timeout=5)
168         except requests.exceptions.Timeout:
169             counter[99] += 1
170             continue
171         counter[rsp.status_code] += 1
172     res = {}
173     for i, v in enumerate(counter):
174         if v > 0:
175             res[i] = v
176     outqueue.put(res)
177
178
179 def get_device_ids(controller='127.0.0.1', port=8181):
180     """Returns a list of switch ids"""
181     rsp = requests.get(url='http://{0}:{1}/onos/v1/devices'.format(controller, port), auth=('onos', 'rocks'))
182     if rsp.status_code != 200:
183         return []
184     devices = json.loads(rsp.content)['devices']
185     ids = [d['id'] for d in devices if 'of:' in d['id']]
186     return ids
187
188
189 def get_flow_ids(controller='127.0.0.1', port=8181):
190     """Returns a list of flow ids"""
191     rsp = requests.get(url='http://{0}:{1}/onos/v1/flows'.format(controller, port), auth=('onos', 'rocks'))
192     if rsp.status_code != 200:
193         return []
194     flows = json.loads(rsp.content)['flows']
195     ids = [f['id'] for f in flows]
196     return ids
197
198
199 def get_flow_simple_stats(controller='127.0.0.1', port=8181):
200     """Returns a list of flow ids"""
201     rsp = requests.get(url='http://{0}:{1}/onos/v1/flows'.format(controller, port), auth=('onos', 'rocks'))
202     if rsp.status_code != 200:
203         return []
204     flows = json.loads(rsp.content)['flows']
205     res = {}
206     for f in flows:
207         if f['state'] not in res:
208             res[f['state']] = 1
209         else:
210             res[f['state']] += 1
211     return res
212
213
214 def get_flow_device_pairs(controller='127.0.0.1', port=8181, flow_details=[]):
215     """Pairing flows from controller with deteils we used ofr creation"""
216     rsp = requests.get(url='http://{0}:{1}/onos/v1/flows'.format(controller, port), auth=('onos', 'rocks'))
217     if rsp.status_code != 200:
218         return
219     flows = json.loads(rsp.content)['flows']
220     # print "Flows", flows
221     # print "Details", flow_details
222     for dev_id, ip in flow_details:
223         # print "looking for details", dev_id, ip
224         for f in flows:
225             # lets identify if it is our flow
226             if f["treatment"]["instructions"][0]["type"] != "DROP":
227                 # print "NOT DROP"
228                 continue
229             if f["deviceId"] == dev_id:
230                 if "ip" in f["selector"]["criteria"][0]:
231                     item_idx = 0
232                 elif "ip" in f["selector"]["criteria"][1]:
233                     item_idx = 1
234                 else:
235                     continue
236                 # print "Comparing", '%s/32' % str(netaddr.IPAddress(ip))
237                 if f["selector"]["criteria"][item_idx]["ip"] == '%s/32' % str(netaddr.IPAddress(ip)):
238                     # print dev_id, ip, f
239                     yield dev_id, f["id"]
240                     break
241
242
243 def get_flow_to_remove(controller='127.0.0.1', port=8181):
244     """Pairing flows from controller with deteils we used ofr creation"""
245     rsp = requests.get(url='http://{0}:{1}/onos/v1/flows'.format(controller, port), auth=('onos', 'rocks'))
246     if rsp.status_code != 200:
247         return
248     flows = json.loads(rsp.content)['flows']
249     # print "Flows", flows
250     # print "Details", flow_details
251
252     for f in flows:
253         # lets identify if it is our flow
254         if f["treatment"]["instructions"][0]["type"] != "NOACTION":
255             # print "NOT DROP"
256             continue
257         if "ip" in f["selector"]["criteria"][0]:
258             item_idx = 0
259         elif "ip" in f["selector"]["criteria"][1]:
260             item_idx = 1
261         else:
262             continue
263             # print "Comparing", '%s/32' % str(netaddr.IPAddress(ip))
264         ipstr = f["selector"]["criteria"][item_idx]["ip"]
265         if '10.' in ipstr and '/32' in ipstr:
266             # print dev_id, ip, f
267             yield (f["deviceId"], f["id"])
268
269
270 def main(*argv):
271
272     parser = argparse.ArgumentParser(description='Flow programming performance test: First adds and then deletes flows '
273                                                  'into the config tree, as specified by optional parameters.')
274
275     parser.add_argument('--host', default='127.0.0.1',
276                         help='Host where onos controller is running (default is 127.0.0.1)')
277     parser.add_argument('--port', default='8181',
278                         help='Port on which onos\'s RESTCONF is listening (default is 8181)')
279
280     in_args = parser.parse_args(*argv)
281     print in_args
282
283     # get device ids
284     base_dev_ids = get_device_ids(controller=in_args.host)
285     base_flow_ids = get_flow_ids(controller=in_args.host)
286     # ip
287     ip_addr = Counter(int(netaddr.IPAddress('10.0.0.1')))  # noqa  # FIXME: This script seems to be unfinished!
288     # prepare func
289     preparefnc = _prepare_post  # noqa  # FIXME: This script seems to be unfinished!
290
291     print "BASELINE:"
292     print "    devices:", len(base_dev_ids)
293     print "    flows  :", len(base_flow_ids)
294
295     # lets print some stats
296     print "\n\nSome stats monitoring ...."
297     print get_flow_simple_stats(controller=in_args.host)
298
299 if __name__ == "__main__":
300     main(sys.argv[1:])