06654b8ff6da075cf41f4711a0b61c2c23746f1d
[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     for dev_id, ip in flow_details:
221         for f in flows:
222             # lets identify if it is our flow
223             if f["treatment"]["instructions"][0]["type"] != "DROP":
224                 continue
225             if f["deviceId"] == dev_id:
226                 if "ip" in f["selector"]["criteria"][0]:
227                     item_idx = 0
228                 elif "ip" in f["selector"]["criteria"][1]:
229                     item_idx = 1
230                 else:
231                     continue
232                 if f["selector"]["criteria"][item_idx]["ip"] == '%s/32' % str(netaddr.IPAddress(ip)):
233                     yield dev_id, f["id"]
234                     break
235
236
237 def get_flow_to_remove(controller='127.0.0.1', port=8181):
238     """Pairing flows from controller with deteils we used ofr creation"""
239     rsp = requests.get(url='http://{0}:{1}/onos/v1/flows'.format(controller, port), auth=('onos', 'rocks'))
240     if rsp.status_code != 200:
241         return
242     flows = json.loads(rsp.content)['flows']
243
244     for f in flows:
245         # lets identify if it is our flow
246         if f["treatment"]["instructions"][0]["type"] != "NOACTION":
247             continue
248         if "ip" in f["selector"]["criteria"][0]:
249             item_idx = 0
250         elif "ip" in f["selector"]["criteria"][1]:
251             item_idx = 1
252         else:
253             continue
254         ipstr = f["selector"]["criteria"][item_idx]["ip"]
255         if '10.' in ipstr and '/32' in ipstr:
256             yield (f["deviceId"], f["id"])
257
258
259 def main(*argv):
260
261     parser = argparse.ArgumentParser(description='Flow programming performance test: First adds and then deletes flows '
262                                                  'into the config tree, as specified by optional parameters.')
263
264     parser.add_argument('--host', default='127.0.0.1',
265                         help='Host where onos controller is running (default is 127.0.0.1)')
266     parser.add_argument('--port', default='8181',
267                         help='Port on which onos\'s RESTCONF is listening (default is 8181)')
268
269     in_args = parser.parse_args(*argv)
270     print(in_args)
271
272     # get device ids
273     base_dev_ids = get_device_ids(controller=in_args.host)
274     base_flow_ids = get_flow_ids(controller=in_args.host)
275     # ip
276     ip_addr = Counter(int(netaddr.IPAddress('10.0.0.1')))  # noqa  # FIXME: This script seems to be unfinished!
277     # prepare func
278     preparefnc = _prepare_post  # noqa  # FIXME: This script seems to be unfinished!
279
280     print("BASELINE:")
281     print("    devices:", len(base_dev_ids))
282     print("    flows  :", len(base_flow_ids))
283
284     # lets print some stats
285     print("\n\nSome stats monitoring ....")
286     print(get_flow_simple_stats(controller=in_args.host))
287
288
289 if __name__ == "__main__":
290     main(sys.argv[1:])