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