CSIT-675: SRv6 performance tests
[csit.git] / resources / libraries / python / DUTSetup.py
1 # Copyright (c) 2018 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 """DUT setup library."""
15
16 from robot.api import logger
17
18 from resources.libraries.python.topology import NodeType, Topology
19 from resources.libraries.python.ssh import SSH
20 from resources.libraries.python.constants import Constants
21 from resources.libraries.python.VatExecutor import VatExecutor
22 from resources.libraries.python.VPPUtil import VPPUtil
23
24
25 class DUTSetup(object):
26     """Contains methods for setting up DUTs."""
27     @staticmethod
28     def start_vpp_service_on_all_duts(nodes):
29         """Start up the VPP service on all nodes.
30
31         :param nodes: Nodes in the topology.
32         :type nodes: dict
33         """
34         ssh = SSH()
35         for node in nodes.values():
36             if node['type'] == NodeType.DUT:
37                 ssh.connect(node)
38                 (ret_code, stdout, stderr) = \
39                     ssh.exec_command_sudo('service vpp restart', timeout=120)
40                 if int(ret_code) != 0:
41                     logger.debug('stdout: {0}'.format(stdout))
42                     logger.debug('stderr: {0}'.format(stderr))
43                     raise Exception('DUT {0} failed to start VPP service'.
44                                     format(node['host']))
45
46     @staticmethod
47     def vpp_show_version_verbose(node):
48         """Run "show version verbose" CLI command.
49
50         :param node: Node to run command on.
51         :type node: dict
52         """
53         vat = VatExecutor()
54         vat.execute_script("show_version_verbose.vat", node, json_out=False)
55
56     @staticmethod
57     def show_vpp_version_on_all_duts(nodes):
58         """Show VPP version verbose on all DUTs.
59
60         :param nodes: VPP nodes
61         :type nodes: dict
62         """
63         for node in nodes.values():
64             if node['type'] == NodeType.DUT:
65                 DUTSetup.vpp_show_version_verbose(node)
66
67     @staticmethod
68     def vpp_api_trace_save(node):
69         """Run "api trace save" CLI command.
70
71         :param node: Node to run command on.
72         :type node: dict
73         """
74         vat = VatExecutor()
75         vat.execute_script("api_trace_save.vat", node, json_out=False)
76
77     @staticmethod
78     def vpp_api_trace_dump(node):
79         """Run "api trace custom-dump" CLI command.
80
81         :param node: Node to run command on.
82         :type node: dict
83         """
84         vat = VatExecutor()
85         vat.execute_script("api_trace_dump.vat", node, json_out=False)
86
87     @staticmethod
88     def setup_all_duts(nodes):
89         """Prepare all DUTs in given topology for test execution."""
90         for node in nodes.values():
91             if node['type'] == NodeType.DUT:
92                 DUTSetup.setup_dut(node)
93
94     @staticmethod
95     def setup_dut(node):
96         """Run script over SSH to setup the DUT node.
97
98         :param node: DUT node to set up.
99         :type node: dict
100
101         :raises Exception: If the DUT setup fails.
102         """
103         ssh = SSH()
104         ssh.connect(node)
105
106         (ret_code, stdout, stderr) = \
107             ssh.exec_command('sudo -Sn bash {0}/{1}/dut_setup.sh'.
108                              format(Constants.REMOTE_FW_DIR,
109                                     Constants.RESOURCES_LIB_SH), timeout=120)
110         logger.trace(stdout)
111         logger.trace(stderr)
112         if int(ret_code) != 0:
113             logger.debug('DUT {0} setup script failed: "{1}"'.
114                          format(node['host'], stdout + stderr))
115             raise Exception('DUT test setup script failed at node {}'.
116                             format(node['host']))
117
118     @staticmethod
119     def get_vpp_pid(node):
120         """Get PID of running VPP process.
121
122         :param node: DUT node.
123         :type node: dict
124         :returns: PID
125         :rtype: int
126         :raises RuntimeError if it is not possible to get the PID.
127         """
128
129         ssh = SSH()
130         ssh.connect(node)
131
132         for i in range(3):
133             logger.trace('Try {}: Get VPP PID'.format(i))
134             ret_code, stdout, stderr = ssh.exec_command('pidof vpp')
135
136             if int(ret_code) != 0:
137                 raise RuntimeError('Not possible to get PID of VPP process '
138                                    'on node: {0}\n {1}'.
139                                    format(node['host'], stdout + stderr))
140
141             if len(stdout.splitlines()) == 1:
142                 return int(stdout)
143             elif len(stdout.splitlines()) == 0:
144                 logger.debug("No VPP PID found on node {0}".
145                              format(node['host']))
146                 continue
147             else:
148                 logger.debug("More then one VPP PID found on node {0}".
149                              format(node['host']))
150                 ret_list = list()
151                 for line in stdout.splitlines():
152                     ret_list.append(int(line))
153                 return ret_list
154
155         return None
156
157     @staticmethod
158     def get_vpp_pids(nodes):
159         """Get PID of running VPP process on all DUTs.
160
161         :param nodes: DUT nodes.
162         :type nodes: dict
163         :returns: PIDs
164         :rtype: dict
165         """
166
167         pids = dict()
168         for node in nodes.values():
169             if node['type'] == NodeType.DUT:
170                 pids[node['host']] = DUTSetup.get_vpp_pid(node)
171         return pids
172
173     @staticmethod
174     def vpp_show_crypto_device_mapping(node):
175         """Run "show crypto device mapping" CLI command.
176
177         :param node: Node to run command on.
178         :type node: dict
179         """
180         vat = VatExecutor()
181         vat.execute_script("show_crypto_device_mapping.vat", node,
182                            json_out=False)
183
184     @staticmethod
185     def crypto_device_verify(node, force_init=False, numvfs=32):
186         """Verify if Crypto QAT device virtual functions are initialized on all
187         DUTs. If parameter force initialization is set to True, then try to
188         initialize or disable QAT.
189
190         :param node: DUT node.
191         :param force_init: If True then try to initialize to specific value.
192         :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
193         :type node: dict
194         :type force_init: bool
195         :type numvfs: int
196         :returns: nothing
197         :raises RuntimeError: If QAT is not initialized or failed to initialize.
198         """
199
200         ssh = SSH()
201         ssh.connect(node)
202
203         cryptodev = Topology.get_cryptodev(node)
204         cmd = 'cat /sys/bus/pci/devices/{0}/sriov_numvfs'.\
205             format(cryptodev.replace(':', r'\:'))
206
207         # Try to read number of VFs from PCI address of QAT device
208         for _ in range(3):
209             ret_code, stdout, _ = ssh.exec_command(cmd)
210             if int(ret_code) == 0:
211                 try:
212                     sriov_numvfs = int(stdout)
213                 except ValueError:
214                     logger.trace('Reading sriov_numvfs info failed on {0}'.
215                                  format(node['host']))
216                 else:
217                     if sriov_numvfs != numvfs:
218                         if force_init:
219                             # QAT is not initialized and we want to initialize
220                             # with numvfs
221                             DUTSetup.crypto_device_init(node, numvfs)
222                         else:
223                             raise RuntimeError('QAT device {0} is not '
224                                                'initialized to {1} on host {2}'
225                                                .format(cryptodev, numvfs,
226                                                        node['host']))
227                     break
228
229     @staticmethod
230     def crypto_device_init(node, numvfs):
231         """Init Crypto QAT device virtual functions on DUT.
232
233         :param node: DUT node.
234         :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
235         :type node: dict
236         :type numvfs: int
237         :returns: nothing
238         :raises RuntimeError: If failed to stop VPP or QAT failed to initialize.
239         """
240         cryptodev = Topology.get_cryptodev(node)
241
242         # QAT device must be re-bound to kernel driver before initialization
243         driver = 'dh895xcc'
244         kernel_module = 'qat_dh895xcc'
245         current_driver = DUTSetup.get_pci_dev_driver(
246             node, cryptodev.replace(':', r'\:'))
247
248         DUTSetup.kernel_module_verify(node, kernel_module, force_load=True)
249
250         VPPUtil.stop_vpp_service(node)
251         if current_driver is not None:
252             DUTSetup.pci_driver_unbind(node, cryptodev)
253         DUTSetup.pci_driver_bind(node, cryptodev, driver)
254
255         ssh = SSH()
256         ssh.connect(node)
257
258         # Initialize QAT VFs
259         if numvfs > 0:
260             cmd = 'echo "{0}" | tee /sys/bus/pci/devices/{1}/sriov_numvfs'.\
261                 format(numvfs, cryptodev.replace(':', r'\:'), timeout=180)
262             ret_code, _, _ = ssh.exec_command_sudo("sh -c '{0}'".format(cmd))
263
264             if int(ret_code) != 0:
265                 raise RuntimeError('Failed to initialize {0} VFs on QAT device '
266                                    ' on host {1}'.format(numvfs, node['host']))
267
268     @staticmethod
269     def pci_driver_unbind(node, pci_addr):
270         """Unbind PCI device from current driver on node.
271
272         :param node: DUT node.
273         :param pci_addr: PCI device address.
274         :type node: dict
275         :type pci_addr: str
276         :returns: nothing
277         :raises RuntimeError: If PCI device unbind failed.
278         """
279
280         ssh = SSH()
281         ssh.connect(node)
282
283         ret_code, _, _ = ssh.exec_command_sudo(
284             "sh -c 'echo {0} | tee /sys/bus/pci/devices/{1}/driver/unbind'"
285             .format(pci_addr, pci_addr.replace(':', r'\:')), timeout=180)
286
287         if int(ret_code) != 0:
288             raise RuntimeError('Failed to unbind PCI device {0} from driver on '
289                                'host {1}'.format(pci_addr, node['host']))
290
291     @staticmethod
292     def pci_driver_bind(node, pci_addr, driver):
293         """Bind PCI device to driver on node.
294
295         :param node: DUT node.
296         :param pci_addr: PCI device address.
297         :param driver: Driver to bind.
298         :type node: dict
299         :type pci_addr: str
300         :type driver: str
301         :returns: nothing
302         :raises RuntimeError: If PCI device bind failed.
303         """
304
305         ssh = SSH()
306         ssh.connect(node)
307
308         ret_code, _, _ = ssh.exec_command_sudo(
309             "sh -c 'echo {0} | tee /sys/bus/pci/drivers/{1}/bind'".format(
310                 pci_addr, driver), timeout=180)
311
312         if int(ret_code) != 0:
313             raise RuntimeError('Failed to bind PCI device {0} to {1} driver on '
314                                'host {2}'.format(pci_addr, driver,
315                                                  node['host']))
316
317     @staticmethod
318     def get_pci_dev_driver(node, pci_addr):
319         """Get current PCI device driver on node.
320
321         :param node: DUT node.
322         :param pci_addr: PCI device address.
323         :type node: dict
324         :type pci_addr: str
325         :returns: Driver or None
326         :raises RuntimeError: If PCI rescan or lspci command execution failed.
327         """
328         ssh = SSH()
329         ssh.connect(node)
330
331         for i in range(3):
332             logger.trace('Try {0}: Get interface driver'.format(i))
333             cmd = 'sh -c "echo 1 > /sys/bus/pci/rescan"'
334             ret_code, _, _ = ssh.exec_command_sudo(cmd)
335             if int(ret_code) != 0:
336                 raise RuntimeError("'{0}' failed on '{1}'"
337                                    .format(cmd, node['host']))
338
339             cmd = 'lspci -vmmks {0}'.format(pci_addr)
340             ret_code, stdout, _ = ssh.exec_command(cmd)
341             if int(ret_code) != 0:
342                 raise RuntimeError("'{0}' failed on '{1}'"
343                                    .format(cmd, node['host']))
344
345             for line in stdout.splitlines():
346                 if len(line) == 0:
347                     continue
348                 name = None
349                 value = None
350                 try:
351                     name, value = line.split("\t", 1)
352                 except ValueError:
353                     if name == "Driver:":
354                         return None
355                 if name == 'Driver:':
356                     return value
357         else:
358             return None
359
360     @staticmethod
361     def kernel_module_verify(node, module, force_load=False):
362         """Verify if kernel module is loaded on all DUTs. If parameter force
363         load is set to True, then try to load the modules.
364
365         :param node: DUT node.
366         :param module: Module to verify.
367         :param force_load: If True then try to load module.
368         :type node: dict
369         :type module: str
370         :type force_load: bool
371         :returns: nothing
372         :raises RuntimeError: If module is not loaded or failed to load.
373         """
374
375         ssh = SSH()
376         ssh.connect(node)
377
378         cmd = 'grep -w {0} /proc/modules'.format(module)
379         ret_code, _, _ = ssh.exec_command(cmd)
380
381         if int(ret_code) != 0:
382             if force_load:
383                 # Module is not loaded and we want to load it
384                 DUTSetup.kernel_module_load(node, module)
385             else:
386                 raise RuntimeError('Kernel module {0} is not loaded on host '
387                                    '{1}'.format(module, node['host']))
388
389     @staticmethod
390     def kernel_module_load(node, module):
391         """Load kernel module on node.
392
393         :param node: DUT node.
394         :param module: Module to load.
395         :type node: dict
396         :type module: str
397         :returns: nothing
398         :raises RuntimeError: If loading failed.
399         """
400
401         ssh = SSH()
402         ssh.connect(node)
403
404         ret_code, _, _ = ssh.exec_command_sudo("modprobe {0}".format(module))
405
406         if int(ret_code) != 0:
407             raise RuntimeError('Failed to load {0} kernel module on host {1}'.
408                                format(module, node['host']))
409
410     @staticmethod
411     def vpp_enable_traces_on_all_duts(nodes):
412         """Enable vpp packet traces on all DUTs in the given topology.
413
414         :param nodes: Nodes in the topology.
415         :type nodes: dict
416         """
417         for node in nodes.values():
418             if node['type'] == NodeType.DUT:
419                 DUTSetup.vpp_enable_traces_on_dut(node)
420
421     @staticmethod
422     def vpp_enable_traces_on_dut(node):
423         """Enable vpp packet traces on the DUT node.
424
425         :param node: DUT node to set up.
426         :type node: dict
427         """
428
429         vat = VatExecutor()
430         vat.execute_script("enable_dpdk_traces.vat", node, json_out=False)
431         vat.execute_script("enable_vhost_user_traces.vat", node, json_out=False)