CSIT-1321 Add IP4base tests using AVF driver
[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.constants import Constants
19 from resources.libraries.python.ssh import SSH, exec_cmd_no_error
20 from resources.libraries.python.topology import NodeType, Topology
21
22
23 class DUTSetup(object):
24     """Contains methods for setting up DUTs."""
25
26     @staticmethod
27     def get_service_logs(node, service):
28         """Get specific service unit logs from node.
29
30         :param node: Node in the topology.
31         :param service: Service unit name.
32         :type node: dict
33         :type service: str
34         """
35         if DUTSetup.running_in_container(node):
36             command = 'echo $(< /var/log/supervisord.log)'
37         else:
38             command = ('journalctl --no-pager --unit={name} '
39                        '--since="$(echo `systemctl show -p '
40                        'ActiveEnterTimestamp {name}` | '
41                        'awk \'{{print $2 $3}}\')"'.
42                        format(name=service))
43         message = 'Node {host} failed to get logs from unit {name}'.\
44             format(host=node['host'], name=service)
45
46         exec_cmd_no_error(node, command, timeout=30, sudo=True,
47                           message=message)
48
49     @staticmethod
50     def get_service_logs_on_all_duts(nodes, service):
51         """Get specific service unit logs from all DUTs.
52
53         :param nodes: Nodes in the topology.
54         :param service: Service unit name.
55         :type nodes: dict
56         :type service: str
57         """
58         for node in nodes.values():
59             if node['type'] == NodeType.DUT:
60                 DUTSetup.get_service_logs(node, service)
61
62     @staticmethod
63     def start_service(node, service):
64         """Start up the named service on node.
65
66         :param node: Node in the topology.
67         :param service: Service unit name.
68         :type node: dict
69         :type service: str
70         """
71         if DUTSetup.running_in_container(node):
72             command = 'supervisorctl restart {name}'.format(name=service)
73         else:
74             command = 'service {name} restart'.format(name=service)
75         message = 'Node {host} failed to start service {name}'.\
76             format(host=node['host'], name=service)
77
78         exec_cmd_no_error(node, command, timeout=30, sudo=True, message=message)
79
80         DUTSetup.get_service_logs(node, service)
81
82     @staticmethod
83     def start_service_on_all_duts(nodes, service):
84         """Start up the named service on all DUTs.
85
86         :param node: Nodes in the topology.
87         :param service: Service unit name.
88         :type node: dict
89         :type service: str
90         """
91         for node in nodes.values():
92             if node['type'] == NodeType.DUT:
93                 DUTSetup.start_service(node, service)
94
95     @staticmethod
96     def stop_service(node, service):
97         """Stop the named service on node.
98
99         :param node: Node in the topology.
100         :param service: Service unit name.
101         :type node: dict
102         :type service: str
103         """
104         if DUTSetup.running_in_container(node):
105             command = 'supervisorctl stop {name}'.format(name=service)
106         else:
107             command = 'service {name} stop'.format(name=service)
108         message = 'Node {host} failed to stop service {name}'.\
109             format(host=node['host'], name=service)
110
111         exec_cmd_no_error(node, command, timeout=30, sudo=True, message=message)
112
113         DUTSetup.get_service_logs(node, service)
114
115     @staticmethod
116     def stop_service_on_all_duts(nodes, service):
117         """Stop the named service on all DUTs.
118
119         :param node: Nodes in the topology.
120         :param service: Service unit name.
121         :type node: dict
122         :type service: str
123         """
124         for node in nodes.values():
125             if node['type'] == NodeType.DUT:
126                 DUTSetup.stop_service(node, service)
127
128     @staticmethod
129     def setup_dut(node):
130         """Run script over SSH to setup the DUT node.
131
132         :param node: DUT node to set up.
133         :type node: dict
134
135         :raises Exception: If the DUT setup fails.
136         """
137         command = 'bash {0}/{1}/dut_setup.sh'.\
138             format(Constants.REMOTE_FW_DIR, Constants.RESOURCES_LIB_SH)
139         message = 'DUT test setup script failed at node {name}'.\
140             format(name=node['host'])
141
142         exec_cmd_no_error(node, command, timeout=120, sudo=True,
143                           message=message)
144
145     @staticmethod
146     def setup_all_duts(nodes):
147         """Run script over SSH to setup all DUT nodes.
148
149         :param nodes: Topology nodes.
150         :type nodes: dict
151         """
152         for node in nodes.values():
153             if node['type'] == NodeType.DUT:
154                 DUTSetup.setup_dut(node)
155
156     @staticmethod
157     def get_vpp_pid(node):
158         """Get PID of running VPP process.
159
160         :param node: DUT node.
161         :type node: dict
162         :returns: PID
163         :rtype: int
164         :raises RuntimeError: If it is not possible to get the PID.
165         """
166         ssh = SSH()
167         ssh.connect(node)
168
169         for i in range(3):
170             logger.trace('Try {}: Get VPP PID'.format(i))
171             ret_code, stdout, stderr = ssh.exec_command('pidof vpp')
172
173             if int(ret_code):
174                 raise RuntimeError('Not possible to get PID of VPP process '
175                                    'on node: {0}\n {1}'.
176                                    format(node['host'], stdout + stderr))
177
178             if len(stdout.splitlines()) == 1:
179                 return int(stdout)
180             elif not stdout.splitlines():
181                 logger.debug("No VPP PID found on node {0}".
182                              format(node['host']))
183                 continue
184             else:
185                 logger.debug("More then one VPP PID found on node {0}".
186                              format(node['host']))
187                 ret_list = list()
188                 for line in stdout.splitlines():
189                     ret_list.append(int(line))
190                 return ret_list
191
192         return None
193
194     @staticmethod
195     def get_vpp_pids(nodes):
196         """Get PID of running VPP process on all DUTs.
197
198         :param nodes: DUT nodes.
199         :type nodes: dict
200         :returns: PIDs
201         :rtype: dict
202         """
203         pids = dict()
204         for node in nodes.values():
205             if node['type'] == NodeType.DUT:
206                 pids[node['host']] = DUTSetup.get_vpp_pid(node)
207         return pids
208
209     @staticmethod
210     def crypto_device_verify(node, force_init=False, numvfs=32):
211         """Verify if Crypto QAT device virtual functions are initialized on all
212         DUTs. If parameter force initialization is set to True, then try to
213         initialize or remove VFs on QAT.
214
215         :param node: DUT node.
216         :param force_init: If True then try to initialize to specific value.
217         :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
218         :type node: dict
219         :type force_init: bool
220         :type numvfs: int
221         :returns: nothing
222         :raises RuntimeError: If QAT VFs are not created and force init is set
223                               to False.
224         """
225         pci_addr = Topology.get_cryptodev(node)
226         sriov_numvfs = DUTSetup.get_sriov_numvfs(node, pci_addr)
227
228         if sriov_numvfs != numvfs:
229             if force_init:
230                 # QAT is not initialized and we want to initialize with numvfs
231                 DUTSetup.crypto_device_init(node, numvfs)
232             else:
233                 raise RuntimeError('QAT device failed to create VFs on {host}'.
234                                    format(host=node['host']))
235
236     @staticmethod
237     def crypto_device_init(node, numvfs):
238         """Init Crypto QAT device virtual functions on DUT.
239
240         :param node: DUT node.
241         :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
242         :type node: dict
243         :type numvfs: int
244         :returns: nothing
245         :raises RuntimeError: If failed to stop VPP or QAT failed to initialize.
246         """
247         pci_addr = Topology.get_cryptodev(node)
248
249         # QAT device must be re-bound to kernel driver before initialization.
250         DUTSetup.verify_kernel_module(node, 'qat_dh895xcc', force_load=True)
251
252         # Stop VPP to prevent deadlock.
253         DUTSetup.stop_service(node, Constants.VPP_UNIT)
254
255         current_driver = DUTSetup.get_pci_dev_driver(
256             node, pci_addr.replace(':', r'\:'))
257         if current_driver is not None:
258             DUTSetup.pci_driver_unbind(node, pci_addr)
259
260         # Bind to kernel driver.
261         DUTSetup.pci_driver_bind(node, pci_addr, 'dh895xcc')
262
263         # Initialize QAT VFs.
264         if numvfs > 0:
265             DUTSetup.set_sriov_numvfs(node, pci_addr, numvfs)
266
267     @staticmethod
268     def get_virtfn_pci_addr(node, pf_pci_addr, vf_id):
269         """Get PCI address of Virtual Function.
270
271         :param node: DUT node.
272         :param pf_pci_addr: Physical Function PCI address.
273         :param vf_id: Virtual Function number.
274         :type node: dict
275         :type pf_pci_addr: str
276         :type vf_id: int
277         :returns: Virtual Function PCI address.
278         :rtype: int
279         :raises RuntimeError: If failed to get Virtual Function PCI address.
280         """
281         command = "sh -c "\
282             "'basename $(readlink /sys/bus/pci/devices/{pci}/virtfn{vf_id})'".\
283             format(pci=pf_pci_addr, vf_id=vf_id)
284         message = 'Failed to get virtual function PCI address.'
285
286         stdout, _ = exec_cmd_no_error(node, command, timeout=30, sudo=True,
287                                       message=message)
288
289         return stdout.strip()
290
291     @staticmethod
292     def get_sriov_numvfs(node, pf_pci_addr):
293         """Get number of SR-IOV VFs.
294
295         :param node: DUT node.
296         :param pf_pci_addr: Physical Function PCI device address.
297         :type node: dict
298         :type pf_pci_addr: str
299         :returns: Number of VFs.
300         :rtype: int
301         :raises RuntimeError: If PCI device is not SR-IOV capable.
302         """
303         command = 'cat /sys/bus/pci/devices/{pci}/sriov_numvfs'.\
304             format(pci=pf_pci_addr.replace(':', r'\:'))
305         message = 'PCI device {pci} is not a SR-IOV device.'.\
306             format(pci=pf_pci_addr)
307
308         for _ in range(3):
309             stdout, _ = exec_cmd_no_error(node, command, timeout=30, sudo=True,
310                                           message=message)
311             try:
312                 sriov_numvfs = int(stdout)
313             except ValueError:
314                 logger.trace('Reading sriov_numvfs info failed on {host}'.
315                              format(host=node['host']))
316             else:
317                 return sriov_numvfs
318
319     @staticmethod
320     def set_sriov_numvfs(node, pf_pci_addr, numvfs=0):
321         """Init or reset SR-IOV virtual functions by setting its number on PCI
322         device on DUT. Setting to zero removes all VFs.
323
324         :param node: DUT node.
325         :param pf_pci_addr: Physical Function PCI device address.
326         :param numvfs: Number of VFs to initialize, 0 - removes the VFs.
327         :type node: dict
328         :type pf_pci_addr: str
329         :type numvfs: int
330         :raises RuntimeError: Failed to create VFs on PCI.
331         """
332         command = "sh -c "\
333             "'echo {num} | tee /sys/bus/pci/devices/{pci}/sriov_numvfs'".\
334             format(num=numvfs, pci=pf_pci_addr.replace(':', r'\:'))
335         message = 'Failed to create {num} VFs on {pci} device on {host}'.\
336             format(num=numvfs, pci=pf_pci_addr, host=node['host'])
337
338         exec_cmd_no_error(node, command, timeout=120, sudo=True,
339                           message=message)
340
341     @staticmethod
342     def pci_driver_unbind(node, pci_addr):
343         """Unbind PCI device from current driver on node.
344
345         :param node: DUT node.
346         :param pci_addr: PCI device address.
347         :type node: dict
348         :type pci_addr: str
349         :raises RuntimeError: If PCI device unbind failed.
350         """
351         command = "sh -c "\
352             "'echo {pci} | tee /sys/bus/pci/devices/{pcie}/driver/unbind'".\
353             format(pci=pci_addr, pcie=pci_addr.replace(':', r'\:'))
354         message = 'Failed to unbind PCI device {pci} on {host}'.\
355             format(pci=pci_addr, host=node['host'])
356
357         exec_cmd_no_error(node, command, timeout=120, sudo=True,
358                           message=message)
359
360     @staticmethod
361     def pci_driver_bind(node, pci_addr, driver):
362         """Bind PCI device to driver on node.
363
364         :param node: DUT node.
365         :param pci_addr: PCI device address.
366         :param driver: Driver to bind.
367         :type node: dict
368         :type pci_addr: str
369         :type driver: str
370         :raises RuntimeError: If PCI device bind failed.
371         """
372         message = 'Failed to bind PCI device {pci} to {driver} on host {host}'.\
373             format(pci=pci_addr, driver=driver, host=node['host'])
374
375         command = "sh -c "\
376             "'echo {driver} | tee /sys/bus/pci/devices/{pci}/driver_override'".\
377             format(driver=driver, pci=pci_addr.replace(':', r'\:'))
378
379         exec_cmd_no_error(node, command, timeout=120, sudo=True,
380                           message=message)
381
382         command = "sh -c "\
383             "'echo {pci} | tee /sys/bus/pci/drivers/{driver}/bind'".\
384             format(pci=pci_addr, driver=driver)
385
386         exec_cmd_no_error(node, command, timeout=120, sudo=True,
387                           message=message)
388
389         command = "sh -c "\
390             "'echo  | tee /sys/bus/pci/devices/{pci}/driver_override'".\
391             format(pci=pci_addr.replace(':', r'\:'))
392
393         exec_cmd_no_error(node, command, timeout=120, sudo=True,
394                           message=message)
395
396     @staticmethod
397     def pci_vf_driver_unbind(node, pf_pci_addr, vf_id):
398         """Unbind Virtual Function from driver on node.
399
400         :param node: DUT node.
401         :param pf_pci_addr: PCI device address.
402         :param vf_id: Virtual Function ID.
403         :type node: dict
404         :type pf_pci_addr: str
405         :type vf_id: int
406         :raises RuntimeError: If Virtual Function unbind failed.
407         """
408         vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
409         vf_path = "/sys/bus/pci/devices/{pf_pci_addr}/virtfn{vf_id}".\
410             format(pf_pci_addr=pf_pci_addr.replace(':', r'\:'), vf_id=vf_id)
411
412         command = "sh -c "\
413             "'echo {vf_pci_addr} | tee {vf_path}/driver/unbind'".\
414             format(vf_pci_addr=vf_pci_addr, vf_path=vf_path)
415
416         message = 'Failed to unbind VF {vf_pci_addr} to on {host}'.\
417             format(vf_pci_addr=vf_pci_addr, host=node['host'])
418
419         exec_cmd_no_error(node, command, timeout=120, sudo=True,
420                           message=message)
421
422     @staticmethod
423     def pci_vf_driver_bind(node, pf_pci_addr, vf_id, driver):
424         """Bind Virtual Function to driver on node.
425
426         :param node: DUT node.
427         :param pf_pci_addr: PCI device address.
428         :param vf_id: Virtual Function ID.
429         :param driver: Driver to bind.
430         :type node: dict
431         :type pf_pci_addr: str
432         :type vf_id: int
433         :type driver: str
434         :raises RuntimeError: If PCI device bind failed.
435         """
436         vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
437         vf_path = "/sys/bus/pci/devices/{pf_pci_addr}/virtfn{vf_id}".\
438             format(pf_pci_addr=pf_pci_addr.replace(':', r'\:'), vf_id=vf_id)
439
440         message = 'Failed to bind VF {vf_pci_addr} to {driver} on {host}'.\
441             format(vf_pci_addr=vf_pci_addr, driver=driver, host=node['host'])
442
443         command = "sh -c "\
444             "'echo {driver} | tee {vf_path}/driver_override'".\
445             format(driver=driver, vf_path=vf_path)
446
447         exec_cmd_no_error(node, command, timeout=120, sudo=True,
448                           message=message)
449
450         command = "sh -c "\
451             "'echo {vf_pci_addr} | tee /sys/bus/pci/drivers/{driver}/bind'".\
452             format(vf_pci_addr=vf_pci_addr, driver=driver)
453
454         exec_cmd_no_error(node, command, timeout=120, sudo=True,
455                           message=message)
456
457         command = "sh -c "\
458             "'echo  | tee {vf_path}/driver_override'".\
459             format(vf_path=vf_path)
460
461         exec_cmd_no_error(node, command, timeout=120, sudo=True,
462                           message=message)
463
464     @staticmethod
465     def get_pci_dev_driver(node, pci_addr):
466         """Get current PCI device driver on node.
467
468         .. note::
469             # lspci -vmmks 0000:00:05.0
470             Slot:   00:05.0
471             Class:  Ethernet controller
472             Vendor: Red Hat, Inc
473             Device: Virtio network device
474             SVendor:        Red Hat, Inc
475             SDevice:        Device 0001
476             PhySlot:        5
477             Driver: virtio-pci
478
479         :param node: DUT node.
480         :param pci_addr: PCI device address.
481         :type node: dict
482         :type pci_addr: str
483         :returns: Driver or None
484         :raises RuntimeError: If PCI rescan or lspci command execution failed.
485         :raises RuntimeError: If it is not possible to get the interface driver
486             information from the node.
487         """
488         ssh = SSH()
489         ssh.connect(node)
490
491         for i in range(3):
492             logger.trace('Try number {0}: Get PCI device driver'.format(i))
493
494             cmd = 'lspci -vmmks {0}'.format(pci_addr)
495             ret_code, stdout, _ = ssh.exec_command(cmd)
496             if int(ret_code):
497                 raise RuntimeError("'{0}' failed on '{1}'"
498                                    .format(cmd, node['host']))
499
500             for line in stdout.splitlines():
501                 if not line:
502                     continue
503                 name = None
504                 value = None
505                 try:
506                     name, value = line.split("\t", 1)
507                 except ValueError:
508                     if name == "Driver:":
509                         return None
510                 if name == 'Driver:':
511                     return value
512
513             if i < 2:
514                 logger.trace('Driver for PCI device {} not found, executing '
515                              'pci rescan and retrying'.format(pci_addr))
516                 cmd = 'sh -c "echo 1 > /sys/bus/pci/rescan"'
517                 ret_code, _, _ = ssh.exec_command_sudo(cmd)
518                 if int(ret_code) != 0:
519                     raise RuntimeError("'{0}' failed on '{1}'"
520                                        .format(cmd, node['host']))
521
522         return None
523
524     @staticmethod
525     def verify_kernel_module(node, module, force_load=False):
526         """Verify if kernel module is loaded on node. If parameter force
527         load is set to True, then try to load the modules.
528
529         :param node: Node.
530         :param module: Module to verify.
531         :param force_load: If True then try to load module.
532         :type node: dict
533         :type module: str
534         :type force_load: bool
535         :raises RuntimeError: If module is not loaded or failed to load.
536         """
537         command = 'grep -w {module} /proc/modules'.format(module=module)
538         message = 'Kernel module {module} is not loaded on host {host}'.\
539             format(module=module, host=node['host'])
540
541         try:
542             exec_cmd_no_error(node, command, timeout=30, sudo=False,
543                               message=message)
544         except RuntimeError:
545             if force_load:
546                 # Module is not loaded and we want to load it
547                 DUTSetup.load_kernel_module(node, module)
548             else:
549                 raise
550
551     @staticmethod
552     def verify_kernel_module_on_all_duts(nodes, module, force_load=False):
553         """Verify if kernel module is loaded on all DUTs. If parameter force
554         load is set to True, then try to load the modules.
555
556         :param node: DUT nodes.
557         :param module: Module to verify.
558         :param force_load: If True then try to load module.
559         :type node: dict
560         :type module: str
561         :type force_load: bool
562         """
563         for node in nodes.values():
564             if node['type'] == NodeType.DUT:
565                 DUTSetup.verify_kernel_module(node, module, force_load)
566
567     @staticmethod
568     def verify_uio_driver_on_all_duts(nodes):
569         """Verify if uio driver kernel module is loaded on all DUTs. If module
570         is not present it will try to load it.
571
572         :param node: DUT nodes.
573         :type node: dict
574         """
575         for node in nodes.values():
576             if node['type'] == NodeType.DUT:
577                 uio_driver = Topology.get_uio_driver(node)
578                 DUTSetup.verify_kernel_module(node, uio_driver, force_load=True)
579
580     @staticmethod
581     def load_kernel_module(node, module):
582         """Load kernel module on node.
583
584         :param node: DUT node.
585         :param module: Module to load.
586         :type node: dict
587         :type module: str
588         :returns: nothing
589         :raises RuntimeError: If loading failed.
590         """
591         command = 'modprobe {module}'.format(module=module)
592         message = 'Failed to load {module} on host {host}'.\
593             format(module=module, host=node['host'])
594
595         exec_cmd_no_error(node, command, timeout=30, sudo=True, message=message)
596
597     @staticmethod
598     def install_vpp_on_all_duts(nodes, vpp_pkg_dir, vpp_rpm_pkgs, vpp_deb_pkgs):
599         """Install VPP on all DUT nodes.
600
601         :param nodes: Nodes in the topology.
602         :param vpp_pkg_dir: Path to directory where VPP packages are stored.
603         :param vpp_rpm_pkgs: List of VPP rpm packages to be installed.
604         :param vpp_deb_pkgs: List of VPP deb packages to be installed.
605         :type nodes: dict
606         :type vpp_pkg_dir: str
607         :type vpp_rpm_pkgs: list
608         :type vpp_deb_pkgs: list
609         :raises RuntimeError: If failed to remove or install VPP.
610         """
611         for node in nodes.values():
612             if node['type'] == NodeType.DUT:
613                 logger.debug("Installing VPP on node {0}".format(node['host']))
614
615                 ssh = SSH()
616                 ssh.connect(node)
617
618                 cmd = "[[ -f /etc/redhat-release ]]"
619                 return_code, _, _ = ssh.exec_command(cmd)
620                 if not int(return_code):
621                     # workaroud - uninstall existing vpp installation until
622                     # start-testcase script is updated on all virl servers
623                     rpm_pkgs_remove = "vpp*"
624                     cmd_u = 'yum -y remove "{0}"'.format(rpm_pkgs_remove)
625                     r_rcode, _, r_err = ssh.exec_command_sudo(cmd_u, timeout=90)
626                     if int(r_rcode):
627                         raise RuntimeError('Failed to remove previous VPP'
628                                            'installation on host {0}:\n{1}'
629                                            .format(node['host'], r_err))
630
631                     rpm_pkgs = "*.rpm ".join(str(vpp_pkg_dir + pkg)
632                                              for pkg in vpp_rpm_pkgs) + "*.rpm"
633                     cmd_i = "rpm -ivh {0}".format(rpm_pkgs)
634                     ret_code, _, err = ssh.exec_command_sudo(cmd_i, timeout=90)
635                     if int(ret_code):
636                         raise RuntimeError('Failed to install VPP on host {0}:'
637                                            '\n{1}'.format(node['host'], err))
638                     else:
639                         ssh.exec_command_sudo("rpm -qai vpp*")
640                         logger.info("VPP installed on node {0}".
641                                     format(node['host']))
642                 else:
643                     # workaroud - uninstall existing vpp installation until
644                     # start-testcase script is updated on all virl servers
645                     deb_pkgs_remove = "vpp*"
646                     cmd_u = 'apt-get purge -y "{0}"'.format(deb_pkgs_remove)
647                     r_rcode, _, r_err = ssh.exec_command_sudo(cmd_u, timeout=90)
648                     if int(r_rcode):
649                         raise RuntimeError('Failed to remove previous VPP'
650                                            'installation on host {0}:\n{1}'
651                                            .format(node['host'], r_err))
652                     deb_pkgs = "*.deb ".join(str(vpp_pkg_dir + pkg)
653                                              for pkg in vpp_deb_pkgs) + "*.deb"
654                     cmd_i = "dpkg -i --force-all {0}".format(deb_pkgs)
655                     ret_code, _, err = ssh.exec_command_sudo(cmd_i, timeout=90)
656                     if int(ret_code):
657                         raise RuntimeError('Failed to install VPP on host {0}:'
658                                            '\n{1}'.format(node['host'], err))
659                     else:
660                         ssh.exec_command_sudo("dpkg -l | grep vpp")
661                         logger.info("VPP installed on node {0}".
662                                     format(node['host']))
663
664                 ssh.disconnect(node)
665
666     @staticmethod
667     def running_in_container(node):
668         """This method tests if topology node is running inside container.
669
670         :param node: Topology node.
671         :type node: dict
672         :returns: True if running in docker container, false if not or failed
673         to detect.
674         :rtype: bool
675         """
676         command = "fgrep docker /proc/1/cgroup"
677         message = 'Failed to get cgroup settings.'
678         try:
679             exec_cmd_no_error(node, command, timeout=30, sudo=False,
680                               message=message)
681         except RuntimeError:
682             return False
683         return True
684
685     @staticmethod
686     def get_huge_page_size(node):
687         """Get default size of huge pages in system.
688
689         :param node: Node in the topology.
690         :type node: dict
691         :returns: Default size of free huge pages in system.
692         :rtype: int
693         :raises RuntimeError: If reading failed for three times.
694         """
695         ssh = SSH()
696         ssh.connect(node)
697
698         for _ in range(3):
699             ret_code, stdout, _ = ssh.exec_command_sudo(
700                 "grep Hugepagesize /proc/meminfo | awk '{ print $2 }'")
701             if ret_code == 0:
702                 try:
703                     huge_size = int(stdout)
704                 except ValueError:
705                     logger.trace('Reading huge page size information failed')
706                 else:
707                     break
708         else:
709             raise RuntimeError('Getting huge page size information failed.')
710         return huge_size
711
712     @staticmethod
713     def get_huge_page_free(node, huge_size):
714         """Get number of free huge pages in system.
715
716         :param node: Node in the topology.
717         :param huge_size: Size of hugepages.
718         :type node: dict
719         :type huge_size: int
720         :returns: Number of free huge pages in system.
721         :rtype: int
722         :raises RuntimeError: If reading failed for three times.
723         """
724         # TODO: add numa aware option
725         ssh = SSH()
726         ssh.connect(node)
727
728         for _ in range(3):
729             ret_code, stdout, _ = ssh.exec_command_sudo(
730                 'cat /sys/kernel/mm/hugepages/hugepages-{0}kB/free_hugepages'.
731                 format(huge_size))
732             if ret_code == 0:
733                 try:
734                     huge_free = int(stdout)
735                 except ValueError:
736                     logger.trace('Reading free huge pages information failed')
737                 else:
738                     break
739         else:
740             raise RuntimeError('Getting free huge pages information failed.')
741         return huge_free
742
743     @staticmethod
744     def get_huge_page_total(node, huge_size):
745         """Get total number of huge pages in system.
746
747         :param node: Node in the topology.
748         :param huge_size: Size of hugepages.
749         :type node: dict
750         :type huge_size: int
751
752         :returns: Total number of huge pages in system.
753         :rtype: int
754         :raises RuntimeError: If reading failed for three times.
755         """
756         # TODO: add numa aware option
757         ssh = SSH()
758         ssh.connect(node)
759
760         for _ in range(3):
761             ret_code, stdout, _ = ssh.exec_command_sudo(
762                 'cat /sys/kernel/mm/hugepages/hugepages-{0}kB/nr_hugepages'.
763                 format(huge_size))
764             if ret_code == 0:
765                 try:
766                     huge_total = int(stdout)
767                 except ValueError:
768                     logger.trace('Reading total huge pages information failed')
769                 else:
770                     break
771         else:
772             raise RuntimeError('Getting total huge pages information failed.')
773         return huge_total
774
775     @staticmethod
776     def check_huge_page(node, huge_mnt, mem_size, allocate=False):
777         """Check if there is enough HugePages in system. If allocate is set to
778         true, try to allocate more HugePages.
779
780         :param node: Node in the topology.
781         :param huge_mnt: HugePage mount point.
782         :param mem_size: Requested memory in MB.
783         :param allocate: Whether to allocate more memory if not enough.
784         :type node: dict
785         :type huge_mnt: str
786         :type mem_size: str
787         :type allocate: bool
788
789         :raises RuntimeError: Mounting hugetlbfs failed or not enough HugePages
790         or increasing map count failed.
791         """
792         # TODO: split function into smaller parts.
793         ssh = SSH()
794         ssh.connect(node)
795
796         # Get huge pages information
797         huge_size = DUTSetup.get_huge_page_size(node)
798         huge_free = DUTSetup.get_huge_page_free(node, huge_size)
799         huge_total = DUTSetup.get_huge_page_total(node, huge_size)
800
801         # Check if memory reqested is available on host
802         if (mem_size * 1024) > (huge_free * huge_size):
803             # If we want to allocate hugepage dynamically
804             if allocate:
805                 mem_needed = (mem_size * 1024) - (huge_free * huge_size)
806                 huge_to_allocate = ((mem_needed / huge_size) * 2) + huge_total
807                 max_map_count = huge_to_allocate*4
808                 # Increase maximum number of memory map areas a process may have
809                 ret_code, _, _ = ssh.exec_command_sudo(
810                     'echo "{0}" | sudo tee /proc/sys/vm/max_map_count'.
811                     format(max_map_count))
812                 if int(ret_code) != 0:
813                     raise RuntimeError('Increase map count failed on {host}'.
814                                        format(host=node['host']))
815                 # Increase hugepage count
816                 ret_code, _, _ = ssh.exec_command_sudo(
817                     'echo "{0}" | sudo tee /proc/sys/vm/nr_hugepages'.
818                     format(huge_to_allocate))
819                 if int(ret_code) != 0:
820                     raise RuntimeError('Mount huge pages failed on {host}'.
821                                        format(host=node['host']))
822             # If we do not want to allocate dynamicaly end with error
823             else:
824                 raise RuntimeError('Not enough free huge pages: {0}, {1} MB'.
825                                    format(huge_free, huge_free * huge_size))
826         # Check if huge pages mount point exist
827         has_huge_mnt = False
828         ret_code, stdout, _ = ssh.exec_command('cat /proc/mounts')
829         if int(ret_code) == 0:
830             for line in stdout.splitlines():
831                 # Try to find something like:
832                 # none /mnt/huge hugetlbfs rw,relatime,pagesize=2048k 0 0
833                 mount = line.split()
834                 if mount[2] == 'hugetlbfs' and mount[1] == huge_mnt:
835                     has_huge_mnt = True
836                     break
837         # If huge page mount point not exist create one
838         if not has_huge_mnt:
839             ret_code, _, _ = ssh.exec_command_sudo(
840                 'mkdir -p {mnt}'.format(mnt=huge_mnt))
841             if int(ret_code) != 0:
842                 raise RuntimeError('Create mount dir failed on {host}'.
843                                    format(host=node['host']))
844             ret_code, _, _ = ssh.exec_command_sudo(
845                 'mount -t hugetlbfs -o pagesize=2048k none {mnt}'.
846                 format(mnt=huge_mnt))
847             if int(ret_code) != 0:
848                 raise RuntimeError('Mount huge pages failed on {host}'.
849                                    format(host=node['host']))