Merge "Added NSB descriptors for vCMTS testcase"
[yardstick.git] / yardstick / network_services / vnf_generic / vnf / tg_vcmts_pktgen.py
1 # Copyright (c) 2019 Viosoft Corporation
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import logging
16 import time
17 import socket
18 import yaml
19 import os
20
21 from yardstick.network_services.vnf_generic.vnf import sample_vnf
22 from yardstick.common import exceptions
23
24
25 LOG = logging.getLogger(__name__)
26
27
28 class PktgenHelper(object):
29
30     RETRY_SECONDS = 0.5
31     RETRY_COUNT = 20
32     CONNECT_TIMEOUT = 5
33
34     def __init__(self, host, port=23000):
35         self.host = host
36         self.port = port
37         self.connected = False
38
39     def _connect(self):
40         self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
41         ret = True
42         try:
43             self._sock.settimeout(self.CONNECT_TIMEOUT)
44             self._sock.connect((self.host, self.port))
45         except (socket.gaierror, socket.error, socket.timeout):
46             self._sock.close()
47             ret = False
48
49         return ret
50
51     def connect(self):
52         if self.connected:
53             return True
54         LOG.info("Connecting to pktgen instance at %s...", self.host)
55         for idx in range(self.RETRY_COUNT):
56             self.connected = self._connect()
57             if self.connected:
58                 return True
59             LOG.debug("Connection attempt %d: Unable to connect to %s, " \
60                       "retrying in %d seconds",
61                       idx, self.host, self.RETRY_SECONDS)
62             time.sleep(self.RETRY_SECONDS)
63
64         LOG.error("Unable to connect to pktgen instance on %s !",
65                   self.host)
66         return False
67
68
69     def send_command(self, command):
70         if not self.connected:
71             LOG.error("Pktgen socket is not connected")
72             return False
73
74         try:
75             self._sock.sendall((command + "\n").encode())
76             time.sleep(1)
77         except (socket.timeout, socket.error):
78             LOG.error("Error sending command '%s'", command)
79             return False
80
81         return True
82
83
84 class VcmtsPktgenSetupEnvHelper(sample_vnf.SetupEnvHelper):
85
86     BASE_PARAMETERS = "export LUA_PATH=/vcmts/Pktgen.lua;"\
87                     + "export CMK_PROC_FS=/host/proc;"
88
89     PORTS_COUNT = 8
90
91     def generate_pcap_filename(self, port_cfg):
92         return port_cfg['traffic_type'] + "_" + port_cfg['num_subs'] \
93              + "cms_" + port_cfg['num_ofdm'] + "ofdm.pcap"
94
95     def find_port_cfg(self, ports_cfg, port_name):
96         for port_cfg in ports_cfg:
97             if port_name in port_cfg:
98                 return port_cfg
99         return None
100
101     def build_pktgen_parameters(self, pod_cfg):
102         ports_cfg = pod_cfg['ports']
103         port_cfg = list()
104
105         for i in range(self.PORTS_COUNT):
106             port_cfg.append(self.find_port_cfg(ports_cfg, 'port_' + str(i)))
107
108         pktgen_parameters = self.BASE_PARAMETERS + " " \
109              + " /pktgen-config/setup.sh " + pod_cfg['pktgen_id'] \
110              + " " + pod_cfg['num_ports']
111
112         for i in range(self.PORTS_COUNT):
113             pktgen_parameters += " " + port_cfg[i]['net_pktgen']
114
115         for i in range(self.PORTS_COUNT):
116             pktgen_parameters += " " + self.generate_pcap_filename(port_cfg[i])
117
118         return pktgen_parameters
119
120     def start_pktgen(self, pod_cfg):
121         self.ssh_helper.drop_connection()
122         cmd = self.build_pktgen_parameters(pod_cfg)
123         LOG.debug("Executing: '%s'", cmd)
124         self.ssh_helper.send_command(cmd)
125         LOG.info("Pktgen executed")
126
127     def setup_vnf_environment(self):
128         pass
129
130
131 class VcmtsPktgen(sample_vnf.SampleVNFTrafficGen):
132
133     TG_NAME = 'VcmtsPktgen'
134     APP_NAME = 'VcmtsPktgen'
135     RUN_WAIT = 4
136     DEFAULT_RATE = 8.0
137
138     PKTGEN_BASE_PORT = 23000
139
140     def __init__(self, name, vnfd, setup_env_helper_type=None,
141                  resource_helper_type=None):
142         if setup_env_helper_type is None:
143             setup_env_helper_type = VcmtsPktgenSetupEnvHelper
144         super(VcmtsPktgen, self).__init__(
145             name, vnfd, setup_env_helper_type, resource_helper_type)
146
147         self.pktgen_address = vnfd['mgmt-interface']['ip']
148         LOG.info("Pktgen container '%s', IP: %s", name, self.pktgen_address)
149
150     def extract_pod_cfg(self, pktgen_pods_cfg, pktgen_id):
151         for pod_cfg in pktgen_pods_cfg:
152             if pod_cfg['pktgen_id'] == pktgen_id:
153                 return pod_cfg
154         return None
155
156     def instantiate(self, scenario_cfg, context_cfg):
157         super(VcmtsPktgen, self).instantiate(scenario_cfg, context_cfg)
158         self._start_server()
159         options = scenario_cfg.get('options', {})
160         self.pktgen_rate = options.get('pktgen_rate', self.DEFAULT_RATE)
161
162         try:
163             pktgen_values_filepath = options['pktgen_values']
164         except KeyError:
165             raise KeyError("Missing pktgen_values key in scenario options" \
166                            "section of the task definition file")
167
168         if not os.path.isfile(pktgen_values_filepath):
169             raise RuntimeError("The pktgen_values file path provided " \
170                                "does not exists")
171
172         # The yaml_loader.py (SafeLoader) underlying regex has an issue
173         # with reading PCI addresses (processed as double). so the
174         # BaseLoader is used here.
175         with open(pktgen_values_filepath) as stream:
176             pktgen_values = yaml.load(stream, Loader=yaml.BaseLoader)
177
178         if pktgen_values == None:
179             raise RuntimeError("Error reading pktgen_values file provided (" +
180                                pktgen_values_filepath + ")")
181
182         self.pktgen_id = int(options[self.name]['pktgen_id'])
183         self.resource_helper.pktgen_id = self.pktgen_id
184
185         self.pktgen_helper = PktgenHelper(self.pktgen_address,
186                                           self.PKTGEN_BASE_PORT + self.pktgen_id)
187
188         pktgen_pods_cfg = pktgen_values['topology']['pktgen_pods']
189
190         self.pod_cfg = self.extract_pod_cfg(pktgen_pods_cfg,
191                                             str(self.pktgen_id))
192
193         if self.pod_cfg == None:
194             raise KeyError("Pktgen with id " + str(self.pktgen_id) + \
195                            " was not found")
196
197         self.setup_helper.start_pktgen(self.pod_cfg)
198
199     def run_traffic(self, traffic_profile):
200         if not self.pktgen_helper.connect():
201             raise exceptions.PktgenActionError(command="connect")
202         LOG.info("Connected to pktgen instance at %s", self.pktgen_address)
203
204         commands = []
205         for i in range(self.setup_helper.PORTS_COUNT):
206             commands.append('pktgen.set("' + str(i) + '", "rate", ' +
207                             "%0.1f" % self.pktgen_rate + ');')
208
209         commands.append('pktgen.start("all");')
210
211         for command in commands:
212             if self.pktgen_helper.send_command(command):
213                 LOG.debug("Command '%s' sent to pktgen", command)
214         LOG.info("Traffic started on %s...", self.name)
215         return True