1d887e3c90f14afb4a080fae8595f6b04e18c3e7
[integration/test.git] / tools / mdsal_benchmark / ntfbenchmark.py
1 #!/usr/bin/python
2 ##############################################################################
3 # Copyright (c) 2015 Cisco Systems  All rights reserved.
4 #
5 # This program and the accompanying materials are made available under the
6 # terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 # and is available at http://www.eclipse.org/legal/epl-v10.html
8 ##############################################################################
9
10 __author__ = "Jan Medved"
11 __copyright__ = "Copyright(c) 2015, Cisco Systems, Inc."
12 __license__ = "Eclipse Public License v1.0"
13 __email__ = "jmedved@cisco.com"
14
15 import argparse
16 import requests
17 import json
18 import csv
19
20 global BASE_URL
21
22
23 def send_test_request(producer_type, producers, listeners, payload_size, iterations):
24     """
25     Sends a request to the rpcbenchmark app to start a data store benchmark test run.
26     The rpcbenchmark app will perform the requested benchmark test and return measured
27     test execution time and RPC throughput
28
29     :param operation: operation type
30     :param clients: number of simulated RPC clients
31     :param servers: Number of simulated RPC servers if operation type is ROUTED-***
32     :param payload_size: Payload size for the test RPCs
33     :param iterations: Number of iterations to run
34     :return: Result from the test request REST call (json)
35     """
36     url = BASE_URL + "operations/ntfbenchmark:start-test"
37     postheaders = {'content-type': 'application/json', 'Accept': 'application/json'}
38
39     test_request_template = '''{
40         "input": {
41             "producer-type": "%s",
42             "producers": "%s",
43             "listeners": "%s",
44             "payload-size": "%s",
45             "iterations": "%s"
46         }
47     }'''
48     data = test_request_template % (producer_type, producers, listeners, payload_size, iterations)
49     r = requests.post(url, data, headers=postheaders, stream=False, auth=('admin', 'admin'))
50     result = {u'http-status': r.status_code}
51     if r.status_code == 200:
52         result = dict(result.items() + json.loads(r.content)['output'].items())
53     else:
54         print 'Error %s, %s' % (r.status_code, r.content)
55     return result
56
57
58 def print_results(run_type, idx, res):
59     """
60     Prints results from a dsbenchmakr test run to console
61     :param run_type: String parameter that can be used to identify the type of the
62                      test run (e.g. WARMUP or TEST)
63     :param idx: Index of the test run
64     :param res: Parsed json (disctionary) that was returned from a dsbenchmark
65                 test run
66     :return: None
67     """
68     print '%s #%d: ProdOk: %d, ProdError: %d, LisOk: %d, ProdRate: %d, LisRate %d, ProdTime: %d, ListTime %d' % \
69           (run_type, idx,
70            res[u'producer-ok'], res[u'producer-error'], res[u'listener-ok'], res[u'producer-rate'],
71            res[u'listener-rate'], res[u'producer-elapsed-time'], res[u'listener-elapsed-time'])
72
73
74 def run_test(warmup_runs, test_runs, producer_type, producers, listeners, payload_size, iterations):
75     """
76     Execute a benchmark test. Performs the JVM 'wamrup' before the test, runs
77     the specified number of dsbenchmark test runs and computes the average time
78     for building the test data (a list of lists) and the average time for the
79     execution of the test.
80     :param warmup_runs: # of warmup runs
81     :param test_runs: # of test runs
82     :param operation: PUT, MERGE or DELETE
83     :param data_fmt: BINDING-AWARE or BINDING-INDEPENDENT
84     :param outer_elem: Number of elements in the outer list
85     :param inner_elem: Number of elements in the inner list
86     :param ops_per_tx: Number of operations (PUTs, MERGEs or DELETEs) on each transaction
87     :return: average build time AND average test execution time
88     """
89     total_exec_time = 0.0
90     total_prate = 0.0
91     total_lrate = 0.0
92
93     for idx in range(warmup_runs):
94         res = send_test_request(producer_type, producers, listeners, payload_size, iterations)
95         print_results('WARM-UP', idx, res)
96
97     for idx in range(test_runs):
98         res = send_test_request(producer_type, producers, listeners, payload_size, iterations)
99         print_results('TEST', idx, res)
100         total_exec_time += res['listener-elapsed-time']
101         total_prate += res['producer-rate']
102         total_lrate += res['listener-rate']
103
104     return total_exec_time / test_runs, total_prate / test_runs, total_lrate / test_runs
105
106
107 if __name__ == "__main__":
108     parser = argparse.ArgumentParser(description='RPC Benchmarking')
109
110     # Host Config
111     parser.add_argument("--host", default="localhost", help="IP of the target host where benchmarks will be run.")
112     parser.add_argument("--port", type=int, default=8181, help="The port number of target host.")
113
114     # Test Parameters
115     parser.add_argument("--ptype", choices=["DROPPING", "BLOCKING"], nargs='+', default='BLOCKING',
116                         help='Producer type. (default: BLOCKING)')
117     parser.add_argument("--warm", type=int, default=10, help='The number of warm-up runs before the measured test runs'
118                                                              '(Default 10)')
119     parser.add_argument("--run", type=int, default=10,
120                         help='The number of measured test runs. Reported results are based on these average of all'
121                              " measured runs. (Default 10)")
122     parser.add_argument("--producers", type=int, nargs='+', default=[1, 2, 4, 8, 16, 32],
123                         help='The number of test producers to start. (Default 10)')
124     parser.add_argument("--listeners", type=int, nargs='+', default=[1, 2, 4, 8, 16, 32],
125                         help='The number of test listeners to start. (Default 10)')
126     parser.add_argument("--iterations", type=int, default=100, help='The number requests that each producer issues '
127                                                                     'during the test run. (Default 10)')
128     parser.add_argument("--payload", type=int, default=10, help='Payload size for the RPC - number of elements in a '
129                                                                 'simple integer list. (Default 10)')
130
131     args = parser.parse_args()
132     BASE_URL = "http://%s:%d/restconf/" % (args.host, args.port)
133
134     # Run the benchmark tests and collect data in a csv file for import into a graphing software
135     f = open('test.csv', 'wt')
136     try:
137         writer = csv.writer(f)
138         lrate_matrix = []
139         prate_matrix = []
140         for prod in args.producers:
141             lrate_row = ['']
142             prate_row = ['']
143             for lis in args.listeners:
144                 exec_time, prate, lrate = run_test(args.warm, args.run, args.ptype, prod, lis,
145                                                    args.payload, args.iterations)
146                 print 'Producers: %d, Listeners: %d, prate: %d, lrate: %d' % (prod, lis, prate, lrate)
147                 lrate_row.append(lrate)
148                 prate_row.append(prate)
149
150             lrate_matrix.append(lrate_row)
151             prate_matrix.append(prate_row)
152
153         print lrate_matrix
154         print prate_matrix
155
156         # writer.writerow((('%s:' % args.ptype), '', '', ''))
157         # writer.writerow(('', exec_time, prate, lrate))
158
159         writer.writerow(('Listener Rates:', ''))
160         writer.writerows(lrate_matrix)
161         writer.writerow(('Producer Rates:', ''))
162         writer.writerows(prate_matrix)
163
164     finally:
165         f.close()