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"
 
 
 __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
 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:'
     """
     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:
 
     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
 
     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.
             """
             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())
             """
             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.
         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 = 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
         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 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)
         :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
     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(",")
         """
 
         hosts = self.host.split(",")
@@ -477,6 +497,9 @@ class FlowConfigBlaster(object):
     def get_ok_rqsts(self):
         return self.total_ok_rqsts
 
     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):
     """
 
 def get_json_from_file(filename):
     """
@@ -498,6 +521,7 @@ def get_json_from_file(filename):
 
     return None
 
 
     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"
 ###############################################################################
 # 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
 #!/usr/bin/python
 from flow_config_blaster import FlowConfigBlaster
 import argparse
-import netaddr
 import time
 import json
 import time
 import json
+import copy
+import requests
 
 
 __author__ = "Jan Medved"
 
 
 __author__ = "Jan Medved"
@@ -21,17 +22,17 @@ class FlowConfigBlasterFLE(FlowConfigBlaster):
         "name": "flow-mod",
         "cookie": "0",
         "priority": "32768",
         "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):
         "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):
         """
 
     def get_num_nodes(self, session):
         """
@@ -52,41 +53,60 @@ class FlowConfigBlasterFLE(FlowConfigBlaster):
 
         return nodes
 
 
         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
 
         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
 
         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__":
 
 
 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 = 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()
 
     # 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
 
     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()
     # 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()