#!/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])