Bump pre-commit-hooks to v4.1.0
[integration/test.git] / tools / mdsal_benchmark / rpcbenchmark.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(operation, clients, servers, 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/rpcbenchmark:start-test"
39     postheaders = {"content-type": "application/json", "Accept": "application/json"}
40
41     test_request_template = """{
42         "input": {
43             "operation": "%s",
44             "num-clients": "%s",
45             "num-servers": "%s",
46             "payload-size": "%s",
47             "iterations": "%s"
48         }
49     }"""
50     data = test_request_template % (
51         operation,
52         clients,
53         servers,
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: Ok: %d, Error: %d, Rate: %d, Exec time: %d"
80         % (
81             run_type,
82             idx,
83             res[u"global-rtc-client-ok"],
84             res[u"global-rtc-client-error"],
85             res[u"rate"],
86             res[u"exec-time"],
87         )
88     )
89
90
91 def run_test(
92     warmup_runs, test_runs, operation, clients, servers, payload_size, iterations
93 ):
94     """
95     Execute a benchmark test. Performs the JVM 'wamrup' before the test, runs
96     the specified number of dsbenchmark test runs and computes the average time
97     for building the test data (a list of lists) and the average time for the
98     execution of the test.
99     :param warmup_runs: # of warmup runs
100     :param test_runs: # of test runs
101     :param operation: PUT, MERGE or DELETE
102     :param data_fmt: BINDING-AWARE or BINDING-INDEPENDENT
103     :param outer_elem: Number of elements in the outer list
104     :param inner_elem: Number of elements in the inner list
105     :param ops_per_tx: Number of operations (PUTs, MERGEs or DELETEs) on each transaction
106     :return: average build time AND average test execution time
107     """
108     total_exec_time = 0.0
109     total_rate = 0.0
110
111     for idx in range(warmup_runs):
112         res = send_test_request(operation, clients, servers, payload_size, iterations)
113         print_results("WARM-UP", idx, res)
114
115     for idx in range(test_runs):
116         res = send_test_request(operation, clients, servers, payload_size, iterations)
117         print_results("TEST", idx, res)
118         total_exec_time += res["exec-time"]
119         total_rate += res["rate"]
120
121     return total_exec_time / test_runs, total_rate / test_runs
122
123
124 if __name__ == "__main__":
125     parser = argparse.ArgumentParser(description="RPC Benchmarking")
126
127     # Host Config
128     parser.add_argument(
129         "--host",
130         default="localhost",
131         help="IP of the target host where benchmarks will be run.",
132     )
133     parser.add_argument(
134         "--port", type=int, default=8181, help="The port number of target host."
135     )
136
137     # Test Parameters
138     parser.add_argument(
139         "--operation",
140         choices=["GLOBAL-RTC", "ROUTED-RTC"],
141         default="GLOBAL-RTC",
142         help="RPC and client type. RPC can be global or routcan be run-to-completion (RTC)."
143         "(default: GLOBAL-RTC - Global RPC, Run-to-completion client)",
144     )
145     parser.add_argument(
146         "--warm",
147         type=int,
148         default=10,
149         help="The number of warm-up runs before the measured test runs" "(Default 10)",
150     )
151     parser.add_argument(
152         "--run",
153         type=int,
154         default=10,
155         help="The number of measured test runs. Reported results are based on these average of all"
156         " measured runs. (Default 10)",
157     )
158     parser.add_argument(
159         "--clients",
160         type=int,
161         nargs="+",
162         default=[1, 2, 4, 8, 16, 32, 64],
163         help="The number of test RPC clients to start. (Default 10)",
164     )
165     parser.add_argument(
166         "--servers",
167         type=int,
168         nargs="+",
169         default=[1, 2, 4, 8, 16, 32, 64],
170         help="The number of routed RPC servers to start in the routed RPC test. Ignored in the global "
171         "RPC test. (Default 10)",
172     )
173     parser.add_argument(
174         "--iterations",
175         type=int,
176         default=10,
177         help="The number requests that each RPC client issues "
178         "during the test run. (Default 10)",
179     )
180     parser.add_argument(
181         "--payload",
182         type=int,
183         default=10,
184         help="Payload size for the RPC - number of elements in a "
185         "simple integer list. (Default 10)",
186     )
187
188     args = parser.parse_args()
189     BASE_URL = "http://%s:%d/restconf/" % (args.host, args.port)
190
191     if args.operation == "GLOBAL-RTC":
192         servers = [1]
193     else:
194         servers = args.servers
195
196     # Run the benchmark tests and collect data in a csv file for import into a graphing software
197     f = open("test.csv", "wt")
198     try:
199         writer = csv.writer(f)
200         rate_matrix = []
201
202         for svr in servers:
203             rate_row = [""]
204             for client in args.clients:
205                 exec_time, rate = run_test(
206                     args.warm,
207                     args.run,
208                     args.operation,
209                     client,
210                     svr,
211                     args.payload,
212                     args.iterations,
213                 )
214                 rate_row.append(rate)
215             rate_matrix.append(rate_row)
216         print(rate_matrix)
217
218         writer.writerow(("RPC Rates:", ""))
219         writer.writerows(rate_matrix)
220     finally:
221         f.close()