From 92cbb44a89ca808df32e4a4cb137bed076a68a94 Mon Sep 17 00:00:00 2001 From: Peter Mikus Date: Tue, 2 Jan 2018 14:37:24 +0100 Subject: TRex ASTF onboarding Part I - Preparing initialization of TRex in L7 mode - Updating Topology files - Adding sample ASTF profiles Change-Id: If71f7f8f3db66425a1b543f1d29069a7543f4090 Signed-off-by: Peter Mikus --- resources/libraries/python/TrafficGenerator.py | 150 ++++++++++++--------- resources/libraries/python/constants.py | 3 + resources/libraries/python/topology.py | 15 +++ .../trex-sf-2n-ethip4tcphttp-1u1s-nginx-cps.py | 109 +++++++++++++++ .../trex/trex-sf-2n-ethip4tcphttp-1u1s-vpp-cps.py | 105 +++++++++++++++ 5 files changed, 319 insertions(+), 63 deletions(-) create mode 100644 resources/traffic_profiles/trex/trex-sf-2n-ethip4tcphttp-1u1s-nginx-cps.py create mode 100644 resources/traffic_profiles/trex/trex-sf-2n-ethip4tcphttp-1u1s-vpp-cps.py (limited to 'resources') diff --git a/resources/libraries/python/TrafficGenerator.py b/resources/libraries/python/TrafficGenerator.py index 88adcc4542..a8083381d1 100644 --- a/resources/libraries/python/TrafficGenerator.py +++ b/resources/libraries/python/TrafficGenerator.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016 Cisco and/or its affiliates. +# 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: @@ -160,7 +160,7 @@ class TrafficGenerator(object): :param tg_if1_adj_if: TG if1 adjecent interface. :param tg_if2_adj_node: TG if2 adjecent node. :param tg_if2_adj_if: TG if2 adjecent interface. - :param test_type: 'L2' or 'L3' - src/dst MAC address. + :param test_type: 'L2', 'L3' or 'L7' - OSI Layer testing type. :param tg_if1_dst_mac: Interface 1 destination MAC address. :param tg_if2_dst_mac: Interface 2 destination MAC address. :type tg_node: dict @@ -176,16 +176,11 @@ class TrafficGenerator(object): :returns: nothing :raises: RuntimeError in case of issue during initialization. """ - - topo = Topology() - if tg_node['type'] != NodeType.TG: raise RuntimeError('Node type is not a TG') self._node = tg_node if tg_node['subtype'] == NodeSubTypeTG.TREX: - trex_path = "/opt/trex-core-2.34" - ssh = SSH() ssh.connect(tg_node) @@ -196,89 +191,118 @@ class TrafficGenerator(object): if int(ret) != 0: raise RuntimeError('TRex installation failed.') - if1_pci = topo.get_interface_pci_addr(tg_node, tg_if1) - if2_pci = topo.get_interface_pci_addr(tg_node, tg_if2) - if1_mac = topo.get_interface_mac(tg_node, tg_if1) - if2_mac = topo.get_interface_mac(tg_node, tg_if2) + if1_pci = Topology().get_interface_pci_addr(tg_node, tg_if1) + if2_pci = Topology().get_interface_pci_addr(tg_node, tg_if2) + if1_addr = Topology().get_interface_mac(tg_node, tg_if1) + if2_addr = Topology().get_interface_mac(tg_node, tg_if2) if test_type == 'L2': - if1_adj_mac = if2_mac - if2_adj_mac = if1_mac + if1_adj_addr = if2_addr + if2_adj_addr = if1_addr elif test_type == 'L3': - if1_adj_mac = topo.get_interface_mac(tg_if1_adj_node, - tg_if1_adj_if) - if2_adj_mac = topo.get_interface_mac(tg_if2_adj_node, - tg_if2_adj_if) + if1_adj_addr = Topology().get_interface_mac(tg_if1_adj_node, + tg_if1_adj_if) + if2_adj_addr = Topology().get_interface_mac(tg_if2_adj_node, + tg_if2_adj_if) + elif test_type == 'L7': + if1_addr = Topology().get_interface_ip4(tg_node, tg_if1) + if2_addr = Topology().get_interface_ip4(tg_node, tg_if2) + if1_adj_addr = Topology().get_interface_ip4(tg_if1_adj_node, + tg_if1_adj_if) + if2_adj_addr = Topology().get_interface_ip4(tg_if2_adj_node, + tg_if2_adj_if) else: - raise ValueError("test_type unknown") + raise ValueError("Unknown Test Type") + # in case of switched environment we can override MAC addresses if tg_if1_dst_mac is not None and tg_if2_dst_mac is not None: - if1_adj_mac = tg_if1_dst_mac - if2_adj_mac = tg_if2_dst_mac + if1_adj_addr = tg_if1_dst_mac + if2_adj_addr = tg_if2_dst_mac if min(if1_pci, if2_pci) != if1_pci: - if1_mac, if2_mac = if2_mac, if1_mac if1_pci, if2_pci = if2_pci, if1_pci - if1_adj_mac, if2_adj_mac = if2_adj_mac, if1_adj_mac + if1_addr, if2_addr = if2_addr, if1_addr + if1_adj_addr, if2_adj_addr = if2_adj_addr, if1_adj_addr self._ifaces_reordered = True - if1_mac_hex = "0x"+if1_mac.replace(":", ",0x") - if2_mac_hex = "0x"+if2_mac.replace(":", ",0x") - if1_adj_mac_hex = "0x"+if1_adj_mac.replace(":", ",0x") - if2_adj_mac_hex = "0x"+if2_adj_mac.replace(":", ",0x") - - (ret, _, _) = ssh.exec_command( - "sudo sh -c 'cat << EOF > /etc/trex_cfg.yaml\n" - "- port_limit : 2\n" - " version : 2\n" - " interfaces : [\"{}\",\"{}\"]\n" - " port_info :\n" - " - dest_mac : [{}]\n" - " src_mac : [{}]\n" - " - dest_mac : [{}]\n" - " src_mac : [{}]\n" - "EOF'"\ - .format(if1_pci, if2_pci, - if1_adj_mac_hex, if1_mac_hex, - if2_adj_mac_hex, if2_mac_hex)) + if test_type == 'L2' or test_type == 'L3': + (ret, _, _) = ssh.exec_command( + "sudo sh -c 'cat << EOF > /etc/trex_cfg.yaml\n" + "- port_limit: 2\n" + " version: 2\n" + " interfaces: [\"{0}\",\"{1}\"]\n" + " port_info:\n" + " - dest_mac: [{2}]\n" + " src_mac: [{3}]\n" + " - dest_mac: [{4}]\n" + " src_mac: [{5}]\n" + "EOF'"\ + .format(if1_pci, if2_pci, + "0x"+if1_adj_addr.replace(":", ",0x"), + "0x"+if1_addr.replace(":", ",0x"), + "0x"+if2_adj_addr.replace(":", ",0x"), + "0x"+if2_addr.replace(":", ",0x"))) + elif test_type == 'L7': + (ret, _, _) = ssh.exec_command( + "sudo sh -c 'cat << EOF > /etc/trex_cfg.yaml\n" + "- port_limit: 2\n" + " version: 2\n" + " interfaces: [\"{0}\",\"{1}\"]\n" + " port_info:\n" + " - ip: [{2}]\n" + " default_gw: [{3}]\n" + " - ip: [{4}]\n" + " default_gw: [{5}]\n" + "EOF'"\ + .format(if1_pci, if2_pci, + if1_addr, if1_adj_addr, + if2_addr, if2_adj_addr)) + else: + raise ValueError("Unknown Test Type") if int(ret) != 0: - raise RuntimeError('trex config generation error') + raise RuntimeError('TRex config generation error') - max_startup_retries = 3 - while max_startup_retries > 0: - # kill T-rex only if it is already running - (ret, _, _) = ssh.exec_command( + for _ in range(0, 3): + # kill TRex only if it is already running + ssh.exec_command( "sh -c 'pgrep t-rex && sudo pkill t-rex && sleep 3'") - # configure T-rex + # configure TRex (ret, _, _) = ssh.exec_command( "sh -c 'cd {0}/scripts/ && sudo ./trex-cfg'"\ - .format(trex_path)) + .format(Constants.TREX_INSTALL_DIR)) if int(ret) != 0: raise RuntimeError('trex-cfg failed') - # start T-rex - (ret, _, _) = ssh.exec_command( - "sh -c 'cd {0}/scripts/ && " - "sudo nohup ./t-rex-64 -i -c 7 --iom 0 > /tmp/trex.log " - "2>&1 &' > /dev/null"\ - .format(trex_path)) + # start TRex + if test_type == 'L2' or test_type == 'L3': + (ret, _, _) = ssh.exec_command( + "sh -c 'cd {0}/scripts/ && " + "sudo nohup ./t-rex-64 -i -c 7 --iom 0 > /tmp/trex.log " + "2>&1 &' > /dev/null"\ + .format(Constants.TREX_INSTALL_DIR)) + elif test_type == 'L7': + (ret, _, _) = ssh.exec_command( + "sh -c 'cd {0}/scripts/ && " + "sudo nohup ./t-rex-64 --astf -i -c 7 --iom 0 > " + "/tmp/trex.log 2>&1 &' > /dev/null"\ + .format(Constants.TREX_INSTALL_DIR)) + else: + raise ValueError("Unknown Test Type") if int(ret) != 0: ssh.exec_command("sh -c 'cat /tmp/trex.log'") raise RuntimeError('t-rex-64 startup failed') - # get T-rex server info + # get TRex server info (ret, _, _) = ssh.exec_command( "sh -c 'sleep 3; " "{0}/resources/tools/trex/trex_server_info.py'"\ .format(Constants.REMOTE_FW_DIR), timeout=120) if int(ret) == 0: - # If we get info T-rex is running + # If we get info TRex is running return - # try again - max_startup_retries -= 1 - # after max retries T-rex is still not responding to API + # after max retries TRex is still not responding to API # critical error occurred raise RuntimeError('t-rex-64 startup failed') @@ -289,7 +313,7 @@ class TrafficGenerator(object): :param node: Traffic generator node. :type node: dict :returns: nothing - :raises: RuntimeError if T-rex teardown failed. + :raises: RuntimeError if TRex teardown failed. :raises: RuntimeError if node type is not a TG. """ if node['type'] != NodeType.TG: @@ -306,7 +330,7 @@ class TrafficGenerator(object): def trex_stl_stop_remote_exec(node): """Execute script on remote node over ssh to stop running traffic. - :param node: T-REX generator node. + :param node: TRex generator node. :type node: dict :returns: Nothing :raises: RuntimeError if stop traffic script fails. @@ -319,7 +343,7 @@ class TrafficGenerator(object): "trex_stateless_stop.py'".format(Constants.REMOTE_FW_DIR)) if int(ret) != 0: - raise RuntimeError('T-rex stateless runtime error') + raise RuntimeError('TRex stateless runtime error') def trex_stl_start_remote_exec(self, duration, rate, framesize, traffic_type, async_call=False, @@ -370,7 +394,7 @@ class TrafficGenerator(object): timeout=int(duration) + 60) if int(ret) != 0: - raise RuntimeError('T-rex stateless runtime error') + raise RuntimeError('TRex stateless runtime error') elif async_call: #no result self._received = None diff --git a/resources/libraries/python/constants.py b/resources/libraries/python/constants.py index 01a96a861b..7ae9cdd06f 100644 --- a/resources/libraries/python/constants.py +++ b/resources/libraries/python/constants.py @@ -35,6 +35,9 @@ class Constants(object): # QEMU install directory QEMU_INSTALL_DIR = '/opt/qemu-2.5.0' + # TRex install directory + TREX_INSTALL_DIR = '/opt/trex-core-2.34' + # Kubernetes templates location RESOURCES_TPL_K8S = 'resources/templates/kubernetes' diff --git a/resources/libraries/python/topology.py b/resources/libraries/python/topology.py index 4ff68ec43d..a5a446008b 100644 --- a/resources/libraries/python/topology.py +++ b/resources/libraries/python/topology.py @@ -599,6 +599,21 @@ class Topology(object): except KeyError: return None + @staticmethod + def get_interface_ip4(node, iface_key): + """Get IP4 address for the interface. + + :param node: Node to get interface mac on. + :param iface_key: Interface key from topology file. + :type node: dict + :type iface_key: str + :returns: Return IP4 or None if not found. + """ + try: + return node['interfaces'][iface_key].get('ip4_address', None) + except KeyError: + return None + @staticmethod def get_adjacent_node_and_interface(nodes_info, node, iface_key): """Get node and interface adjacent to specified interface diff --git a/resources/traffic_profiles/trex/trex-sf-2n-ethip4tcphttp-1u1s-nginx-cps.py b/resources/traffic_profiles/trex/trex-sf-2n-ethip4tcphttp-1u1s-nginx-cps.py new file mode 100644 index 0000000000..4e838e0088 --- /dev/null +++ b/resources/traffic_profiles/trex/trex-sf-2n-ethip4tcphttp-1u1s-nginx-cps.py @@ -0,0 +1,109 @@ +# 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. + +"""ASTF profile for TRex traffic generator. + +ASTF profile: + - Client side traffic in directions 0 --> 1. + - Server side traffic disabled. + - Packet: ETH / IP / TCP / HTTP1.1 + - Direction 0 --> 1: + - Source IP address range: 172.16.130.2 - 172.16.130.2 + - Destination IP address range: 192.168.0.1 +""" + +from trex_astf_lib.api import * + + +class Prof1(): + def __init__(self): + """Initialization and setting of streams' parameters.""" + + # Response content length. + self.content_len = 0 + # Number of requests per HTTP transaction. + self.requests = 1 + # Number of transactions per HTTP connection. + self.transaction_per_conn = 1 + # Use TCP RST instead of FIN+ACK. + self.tcp_reset = False + + # IP used in packet headers. + self.p1_src_start_ip = '172.16.130.2' + self.p1_src_end_ip = '172.16.130.2' + self.p1_dst_start_ip = '192.168.0.1' + self.p1_dst_end_ip = '192.168.0.1' + + self.http_req = (b'GET /0KB.bin HTTP/1.1\r\n' + 'Host: {host}\r\n' + 'User-Agent: trex/astf\r\n' + 'Accept: */*\r\n' + 'Connection: keep-alive\r\n\r\n' + .format(host=self.p1_dst_start_ip)) + self.http_res = (b'HTTP/1.1 200 OK\r\n' + 'Server: nginx/1.13.7\r\n' + 'Date: Mon, 01 Jan 2018 00:00:00 GMT\r\n' + 'Content-Type: application/octet-stream\r\n' + 'Content-Length: {length}\r\n' + 'Last-Modified: Mon, 01 Jan 2018 00:00:00 GMT\r\n' + 'Connection: keep-alive\r\n' + 'ETag: "5a027c14-0"\r\n' + 'Accept-Ranges: bytes\r\n\r\n' + .format(length=self.content_len)) + + def create_profile(self): + # client operations + prog_c = ASTFProgram() + prog_c.connect() + for i in range(self.transaction_per_conn): + prog_c.send(self.http_req * self.requests) + prog_c.recv((len(self.http_res) + self.content_len) * self.requests) + if self.tcp_reset: + prog_c.reset() + + # ip generator + ip_gen_c = ASTFIPGenDist(ip_range=[self.p1_src_start_ip, + self.p1_src_end_ip], + distribution="seq") + ip_gen_s = ASTFIPGenDist(ip_range=[self.p1_dst_start_ip, + self.p1_dst_end_ip], + distribution="seq") + ip_gen = ASTFIPGen(glob=ASTFIPGenGlobal(ip_offset="0.0.0.1"), + dist_client=ip_gen_c, + dist_server=ip_gen_s) + + # TCP parameters + tcp_params = ASTFTCPInfo(window=32768) + # client tunables + c_glob_info = ASTFGlobalInfo() + + # template + client_template = ASTFTCPClientTemplate(program=prog_c, + tcp_info=tcp_params, + ip_gen=ip_gen) + server_template = ASTFTCPServerTemplate(program=ASTFProgram(), + tcp_info=tcp_params) + template = ASTFTemplate(client_template=client_template, + server_template=server_template) + + # profile + return ASTFProfile(default_ip_gen=ip_gen, templates=template, + default_c_glob_info=c_glob_info) + + def get_profile(self): + return self.create_profile() + + +def register(): + return Prof1() + diff --git a/resources/traffic_profiles/trex/trex-sf-2n-ethip4tcphttp-1u1s-vpp-cps.py b/resources/traffic_profiles/trex/trex-sf-2n-ethip4tcphttp-1u1s-vpp-cps.py new file mode 100644 index 0000000000..0533978830 --- /dev/null +++ b/resources/traffic_profiles/trex/trex-sf-2n-ethip4tcphttp-1u1s-vpp-cps.py @@ -0,0 +1,105 @@ +# 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. + +"""ASTF profile for TRex traffic generator. + +ASTF profile: + - Client side traffic in directions 0 --> 1. + - Server side traffic disabled. + - Packet: ETH / IP / TCP / HTTP1.1 + - Direction 0 --> 1: + - Source IP address range: 172.16.130.2 - 172.16.130.2 + - Destination IP address range: 192.168.0.1 +""" + +from trex_astf_lib.api import * + + +class Prof1(): + def __init__(self): + """Initialization and setting of streams' parameters.""" + + # Response content length. + self.content_len = 115 + # Number of requests per HTTP transaction. + self.requests = 1 + # Number of transactions per HTTP connection. + self.transaction_per_conn = 1 + # Use TCP RST instead of FIN+ACK. + self.tcp_reset = False + + # IP used in packet headers. + self.p1_src_start_ip = '172.16.130.2' + self.p1_src_end_ip = '172.16.130.2' + self.p1_dst_start_ip = '172.16.130.1' + self.p1_dst_end_ip = '172.16.130.1' + + self.http_req = (b'GET / HTTP/1.1\r\n' + 'Host: {host}\r\n' + 'User-Agent: trex/astf\r\n' + 'Accept: */*\r\n\r\n' + .format(host=self.p1_dst_start_ip)) + self.http_res = (b'HTTP/1.1 200 OK\r\n' + 'Content-Type: text/html\r\n' + 'Expires: Mon, 11 Jan 1970 10:10:10 GMT\r\n' + 'Connection: close\r\n' + 'Pragma: no-cache\r\n' + 'Content-Length: {length}\r\n\r\n' + .format(length=self.content_len)) + + def create_profile(self): + # client operations + prog_c = ASTFProgram() + prog_c.connect() + for i in range(self.transaction_per_conn): + prog_c.send(self.http_req * self.requests) + prog_c.recv((len(self.http_res) + self.content_len) * self.requests) + if self.tcp_reset: + prog_c.reset() + + # ip generator + ip_gen_c = ASTFIPGenDist(ip_range=[self.p1_src_start_ip, + self.p1_src_end_ip], + distribution="seq") + ip_gen_s = ASTFIPGenDist(ip_range=[self.p1_dst_start_ip, + self.p1_dst_end_ip], + distribution="seq") + ip_gen = ASTFIPGen(glob=ASTFIPGenGlobal(ip_offset="0.0.0.1"), + dist_client=ip_gen_c, + dist_server=ip_gen_s) + + # TCP parameters + tcp_params = ASTFTCPInfo(window=32768) + # client tunables + c_glob_info = ASTFGlobalInfo() + + # template + client_template = ASTFTCPClientTemplate(program=prog_c, + tcp_info=tcp_params, + ip_gen=ip_gen) + server_template = ASTFTCPServerTemplate(program=ASTFProgram(), + tcp_info=tcp_params) + template = ASTFTemplate(client_template=client_template, + server_template=server_template) + + # profile + return ASTFProfile(default_ip_gen=ip_gen, templates=template, + default_c_glob_info=c_glob_info) + + def get_profile(self): + return self.create_profile() + + +def register(): + return Prof1() + -- cgit 1.2.3-korg