Fixed flow_config_blaster Floodlight edition
[integration/test.git] / tools / odl-mdsal-clustering-tests / clustering-performance-test / flow_config_blaster_fle.py
1 #!/usr/bin/python
2 from flow_config_blaster import FlowConfigBlaster
3 import argparse
4 import time
5 import json
6 import copy
7 import requests
8
9
10 __author__ = "Jan Medved"
11 __copyright__ = "Copyright(c) 2014, Cisco Systems, Inc."
12 __license__ = "New-style BSD"
13 __email__ = "jmedved@cisco.com"
14
15
16 class FlowConfigBlasterFLE(FlowConfigBlaster):
17     """
18     FlowConfigBlaster, Floodlight Edition; Uses the Floodlight Static Flow Entry Pusher REST API to inject flows.
19     """
20     flow = {
21         'switch': "00:00:00:00:00:00:00:01",
22         "name": "flow-mod",
23         "cookie": "0",
24         "priority": "32768",
25         "eth_type": "2048",
26         "ipv4_dst": "10.0.0.1/32",
27         "active": "true",
28         "actions": "output=flood"
29     }
30
31     def __init__(self, host, port, ncycles, nthreads, nnodes, nflows, startflow):
32         FlowConfigBlaster.__init__(self, host, port, ncycles, nthreads, 1, nnodes, nflows, startflow, False)
33
34     def create_floodlight_url(self, host):
35         return 'http://' + host + ":" + self.port + '/wm/staticflowpusher/json'
36
37     def get_num_nodes(self, session):
38         """
39         Determines the number of nodes in the network. Overrides the get_num_nodes method in FlowConfigBlaster.
40         :param session:
41         :return:
42         """
43         url = 'http://' + self.host + ":" + self.port + '/wm/core/controller/switches/json'
44         nodes = self.nnodes
45
46         r = session.get(url, headers=self.getheaders, stream=False)
47
48         if r.status_code == 200:
49             try:
50                 nodes = len(json.loads(r.content))
51             except KeyError:
52                 pass
53
54         return nodes
55
56     def post_flows(self, session, node, flow_list, flow_count):
57         """
58         Performs a RESTCONF post of flows passed in the 'flow_list' parameters
59         :param session: 'requests' session on which to perform the POST
60         :param node: The ID of the openflow node to which to post the flows
61         :param flow_list: List of flows (in dictionary form) to POST
62         :param flow_count: Number of flows in flow_list (must be 1)
63         :return: status code from the POST operation
64         """
65         flow = copy.deepcopy(self.flow)
66         flow['switch'] = "00:00:00:00:00:00:00:%s" % '{0:02x}'.format(node)
67         flow['name'] = flow_list[0]['flow-name']
68         flow['table'] = flow_list[0]['table_id']
69         flow['cookie'] = flow_list[0]['cookie']
70         # flow['cookie_mask'] = flow_list[0]['cookie_mask']
71         flow['idle_timeout'] = flow_list[0]['idle-timeout']
72         flow['hard_timeout'] = flow_list[0]['hard-timeout']
73         flow['ipv4_dst'] = flow_list[0]['match']['ipv4-destination']
74
75         flow_data = json.dumps(flow)
76
77         hosts = self.host.split(",")
78         host = hosts[flow_count % len(hosts)]
79         flow_url = self.create_floodlight_url(host)
80
81         r = session.post(flow_url, data=flow_data, headers=self.putheaders, stream=False)
82         return r.status_code
83
84     def delete_flow(self, session, node, flow_id, flow_count):
85         """
86         Deletes a single flow from the ODL config data store using RESTCONF
87         :param session: 'requests' session on which to perform the POST
88         :param node: Id of the openflow node from which to delete the flow
89         :param flow_id: ID of the to-be-deleted flow
90         :param flow_count: Flow counter for round-robin of delete operations
91         :return: status code from the DELETE operation
92         """
93
94         hosts = self.host.split(",")
95         host = hosts[flow_count % len(hosts)]
96         flow_url = self.create_floodlight_url(host)
97         flow_data = json.dumps({'name': self.create_flow_name(flow_id)})
98
99         r = session.delete(flow_url, data=flow_data, headers=self.getheaders)
100         return r.status_code
101
102     def clear_all_flows(self):
103         clear_url = 'http://' + self.host + ":" + self.port + '/wm/staticflowpusher/clear/all/json'
104         r = requests.get(clear_url)
105         if r.status_code == 200:
106             print "All flows cleared before the test"
107         else:
108             print "Failed to clear flows from the controller, your results may vary"
109
110
111 if __name__ == "__main__":
112
113     parser = argparse.ArgumentParser(description='Flow programming performance test for Floodlight: First adds and '
114                                                  'then deletes flows using the Static Flow Entry Pusher REST API.')
115
116     parser.add_argument('--host', default='127.0.0.1',
117                         help='Host where the controller is running (default is 127.0.0.1)')
118     parser.add_argument('--port', default='8080',
119                         help='Port on which the controller\'s RESTCONF is listening (default is 8080)')
120     parser.add_argument('--cycles', type=int, default=1,
121                         help='Number of flow add/delete cycles; default 1. Both Flow Adds and Flow Deletes are '
122                              'performed in cycles. <THREADS> worker threads are started in each cycle and the cycle '
123                              'ends when all threads finish. Another cycle is started when the previous cycle finished.')
124     parser.add_argument('--threads', type=int, default=1,
125                         help='Number of request worker threads to start in each cycle; default=1. '
126                              'Each thread will add/delete <FLOWS> flows.')
127     parser.add_argument('--flows', type=int, default=10,
128                         help='Number of flows that will be added/deleted by each worker thread in each cycle; '
129                              'default 10')
130     parser.add_argument('--nodes', type=int, default=16,
131                         help='Number of nodes if mininet is not connected; default=16. If mininet is connected, '
132                              'flows will be evenly distributed (programmed) into connected nodes.')
133     parser.add_argument('--delay', type=int, default=0,
134                         help='Time (in seconds) to wait between the add and delete cycles; default=0')
135     parser.add_argument('--no-delete', dest='delete', action='store_false',
136                         help='Do not perform the delete cycle.')
137     parser.add_argument('--startflow', type=int, default=0,
138                         help='The starting Flow ID; default=0')
139
140     in_args = parser.parse_args()
141
142     fct = FlowConfigBlasterFLE(in_args.host, in_args.port, in_args.cycles, in_args.threads, in_args.nodes,
143                                in_args.flows, in_args.startflow)
144
145     fct.clear_all_flows()
146
147     # Run through <cycles>, where <threads> are started in each cycle and <flows> are added from each thread
148     fct.add_blaster()
149
150     print '\n*** Total flows added: %s' % fct.get_ok_flows()
151     print '    HTTP[OK] results:  %d\n' % fct.get_ok_rqsts()
152
153     if in_args.delay > 0:
154         print '*** Waiting for %d seconds before the delete cycle ***\n' % in_args.delay
155         time.sleep(in_args.delay)
156
157     # Run through <cycles>, where <threads> are started in each cycle and <flows> previously added in an add cycle are
158     # deleted in each thread
159     if in_args.delete:
160         fct.delete_blaster()
161         print '\n*** Total flows deleted: %s' % fct.get_ok_flows()
162         print '    HTTP[OK] results:    %d\n' % fct.get_ok_rqsts()