2 # Copyright (c) 2021 Intel and/or its affiliates.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 """ab fork library."""
17 from multiprocessing import Pool
22 REGEX_RPS = r"Requests per second:\s*" \
24 REGEX_LATENCY = r"Time per request:\s*" \
26 REGEX_PROCESS = r"Time per request:\s*" \
28 REGEX_TR = r"Transfer rate:\s*" \
30 REGEX_TT = r"Total transferred:\s*" \
32 REGEX_OK_NUM = r"Complete requests:\s*" \
34 REGEX_FAILED_NUM = r"Failed requests:\s*" \
36 REGEX_NUM = r"(\d*\.*\d*)(\D*)"
40 """ main function. get option and run ab test.
46 parser = argparse.ArgumentParser(description=u"Get option and run ab test")
48 # Number of requests to perform.
49 parser.add_argument(u"-r", u"--requests", type=int,
50 required=True, help=u"Number of requests to perform.")
52 # Server port number to use.
53 parser.add_argument(u"-p", u"--port", type=int, required=True,
54 help=u"Server port number to use.")
56 # Number of clients being processed at the same time.
57 parser.add_argument(u"-c", u"--clients", type=int, required=True,
58 help=u"Number of clients being processed at "
61 # Filename to be requested from the servers.
62 parser.add_argument(u"-f", u"--files", type=str, required=True,
63 help="Filename to be requested from the servers.")
66 parser.add_argument(u"-i", u"--ip", type=str, required=True,
67 help=u"Server bind IP address.")
70 parser.add_argument(u"-g", u"--tip", type=str, required=True,
71 help=u"TG bind IP address.")
73 # Specify SSL/TLS cipher suite.
74 parser.add_argument(u"-z", u"--cipher", type=str, default=u"0",
75 help=u"Specify SSL/TLS cipher.")
77 # Specify SSL/TLS protocol.
78 parser.add_argument(u"-t", u"--protocol", type=str, default=u"0",
79 help=u"Specify SSL/TLS protocol.")
82 parser.add_argument(u"-m", u"--mode", type=str, required=True,
83 help=u"Send requests mode:RPS/CPS.")
85 args = parser.parse_args()
87 req_num = args.requests
89 cli_num = args.clients
94 protocol = args.protocol
98 print(u"Failed number of req_num!")
101 # The number of processing units available to the current process.
102 _, cpu_num = subprocess.getstatusoutput(u"nproc --all")
103 cpu_num = int(cpu_num)
107 # Requests and Clients are evenly distributed on each CPU.
108 per_req = round(req_num / cpu_num)
109 per_cli = round(cli_num / cpu_num)
111 # Revise rounding request, This will be in the first ab request
112 all_total = per_req * cpu_num
113 per_req_1st = per_req + (req_num - all_total)
116 # Start process pool.
117 pool = Pool(processes=cpu_num)
119 for i in range(1, cpu_num + 1):
121 pool.apply_async(one, (
122 i, per_req_1st if i == 1 else per_req, per_cli, cipher,
123 protocol, ip_address, tg_address, files, port, mode)))
128 info_list = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
130 # Statistical test results.
134 info_list = [a + b for a, b in zip(info_list, stats)]
137 print(f"Transfer Rate: {round(info_list[6], 2)} [Kbytes/sec]")
138 print(f"Latency: {round(info_list[4] / 8, 2)} ms")
139 print(f"Connection {mode} rate:{round(info_list[3], 2)} per sec")
140 print(f"Total data transferred: {round(info_list[2])} bytes")
141 print(f"Completed requests: {round(info_list[0])} ")
142 print(f"Failed requests: {round(info_list[1])} ")
145 def one(cpu, requests, clients, cipher, protocol, ip_addr, tg_addr, files, port,
149 :param cpu: Core number id.
150 :param requests: Request number.
151 :param clients: Clients number.
152 :param cipher: Specify SSL/TLS cipher suite.
153 :param protocol: Specify SSL/TLS protocol.
154 :param ip_addr: Server ip address.
155 :param tg_addr: Tg ip address.
156 :param files: Filename to be requested from the servers.
157 :param port: Server port.
168 :returns: Test results.
172 cmd = f"sudo -E -S taskset --cpu-list {cpu} ab -n {requests} -c {clients}"
173 cmd = f"{cmd} -B {tg_addr} -r "
178 cmd = f"{cmd} http://{ip_addr}:{port}/{files}"
180 cmd = f"{cmd} -Z {cipher} -f {protocol}"
181 cmd = f"{cmd} https://{ip_addr}:{port}/{files}"
183 _, output = subprocess.getstatusoutput(cmd)
184 ret = _parse_output(output)
189 def _parse_output(msg):
190 """Parse the stdout with the results.
192 :param msg: stdout of ab.
194 :returns: Parsed results.
198 msg_lst = msg.splitlines(False)
202 if u"Requests per second" in line:
204 _float_number(re.search(REGEX_RPS, line).group(1))
206 elif u"Time per request" in line:
208 _float_number(re.search(REGEX_LATENCY, line).group(1))
210 elif u"Transfer rate" in line:
212 _float_number(re.search(REGEX_TR, line).group(1))
214 elif u"Total transferred" in line:
216 _float_number(re.search(REGEX_TT, line).group(1))
218 elif u"Complete requests" in line:
220 _float_number(re.search(REGEX_OK_NUM, line).group(1))
222 elif u"Failed requests" in line:
224 _float_number(re.search(REGEX_FAILED_NUM, line).group(1))
230 def _float_number(num):
231 """float value of the number.
233 :param num: Number to evaluate.
235 :returns: float number.
239 val = re.search(REGEX_NUM, num)
241 val_num = float(val.group(1))
243 raise RuntimeError(u"The output of ab does not include the results.")
247 if __name__ == "__main__":