Bump pre-commit-hooks to v4.1.0
[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 import argparse
11 import requests
12 import json
13 import csv
14
15
16 __author__ = "Jan Medved"
17 __copyright__ = "Copyright(c) 2015, Cisco Systems, Inc."
18 __license__ = "Eclipse Public License v1.0"
19 __email__ = "jmedved@cisco.com"
20
21
22 global BASE_URL
23
24
25 def send_test_request(producer_type, producers, listeners, payload_size, iterations):
26     """
27     Sends a request to the rpcbenchmark app to start a data store benchmark test run.
28     The rpcbenchmark app will perform the requested benchmark test and return measured
29     test execution time and RPC throughput
30
31     :param operation: operation type
32     :param clients: number of simulated RPC clients
33     :param servers: Number of simulated RPC servers if operation type is ROUTED-***
34     :param payload_size: Payload size for the test RPCs
35     :param iterations: Number of iterations to run
36     :return: Result from the test request REST call (json)
37     """
38     url = BASE_URL + "operations/ntfbenchmark:start-test"
39     postheaders = {"content-type": "application/json", "Accept": "application/json"}
40
41     test_request_template = """{
42         "input": {
43             "producer-type": "%s",
44             "producers": "%s",
45             "listeners": "%s",
46             "payload-size": "%s",
47             "iterations": "%s"
48         }
49     }"""
50     data = test_request_template % (
51         producer_type,
52         producers,
53         listeners,
54         payload_size,
55         iterations,
56     )
57     r = requests.post(
58         url, data, headers=postheaders, stream=False, auth=("admin", "admin")
59     )
60     result = {u"http-status": r.status_code}
61     if r.status_code == 200:
62         result = dict(result.items() + json.loads(r.content)["output"].items())
63     else:
64         print("Error %s, %s" % (r.status_code, r.content))
65     return result
66
67
68 def print_results(run_type, idx, res):
69     """
70     Prints results from a dsbenchmakr test run to console
71     :param run_type: String parameter that can be used to identify the type of the
72                      test run (e.g. WARMUP or TEST)
73     :param idx: Index of the test run
74     :param res: Parsed json (disctionary) that was returned from a dsbenchmark
75                 test run
76     :return: None
77     """
78     print(
79         "%s #%d: ProdOk: %d, ProdError: %d, LisOk: %d, ProdRate: %d, LisRate %d, ProdTime: %d, ListTime %d"
80         % (
81             run_type,
82             idx,
83             res[u"producer-ok"],
84             res[u"producer-error"],
85             res[u"listener-ok"],
86             res[u"producer-rate"],
87             res[u"listener-rate"],
88             res[u"producer-elapsed-time"],
89             res[u"listener-elapsed-time"],
90         )
91     )
92
93
94 def run_test(
95     warmup_runs,
96     test_runs,
97     producer_type,
98     producers,
99     listeners,
100     payload_size,
101     iterations,
102 ):
103     """
104     Execute a benchmark test. Performs the JVM 'wamrup' before the test, runs
105     the specified number of dsbenchmark test runs and computes the average time
106     for building the test data (a list of lists) and the average time for the
107     execution of the test.
108     :param warmup_runs: # of warmup runs
109     :param test_runs: # of test runs
110     :param operation: PUT, MERGE or DELETE
111     :param data_fmt: BINDING-AWARE or BINDING-INDEPENDENT
112     :param outer_elem: Number of elements in the outer list
113     :param inner_elem: Number of elements in the inner list
114     :param ops_per_tx: Number of operations (PUTs, MERGEs or DELETEs) on each transaction
115     :return: average build time AND average test execution time
116     """
117     total_exec_time = 0.0
118     total_prate = 0.0
119     total_lrate = 0.0
120
121     for idx in range(warmup_runs):
122         res = send_test_request(
123             producer_type, producers, listeners, payload_size, iterations
124         )
125         print_results("WARM-UP", idx, res)
126
127     for idx in range(test_runs):
128         res = send_test_request(
129             producer_type, producers, listeners, payload_size, iterations
130         )
131         print_results("TEST", idx, res)
132         total_exec_time += res["listener-elapsed-time"]
133         total_prate += res["producer-rate"]
134         total_lrate += res["listener-rate"]
135
136     return total_exec_time / test_runs, total_prate / test_runs, total_lrate / test_runs
137
138
139 if __name__ == "__main__":
140     parser = argparse.ArgumentParser(description="RPC Benchmarking")
141
142     # Host Config
143     parser.add_argument(
144         "--host",
145         default="localhost",
146         help="IP of the target host where benchmarks will be run.",
147     )
148     parser.add_argument(
149         "--port", type=int, default=8181, help="The port number of target host."
150     )
151
152     # Test Parameters
153     parser.add_argument(
154         "--ptype",
155         choices=["DROPPING", "BLOCKING"],
156         nargs="+",
157         default="BLOCKING",
158         help="Producer type. (default: BLOCKING)",
159     )
160     parser.add_argument(
161         "--warm",
162         type=int,
163         default=10,
164         help="The number of warm-up runs before the measured test runs" "(Default 10)",
165     )
166     parser.add_argument(
167         "--run",
168         type=int,
169         default=10,
170         help="The number of measured test runs. Reported results are based on these average of all"
171         " measured runs. (Default 10)",
172     )
173     parser.add_argument(
174         "--producers",
175         type=int,
176         nargs="+",
177         default=[1, 2, 4, 8, 16, 32],
178         help="The number of test producers to start. (Default 10)",
179     )
180     parser.add_argument(
181         "--listeners",
182         type=int,
183         nargs="+",
184         default=[1, 2, 4, 8, 16, 32],
185         help="The number of test listeners to start. (Default 10)",
186     )
187     parser.add_argument(
188         "--iterations",
189         type=int,
190         default=100,
191         help="The number requests that each producer issues "
192         "during the test run. (Default 10)",
193     )
194     parser.add_argument(
195         "--payload",
196         type=int,
197         default=10,
198         help="Payload size for the RPC - number of elements in a "
199         "simple integer list. (Default 10)",
200     )
201
202     args = parser.parse_args()
203     BASE_URL = "http://%s:%d/restconf/" % (args.host, args.port)
204
205     # Run the benchmark tests and collect data in a csv file for import into a graphing software
206     f = open("test.csv", "wt")
207     try:
208         writer = csv.writer(f)
209         lrate_matrix = []
210         prate_matrix = []
211         for prod in args.producers:
212             lrate_row = [""]
213             prate_row = [""]
214             for lis in args.listeners:
215                 exec_time, prate, lrate = run_test(
216                     args.warm,
217                     args.run,
218                     args.ptype,
219                     prod,
220                     lis,
221                     args.payload,
222                     args.iterations,
223                 )
224                 print(
225                     "Producers: %d, Listeners: %d, prate: %d, lrate: %d"
226                     % (prod, lis, prate, lrate)
227                 )
228                 lrate_row.append(lrate)
229                 prate_row.append(prate)
230
231             lrate_matrix.append(lrate_row)
232             prate_matrix.append(prate_row)
233
234         print(lrate_matrix)
235         print(prate_matrix)
236
237         # writer.writerow((('%s:' % args.ptype), '', '', ''))
238         # writer.writerow(('', exec_time, prate, lrate))
239
240         writer.writerow(("Listener Rates:", ""))
241         writer.writerows(lrate_matrix)
242         writer.writerow(("Producer Rates:", ""))
243         writer.writerows(prate_matrix)
244
245     finally:
246         f.close()