diff options
Diffstat (limited to 'resources/tools')
-rw-r--r-- | resources/tools/testbed-setup/ansible/roles/cleanup/tasks/tg.yaml | 7 | ||||
-rwxr-xr-x | resources/tools/testbed-setup/ansible/roles/tg/files/csit-initialize-docker-tg.sh | 2 | ||||
-rw-r--r-- | resources/tools/testbed-setup/ansible/roles/wrk/defaults/main.yaml | 21 | ||||
-rw-r--r-- | resources/tools/testbed-setup/ansible/roles/wrk/tasks/main.yaml | 48 | ||||
-rw-r--r-- | resources/tools/testbed-setup/ansible/tg.yaml | 2 | ||||
-rw-r--r-- | resources/tools/testbed-setup/ansible/tg_aws.yaml | 2 | ||||
-rw-r--r-- | resources/tools/testbed-setup/ansible/tg_azure.yaml | 2 | ||||
-rw-r--r-- | resources/tools/wrk/__init__.py | 16 | ||||
-rw-r--r-- | resources/tools/wrk/doc/wrk_lld.rst | 293 | ||||
-rw-r--r-- | resources/tools/wrk/wrk.py | 299 | ||||
-rw-r--r-- | resources/tools/wrk/wrk_errors.py | 55 | ||||
-rw-r--r-- | resources/tools/wrk/wrk_traffic_profile_parser.py | 294 | ||||
-rwxr-xr-x | resources/tools/wrk/wrk_utils.sh | 246 |
13 files changed, 0 insertions, 1287 deletions
diff --git a/resources/tools/testbed-setup/ansible/roles/cleanup/tasks/tg.yaml b/resources/tools/testbed-setup/ansible/roles/cleanup/tasks/tg.yaml index a026ec2acd..9ac83bc9fc 100644 --- a/resources/tools/testbed-setup/ansible/roles/cleanup/tasks/tg.yaml +++ b/resources/tools/testbed-setup/ansible/roles/cleanup/tasks/tg.yaml @@ -7,10 +7,3 @@ process: "_t-rex" when: docker_tg is undefined tags: kill-process - -- name: Kill processes - WRK - import_tasks: kill_process.yaml - vars: - process: "wrk" - tags: kill-process - when: docker_tg is undefined
\ No newline at end of file diff --git a/resources/tools/testbed-setup/ansible/roles/tg/files/csit-initialize-docker-tg.sh b/resources/tools/testbed-setup/ansible/roles/tg/files/csit-initialize-docker-tg.sh index e9f19fd899..7b90d20bda 100755 --- a/resources/tools/testbed-setup/ansible/roles/tg/files/csit-initialize-docker-tg.sh +++ b/resources/tools/testbed-setup/ansible/roles/tg/files/csit-initialize-docker-tg.sh @@ -44,8 +44,6 @@ case "${1:-start}" in dcr_stc_params+="--volume /dev:/dev " # Mount /opt/boot/ where VM kernel and initrd are located. dcr_stc_params+="--volume /opt:/opt " - # Mount /usr/local/bin/wrk where WRK is located. - dcr_stc_params+="--volume /usr/local/bin/wrk:/usr/local/bin/wrk " # Mount host hugepages for VMs. dcr_stc_params+="--volume /dev/hugepages:/dev/hugepages " diff --git a/resources/tools/testbed-setup/ansible/roles/wrk/defaults/main.yaml b/resources/tools/testbed-setup/ansible/roles/wrk/defaults/main.yaml deleted file mode 100644 index 2d378487df..0000000000 --- a/resources/tools/testbed-setup/ansible/roles/wrk/defaults/main.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -# file: roles/wrk/defaults/main.yaml - -packages: "{{ packages_base + packages_by_distro[ansible_distribution | lower] + packages_by_arch[ansible_machine] }}" - -packages_base: - - [] - -packages_by_distro: - ubuntu: - - "build-essential" - -packages_by_arch: - aarch64: - - [] - x86_64: - - [] - -wrk_target_dir: "/opt" -wrk_version: - - "4.0.2" diff --git a/resources/tools/testbed-setup/ansible/roles/wrk/tasks/main.yaml b/resources/tools/testbed-setup/ansible/roles/wrk/tasks/main.yaml deleted file mode 100644 index 163044de43..0000000000 --- a/resources/tools/testbed-setup/ansible/roles/wrk/tasks/main.yaml +++ /dev/null @@ -1,48 +0,0 @@ ---- -# file: roles/wrk/tasks/main.yaml - -- name: WRK - Install Distribution - Release - Machine Prerequisites - package: - name: "{{ packages | flatten(levels=1) }}" - state: latest - update_cache: true - tags: - - install-dependencies - -- name: WRK - Get Release Archive - get_url: - url: "https://github.com/wg/wrk/archive/{{ item }}.tar.gz" - dest: "{{ wrk_target_dir }}/wrk-{{ item }}.tar.gz" - mode: 0644 - loop: "{{ wrk_version }}" - register: wrk_downloaded - tags: - - install-wrk - -- name: WRK - Extract Release Archive - unarchive: - remote_src: true - src: "{{ wrk_target_dir }}/wrk-{{ item }}.tar.gz" - dest: "{{ wrk_target_dir }}/" - creates: "{{ wrk_target_dir }}/wrk-{{ item }}/src" - loop: "{{ wrk_version }}" - register: wrk_extracted - tags: - - install-wrk - -- name: WRK - Compile Release I - command: "make" - args: - chdir: "{{ wrk_target_dir }}/wrk-{{ item }}" - loop: "{{ wrk_version }}" - when: wrk_extracted - register: wrk_compiled - tags: - - install-wrk - -- name: WRK - Copy Binary - command: "cp {{ wrk_target_dir }}/wrk-{{ item }}/wrk /usr/local/bin/" - loop: "{{ wrk_version }}" - when: wrk_compiled - tags: - - install-wrk diff --git a/resources/tools/testbed-setup/ansible/tg.yaml b/resources/tools/testbed-setup/ansible/tg.yaml index e800881127..e6c5b4b9db 100644 --- a/resources/tools/testbed-setup/ansible/tg.yaml +++ b/resources/tools/testbed-setup/ansible/tg.yaml @@ -20,8 +20,6 @@ tags: iperf - role: trex tags: trex - - role: wrk - tags: wrk - role: docker tags: docker - role: performance_tuning diff --git a/resources/tools/testbed-setup/ansible/tg_aws.yaml b/resources/tools/testbed-setup/ansible/tg_aws.yaml index dda50f416e..77fde766c9 100644 --- a/resources/tools/testbed-setup/ansible/tg_aws.yaml +++ b/resources/tools/testbed-setup/ansible/tg_aws.yaml @@ -19,8 +19,6 @@ tags: iperf - role: trex tags: trex - - role: wrk - tags: wrk - role: docker tags: docker - role: cleanup diff --git a/resources/tools/testbed-setup/ansible/tg_azure.yaml b/resources/tools/testbed-setup/ansible/tg_azure.yaml index afe2d11c4a..7ecf6d074c 100644 --- a/resources/tools/testbed-setup/ansible/tg_azure.yaml +++ b/resources/tools/testbed-setup/ansible/tg_azure.yaml @@ -17,8 +17,6 @@ tags: iperf - role: trex tags: trex - - role: wrk - tags: wrk - role: docker tags: docker - role: cleanup diff --git a/resources/tools/wrk/__init__.py b/resources/tools/wrk/__init__.py deleted file mode 100644 index 977169c00f..0000000000 --- a/resources/tools/wrk/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2018 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: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -__init__ file for directory tools/wrk -""" diff --git a/resources/tools/wrk/doc/wrk_lld.rst b/resources/tools/wrk/doc/wrk_lld.rst deleted file mode 100644 index 1437fd8948..0000000000 --- a/resources/tools/wrk/doc/wrk_lld.rst +++ /dev/null @@ -1,293 +0,0 @@ -Onboarding of wrk as a http traffic generator in CSIT ------------------------------------------------------ - -wrk is a modern HTTP benchmarking tool capable of generating significant -load when run on a single multi-core CPU. - -An optional LuaJIT script can perform HTTP request generation, response -processing, and custom reporting. - - -wrk installation on TG node -''''''''''''''''''''''''''' - -**Procedure** - - #. Check if wrk is installed on the TG node. - #. If not, install it. - -**wrk installation** - -:: - - # Install pre-requisites: - sudo apt-get install build-essential libssl-dev git -y - - # Get the specified version: - wget ${WRK_DWNLD_PATH}/${WRK_TAR} - tar xzf ${WRK_TAR} - cd wrk-${WRK_VERSION} - - # Build the wrk: - cd wrk - make - - # Move the executable to somewhere in the PATH, e.q: - sudo cp wrk /usr/local/bin - - -wrk traffic profile -''''''''''''''''''' - -**The traffic profile can include these items:** - - - List of URLs - mandatory, - - The first CPU used to run wrk - mandatory, - - Number of CPUs used for wrk - mandatory, - - Test duration - mandatory, - - Number of threads - mandatory, - - Number of connections - mandatory, - - LuaJIT script - optional, defaults to no script, - - HTTP header - optional, defaults to no header, - - Latency - optional, defaults to False, - - Timeout - optional, defaults to wrk default. - -**List of URLs** - -List of URLs for requests. Each URL is requested in a separate instance of wrk. -Type: list - -*Example:* - -:: - - urls: - - "http://192.168.1.1/1kB.bin" - - "http://192.168.1.2/1kB.bin" - - "http://192.168.1.3/1kB.bin" - -**The first CPU used to run wrk** -The first CPU used to run wrk. The other CPUs follow this one. -Type: integer - -*Example:* - -:: - - first-cpu: 1 - -**Number of CPUs used for wrk** - -The number of CPUs used for wrk. The number of CPUs must be a multiplication -of the number of URLs. -Type: integer - -*Example:* - -:: - - cpus: 6 - -.. note:: - - The combinations of URLs and a number of CPUs create following use cases: - - - One URL and one CPU - One instance of wrk sends one request (URL) via - one NIC - - One URL and n CPUs - n instances of wrk send the same request (URL) - via one or more NICs - - n URLs and n CPUs - n instances of wrk send n requests (URL) via one - or more NICs - - n URLs and m CPUs, m = a * n - m instances of wrk send n requests - (URL) via one or more NICs - -**Test duration** - -Duration of the test in seconds. -Type: integer - -*Example:* - -:: - - duration: 30 - -**Number of threads** - -Total number of threads to use by wrk to send traffic. -Type: integer - -*Example:* - -:: - - nr-of-threads: 1 - -**Number of connections** - -Total number of HTTP connections to keep open with each thread handling -N = connections / threads. -Type: integer - -*Example:* - -:: - - nr-of-connections: 50 - -**LuaJIT script** - -Path to LuaJIT script. -Type: string - -For more information see: https://github.com/wg/wrk/blob/master/SCRIPTING - -*Example:* - -:: - - script: "scripts/report.lua" - -**HTTP header** - -HTTP header to add to request. -Type: string (taken as it is) or dictionary - -*Example:* - -:: - - # Dictionary: - header: - Connection: "close" - -or - -:: - - # String: - header: "Connection: close" - -**Latency** - -Print detailed latency statistics. -Type: boolean - -*Example:* - -:: - - latency: False - -**Timeout** - -Record a timeout if a response is not received within this amount of time. -Type: integer - -:: - - timeout: 5 - -**Examples of a wrk traffic profile** - -*Get the number of connections per second:* - -- Use 3 CPUs to send 3 different requests via 3 NICs. -- The test takes 30 seconds. -- wrk sends traffic in one thread per CPU. -- There will be open max 50 connection at the same time. -- The header is set to 'Connection: "close"' so wrk opens separate connection - for each request. Then the number of requests equals to the number of - connections. -- Timeout for responses from the server is set to 5 seconds. - -:: - - urls: - - "http://192.168.1.1/0B.bin" - - "http://192.168.1.2/0B.bin" - - "http://192.168.1.3/0B.bin" - cpus: 3 - duration: 30 - nr-of-threads: 1 - nr-of-connections: 50 - header: - Connection: "close" - timeout: 5 - -*Get the number of requests per second:* - -- Use 3 CPUs to send 3 different requests via 3 NICs. -- The test takes 30 seconds. -- wrk sends traffic in one thread per CPU. -- There will be max 50 concurrent open connections. - -:: - - urls: - - "http://192.168.1.1/1kB.bin" - - "http://192.168.1.2/1kB.bin" - - "http://192.168.1.3/1kB.bin" - cpus: 3 - duration: 30 - nr-of-threads: 1 - nr-of-connections: 50 - -*Get the bandwidth:* - -- Use 3 CPUs to send 3 different requests via 3 NICs. -- The test takes 30 seconds. -- wrk sends traffic in one thread per CPU. -- There will be open max 50 connection at the same time. -- Timeout for responses from the server is set to 5 seconds. - -:: - - urls: - - "http://192.168.1.1/1MB.bin" - - "http://192.168.1.2/1MB.bin" - - "http://192.168.1.3/1MB.bin" - cpus: 3 - duration: 30 - nr-of-threads: 1 - nr-of-connections: 50 - timeout: 5 - - -Running wrk -''''''''''' - -**Suite setup phase** - -CSIT framework checks if wrk is installed on the TG node. If not, or if the -installation is forced, it installs it on the TG node. - -*Procedure:* - - #. Make sure TRex is stopped. - #. Bind used TG interfaces to corresponding drivers (defined in the topology - file). - #. If the wrk installation is forced: - - - Destroy existing wrk - - #. If the wrk installation is not forced: - - - Check if wrk is installed. - - If installed, exit. - - #. Clone wrk from git (https://github.com/wg/wrk.git) - #. Build wrk. - #. Copy the executable to /usr/local/bin so it is in the PATH. - -**Test phase** - -*Procedure:* - -#. Read the wrk traffic profile. -#. Verify the profile. -#. Use the information from the profile to set the wrk parameters. -#. Run wrk. -#. Read the output. -#. Evaluate and log the output. - diff --git a/resources/tools/wrk/wrk.py b/resources/tools/wrk/wrk.py deleted file mode 100644 index 85d18d4a9c..0000000000 --- a/resources/tools/wrk/wrk.py +++ /dev/null @@ -1,299 +0,0 @@ -# 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: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""wrk implementation into CSIT framework. -""" - -import re - -from copy import deepcopy -from time import sleep - -from robot.api import logger - -from resources.libraries.python.ssh import SSH -from resources.libraries.python.topology import NodeType -from resources.libraries.python.CpuUtils import CpuUtils -from resources.libraries.python.Constants import Constants - -from resources.tools.wrk.wrk_traffic_profile_parser import WrkTrafficProfile -from resources.tools.wrk.wrk_errors import WrkError - - -REGEX_LATENCY_STATS = \ - r"Latency\s*" \ - r"(\d*\.*\d*\S*)\s*" \ - r"(\d*\.*\d*\S*)\s*" \ - r"(\d*\.*\d*\S*)\s*" \ - r"(\d*\.*\d*\%)" -REGEX_RPS_STATS = \ - r"Req/Sec\s*" \ - r"(\d*\.*\d*\S*)\s*" \ - r"(\d*\.*\d*\S*)\s*" \ - r"(\d*\.*\d*\S*)\s*" \ - r"(\d*\.*\d*\%)" -REGEX_RPS = r"Requests/sec:\s*" \ - r"(\d*\.*\S*)" -REGEX_BW = r"Transfer/sec:\s*" \ - r"(\d*\.*\S*)" -REGEX_LATENCY_DIST = \ - r"Latency Distribution\n" \ - r"\s*50\%\s*(\d*\.*\d*\D*)\n" \ - r"\s*75\%\s*(\d*\.*\d*\D*)\n" \ - r"\s*90\%\s*(\d*\.*\d*\D*)\n" \ - r"\s*99\%\s*(\d*\.*\d*\D*)\n" - -# Split number and multiplicand, e.g. 14.25k --> 14.25 and k -REGEX_NUM = r"(\d*\.*\d*)(\D*)" - - -def check_wrk(tg_node): - """Check if wrk is installed on the TG node. - - :param tg_node: Traffic generator node. - :type tg_node: dict - :raises: RuntimeError if the given node is not a TG node or if the - command is not availble. - """ - - 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( - f"sudo -E sh -c '{Constants.REMOTE_FW_DIR}/resources/tools/" - f"wrk/wrk_utils.sh installed'" - ) - if int(ret) != 0: - raise RuntimeError(u"WRK is not installed on TG node.") - - -def run_wrk(tg_node, profile_name, tg_numa, test_type, warm_up=False): - """Send the traffic as defined in the profile. - - :param tg_node: Traffic generator node. - :param profile_name: The name of wrk traffic profile. - :param tg_numa: Numa node on which wrk will run. - :param test_type: The type of the tests: cps, rps, bw - :param warm_up: If True, warm-up traffic is generated before test traffic. - :type profile_name: str - :type tg_node: dict - :type tg_numa: int - :type test_type: str - :type warm_up: bool - :returns: Message with measured data. - :rtype: str - :raises: RuntimeError if 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 = 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[u"first-cpu"]] - - if len(profile[u"urls"]) == 1 and profile[u"cpus"] == 1: - params = [ - u"traffic_1_url_1_core", - str(first_cpu), - 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] = u"10s" - elif len(profile[u"urls"]) == profile[u"cpus"]: - params = [ - u"traffic_n_urls_n_cores", - str(first_cpu), - 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] = u"10s" - else: - params = [ - u"traffic_n_urls_m_cores", - str(first_cpu), - 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] = u"10s" - - args = u" ".join(params) - - ssh = SSH() - ssh.connect(tg_node) - - if warm_up: - warm_up_args = u" ".join(warm_up_params) - ret, _, _ = ssh.exec_command( - f"{Constants.REMOTE_FW_DIR}/resources/tools/wrk/wrk_utils.sh " - f"{warm_up_args}", timeout=1800 - ) - if int(ret) != 0: - raise RuntimeError(u"wrk runtime error.") - sleep(60) - - ret, stdout, _ = ssh.exec_command( - 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 = 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 += u" / ".join(map(str, item)) + u"\n" - 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 += u" / ".join(map(str, item)) + u"\n" - 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) - - return log_msg - - -def _parse_wrk_output(msg): - """Parse the wrk stdout with the results. - - :param msg: stdout of wrk. - :type msg: str - :returns: Parsed results. - :rtype: dict - :raises: WrkError if the message 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 = { - 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 u"Latency Distribution" in line: - # Latency distribution - 50%, 75%, 90%, 99% - pass - elif u"Latency" in line: - # Latency statistics - Avg, Stdev, Max, +/- Stdev - pass - elif u"Req/Sec" in line: - # rps statistics - Avg, Stdev, Max, +/- Stdev - 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[u"rps-lst"].append( - _evaluate_number(re.search(REGEX_RPS, line).group(1)) - ) - elif u"Transfer/sec:" in line: - # BW - stats[u"bw-lst"].append( - _evaluate_number(re.search(REGEX_BW, line).group(1)) - ) - - 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 - - -def _evaluate_number(num): - """Evaluate the numeric value of the number with multiplicands, e.g.: - 12.25k --> 12250 - - :param num: Number to evaluate. - :type num: str - :returns: Evaluated number. - :rtype: float - :raises: WrkError if it is not possible to evaluate the given number. - """ - - val = re.search(REGEX_NUM, num) - try: - val_num = float(val.group(1)) - except ValueError: - 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 u"k" in val_mul: - val_num *= 1000 - elif u"m" in val_mul: - val_num *= 1000000 - elif u"g" in val_mul: - val_num *= 1000000000 - elif u"b" in val_mul: - pass - elif u"%" in val_mul: - pass - elif u"" in val_mul: - pass - else: - 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 deleted file mode 100644 index 2cdd76815a..0000000000 --- a/resources/tools/wrk/wrk_errors.py +++ /dev/null @@ -1,55 +0,0 @@ -# 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: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Implementation of exceptions used in the wrk traffic generator. -""" - - -from robot.api import logger - - -class WrkError(Exception): - """Exception(s) raised by the wrk traffic generator. - - When raising this exception, put this information to the message in this - order: - - short description of the encountered problem (parameter msg), - - relevant messages if there are any collected, e.g., from caught - exception (optional parameter details), - - relevant data if there are any collected (optional parameter details). - """ - - def __init__(self, msg, details=u""): - """Sets the exception message and the level. - - :param msg: Short description of the encountered problem. - :param details: Relevant messages if there are any collected, e.g.: - from caught exception (optional parameter details), or relevant data if - there are any collected (optional parameter details). - :type msg: str - :type details: str - """ - - super(WrkError, self).__init__() - self._msg = msg - self._details = details - - logger.error(self._msg) - if self._details: - logger.error(self._details) - - def __repr__(self): - return repr(self._msg) - - def __str__(self): - return str(self._msg) diff --git a/resources/tools/wrk/wrk_traffic_profile_parser.py b/resources/tools/wrk/wrk_traffic_profile_parser.py deleted file mode 100644 index f553465705..0000000000 --- a/resources/tools/wrk/wrk_traffic_profile_parser.py +++ /dev/null @@ -1,294 +0,0 @@ -# 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: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions -# and limitations under the License. - -"""wrk traffic profile parser. - -See LLD for the structure of a wrk traffic profile. -""" - - -from os.path import isfile -from pprint import pformat - -from yaml import safe_load, YAMLError -from robot.api import logger - -from resources.tools.wrk.wrk_errors import WrkError - - -class WrkTrafficProfile: - """The wrk traffic profile. - """ - - 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. - - :param profile_name: Path to the yaml file with the profile. - :type profile_name: str - :raises: WrkError if it is not possible to parse the profile. - """ - - self._profile_name = None - self._traffic_profile = None - - self.profile_name = profile_name - - try: - with open(self.profile_name, u"rt") as profile_file: - self.traffic_profile = safe_load(profile_file) - except IOError as 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=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( - 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( - 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) - - def __str__(self): - return pformat(self.traffic_profile) - - def _validate_traffic_profile(self): - """Validate the traffic profile. - - The specification, the structure and the rules are described in - doc/wrk_lld.rst - """ - - 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() - and self._validate_dependencies()): - self.traffic_profile = None - - def _validate_mandatory_structure(self): - """Validate presence of mandatory parameters in trafic profile dict - - :returns: whether mandatory structure is followed by the profile - :rtype: bool - """ - # Level 1: Check if the profile is a dictionary: - if not isinstance(self.traffic_profile, dict): - 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(f"The parameter '{param}' in mandatory.") - is_valid = False - return is_valid - - def _validate_mandatory_values(self): - """Validate that mandatory profile values satisfy their constraints - - :returns: whether mandatory values are acceptable - :rtype: bool - """ - # Level 3: Mandatory params: Check if urls is a list: - is_valid = True - 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 - for param, minimum in self.INTEGER_PARAMS: - if not self._validate_int_param(param, minimum): - is_valid = False - return is_valid - - def _validate_optional_values(self): - """Validate values for optional parameters, if present - - :returns: whether present optional values are acceptable - :rtype: bool - """ - is_valid = True - # Level 4: Optional params: Check if script is present: - script = self.traffic_profile.get(u"script", None) - if script is not None: - if not isinstance(script, str): - logger.error(u"The path to LuaJIT script in invalid") - is_valid = False - else: - if not isfile(script): - logger.error(f"The file '{script}' does not exist.") - is_valid = False - else: - 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(u"header", None) - if header is not None: - if isinstance(header, dict): - 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(u"The parameter 'header' type is not valid.") - is_valid = False - - if not header: - logger.error(u"The parameter 'header' is defined but empty.") - is_valid = False - else: - 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(u"latency", None) - if latency is not None: - if not isinstance(latency, bool): - logger.error(u"The parameter 'latency' must be boolean.") - is_valid = False - else: - 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 u"timeout" in self.traffic_profile: - if not self._validate_int_param(u"timeout", 1): - is_valid = False - else: - self.traffic_profile[u"timeout"] = None - logger.debug( - u"The optional parameter 'timeout' is not defined. No problem." - ) - - return is_valid - - def _validate_dependencies(self): - """Validate dependencies between parameters - - :returns: whether dependencies between parameters are acceptable - :rtype: bool - """ - # Level 5: Check urls and cpus: - 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 - - def _validate_int_param(self, param, minimum): - """Validate that an int parameter is set acceptably - If it is not an int already but a string, convert and store it as int. - - :param param: Name of a traffic profile parameter - :param minimum: The minimum value for the named parameter - :type param: str - :type minimum: int - :returns: whether param is set to an int of at least minimum value - :rtype: bool - """ - value = self._traffic_profile[param] - if isinstance(value, str): - if value.isdigit(): - value = int(value) - else: - value = minimum - 1 - if isinstance(value, int) and value >= minimum: - self.traffic_profile[param] = value - return True - logger.error( - f"The parameter '{param}' must be an integer and at least {minimum}" - ) - return False - - @property - def profile_name(self): - """Getter - Profile name. - - :returns: The traffic profile file path - :rtype: str - """ - return self._profile_name - - @profile_name.setter - def profile_name(self, profile_name): - """ - - :param profile_name: - :type profile_name: str - """ - self._profile_name = profile_name - - @property - def traffic_profile(self): - """Getter: Traffic profile. - - :returns: The traffic profile. - :rtype: dict - """ - return self._traffic_profile - - @traffic_profile.setter - def traffic_profile(self, profile): - """Setter - Traffic profile. - - :param profile: The new traffic profile. - :type profile: dict - """ - self._traffic_profile = profile diff --git a/resources/tools/wrk/wrk_utils.sh b/resources/tools/wrk/wrk_utils.sh deleted file mode 100755 index 16af61416b..0000000000 --- a/resources/tools/wrk/wrk_utils.sh +++ /dev/null @@ -1,246 +0,0 @@ -#!/bin/bash -# 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: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -x - -function wrk_utils.installed { - - # Check if the WRK utility is installed. Fail if not installed. - - # Returns: - # - 0 - If command is installed. - # - 1 - If command is not installed. - - set -exuo pipefail - - command -v wrk -} - - -function wrk_utils.traffic_1_url_1_core { - # Send traffic - # - to n URL (NIC) - # - using n instances of wrk, each on separate core. - - # The CPU used for wrk - cpu=${1} - # Total number of threads to use by one instance of wrk to send traffic. - threads=${2} - # Total number of HTTP connections to keep open with each thread handling - # N = connections / threads. - connections=${3} - # Duration of the test. - duration=${4} - # HTTP header to add to request. - header=${5} - # Record a timeout if a response is not received within this amount of time. - timeout=${6} - # Path to LuaJIT script. - script=${7} - # Print detailed latency statistics. - latency=${8} - # URL to send the traffic to. - url=${9} - - if [ "${timeout}" != "None" ]; then - timeout="--timeout ${timeout}" - else - timeout="" - fi - - if [ "${latency}" = "True" ]; then - latency="--latency" - else - latency="" - fi - - if [ "${script}" != "None" ]; then - script="--script '${script}'" - else - script="" - fi - - if [ "${header}" != "None" ]; then - header="${header}" - else - header="''" - fi - - taskset --cpu-list ${cpu} \ - wrk --threads ${threads} \ - --connections ${connections} \ - --duration ${duration} \ - --header "${header}" \ - ${timeout} \ - ${script} \ - ${latency} \ - ${url} -} - -function wrk_utils.traffic_n_urls_n_cores { - # Send traffic - # - to n URL (NIC) - # - using n instances of wrk, each on separate core. - - # The first CPU used for wrk - first_cpu=${1} - # Total number of threads to use by one instance of wrk to send traffic. - threads=${2} - # Total number of HTTP connections to keep open with each thread handling - # N = connections / threads. - connections=${3} - # Duration of the test. - duration=${4} - # HTTP header to add to request. - header=${5} - # Record a timeout if a response is not received within this amount of time. - timeout=${6} - # Path to LuaJIT script. - script=${7} - # Print detailed latency statistics. - latency=${8} - # URL to send the traffic to. - urls=${9} - - if [ "${timeout}" != "None" ]; then - timeout="--timeout ${timeout}" - else - timeout="" - fi - - if [ "${latency}" = "True" ]; then - latency="--latency" - else - latency="" - fi - - if [ "${script}" != "None" ]; then - script="--script '${script}'" - else - script="" - fi - - if [ "${header}" != "None" ]; then - header="${header}" - else - header="''" - fi - - urls=$(echo ${urls} | tr ";" "\n") - cpu=${first_cpu} - for url in ${urls}; do - taskset --cpu-list ${cpu} \ - wrk --threads ${threads} \ - --connections ${connections} \ - --duration ${duration} \ - --header "${header}" \ - ${timeout} \ - ${script} \ - ${latency} \ - ${url} & - cpu=$((cpu+1)) - done - - sleep ${duration} - sleep 2 -} - -function wrk_utils.traffic_n_urls_m_cores { - # Send traffic - # - to n URL (NIC) - # - using m instances of wrk, each on separate core. - - # The first CPU used for wrk - first_cpu=${1} - # The last CPU used for wrk - cpus_per_url=${2} - # Total number of threads to use by one instance of wrk to send traffic. - threads=${3} - # Total number of HTTP connections to keep open with each thread handling - # N = connections / threads. - connections=${4} - # Duration of the test. - duration=${5} - # HTTP header to add to request. - header=${6} - # Record a timeout if a response is not received within this amount of time. - timeout=${7} - # Path to LuaJIT script. - script=${8} - # Print detailed latency statistics. - latency=${9} - # URL to send the traffic to. - urls=${10} - - if [ "${timeout}" != "None" ]; then - timeout="--timeout ${timeout}" - else - timeout="" - fi - - if [ "${latency}" = "True" ]; then - latency="--latency" - else - latency="" - fi - - if [ "${script}" != "None" ]; then - script="--script '${script}'" - else - script="" - fi - - if [ "${header}" != "None" ]; then - header="${header}" - else - header="''" - fi - - urls=$(echo ${urls} | tr ";" "\n") - - cpu=${first_cpu} - for i in `seq 1 ${cpus_per_url}`; do - for url in ${urls}; do - taskset --cpu-list ${cpu} \ - wrk --threads ${threads} \ - --connections ${connections} \ - --duration ${duration} \ - --header "${header}" \ - ${timeout} \ - ${script} \ - ${latency} \ - ${url} & - cpu=$((cpu+1)) - done - done - - sleep ${duration} - sleep 2 -} - -args=("$@") -case ${1} in - installed) - wrk_utils.installed - ;; - traffic_1_url_1_core) - wrk_utils.traffic_1_url_1_core "${args[@]:1}" - ;; - traffic_n_urls_n_cores) - wrk_utils.traffic_n_urls_n_cores "${args[@]:1}" - ;; - traffic_n_urls_m_cores) - wrk_utils.traffic_n_urls_m_cores "${args[@]:1}" - ;; -esac |