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