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