__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
"""
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
"""
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())
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
: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)
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(",")
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):
"""
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"
#!/usr/bin/python
from flow_config_blaster import FlowConfigBlaster
import argparse
-import netaddr
import time
import json
+import copy
+import requests
__author__ = "Jan Medved"
"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):
"""
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__":
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
# 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()