Fixed flow_config_blaster Floodlight edition 59/35859/3
authorJan Medved <jmedved@cisco.com>
Mon, 7 Mar 2016 00:13:27 +0000 (16:13 -0800)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 7 Mar 2016 20:26:52 +0000 (20:26 +0000)
Fixed measurements of time for stats collection in flow_add_delete_test
Fixed PIP-8 violations (at least the fixable ones)

Change-Id: I01a361a7245fe479fbb65eea95bea93808d787a8
Signed-off-by: Jan Medved <jmedved@cisco.com>
tools/odl-mdsal-clustering-tests/clustering-performance-test/flow_add_delete_test.py
tools/odl-mdsal-clustering-tests/clustering-performance-test/flow_config_blaster.py
tools/odl-mdsal-clustering-tests/clustering-performance-test/flow_config_blaster_fle.py

index ecbcbb971df13bd6b26a024b382bddf14b458bb7..cc36ed93a71f37e21a0a01e6e5a11f0499192d93 100755 (executable)
@@ -13,6 +13,22 @@ __license__ = "New-style BSD"
 __email__ = "jmedved@cisco.com"
 
 
+class Timer(object):
+    def __init__(self, verbose=False):
+        self.verbose = verbose
+
+    def __enter__(self):
+        self.start = time.time()
+        return self
+
+    def __exit__(self, *args):
+        self.end = time.time()
+        self.secs = self.end - self.start
+        self.msecs = self.secs * 1000  # millisecs
+        if self.verbose:
+            print ("elapsed time: %f ms" % self.msecs)
+
+
 def wait_for_stats(crawler, exp_found, timeout, delay):
     """
     Waits for the ODL stats manager to catch up. Polls ODL inventory every
@@ -28,16 +44,18 @@ def wait_for_stats(crawler, exp_found, timeout, delay):
     """
     total_delay = 0
     print 'Waiting for stats to catch up:'
-    while True:
-        crawler.crawl_inventory()
-        print '   %d, %d' % (crawler.reported_flows, crawler.found_flows)
-        if crawler.found_flows == exp_found or total_delay > timeout:
-            break
-        total_delay += delay
-        time.sleep(delay)
+
+    with Timer() as t:
+        while True:
+            crawler.crawl_inventory()
+            print '   %d, %d' % (crawler.reported_flows, crawler.found_flows)
+            if crawler.found_flows == exp_found or total_delay > timeout:
+                break
+            total_delay += delay
+            time.sleep(delay)
 
     if total_delay < timeout:
-        print 'Stats collected in %d seconds.' % total_delay
+        print 'Stats collected in %d seconds.' % t.secs
     else:
         print 'Stats collection did not finish in %d seconds. Aborting...' % total_delay
 
index ddf3bb1239b92362d27844150176946a12a4fe13..8b38b14b8fc89e81e599a2fe942e253a651be8b1 100755 (executable)
@@ -120,6 +120,15 @@ class FlowConfigBlaster(object):
             """
             Calculates the stats for RESTCONF request and flow programming
             throughput, and aggregates statistics across all Blaster threads.
+
+            Args:
+                rqst_stats: Request statistics dictionary
+                flow_stats: Flow statistcis dictionary
+                elapsed_time: Elapsed time for the test
+
+            Returns: Rates (requests/sec) for successfully finished requests,
+                     the total number of requests, sucessfully installed flow and
+                     the total number of flows
             """
             ok_rqsts = rqst_stats[200] + rqst_stats[204]
             total_rqsts = sum(rqst_stats.values())
@@ -236,13 +245,18 @@ class FlowConfigBlaster(object):
         FlowConfigBlaster instantiation. Flow templates are json-compatible
         dictionaries that MUST contain elements for flow cookie, flow name,
         flow id and the destination IPv4 address in the flow match field.
-        :param flow_id: Id for the new flow to create
-        :param ipaddr: IP Address to put into the flow's match
-        :return: The newly created flow instance
+
+        Args:
+            flow_id: Id for the new flow to create
+            ipaddr: IP Address to put into the flow's match
+            node_id: ID of the node where to create the flow
+
+        Returns: The flow that gas been created from the template
+
         """
         flow = copy.deepcopy(self.flow_mode_template['flow'][0])
         flow['cookie'] = flow_id
-        flow['flow-name'] = 'TestFlow-%d' % flow_id
+        flow['flow-name'] = self.create_flow_name(flow_id)
         flow['id'] = str(flow_id)
         flow['match']['ipv4-destination'] = '%s/32' % str(netaddr.IPAddress(ipaddr))
         return flow
@@ -253,6 +267,8 @@ class FlowConfigBlaster(object):
         :param session: 'requests' session on which to perform the POST
         :param node: The ID of the openflow node to which to post the flows
         :param flow_list: List of flows (in dictionary form) to POST
+        :param flow_count: Flow counter for round-robin host load balancing
+
         :return: status code from the POST operation
         """
         flow_data = self.convert_to_json(flow_list, node)
@@ -355,10 +371,14 @@ class FlowConfigBlaster(object):
     def delete_flow(self, session, node, flow_id, flow_count):
         """
         Deletes a single flow from the ODL config data store using RESTCONF
-        :param session: 'requests' session on which to perform the POST
-        :param node: Id of the openflow node from which to delete the flow
-        :param flow_id: ID of the to-be-deleted flow
-        :return: status code from the DELETE operation
+        Args:
+            session: 'requests' session on which to perform the POST
+            node:  Id of the openflow node from which to delete the flow
+            flow_id: ID of the to-be-deleted flow
+            flow_count: Index of the flow being processed (for round-robin LB)
+
+        Returns: status code from the DELETE operation
+
         """
 
         hosts = self.host.split(",")
@@ -477,6 +497,9 @@ class FlowConfigBlaster(object):
     def get_ok_rqsts(self):
         return self.total_ok_rqsts
 
+    def create_flow_name(self, flow_id):
+        return 'TestFlow-%d' % flow_id
+
 
 def get_json_from_file(filename):
     """
@@ -498,6 +521,7 @@ def get_json_from_file(filename):
 
     return None
 
+
 ###############################################################################
 # This is an example of what the content of a JSON flow mode template should
 # look like. Cut & paste to create a custom template. "id" and "ipv4-destination"
index 3c3535d5f34dfcdb3432a1e53d75cb332a997dc3..b54cf5cce5a85ca5e5f5c29396940461ee767d05 100755 (executable)
@@ -1,9 +1,10 @@
 #!/usr/bin/python
 from flow_config_blaster import FlowConfigBlaster
 import argparse
-import netaddr
 import time
 import json
+import copy
+import requests
 
 
 __author__ = "Jan Medved"
@@ -21,17 +22,17 @@ class FlowConfigBlasterFLE(FlowConfigBlaster):
         "name": "flow-mod",
         "cookie": "0",
         "priority": "32768",
-        "ether-type": "2048",
-        "dst-ip": "10.0.0.1/32",
+        "eth_type": "2048",
+        "ipv4_dst": "10.0.0.1/32",
         "active": "true",
         "actions": "output=flood"
     }
 
     def __init__(self, host, port, ncycles, nthreads, nnodes, nflows, startflow):
-        FlowConfigBlaster.__init__(self, host, port, ncycles, nthreads, nnodes, nflows, startflow, False, '')
+        FlowConfigBlaster.__init__(self, host, port, ncycles, nthreads, 1, nnodes, nflows, startflow, False)
 
-        # Create the service URL
-        self.url = 'http://' + self.host + ":" + self.port + '/wm/staticflowentrypusher/json'
+    def create_floodlight_url(self, host):
+        return 'http://' + host + ":" + self.port + '/wm/staticflowpusher/json'
 
     def get_num_nodes(self, session):
         """
@@ -52,41 +53,60 @@ class FlowConfigBlasterFLE(FlowConfigBlaster):
 
         return nodes
 
-    def post_flows(self, session, node, flow_id, ipaddr):
+    def post_flows(self, session, node, flow_list, flow_count):
         """
-        Adds a flow. Overrides the add_flow method in FlowConfigBlaster.
-        :param session:
-        :param node:
-        :param flow_id:
-        :param ipaddr:
-        :return:
+        Performs a RESTCONF post of flows passed in the 'flow_list' parameters
+        :param session: 'requests' session on which to perform the POST
+        :param node: The ID of the openflow node to which to post the flows
+        :param flow_list: List of flows (in dictionary form) to POST
+        :param flow_count: Number of flows in flow_list (must be 1)
+        :return: status code from the POST operation
         """
-        self.flow['switch'] = "00:00:00:00:00:00:00:%s" % '{0:02x}'.format(node)
-        self.flow['name'] = 'TestFlow-%d' % flow_id
-        self.flow['cookie'] = str(flow_id)
-        self.flow['dst-ip'] = "%s/32" % str(netaddr.IPAddress(ipaddr))
-
-        flow_data = json.dumps(self.flow)
-        # print flow_data
-        # print flow_url
-
-        r = session.post(self.url, data=flow_data, headers=self.putheaders, stream=False)
+        flow = copy.deepcopy(self.flow)
+        flow['switch'] = "00:00:00:00:00:00:00:%s" % '{0:02x}'.format(node)
+        flow['name'] = flow_list[0]['flow-name']
+        flow['table'] = flow_list[0]['table_id']
+        flow['cookie'] = flow_list[0]['cookie']
+        # flow['cookie_mask'] = flow_list[0]['cookie_mask']
+        flow['idle_timeout'] = flow_list[0]['idle-timeout']
+        flow['hard_timeout'] = flow_list[0]['hard-timeout']
+        flow['ipv4_dst'] = flow_list[0]['match']['ipv4-destination']
+
+        flow_data = json.dumps(flow)
+
+        hosts = self.host.split(",")
+        host = hosts[flow_count % len(hosts)]
+        flow_url = self.create_floodlight_url(host)
+
+        r = session.post(flow_url, data=flow_data, headers=self.putheaders, stream=False)
         return r.status_code
 
-    def delete_flow(self, session, node, flow_id):
+    def delete_flow(self, session, node, flow_id, flow_count):
         """
-        Deletes a flow. Overrides the delete_flow method in FlowConfigBlaster.
-        :param session:
-        :param node:
-        :param flow_id:
-        :return:
+        Deletes a single flow from the ODL config data store using RESTCONF
+        :param session: 'requests' session on which to perform the POST
+        :param node: Id of the openflow node from which to delete the flow
+        :param flow_id: ID of the to-be-deleted flow
+        :param flow_count: Flow counter for round-robin of delete operations
+        :return: status code from the DELETE operation
         """
-        f = {'name': 'TestFlow-%d' % flow_id}
-        flow_data = json.dumps(f)
 
-        r = session.delete(self.url, data=flow_data, headers=self.getheaders)
+        hosts = self.host.split(",")
+        host = hosts[flow_count % len(hosts)]
+        flow_url = self.create_floodlight_url(host)
+        flow_data = json.dumps({'name': self.create_flow_name(flow_id)})
+
+        r = session.delete(flow_url, data=flow_data, headers=self.getheaders)
         return r.status_code
 
+    def clear_all_flows(self):
+        clear_url = 'http://' + self.host + ":" + self.port + '/wm/staticflowpusher/clear/all/json'
+        r = requests.get(clear_url)
+        if r.status_code == 200:
+            print "All flows cleared before the test"
+        else:
+            print "Failed to clear flows from the controller, your results may vary"
+
 
 if __name__ == "__main__":
 
@@ -122,11 +142,13 @@ if __name__ == "__main__":
     fct = FlowConfigBlasterFLE(in_args.host, in_args.port, in_args.cycles, in_args.threads, in_args.nodes,
                                in_args.flows, in_args.startflow)
 
+    fct.clear_all_flows()
+
     # Run through <cycles>, where <threads> are started in each cycle and <flows> are added from each thread
     fct.add_blaster()
 
-    print '\n*** Total flows added: %s' % fct.get_total_flows()
-    print '    HTTP[OK] results:  %d\n' % fct.get_ok_flows()
+    print '\n*** Total flows added: %s' % fct.get_ok_flows()
+    print '    HTTP[OK] results:  %d\n' % fct.get_ok_rqsts()
 
     if in_args.delay > 0:
         print '*** Waiting for %d seconds before the delete cycle ***\n' % in_args.delay
@@ -136,3 +158,5 @@ if __name__ == "__main__":
     # deleted in each thread
     if in_args.delete:
         fct.delete_blaster()
+        print '\n*** Total flows deleted: %s' % fct.get_ok_flows()
+        print '    HTTP[OK] results:    %d\n' % fct.get_ok_rqsts()