summaryrefslogtreecommitdiffstats
path: root/nfvbench/traffic_server.py
blob: 91608dd7b348df6a962698c6c1a37d138ef319c0 (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
# Copyright 2016 Cisco Systems, Inc.  All rights reserved.
#
#    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.

import os
import subprocess
import yaml

from log import LOG


class TrafficServerException(Exception):
    pass

class TrafficServer(object):
    """Base class for traffic servers."""

class TRexTrafficServer(TrafficServer):
    """Creates configuration file for TRex and runs server."""

    def __init__(self, trex_base_dir='/opt/trex'):
        contents = os.listdir(trex_base_dir)
        # only one version of TRex should be supported in container
        assert len(contents) == 1
        self.trex_dir = os.path.join(trex_base_dir, contents[0])

    def run_server(self, generator_config, filename='/etc/trex_cfg.yaml'):
        """Run TRex server for specified traffic profile.

        :param traffic_profile: traffic profile object based on config file
        :param filename: path where to save TRex config file
        """
        cfg = self.__save_config(generator_config, filename)
        cores = generator_config.cores
        vtep_vlan = generator_config.gen_config.get('vtep_vlan')
        sw_mode = "--software" if generator_config.software_mode else ""
        vlan_opt = "--vlan" if (generator_config.vlan_tagging or vtep_vlan) else ""
        if generator_config.mbuf_factor:
            mbuf_opt = "--mbuf-factor " + str(generator_config.mbuf_factor)
        else:
            mbuf_opt = ""
        # --unbind-unused-ports: for NIC that have more than 2 ports such as Intel X710
        # this will instruct trex to unbind all ports that are unused instead of
        # erroring out with an exception (i40e only)
        subprocess.Popen(['nohup', '/bin/bash', '-c',
                          './t-rex-64 -i -c {} --iom 0 --no-scapy-server '
                          '--unbind-unused-ports --close-at-end {} '
                          '{} {} --cfg {} &> /tmp/trex.log & disown'.format(cores, sw_mode,
                                                                            vlan_opt,
                                                                            mbuf_opt, cfg)],
                         cwd=self.trex_dir)
        LOG.info('TRex server is running...')

    def __load_config(self, filename):
        result = {}
        if os.path.exists(filename):
            with open(filename, 'r') as stream:
                try:
                    result = yaml.load(stream)
                except yaml.YAMLError as exc:
                    print exc
        return result

    def __save_config(self, generator_config, filename):
        result = self.__prepare_config(generator_config)
        yaml.safe_load(result)
        if os.path.exists(filename):
            os.remove(filename)
        with open(filename, 'w') as f:
            f.write(result)
        return filename

    def __prepare_config(self, generator_config):
        ifs = ",".join([repr(pci) for pci in generator_config.pcis])
        result = """# Config generated by NFVbench
        - port_limit : 2
          version    : 2
          zmq_pub_port : {zmq_pub_port}
          zmq_rpc_port : {zmq_rpc_port}
          prefix       : {prefix}
          limit_memory : {limit_memory}
          interfaces : [{ifs}]""".format(zmq_pub_port=generator_config.zmq_pub_port,
                                         zmq_rpc_port=generator_config.zmq_rpc_port,
                                         prefix=generator_config.name,
                                         limit_memory=generator_config.limit_memory,
                                         ifs=ifs)
        if hasattr(generator_config, 'platform'):
            if generator_config.platform.master_thread_id \
                    and generator_config.platform.latency_thread_id:
                platform = """
          platform     :
            master_thread_id  : {master_thread_id}
            latency_thread_id : {latency_thread_id}
            dual_if:""".format(master_thread_id=generator_config.platform.master_thread_id,
                               latency_thread_id=generator_config.platform.latency_thread_id)
                result += platform

                for core in generator_config.platform.dual_if:
                    threads = ""
                    try:
                        threads = ",".join([repr(thread) for thread in core.threads])
                    except TypeError:
                        LOG.warn("No threads defined for socket %s", core.socket)
                    core_result = """
                  - socket : {socket}
                    threads : [{threads}]""".format(socket=core.socket, threads=threads)
                    result += core_result
            else:
                LOG.info("Generator profile 'platform' sub-properties are set but not filled in \
                         config file. TRex will use default values.")
        return result

    def check_config_updated(self, generator_config):
        existing_config = self.__load_config(filename='/etc/trex_cfg.yaml')
        new_config = yaml.safe_load(self.__prepare_config(generator_config))
        LOG.debug("Existing config: %s", existing_config)
        LOG.debug("New config: %s", new_config)
        if existing_config == new_config:
            return False
        return True