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