License for PCAP files
[yardstick.git] / yardstick / vTC / apexlake / experimental_framework / packet_generators / dpdk_packet_generator.py
1 # Copyright (c) 2015 Intel Research and Development Ireland Ltd.
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 os
16 import base_packet_generator
17 import experimental_framework.common as common
18 import time
19 from experimental_framework.constants import conf_file_sections as conf_file
20 from experimental_framework.constants import framework_parameters as fp
21
22
23 class DpdkPacketGenerator(base_packet_generator.BasePacketGenerator):
24
25     def __init__(self):
26         base_packet_generator.BasePacketGenerator.__init__(self)
27         self.command = ''
28         self.directory = ''
29         self.program_name = ''
30         self.command_options = list()
31         self.dpdk_interfaces = -1
32
33     def send_traffic(self):
34         '''
35         Calls the packet generator and starts to send traffic
36         Blocking call
37         '''
38         current_dir = os.path.dirname(os.path.realpath(__file__))
39         DpdkPacketGenerator._chdir(self.directory)
40         dpdk_vars = common.get_dpdk_pktgen_vars()
41         self._init_physical_nics(self.dpdk_interfaces, dpdk_vars)
42         common.run_command(self.command)
43         self._finalize_physical_nics(self.dpdk_interfaces, dpdk_vars)
44         DpdkPacketGenerator._chdir(current_dir)
45
46     def init_dpdk_pktgen(self,
47                          dpdk_interfaces,
48                          lua_script='generic_test.lua',
49                          pcap_file_0='',
50                          pcap_file_1='',
51                          vlan_0='',
52                          vlan_1=''):
53         """
54         Initializes internal parameters and configuration of the module.
55         Needs to be called before the send_traffic
56         :param dpdk_interfaces: Number of interfaces to be used (type: int)
57         :param lua_script: Full path of the Lua script to be used (type: str)
58         :param pcap_file_0: Full path of the Pcap file to be used for port 0
59                             (type: str)
60         :param pcap_file_1: Full path of the Pcap file to be used for port 1
61                             (type: str)
62         :param vlan_0: VLAN tag to be used for port 0 (type: str)
63         :param vlan_1: VLAN tag to be used for port 1 (type: str)
64         :return:
65         """
66         # Input Validation
67         if pcap_file_0 and not vlan_0:
68             message = 'In order to use NIC_0, the parameter vlan_0 is required'
69             raise ValueError(message)
70         if dpdk_interfaces > 1 and pcap_file_1 and not vlan_1:
71             message = 'in order to use NIC_1, the parameter vlan_1 is required'
72             raise ValueError(message)
73
74         self.dpdk_interfaces = dpdk_interfaces
75         vars = common.get_dpdk_pktgen_vars()
76
77         lua_directory = common.get_base_dir()
78         lua_directory += fp.EXPERIMENTAL_FRAMEWORK_DIR
79         lua_directory += fp.DPDK_PKTGEN_DIR
80
81         pcap_directory = common.get_base_dir()
82         pcap_directory += fp.EXPERIMENTAL_FRAMEWORK_DIR
83         pcap_directory += fp.PCAP_DIR
84
85         DpdkPacketGenerator._init_input_validation(pcap_file_0,
86                                                    pcap_file_1,
87                                                    lua_script,
88                                                    pcap_directory,
89                                                    lua_directory,
90                                                    vars)
91
92         self.directory = vars[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY]
93         self.program_name = vars[conf_file.CFSP_DPDK_PROGRAM_NAME]
94
95         core_nics = DpdkPacketGenerator.\
96             _get_core_nics(dpdk_interfaces, vars[conf_file.CFSP_DPDK_COREMASK])
97         self.command_options = ['-c ' + vars[conf_file.CFSP_DPDK_COREMASK],
98                                 '-n ' + vars[conf_file.
99                                              CFSP_DPDK_MEMORY_CHANNEL],
100                                 '--proc-type auto',
101                                 '--file-prefix pg',
102                                 '-- -T',
103                                 '-P',
104                                 '-m "' + core_nics + '"',
105                                 '-f ' + lua_directory + lua_script,
106                                 '-s 0:' + pcap_directory + pcap_file_0]
107
108         if pcap_file_1:
109             self.command_options.append('-s 1:' + pcap_directory + pcap_file_1)
110
111         # Avoid to show the output of the packet generator
112         self.command_options.append('> /dev/null')
113         # Prepare the command to be invoked
114         self.command = 'sudo ' + self.directory + self.program_name
115         for opt in self.command_options:
116             self.command += (' ' + opt)
117         if pcap_file_0 and vlan_0:
118             DpdkPacketGenerator._change_vlan(pcap_directory, pcap_file_0,
119                                              vlan_0)
120         if pcap_file_1 and vlan_1:
121             DpdkPacketGenerator._change_vlan(pcap_directory, pcap_file_1,
122                                              vlan_1)
123
124     @staticmethod
125     def _get_core_nics(dpdk_interfaces, coremask):
126         """
127         Retruns the core_nics string to be used in the dpdk pktgen command
128         :param dpdk_interfaces: number of interfaces to be used in the pktgen
129                                 (type: int)
130         :param coremask: hexadecimal value representing the cores assigned to
131                          the pktgen (type: str)
132         :return: Returns the core nics param for pktgen (type: str)
133         """
134         if dpdk_interfaces == 1:
135             return DpdkPacketGenerator._cores_configuration(coremask, 1, 2, 0)
136         elif dpdk_interfaces == 2:
137             return DpdkPacketGenerator._cores_configuration(coremask, 1, 2, 2)
138         raise ValueError("This framework only supports two ports to generate "
139                          "traffic")
140
141     @staticmethod
142     def _change_vlan(pcap_directory, pcap_file, vlan):
143         common.LOG.info("Changing VLAN Tag on Packet: " + pcap_file +
144                         ". New VLAN Tag is " + vlan)
145         command = "chmod +x {}{}".format(pcap_directory, 'vlan_tag.sh')
146         common.run_command(command)
147         command = pcap_directory + 'vlan_tag.sh '
148         command += pcap_directory + pcap_file + ' ' + vlan
149         common.run_command(command)
150
151     @staticmethod
152     def _init_input_validation(pcap_file_0, pcap_file_1, lua_script,
153                                pcap_directory, lua_directory, variables):
154         """
155         Validates the input parameters values and raises an exception if
156         there is any non valid param
157         :param pcap_file_0: file name of the pcap file for NIC 0
158                             (it does not includes the path) (type: str)
159         :param pcap_file_1: file name of the pcap file for NIC 1
160                             (it does not includes the path) (type: str)
161         :param lua_script: file name of the lua script to be used
162                             (it does not includes the path) (type: str)
163         :param pcap_directory: directory where the pcap files are located
164                                (type: str)
165         :param lua_directory:  directory where the lua scripts are located
166                                (type: str)
167         :param variables: variables for the packet gen from configuration file
168                           (type: dict)
169         :return: None
170         """
171         if not pcap_directory:
172             raise ValueError("pcap_directory not provided correctly")
173         if not pcap_file_0:
174             raise ValueError("pcap_file_0 not provided correctly")
175         if not pcap_file_1:
176             raise ValueError("pcap_file_1 not provided correctly")
177         if not lua_script:
178             raise ValueError("lua_script not provided correctly")
179         if not os.path.isfile(pcap_directory + pcap_file_0):
180             raise ValueError("The file " + pcap_file_0 + " does not exist")
181         if not os.path.isfile(pcap_directory + pcap_file_1):
182             raise ValueError("The file " + pcap_file_1 + " does not exist")
183         if not os.path.isfile(lua_directory + lua_script):
184             raise ValueError("The file " + lua_script + " does not exist")
185         for var in [conf_file.CFSP_DPDK_PKTGEN_DIRECTORY,
186                     conf_file.CFSP_DPDK_PROGRAM_NAME,
187                     conf_file.CFSP_DPDK_COREMASK,
188                     conf_file.CFSP_DPDK_MEMORY_CHANNEL]:
189             if var not in variables.keys() or (var in variables.keys() and
190                variables[var] is ''):
191                 raise ValueError("The variable " + var + " does not exist")
192
193     @staticmethod
194     def _chdir(directory):
195         """
196         Changes the current directory
197         :param directory: directory where to move (type: str)
198         :return: None
199         """
200         os.chdir(directory)
201
202     def _init_physical_nics(self, dpdk_interfaces, dpdk_vars):
203         """
204         Initializes the physical interfaces
205         :param dpdk_interfaces: Number of interfaces to be used (type: int)
206         :param dpdk_vars: variables from config file related to DPDK pktgen
207                           (type: dict)
208         :return: None
209         """
210         if not dpdk_interfaces == 1 and not dpdk_interfaces == 2:
211             raise ValueError('The number of NICs can be 1 or 2')
212         # Initialize NIC 1
213         # bus_address_1 = dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_1]
214         interface_1 = dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_1]
215         common.run_command('sudo ifconfig ' + interface_1 + ' down')
216         common.run_command('sudo ' +
217                            dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] +
218                            'tools/dpdk_nic_bind.py --unbind ' +
219                            dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_1])
220         common.run_command('sudo ' +
221                            dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] +
222                            'tools/dpdk_nic_bind.py --bind=igb_uio ' +
223                            dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_1])
224         if dpdk_interfaces == 2:
225             # Initialize NIC 2
226             # bus_address_2 = dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_2]
227             interface_2 = dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_2]
228             common.run_command('sudo ifconfig ' + interface_2 + ' down')
229             common.run_command('sudo ' +
230                                dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] +
231                                'tools/dpdk_nic_bind.py --unbind ' +
232                                dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_2])
233             common.run_command('sudo ' +
234                                dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] +
235                                'tools/dpdk_nic_bind.py --bind=igb_uio ' +
236                                dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_2])
237
238     def _finalize_physical_nics(self, dpdk_interfaces, dpdk_vars):
239         """
240         Finalizes the physical interfaces
241         :param dpdk_interfaces: Number of interfaces to be used (type: int)
242         :param dpdk_vars: variables from config file related to DPDK pktgen
243                           (type: dict)
244         :return: None
245         """
246         if not dpdk_interfaces == 1 and not dpdk_interfaces == 2:
247             raise ValueError('No interfaces have been indicated for packet '
248                              'generation usage. Please specify one or two '
249                              'NICs')
250         # Initialize NIC 1
251         common.run_command('sudo ' +
252                            dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] +
253                            'tools/dpdk_nic_bind.py --unbind ' +
254                            dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_1])
255         time.sleep(5)
256         common.run_command('sudo ' +
257                            dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] +
258                            'tools/dpdk_nic_bind.py --bind=ixgbe ' +
259                            dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_1])
260         common.run_command('sudo ifconfig ' +
261                            dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_1] +
262                            ' up')
263         if dpdk_interfaces == 2:
264             # Initialize NIC 2
265             common.run_command('sudo ' +
266                                dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] +
267                                'tools/dpdk_nic_bind.py --unbind ' +
268                                dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_2])
269             time.sleep(5)
270             common.run_command('sudo ' +
271                                dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] +
272                                'tools/dpdk_nic_bind.py --bind=ixgbe ' +
273                                dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_2])
274             common.run_command('sudo ifconfig ' +
275                                dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_2] +
276                                ' up')
277
278     @staticmethod
279     def _cores_configuration(coremask, pktgen_cores=1, nic_1_cores=2,
280                              nic_2_cores=2):
281         """
282         Calculation of the core_nics parameter which is necessary for the
283         packet generator to run
284
285         :param coremask: Hexadecimal value indicating the cores to be assigned
286                          to the whole dpdk pktgen software (included the
287                          ones to receive and send packets from NICs)
288                          (type: str)
289         :param pktgen_cores: number of cores to be assigned to main thread of
290                              the pktgen directly
291         :param nic_1_cores: number of cores to be assigned to the first NIC
292         :param nic_2_nics: number of cores to be assigned to the second NIC
293         :return: returns the core_nics parameter (type: str)
294         """
295         required_cores = pktgen_cores + nic_1_cores + nic_2_cores
296         bin_coremask = bin(int(coremask, 16))[2:]
297         index = len(bin_coremask)
298         cores = []
299         while index >= 0:
300             index -= 1
301             if bin_coremask[index] == '1':
302                 core = index
303                 cores.append(core)
304         if len(cores) < required_cores:
305             raise ValueError('The provided coremask does not provide'
306                              ' enough cores for the DPDK packet generator')
307         # ret_pktgen_cores = []
308         ret_nic_1_cores = []
309         ret_nic_2_cores = []
310         current_core = 0
311
312         if nic_2_cores > 0:
313             ret_nic_2_cores.append(cores[current_core])
314             current_core += 1
315         if nic_2_cores > 1:
316             ret_nic_2_cores.append(cores[current_core])
317             current_core += 1
318
319         if nic_1_cores > 0:
320             ret_nic_1_cores.append(cores[current_core])
321             current_core += 1
322         if nic_1_cores > 1:
323             ret_nic_1_cores.append(cores[current_core])
324             current_core += 1
325
326         # for n in range(0, pktgen_cores):
327         #     ret_pktgen_cores.append(cores[n])
328         # for n in range(0, nic_1_cores):
329         #     ret_nic_1_cores.append(cores[pktgen_cores + n])
330         # for n in range(0, nic_2_cores):
331         #     ret_nic_2_cores.append(cores[pktgen_cores + nic_1_cores + n])
332
333         corenics = ''
334         if nic_1_cores > 0:
335             if nic_1_cores < 2:
336                 corenics += str(ret_nic_1_cores[0]) + '.0'
337             if nic_1_cores == 2:
338                 corenics += '[' + str(ret_nic_1_cores[0])
339                 corenics += ':' + str(ret_nic_1_cores[1])
340                 corenics += '].0'
341         if nic_2_cores > 0:
342             corenics += ','
343             if nic_2_cores < 2:
344                 corenics += str(ret_nic_2_cores[0]) + '.1'
345             if nic_2_cores == 2:
346                 corenics += '[' + str(ret_nic_2_cores[0])
347                 corenics += ':' + str(ret_nic_2_cores[1])
348                 corenics += '].1'
349         return corenics