#!/usr/bin/python import requests import argparse import time import threading import functools import operator import collections from Queue import Queue __author__ = "Gary Wu" __email__ = "gary.wu1@huawei.com" GET_HEADERS = {'Accept': 'application/json'} INVENTORY_URL = 'http://%s:%d/restconf/%s/opendaylight-inventory:nodes' 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 read(hosts, port, auth, datastore, print_lock, cycles, results_queue): """ Make RESTconf request to read the flow configuration from the specified data store. Args: :param hosts: A comma-separated list of hosts to read from. :param port: The port number to read from. :param auth: The username and password pair to use for basic authentication, or None if no authentication is required. :param datastore: The datastore (operational/config) to read flows from. :param print_lock: The thread lock to allow only one thread to output at a time. :param cycles: The number of reads that this thread will perform. :param results_queue: Used to store the HTTP status results of this method call. """ s = requests.Session() stats = {} for i in range(cycles): host = hosts[i % len(hosts)] url = INVENTORY_URL % (host, port, datastore) r = s.get(url, headers=GET_HEADERS, stream=False, auth=auth) # If dict has no such entry, default to 0 stats[r.status_code] = stats.get(r.status_code, 0) + 1 with print_lock: print(' %s results: %s' % (threading.current_thread().name, stats)) results_queue.put(stats) if __name__ == "__main__": parser = argparse.ArgumentParser(description='Inventory read performance test: Repeatedly read openflow node data ' 'from config datastore. Note that the data needs to be created in the datastore ' 'first using flow_config_blaster.py --no-delete.') parser.add_argument('--host', default='127.0.0.1', help='Host where odl controller is running (default is 127.0.0.1). ' 'Specify a comma-separated list of hosts to perform round-robin load-balancing.') parser.add_argument('--port', default='8181', type=int, help='Port on which odl\'s RESTCONF is listening (default is 8181)') parser.add_argument('--datastore', choices=['operational', 'config'], default='operational', help='Which data store to crawl; default operational') parser.add_argument('--cycles', type=int, default=100, help='Number of repeated reads; default 100. ') parser.add_argument('--threads', type=int, default=1, help='Number of request worker threads to start in each cycle; default=1. ' 'Each thread will add/delete flows.') parser.add_argument('--auth', dest='auth', action='store_true', default=False, help="Use the ODL default username/password 'admin'/'admin' to authenticate access to REST; " 'default: no authentication') args = parser.parse_args() hosts = args.host.split(",") port = args.port auth = ("admin", "admin") if args.auth else None # Use a lock to ensure that output from multiple threads don't interrupt/overlap each other print_lock = threading.Lock() results = Queue() with Timer() as t: threads = [] for i in range(args.threads): thread = threading.Thread(target=read, args=(hosts, port, auth, args.datastore, print_lock, args.cycles, results)) threads.append(thread) thread.start() # Wait for all threads to finish and measure the execution time for thread in threads: thread.join() # Aggregate the results stats = functools.reduce(operator.add, map(collections.Counter, results.queue)) print('\n*** Test summary:') print(' Elapsed time: %.2fs' % t.secs) print(' HTTP[OK] results: %d\n' % stats[200])