aboutsummaryrefslogtreecommitdiffstats
path: root/yardstick/network_services/vnf_generic/vnf/tg_trex.py
blob: 93ba8557af0d718dfc813c28457a13029c2a2655 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# Copyright (c) 2016-2017 Intel Corporation
#
# 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.
""" Trex acts as traffic generation and vnf definitions based on IETS Spec """

from __future__ import absolute_import
from __future__ import print_function

import logging
import os

import yaml

from yardstick.common.utils import mac_address_to_hex_list, try_int
from yardstick.network_services.utils import get_nsb_option
from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNFTrafficGen
from yardstick.network_services.vnf_generic.vnf.sample_vnf import ClientResourceHelper
from yardstick.network_services.vnf_generic.vnf.sample_vnf import DpdkVnfSetupEnvHelper

LOG = logging.getLogger(__name__)


class TrexDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper):
    APP_NAME = "t-rex-64"
    CFG_CONFIG = ""
    CFG_SCRIPT = ""
    PIPELINE_COMMAND = ""
    VNF_TYPE = "TG"


class TrexResourceHelper(ClientResourceHelper):

    CONF_FILE = '/tmp/trex_cfg.yaml'
    QUEUE_WAIT_TIME = 1
    RESOURCE_WORD = 'trex'
    RUN_DURATION = 0

    ASYNC_PORT = 4500
    SYNC_PORT = 4501

    def __init__(self, setup_helper):
        super(TrexResourceHelper, self).__init__(setup_helper)
        self.port_map = {}
        self.dpdk_to_trex_port_map = {}

    def generate_cfg(self):
        port_names = self.vnfd_helper.port_pairs.all_ports
        vpci_list = []
        port_list = []
        self.port_map = {}
        self.dpdk_to_trex_port_map = {}

        sorted_ports = sorted((self.vnfd_helper.port_num(port_name), port_name) for port_name in
                              port_names)
        for index, (port_num, port_name) in enumerate(sorted_ports):
            interface = self.vnfd_helper.find_interface(name=port_name)
            virtual_interface = interface['virtual-interface']
            dst_mac = virtual_interface["dst_mac"]

            # this is to check for unused ports, all ports in the topology
            # will always have dst_mac
            if not dst_mac:
                continue
            # TRex ports are in logical order roughly based on DPDK port number sorting
            vpci_list.append(virtual_interface["vpci"])
            local_mac = virtual_interface["local_mac"]
            port_list.append({
                "src_mac": mac_address_to_hex_list(local_mac),
                "dest_mac": mac_address_to_hex_list(dst_mac),
            })
            self.port_map[port_name] = index
            self.dpdk_to_trex_port_map[port_num] = index
        trex_cfg = {
            'interfaces': vpci_list,
            'port_info': port_list,
            "port_limit": len(port_names),
            "version": '2',
        }
        cfg_file = [trex_cfg]

        cfg_str = yaml.safe_dump(cfg_file, default_flow_style=False, explicit_start=True)
        self.ssh_helper.upload_config_file(os.path.basename(self.CONF_FILE), cfg_str)

    def _build_ports(self):
        super(TrexResourceHelper, self)._build_ports()
        # override with TRex logic port number
        self.uplink_ports = [self.dpdk_to_trex_port_map[p] for p in self.uplink_ports]
        self.downlink_ports = [self.dpdk_to_trex_port_map[p] for p in self.downlink_ports]
        self.all_ports = [self.dpdk_to_trex_port_map[p] for p in self.all_ports]

    def port_num(self, intf):
        # return logical TRex port
        return self.port_map[intf]

    def check_status(self):
        status, _, _ = self.ssh_helper.execute("sudo lsof -i:%s" % self.SYNC_PORT)
        return status

    # temp disable
    DISABLE_DEPLOY = True

    def setup(self):
        super(TrexResourceHelper, self).setup()
        if self.DISABLE_DEPLOY:
            return

        trex_path = self.ssh_helper.join_bin_path('trex')

        err = self.ssh_helper.execute("which {}".format(trex_path))[0]
        if err == 0:
            return

        LOG.info("Copying %s to destination...", self.RESOURCE_WORD)
        self.ssh_helper.run("sudo mkdir -p '{}'".format(os.path.dirname(trex_path)))
        self.ssh_helper.put("~/.bash_profile", "~/.bash_profile")
        self.ssh_helper.put(trex_path, trex_path, True)
        ko_src = os.path.join(trex_path, "scripts/ko/src/")
        self.ssh_helper.execute(self.MAKE_INSTALL.format(ko_src))

    def start(self, ports=None, *args, **kwargs):
        cmd = "sudo fuser -n tcp {0.SYNC_PORT} {0.ASYNC_PORT} -k > /dev/null 2>&1"
        self.ssh_helper.execute(cmd.format(self))

        self.ssh_helper.execute("sudo pkill -9 rex > /dev/null 2>&1")

        # We MUST default to 1 because TRex won't work on single-queue devices with
        # more than one core per port
        # We really should be trying to find the number of queues in the driver,
        # but there doesn't seem to be a way to do this
        # TRex Error: the number of cores should be 1 when the driver
        # support only one tx queue and one rx queue. Please use -c 1
        threads_per_port = try_int(self.scenario_helper.options.get("queues_per_port"), 1)

        trex_path = self.ssh_helper.join_bin_path("trex", "scripts")
        path = get_nsb_option("trex_path", trex_path)

        cmd = "./t-rex-64 --no-scapy-server -i -c {} --cfg '{}'".format(threads_per_port,
                                                                        self.CONF_FILE)

        if self.scenario_helper.options.get("trex_server_debug"):
            # if there are errors we want to see them
            redir = ""
        else:
            redir = ">/dev/null"
        # we have to sudo cd because the path might be owned by root
        trex_cmd = """sudo bash -c "cd '{}' ; {}" {}""".format(path, cmd, redir)
        LOG.debug(trex_cmd)
        self.ssh_helper.execute(trex_cmd)

    def terminate(self):
        super(TrexResourceHelper, self).terminate()
        cmd = "sudo fuser -n tcp %s %s -k > /dev/null 2>&1"
        self.ssh_helper.execute(cmd % (self.SYNC_PORT, self.ASYNC_PORT))


class TrexTrafficGen(SampleVNFTrafficGen):
    """
    This class handles mapping traffic profile and generating
    traffic for given testcase
    """

    APP_NAME = 'TRex'

    def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None):
        if resource_helper_type is None:
            resource_helper_type = TrexResourceHelper

        if setup_env_helper_type is None:
            setup_env_helper_type = TrexDpdkVnfSetupEnvHelper

        super(TrexTrafficGen, self).__init__(name, vnfd, setup_env_helper_type,
                                             resource_helper_type)

    def _check_status(self):
        return self.resource_helper.check_status()

    def _start_server(self):
        super(TrexTrafficGen, self)._start_server()
        self.resource_helper.start()

    def scale(self, flavor=""):
        pass

    def listen_traffic(self, traffic_profile):
        pass

    def terminate(self):
        self.resource_helper.terminate()