aboutsummaryrefslogtreecommitdiffstats
path: root/resources/tools/wrk
diff options
context:
space:
mode:
Diffstat (limited to 'resources/tools/wrk')
-rw-r--r--resources/tools/wrk/wrk.py207
-rw-r--r--resources/tools/wrk/wrk_errors.py4
-rw-r--r--resources/tools/wrk/wrk_traffic_profile_parser.py150
3 files changed, 192 insertions, 169 deletions
diff --git a/resources/tools/wrk/wrk.py b/resources/tools/wrk/wrk.py
index 84d17ee7a1..381e9b9da0 100644
--- a/resources/tools/wrk/wrk.py
+++ b/resources/tools/wrk/wrk.py
@@ -66,18 +66,18 @@ def check_wrk(tg_node):
command is not availble.
"""
- if tg_node['type'] != NodeType.TG:
- raise RuntimeError('Node type is not a TG.')
+ if tg_node[u"type"] != NodeType.TG:
+ raise RuntimeError(u"Node type is not a TG.")
ssh = SSH()
ssh.connect(tg_node)
ret, _, _ = ssh.exec_command(
- "sudo -E "
- "sh -c '{0}/resources/tools/wrk/wrk_utils.sh installed'".
- format(Constants.REMOTE_FW_DIR))
+ f"sudo -E sh -c '{Constants.REMOTE_FW_DIR}/resources/tools/"
+ f"wrk/wrk_utils.sh installed'"
+ )
if int(ret) != 0:
- raise RuntimeError('WRK is not installed on TG node.')
+ raise RuntimeError(u"WRK is not installed on TG node.")
def run_wrk(tg_node, profile_name, tg_numa, test_type, warm_up=False):
@@ -98,102 +98,103 @@ def run_wrk(tg_node, profile_name, tg_numa, test_type, warm_up=False):
:raises: RuntimeError if node type is not a TG.
"""
- if tg_node['type'] != NodeType.TG:
- raise RuntimeError('Node type is not a TG.')
+ if tg_node[u"type"] != NodeType.TG:
+ raise RuntimeError(u"Node type is not a TG.")
# Parse and validate the profile
- profile_path = ("resources/traffic_profiles/wrk/{0}.yaml".
- format(profile_name))
+ profile_path = f"resources/traffic_profiles/wrk/{profile_name}.yaml"
profile = WrkTrafficProfile(profile_path).traffic_profile
cores = CpuUtils.cpu_list_per_node(tg_node, tg_numa)
- first_cpu = cores[profile["first-cpu"]]
+ first_cpu = cores[profile[u"first-cpu"]]
- if len(profile["urls"]) == 1 and profile["cpus"] == 1:
+ if len(profile[u"urls"]) == 1 and profile[u"cpus"] == 1:
params = [
- "traffic_1_url_1_core",
+ u"traffic_1_url_1_core",
str(first_cpu),
- str(profile["nr-of-threads"]),
- str(profile["nr-of-connections"]),
- "{0}s".format(profile["duration"]),
- "'{0}'".format(profile["header"]),
- str(profile["timeout"]),
- str(profile["script"]),
- str(profile["latency"]),
- "'{0}'".format(" ".join(profile["urls"]))
+ str(profile[u"nr-of-threads"]),
+ str(profile[u"nr-of-connections"]),
+ f"{profile[u'duration']}s",
+ f"'{profile[u'header']}'",
+ str(profile[u"timeout"]),
+ str(profile[u"script"]),
+ str(profile[u"latency"]),
+ f"'{u' '.join(profile[u'urls'])}'"
]
if warm_up:
warm_up_params = deepcopy(params)
- warm_up_params[4] = "10s"
- elif len(profile["urls"]) == profile["cpus"]:
+ warm_up_params[4] = u"10s"
+ elif len(profile[u"urls"]) == profile[u"cpus"]:
params = [
- "traffic_n_urls_n_cores",
+ u"traffic_n_urls_n_cores",
str(first_cpu),
- str(profile["nr-of-threads"]),
- str(profile["nr-of-connections"]),
- "{0}s".format(profile["duration"]),
- "'{0}'".format(profile["header"]),
- str(profile["timeout"]),
- str(profile["script"]),
- str(profile["latency"]),
- "'{0}'".format(" ".join(profile["urls"]))
+ str(profile[u"nr-of-threads"]),
+ str(profile[u"nr-of-connections"]),
+ f"{profile[u'duration']}s",
+ f"'{profile[u'header']}'",
+ str(profile[u"timeout"]),
+ str(profile[u"script"]),
+ str(profile[u"latency"]),
+ f"'{u' '.join(profile[u'urls'])}'"
]
if warm_up:
warm_up_params = deepcopy(params)
- warm_up_params[4] = "10s"
+ warm_up_params[4] = u"10s"
else:
params = [
- "traffic_n_urls_m_cores",
+ u"traffic_n_urls_m_cores",
str(first_cpu),
- str(profile["cpus"] / len(profile["urls"])),
- str(profile["nr-of-threads"]),
- str(profile["nr-of-connections"]),
- "{0}s".format(profile["duration"]),
- "'{0}'".format(profile["header"]),
- str(profile["timeout"]),
- str(profile["script"]),
- str(profile["latency"]),
- "'{0}'".format(" ".join(profile["urls"]))
+ str(profile[u"cpus"] // len(profile[u"urls"])),
+ str(profile[u"nr-of-threads"]),
+ str(profile[u"nr-of-connections"]),
+ f"{profile[u'duration']}s",
+ f"'{profile[u'header']}'",
+ str(profile[u"timeout"]),
+ str(profile[u"script"]),
+ str(profile[u"latency"]),
+ f"'{u' '.join(profile[u'urls'])}'"
]
if warm_up:
warm_up_params = deepcopy(params)
- warm_up_params[5] = "10s"
+ warm_up_params[5] = u"10s"
- args = " ".join(params)
+ args = u" ".join(params)
ssh = SSH()
ssh.connect(tg_node)
if warm_up:
- warm_up_args = " ".join(warm_up_params)
+ warm_up_args = u" ".join(warm_up_params)
ret, _, _ = ssh.exec_command(
- "{0}/resources/tools/wrk/wrk_utils.sh {1}".
- format(Constants.REMOTE_FW_DIR, warm_up_args), timeout=1800)
+ f"{Constants.REMOTE_FW_DIR}/resources/tools/wrk/wrk_utils.sh "
+ f"{warm_up_args}", timeout=1800
+ )
if int(ret) != 0:
- raise RuntimeError('wrk runtime error.')
+ raise RuntimeError(u"wrk runtime error.")
sleep(60)
ret, stdout, _ = ssh.exec_command(
- "{0}/resources/tools/wrk/wrk_utils.sh {1}".
- format(Constants.REMOTE_FW_DIR, args), timeout=1800)
+ f"{Constants.REMOTE_FW_DIR}/resources/tools/wrk/wrk_utils.sh {args}",
+ timeout=1800
+ )
if int(ret) != 0:
raise RuntimeError('wrk runtime error.')
stats = _parse_wrk_output(stdout)
- log_msg = "\nMeasured values:\n"
- if test_type == "cps":
- log_msg += "Connections/sec: Avg / Stdev / Max / +/- Stdev\n"
- for item in stats["rps-stats-lst"]:
- log_msg += "{0} / {1} / {2} / {3}\n".format(*item)
- log_msg += "Total cps: {0}cps\n".format(stats["rps-sum"])
- elif test_type == "rps":
- log_msg += "Requests/sec: Avg / Stdev / Max / +/- Stdev\n"
- for item in stats["rps-stats-lst"]:
- log_msg += "{0} / {1} / {2} / {3}\n".format(*item)
- log_msg += "Total rps: {0}rps\n".format(stats["rps-sum"])
- elif test_type == "bw":
- log_msg += "Transfer/sec: {0}Bps".format(stats["bw-sum"])
+ log_msg = u"\nMeasured values:\n"
+ if test_type == u"cps":
+ log_msg += u"Connections/sec: Avg / Stdev / Max / +/- Stdev\n"
+ for item in stats[u"rps-stats-lst"]:
+ log_msg += f"{0} / {1} / {2} / {3}\n".format(*item)
+ log_msg += f"Total cps: {stats[u'rps-sum']}cps\n"
+ elif test_type == u"rps":
+ log_msg += u"Requests/sec: Avg / Stdev / Max / +/- Stdev\n"
+ for item in stats[u"rps-stats-lst"]:
+ log_msg += f"{0} / {1} / {2} / {3}\n".format(*item)
+ log_msg += f"Total rps: {stats[u'rps-sum']}rps\n"
+ elif test_type == u"bw":
+ log_msg += f"Transfer/sec: {stats[u'bw-sum']}Bps"
logger.info(log_msg)
@@ -210,47 +211,52 @@ def _parse_wrk_output(msg):
:raises: WrkError if the message does not include the results.
"""
- if "Thread Stats" not in msg:
- raise WrkError("The output of wrk does not include the results.")
+ if u"Thread Stats" not in msg:
+ raise WrkError(u"The output of wrk does not include the results.")
msg_lst = msg.splitlines(False)
stats = {
- "latency-dist-lst": list(),
- "latency-stats-lst": list(),
- "rps-stats-lst": list(),
- "rps-lst": list(),
- "bw-lst": list(),
- "rps-sum": 0,
- "bw-sum": None
+ u"latency-dist-lst": list(),
+ u"latency-stats-lst": list(),
+ u"rps-stats-lst": list(),
+ u"rps-lst": list(),
+ u"bw-lst": list(),
+ u"rps-sum": 0,
+ u"bw-sum": None
}
for line in msg_lst:
- if "Latency Distribution" in line:
+ if u"Latency Distribution" in line:
# Latency distribution - 50%, 75%, 90%, 99%
pass
- elif "Latency" in line:
+ elif u"Latency" in line:
# Latency statistics - Avg, Stdev, Max, +/- Stdev
pass
- elif "Req/Sec" in line:
+ elif u"Req/Sec" in line:
# rps statistics - Avg, Stdev, Max, +/- Stdev
- stats["rps-stats-lst"].append((
- _evaluate_number(re.search(REGEX_RPS_STATS, line).group(1)),
- _evaluate_number(re.search(REGEX_RPS_STATS, line).group(2)),
- _evaluate_number(re.search(REGEX_RPS_STATS, line).group(3)),
- _evaluate_number(re.search(REGEX_RPS_STATS, line).group(4))))
- elif "Requests/sec:" in line:
+ stats[u"rps-stats-lst"].append(
+ (
+ _evaluate_number(re.search(REGEX_RPS_STATS, line).group(1)),
+ _evaluate_number(re.search(REGEX_RPS_STATS, line).group(2)),
+ _evaluate_number(re.search(REGEX_RPS_STATS, line).group(3)),
+ _evaluate_number(re.search(REGEX_RPS_STATS, line).group(4))
+ )
+ )
+ elif u"Requests/sec:" in line:
# rps (cps)
- stats["rps-lst"].append(
- _evaluate_number(re.search(REGEX_RPS, line).group(1)))
- elif "Transfer/sec:" in line:
+ stats[u"rps-lst"].append(
+ _evaluate_number(re.search(REGEX_RPS, line).group(1))
+ )
+ elif u"Transfer/sec:" in line:
# BW
- stats["bw-lst"].append(
- _evaluate_number(re.search(REGEX_BW, line).group(1)))
+ stats[u"bw-lst"].append(
+ _evaluate_number(re.search(REGEX_BW, line).group(1))
+ )
- for item in stats["rps-stats-lst"]:
- stats["rps-sum"] += item[0]
- stats["bw-sum"] = sum(stats["bw-lst"])
+ for item in stats[u"rps-stats-lst"]:
+ stats[u"rps-sum"] += item[0]
+ stats[u"bw-sum"] = sum(stats[u"bw-lst"])
return stats
@@ -270,23 +276,24 @@ def _evaluate_number(num):
try:
val_num = float(val.group(1))
except ValueError:
- raise WrkError("The output of wrk does not include the results "
- "or the format of results has changed.")
+ raise WrkError(
+ u"The output of wrk does not include the results or the format "
+ u"of results has changed."
+ )
val_mul = val.group(2).lower()
if val_mul:
- if "k" in val_mul:
+ if u"k" in val_mul:
val_num *= 1000
- elif "m" in val_mul:
+ elif u"m" in val_mul:
val_num *= 1000000
- elif "g" in val_mul:
+ elif u"g" in val_mul:
val_num *= 1000000000
- elif "b" in val_mul:
+ elif u"b" in val_mul:
pass
- elif "%" in val_mul:
+ elif u"%" in val_mul:
pass
- elif "" in val_mul:
+ elif u"" in val_mul:
pass
else:
- raise WrkError("The multiplicand {0} is not defined.".
- format(val_mul))
+ raise WrkError(f"The multiplicand {val_mul} is not defined.")
return val_num
diff --git a/resources/tools/wrk/wrk_errors.py b/resources/tools/wrk/wrk_errors.py
index 3173dd4223..2cdd76815a 100644
--- a/resources/tools/wrk/wrk_errors.py
+++ b/resources/tools/wrk/wrk_errors.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2018 Cisco and/or its affiliates.
+# Copyright (c) 2019 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
@@ -29,7 +29,7 @@ class WrkError(Exception):
- relevant data if there are any collected (optional parameter details).
"""
- def __init__(self, msg, details=''):
+ def __init__(self, msg, details=u""):
"""Sets the exception message and the level.
:param msg: Short description of the encountered problem.
diff --git a/resources/tools/wrk/wrk_traffic_profile_parser.py b/resources/tools/wrk/wrk_traffic_profile_parser.py
index 1d40aa3d8a..1994b6195d 100644
--- a/resources/tools/wrk/wrk_traffic_profile_parser.py
+++ b/resources/tools/wrk/wrk_traffic_profile_parser.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2018 Cisco and / or its affiliates.
+# Copyright (c) 2019 Cisco and / or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at:
@@ -26,22 +26,26 @@ from robot.api import logger
from resources.tools.wrk.wrk_errors import WrkError
-class WrkTrafficProfile(object):
+class WrkTrafficProfile:
"""The wrk traffic profile.
"""
- MANDATORY_PARAMS = ("urls",
- "first-cpu",
- "cpus",
- "duration",
- "nr-of-threads",
- "nr-of-connections")
-
- INTEGER_PARAMS = (("cpus", 1),
- ("first-cpu", 0),
- ("duration", 1),
- ("nr-of-threads", 1),
- ("nr-of-connections", 1))
+ MANDATORY_PARAMS = (
+ u"urls",
+ u"first-cpu",
+ u"cpus",
+ u"duration",
+ u"nr-of-threads",
+ u"nr-of-connections"
+ )
+
+ INTEGER_PARAMS = (
+ (u"cpus", 1),
+ (u"first-cpu", 0),
+ (u"duration", 1),
+ (u"nr-of-threads", 1),
+ (u"nr-of-connections", 1)
+ )
def __init__(self, profile_name):
"""Read the traffic profile from the yaml file.
@@ -57,29 +61,34 @@ class WrkTrafficProfile(object):
self.profile_name = profile_name
try:
- with open(self.profile_name, 'r') as profile_file:
+ with open(self.profile_name, "r") as profile_file:
self.traffic_profile = load(profile_file)
except IOError as err:
- raise WrkError(msg="An error occurred while opening the file '{0}'."
- .format(self.profile_name),
- details=str(err))
+ raise WrkError(
+ msg=f"An error occurred while opening the file "
+ f"'{self.profile_name}'.", details=str(err)
+ )
except YAMLError as err:
- raise WrkError(msg="An error occurred while parsing the traffic "
- "profile '{0}'.".format(self.profile_name),
- details=str(err))
+ raise WrkError(
+ msg=f"An error occurred while parsing the traffic profile "
+ f"'{self.profile_name}'.", details=str(err)
+ )
self._validate_traffic_profile()
if self.traffic_profile:
- logger.debug("\nThe wrk traffic profile '{0}' is valid.\n".
- format(self.profile_name))
- logger.debug("wrk traffic profile '{0}':".format(self.profile_name))
+ logger.debug(
+ f"\nThe wrk traffic profile '{self.profile_name}' is valid.\n"
+ )
+ logger.debug(f"wrk traffic profile '{self.profile_name}':")
logger.debug(pformat(self.traffic_profile))
else:
- logger.debug("\nThe wrk traffic profile '{0}' is invalid.\n".
- format(self.profile_name))
- raise WrkError("\nThe wrk traffic profile '{0}' is invalid.\n".
- format(self.profile_name))
+ logger.debug(
+ f"\nThe wrk traffic profile '{self.profile_name}' is invalid.\n"
+ )
+ raise WrkError(
+ f"\nThe wrk traffic profile '{self.profile_name}' is invalid.\n"
+ )
def __repr__(self):
return pformat(self.traffic_profile)
@@ -94,8 +103,9 @@ class WrkTrafficProfile(object):
doc/wrk_lld.rst
"""
- logger.debug("\nValidating the wrk traffic profile '{0}'...\n".
- format(self.profile_name))
+ logger.debug(
+ f"\nValidating the wrk traffic profile '{self.profile_name}'...\n"
+ )
if not (self._validate_mandatory_structure()
and self._validate_mandatory_values()
and self._validate_optional_values()
@@ -110,14 +120,14 @@ class WrkTrafficProfile(object):
"""
# Level 1: Check if the profile is a dictionary:
if not isinstance(self.traffic_profile, dict):
- logger.error("The wrk traffic profile must be a dictionary.")
+ logger.error(u"The wrk traffic profile must be a dictionary.")
return False
# Level 2: Check if all mandatory parameters are present:
is_valid = True
for param in self.MANDATORY_PARAMS:
if self.traffic_profile.get(param, None) is None:
- logger.error("The parameter '{0}' in mandatory.".format(param))
+ logger.error(f"The parameter '{param}' in mandatory.")
is_valid = False
return is_valid
@@ -129,8 +139,8 @@ class WrkTrafficProfile(object):
"""
# Level 3: Mandatory params: Check if urls is a list:
is_valid = True
- if not isinstance(self.traffic_profile["urls"], list):
- logger.error("The parameter 'urls' must be a list.")
+ if not isinstance(self.traffic_profile[u"urls"], list):
+ logger.error(u"The parameter 'urls' must be a list.")
is_valid = False
# Level 3: Mandatory params: Check if integers are not below minimum
@@ -147,60 +157,64 @@ class WrkTrafficProfile(object):
"""
is_valid = True
# Level 4: Optional params: Check if script is present:
- script = self.traffic_profile.get("script", None)
+ script = self.traffic_profile.get(u"script", None)
if script is not None:
if not isinstance(script, str):
- logger.error("The path to LuaJIT script in invalid")
+ logger.error(u"The path to LuaJIT script in invalid")
is_valid = False
else:
if not isfile(script):
- logger.error("The file '{0}' does not exist.".
- format(script))
+ logger.error(f"The file '{script}' does not exist.")
is_valid = False
else:
- self.traffic_profile["script"] = None
- logger.debug("The optional parameter 'LuaJIT script' is not "
- "defined. No problem.")
+ self.traffic_profile[u"script"] = None
+ logger.debug(
+ u"The optional parameter 'LuaJIT script' is not defined. "
+ u"No problem."
+ )
# Level 4: Optional params: Check if header is present:
- header = self.traffic_profile.get("header", None)
+ header = self.traffic_profile.get(u"header", None)
if header is not None:
if isinstance(header, dict):
- header = ", ".join("{0}: {1}".format(*item)
- for item in header.items())
- self.traffic_profile["header"] = header
+ header = u", ".join(
+ f"{0}: {1}".format(*item) for item in header.items()
+ )
+ self.traffic_profile[u"header"] = header
elif not isinstance(header, str):
- logger.error("The parameter 'header' type is not valid.")
+ logger.error(u"The parameter 'header' type is not valid.")
is_valid = False
if not header:
- logger.error("The parameter 'header' is defined but "
- "empty.")
+ logger.error(u"The parameter 'header' is defined but empty.")
is_valid = False
else:
- self.traffic_profile["header"] = None
- logger.debug("The optional parameter 'header' is not defined. "
- "No problem.")
+ self.traffic_profile[u"header"] = None
+ logger.debug(
+ u"The optional parameter 'header' is not defined. No problem."
+ )
# Level 4: Optional params: Check if latency is present:
- latency = self.traffic_profile.get("latency", None)
+ latency = self.traffic_profile.get(u"latency", None)
if latency is not None:
if not isinstance(latency, bool):
- logger.error("The parameter 'latency' must be boolean.")
+ logger.error(u"The parameter 'latency' must be boolean.")
is_valid = False
else:
- self.traffic_profile["latency"] = False
- logger.debug("The optional parameter 'latency' is not defined. "
- "No problem.")
+ self.traffic_profile[u"latency"] = False
+ logger.debug(
+ u"The optional parameter 'latency' is not defined. No problem."
+ )
# Level 4: Optional params: Check if timeout is present:
- if 'timeout' in self.traffic_profile:
- if not self._validate_int_param('timeout', 1):
+ if u"timeout" in self.traffic_profile:
+ if not self._validate_int_param(u"timeout", 1):
is_valid = False
else:
- self.traffic_profile["timeout"] = None
- logger.debug("The optional parameter 'timeout' is not defined. "
- "No problem.")
+ self.traffic_profile[u"timeout"] = None
+ logger.debug(
+ u"The optional parameter 'timeout' is not defined. No problem."
+ )
return is_valid
@@ -211,9 +225,10 @@ class WrkTrafficProfile(object):
:rtype: bool
"""
# Level 5: Check urls and cpus:
- if self.traffic_profile["cpus"] % len(self.traffic_profile["urls"]):
- logger.error("The number of CPUs must be a multiple of the "
- "number of URLs.")
+ if self.traffic_profile[u"cpus"] % len(self.traffic_profile[u"urls"]):
+ logger.error(
+ u"The number of CPUs must be a multiple of the number of URLs."
+ )
return False
return True
@@ -229,7 +244,7 @@ class WrkTrafficProfile(object):
:rtype: bool
"""
value = self._traffic_profile[param]
- if isinstance(value, (str, unicode)):
+ if isinstance(value, str):
if value.isdigit():
value = int(value)
else:
@@ -237,8 +252,9 @@ class WrkTrafficProfile(object):
if isinstance(value, int) and value >= minimum:
self.traffic_profile[param] = value
return True
- logger.error("The parameter '{param}' must be an integer and "
- "at least {minimum}".format(param=param, minimum=minimum))
+ logger.error(
+ f"The parameter '{param}' must be an integer and at least {minimum}"
+ )
return False
@property