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