Merge "Fix pep8 violations in flow_add_delete_test"
[integration/test.git] / test / tools / odl-mdsal-clustering-tests / clustering-performance-test / flow_add_delete_test.py
1 #!/usr/bin/python
2
3 __author__ = "Jan Medved"
4 __copyright__ = "Copyright(c) 2014, Cisco Systems, Inc."
5 __license__ = "New-style BSD"
6 __email__ = "jmedved@cisco.com"
7
8 import argparse
9 import time
10 from flow_config_blaster import FlowConfigBlaster, get_json_from_file
11 from inventory_crawler import InventoryCrawler
12 from config_cleanup import cleanup_config_odl
13
14
15 def wait_for_stats(crawler, exp_found, timeout, delay):
16     """
17     Waits for the ODL stats manager to catch up. Polls ODL inventory every <delay> seconds and compares the
18     retrieved stats to the expected values. If stats collection has not finished within <timeout> seconds,
19     Gives up/
20
21     :param crawler: Inventory crawler object
22     :param exp_found: Expected value for flows found in the network
23     :param timeout: Max number of seconds to wait for stats collector to collect all stats
24     :param delay: poll interval for inventory
25     :return: None
26     """
27     total_delay = 0
28     print 'Waiting for stats to catch up:'
29     while True:
30         crawler.crawl_inventory()
31         print '   %d, %d' % (crawler.reported_flows, crawler.found_flows)
32         if crawler.found_flows == exp_found or total_delay > timeout:
33             break
34         total_delay += delay
35         time.sleep(delay)
36
37     if total_delay < timeout:
38         print 'Stats collected in %d seconds.' % total_delay
39     else:
40         print 'Stats collection did not finish in %d seconds. Aborting...' % total_delay
41
42
43 if __name__ == "__main__":
44     ########################################################################################
45     # This program executes an ODL performance test. The test is executed in three steps:
46     #
47     # 1. The specified number of flows is added in the 'add cycle' (uses flow_config_blaster to blast flows)
48     #
49     # 2. The network is polled for flow statistics from the network (using the inventory_crawler) to make sure
50     #    that all flows have been properly programmed into the network and the ODL statistics collector can
51     #    properly read them
52     #
53     # 3. The flows are deleted in the flow cycle. Deletion happens either in 'bulk' (using the config_cleanup)
54     #    script or one by one (using the flow_config_blaster 'delete' method)
55     ########################################################################################
56
57     JSON_FLOW_MOD1 = '''{
58         "flow-node-inventory:flow": [
59             {
60                 "flow-node-inventory:cookie": %d,
61                 "flow-node-inventory:cookie_mask": 4294967295,
62                 "flow-node-inventory:flow-name": "%s",
63                 "flow-node-inventory:hard-timeout": %d,
64                 "flow-node-inventory:id": "%s",
65                 "flow-node-inventory:idle-timeout": %d,
66                 "flow-node-inventory:installHw": false,
67                 "flow-node-inventory:instructions": {
68                     "flow-node-inventory:instruction": [
69                         {
70                             "flow-node-inventory:apply-actions": {
71                                 "flow-node-inventory:action": [
72                                     {
73                                         "flow-node-inventory:drop-action": {},
74                                         "flow-node-inventory:order": 0
75                                     }
76                                 ]
77                             },
78                             "flow-node-inventory:order": 0
79                         }
80                     ]
81                 },
82                 "flow-node-inventory:match": {
83                     "flow-node-inventory:ipv4-destination": "%s/32",
84                     "flow-node-inventory:ethernet-match": {
85                         "flow-node-inventory:ethernet-type": {
86                             "flow-node-inventory:type": 2048
87                         }
88                     }
89                 },
90                 "flow-node-inventory:priority": 2,
91                 "flow-node-inventory:strict": false,
92                 "flow-node-inventory:table_id": 0
93             }
94         ]
95     }'''
96
97     parser = argparse.ArgumentParser(description='Flow programming performance test: First adds and then deletes flows '
98                                                  'into the config tree, as specified by optional parameters.')
99
100     parser.add_argument('--host', default='127.0.0.1',
101                         help='Host where odl controller is running (default is 127.0.0.1)')
102     parser.add_argument('--port', default='8181',
103                         help='Port on which odl\'s RESTCONF is listening (default is 8181)')
104     parser.add_argument('--cycles', type=int, default=1,
105                         help='Number of flow add/delete cycles; default 1. Both Flow Adds and Flow Deletes are '
106                              'performed in cycles. <THREADS> worker threads are started in each cycle and the cycle '
107                              'ends when all threads finish. Another cycle is started when the previous cycle finished.')
108     parser.add_argument('--threads', type=int, default=1,
109                         help='Number of request worker threads to start in each cycle; default=1. '
110                              'Each thread will add/delete <FLOWS> flows.')
111     parser.add_argument('--flows', type=int, default=10,
112                         help='Number of flows that will be added/deleted by each worker thread in each cycle; '
113                              'default 10')
114     parser.add_argument('--nodes', type=int, default=16,
115                         help='Number of nodes if mininet is not connected; default=16. If mininet is connected, '
116                              'flows will be evenly distributed (programmed) into connected nodes.')
117     parser.add_argument('--delay', type=int, default=2,
118                         help='Time (seconds) to between inventory polls when waiting for stats to catch up; default=1')
119     parser.add_argument('--timeout', type=int, default=100,
120                         help='The maximum time (seconds) to wait between the add and delete cycles; default=100')
121     parser.add_argument('--delete', dest='delete', action='store_true', default=True,
122                         help='Delete all added flows one by one, benchmark delete '
123                              'performance.')
124     parser.add_argument('--bulk-delete', dest='bulk_delete', action='store_true', default=False,
125                         help='Delete all flows in bulk; default=False')
126     parser.add_argument('--auth', dest='auth', action='store_true',
127                         help="Use authenticated access to REST (username: 'admin', password: 'admin'); default=False")
128     parser.add_argument('--startflow', type=int, default=0,
129                         help='The starting Flow ID; default=0')
130     parser.add_argument('--file', default='',
131                         help='File from which to read the JSON flow template; default: no file, use a built in '
132                              'template.')
133
134     in_args = parser.parse_args()
135
136     # Initialize
137     if in_args.file != '':
138         flow_template = get_json_from_file(in_args.file)
139     else:
140         flow_template = JSON_FLOW_MOD1
141
142     ic = InventoryCrawler(in_args.host, in_args.port, 0, 'operational', in_args.auth, False)
143
144     fct = FlowConfigBlaster(in_args.host, in_args.port, in_args.cycles, in_args.threads, in_args.nodes,
145                             in_args.flows, in_args.startflow, in_args.auth, flow_template)
146
147     # Get the baseline stats. Required in Step 3 to validate if the delete function gets the controller back to
148     # the baseline
149     ic.crawl_inventory()
150     reported = ic.reported_flows
151     found = ic.found_flows
152
153     print 'Baseline:'
154     print '   Reported nodes: %d' % reported
155     print '   Found nodes:    %d' % found
156
157     # Run through <CYCLES> add cycles, where <THREADS> threads are started in each cycle and <FLOWS> flows are
158     # added from each thread
159     fct.add_blaster()
160
161     print '\n*** Total flows added: %s' % fct.get_total_flows()
162     print '    HTTP[OK] results:  %d\n' % fct.get_ok_flows()
163
164     # Wait for stats to catch up
165     wait_for_stats(ic, found + fct.get_ok_flows(), in_args.timeout, in_args.delay)
166
167     # Run through <CYCLES> delete cycles, where <THREADS> threads  are started in each cycle and <FLOWS> flows
168     # previously added in an add cycle are deleted in each thread
169     if in_args.bulk_delete:
170         print '\nDeleting all flows in bulk:'
171         sts = cleanup_config_odl(in_args.host, in_args.port, in_args.auth)
172         if sts != 200:
173             print '   Failed to delete flows, code %d' % sts
174         else:
175             print '   All flows deleted.'
176     else:
177         print '\nDeleting flows one by one\n   ',
178         fct.delete_blaster()
179
180     # Wait for stats to catch up back to baseline
181     wait_for_stats(ic, found, in_args.timeout, in_args.delay)