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