419a4e291fa171b68aaec71b828fa7c0cf29d5aa
[csit.git] / resources / libraries / python / DUTSetup.py
1 # Copyright (c) 2023 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, retries=3):
211         """Get PID of running process.
212
213         :param node: DUT node.
214         :param process: process name.
215         :param retries: How many times to retry on failure.
216         :type node: dict
217         :type process: str
218         :type retries: int
219         :returns: PID
220         :rtype: int
221         :raises RuntimeError: If it is not possible to get the PID.
222         """
223         cmd = f"pidof {process}"
224         stdout, _ = exec_cmd_no_error(
225             node, cmd, retries=retries,
226             message=f"No {process} PID found on node {node[u'host']}")
227         pid_list = stdout.split()
228         return [int(pid) for pid in pid_list]
229
230     @staticmethod
231     def get_vpp_pids(nodes):
232         """Get PID of running VPP process on all DUTs.
233
234         :param nodes: DUT nodes.
235         :type nodes: dict
236         :returns: PIDs
237         :rtype: dict
238         """
239         pids = dict()
240         for node in nodes.values():
241             if node[u"type"] == NodeType.DUT:
242                 pids[node[u"host"]] = DUTSetup.get_pid(node, u"vpp")
243         return pids
244
245     @staticmethod
246     def crypto_device_verify(node, crypto_type, numvfs, force_init=False):
247         """Verify if Crypto QAT device virtual functions are initialized on all
248         DUTs. If parameter force initialization is set to True, then try to
249         initialize or remove VFs on QAT.
250
251         :param node: DUT node.
252         :crypto_type: Crypto device type - HW_DH895xcc, HW_C3xxx or HW_C4xxx.
253         :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
254         :param force_init: If True then try to initialize to specific value.
255         :type node: dict
256         :type crypto_type: string
257         :type numvfs: int
258         :type force_init: bool
259         :returns: nothing
260         :raises RuntimeError: If QAT VFs are not created and force init is set
261                               to False.
262         """
263         pci_addr = Topology.get_cryptodev(node)
264         sriov_numvfs = DUTSetup.get_sriov_numvfs(node, pci_addr)
265
266         if sriov_numvfs != numvfs:
267             if force_init:
268                 # QAT is not initialized and we want to initialize with numvfs
269                 DUTSetup.crypto_device_init(node, crypto_type, numvfs)
270             else:
271                 raise RuntimeError(
272                     f"QAT device failed to create VFs on {node[u'host']}"
273                 )
274
275     @staticmethod
276     def crypto_device_init(node, crypto_type, numvfs):
277         """Init Crypto QAT device virtual functions on DUT.
278
279         :param node: DUT node.
280         :crypto_type: Crypto device type - HW_DH895xcc, HW_C3xxx or HW_C4xxx.
281         :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
282         :type node: dict
283         :type crypto_type: string
284         :type numvfs: int
285         :returns: nothing
286         :raises RuntimeError: If failed to stop VPP or QAT failed to initialize.
287         """
288         if crypto_type == u"HW_DH895xcc":
289             kernel_mod = u"qat_dh895xcc"
290             kernel_drv = u"dh895xcc"
291         elif crypto_type == u"HW_C3xxx":
292             kernel_mod = u"qat_c3xxx"
293             kernel_drv = u"c3xxx"
294         elif crypto_type == u"HW_C4xxx":
295             kernel_mod = u"qat_c4xxx"
296             kernel_drv = u"c4xxx"
297         else:
298             raise RuntimeError(
299                 f"Unsupported crypto device type on {node[u'host']}"
300             )
301
302         pci_addr = Topology.get_cryptodev(node)
303
304         # QAT device must be re-bound to kernel driver before initialization.
305         DUTSetup.verify_kernel_module(node, kernel_mod, force_load=True)
306
307         # Stop VPP to prevent deadlock.
308         DUTSetup.stop_service(node, Constants.VPP_UNIT)
309
310         current_driver = DUTSetup.get_pci_dev_driver(
311             node, pci_addr.replace(u":", r"\:")
312         )
313         if current_driver is not None:
314             DUTSetup.pci_driver_unbind(node, pci_addr)
315
316         # Bind to kernel driver.
317         DUTSetup.pci_driver_bind(node, pci_addr, kernel_drv)
318
319         # Initialize QAT VFs.
320         if numvfs > 0:
321             DUTSetup.set_sriov_numvfs(node, pci_addr, numvfs)
322
323     @staticmethod
324     def get_virtfn_pci_addr(node, pf_pci_addr, vf_id):
325         """Get PCI address of Virtual Function.
326
327         :param node: DUT node.
328         :param pf_pci_addr: Physical Function PCI address.
329         :param vf_id: Virtual Function number.
330         :type node: dict
331         :type pf_pci_addr: str
332         :type vf_id: int
333         :returns: Virtual Function PCI address.
334         :rtype: str
335         :raises RuntimeError: If failed to get Virtual Function PCI address.
336         """
337         command = f"sh -c \"basename $(readlink " \
338             f"/sys/bus/pci/devices/{pf_pci_addr}/virtfn{vf_id})\""
339         message = u"Failed to get virtual function PCI address."
340
341         stdout, _ = exec_cmd_no_error(
342             node, command, timeout=30, sudo=True, message=message
343         )
344
345         return stdout.strip()
346
347     @staticmethod
348     def get_sriov_numvfs(node, pf_pci_addr):
349         """Get number of SR-IOV VFs.
350
351         :param node: DUT node.
352         :param pf_pci_addr: Physical Function PCI device address.
353         :type node: dict
354         :type pf_pci_addr: str
355         :returns: Number of VFs.
356         :rtype: int
357         :raises RuntimeError: If PCI device is not SR-IOV capable.
358         """
359         pci = pf_pci_addr.replace(u":", r"\:")
360         command = f"cat /sys/bus/pci/devices/{pci}/sriov_numvfs"
361         message = f"PCI device {pf_pci_addr} is not a SR-IOV device."
362
363         for _ in range(3):
364             stdout, _ = exec_cmd_no_error(
365                 node, command, timeout=30, sudo=True, message=message
366             )
367             try:
368                 sriov_numvfs = int(stdout)
369             except ValueError:
370                 logger.trace(
371                     f"Reading sriov_numvfs info failed on {node[u'host']}"
372                 )
373             else:
374                 return sriov_numvfs
375
376     @staticmethod
377     def set_sriov_numvfs(node, pf_pci_addr, numvfs=0):
378         """Init or reset SR-IOV virtual functions by setting its number on PCI
379         device on DUT. Setting to zero removes all VFs.
380
381         :param node: DUT node.
382         :param pf_pci_addr: Physical Function PCI device address.
383         :param numvfs: Number of VFs to initialize, 0 - removes the VFs.
384         :type node: dict
385         :type pf_pci_addr: str
386         :type numvfs: int
387         :raises RuntimeError: Failed to create VFs on PCI.
388         """
389         cmd = f"test -f /sys/bus/pci/devices/{pf_pci_addr}/sriov_numvfs"
390         sriov_unsupported, _, _ = exec_cmd(node, cmd)
391         # if sriov_numvfs doesn't exist, then sriov_unsupported != 0
392         if int(sriov_unsupported):
393             if numvfs == 0:
394                 # sriov is not supported and we want 0 VFs
395                 # no need to do anything
396                 return
397
398             raise RuntimeError(
399                 f"Can't configure {numvfs} VFs on {pf_pci_addr} device "
400                 f"on {node[u'host']} since it doesn't support SR-IOV."
401             )
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 unbind_pci_devices_from_other_driver(node, driver, *pci_addrs):
434         """Unbind PCI devices from driver other than input driver on node.
435
436         :param node: DUT node.
437         :param driver: Driver to not unbind from. If None or empty string,
438             will attempt to unbind from the current driver.
439         :param pci_addrs: PCI device addresses.
440         :type node: dict
441         :type driver: str
442         :type pci_addrs: list
443         """
444         for pci_addr in pci_addrs:
445             if not driver or \
446                     DUTSetup.get_pci_dev_driver(node, pci_addr) != driver:
447                 DUTSetup.pci_driver_unbind(node, pci_addr)
448
449     @staticmethod
450     def pci_driver_bind(node, pci_addr, driver):
451         """Bind PCI device to driver on node.
452
453         :param node: DUT node.
454         :param pci_addr: PCI device address.
455         :param driver: Driver to bind.
456         :type node: dict
457         :type pci_addr: str
458         :type driver: str
459         :raises RuntimeError: If PCI device bind failed.
460         """
461         message = f"Failed to bind PCI device {pci_addr} to {driver} " \
462             f"on host {node[u'host']}"
463         pci = pci_addr.replace(u":", r"\:")
464         command = f"sh -c \"echo {driver} | " \
465             f"tee /sys/bus/pci/devices/{pci}/driver_override\""
466
467         exec_cmd_no_error(
468             node, command, timeout=120, sudo=True, message=message
469         )
470
471         command = f"sh -c \"echo {pci_addr} | " \
472             f"tee /sys/bus/pci/drivers/{driver}/bind\""
473
474         exec_cmd_no_error(
475             node, command, timeout=120, sudo=True, message=message
476         )
477
478         command = f"sh -c \"echo  | " \
479             f"tee /sys/bus/pci/devices/{pci}/driver_override\""
480
481         exec_cmd_no_error(
482             node, command, timeout=120, sudo=True, message=message
483         )
484
485     @staticmethod
486     def pci_vf_driver_unbind(node, pf_pci_addr, vf_id):
487         """Unbind Virtual Function from driver on node.
488
489         :param node: DUT node.
490         :param pf_pci_addr: PCI device address.
491         :param vf_id: Virtual Function ID.
492         :type node: dict
493         :type pf_pci_addr: str
494         :type vf_id: int
495         :raises RuntimeError: If Virtual Function unbind failed.
496         """
497         vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
498         pf_pci = pf_pci_addr.replace(u":", r"\:")
499         vf_path = f"/sys/bus/pci/devices/{pf_pci}/virtfn{vf_id}"
500
501         command = f"sh -c \"echo {vf_pci_addr} | tee {vf_path}/driver/unbind\""
502         message = f"Failed to unbind VF {vf_pci_addr} on {node[u'host']}"
503
504         exec_cmd_no_error(
505             node, command, timeout=120, sudo=True, message=message
506         )
507
508     @staticmethod
509     def pci_vf_driver_bind(node, pf_pci_addr, vf_id, driver):
510         """Bind Virtual Function to driver on node.
511
512         :param node: DUT node.
513         :param pf_pci_addr: PCI device address.
514         :param vf_id: Virtual Function ID.
515         :param driver: Driver to bind.
516         :type node: dict
517         :type pf_pci_addr: str
518         :type vf_id: int
519         :type driver: str
520         :raises RuntimeError: If PCI device bind failed.
521         """
522         vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
523         pf_pci = pf_pci_addr.replace(u":", r'\:')
524         vf_path = f"/sys/bus/pci/devices/{pf_pci}/virtfn{vf_id}"
525
526         message = f"Failed to bind VF {vf_pci_addr} to {driver} " \
527             f"on {node[u'host']}"
528         command = f"sh -c \"echo {driver} | tee {vf_path}/driver_override\""
529
530         exec_cmd_no_error(
531             node, command, timeout=120, sudo=True, message=message
532         )
533
534         command = f"sh -c \"echo {vf_pci_addr} | " \
535             f"tee /sys/bus/pci/drivers/{driver}/bind\""
536
537         exec_cmd_no_error(
538             node, command, timeout=120, sudo=True, message=message
539         )
540
541         command = f"sh -c \"echo  | tee {vf_path}/driver_override\""
542
543         exec_cmd_no_error(
544             node, command, timeout=120, sudo=True, message=message
545         )
546
547     @staticmethod
548     def get_pci_dev_driver(node, pci_addr):
549         """Get current PCI device driver on node.
550
551         :param node: DUT node.
552         :param pci_addr: PCI device address.
553         :type node: dict
554         :type pci_addr: str
555         :returns: Driver or None
556         :raises RuntimeError: If it is not possible to get the interface driver
557             information from the node.
558         """
559         driver_path = f"/sys/bus/pci/devices/{pci_addr}/driver"
560         cmd = f"test -d {driver_path}"
561         ret_code, ret_val, _ = exec_cmd(node, cmd)
562         if int(ret_code):
563             # the directory doesn't exist which means the device is not bound
564             # to any driver
565             return None
566         cmd = f"basename $(readlink -f {driver_path})"
567         ret_val, _ = exec_cmd_no_error(node, cmd)
568         return ret_val.strip()
569
570     @staticmethod
571     def verify_kernel_module(node, module, force_load=False):
572         """Verify if kernel module is loaded on node. If parameter force
573         load is set to True, then try to load the modules.
574
575         :param node: Node.
576         :param module: Module to verify.
577         :param force_load: If True then try to load module.
578         :type node: dict
579         :type module: str
580         :type force_load: bool
581         :raises RuntimeError: If module is not loaded or failed to load.
582         """
583         command = f"grep -w {module} /proc/modules"
584         message = f"Kernel module {module} is not loaded " \
585             f"on host {node[u'host']}"
586
587         try:
588             exec_cmd_no_error(
589                 node, command, timeout=30, sudo=False, message=message
590             )
591         except RuntimeError:
592             if force_load:
593                 # Module is not loaded and we want to load it
594                 DUTSetup.load_kernel_module(node, module)
595             else:
596                 raise
597
598     @staticmethod
599     def verify_kernel_module_on_all_duts(nodes, module, force_load=False):
600         """Verify if kernel module is loaded on all DUTs. If parameter force
601         load is set to True, then try to load the modules.
602
603         :param nodes: DUT nodes.
604         :param module: Module to verify.
605         :param force_load: If True then try to load module.
606         :type nodes: dict
607         :type module: str
608         :type force_load: bool
609         """
610         for node in nodes.values():
611             if node[u"type"] == NodeType.DUT:
612                 DUTSetup.verify_kernel_module(node, module, force_load)
613
614     @staticmethod
615     def verify_uio_driver_on_all_duts(nodes):
616         """Verify if uio driver kernel module is loaded on all DUTs. If module
617         is not present it will try to load it.
618
619         :param nodes: DUT nodes.
620         :type nodes: dict
621         """
622         for node in nodes.values():
623             if node[u"type"] == NodeType.DUT:
624                 uio_driver = Topology.get_uio_driver(node)
625                 DUTSetup.verify_kernel_module(node, uio_driver, force_load=True)
626
627     @staticmethod
628     def load_kernel_module(node, module):
629         """Load kernel module on node.
630
631         :param node: DUT node.
632         :param module: Module to load.
633         :type node: dict
634         :type module: str
635         :returns: nothing
636         :raises RuntimeError: If loading failed.
637         """
638         command = f"modprobe {module}"
639         message = f"Failed to load {module} on host {node[u'host']}"
640
641         exec_cmd_no_error(node, command, timeout=30, sudo=True, message=message)
642
643     @staticmethod
644     def install_vpp_on_all_duts(nodes, vpp_pkg_dir):
645         """Install VPP on all DUT nodes. Start the VPP service in case of
646         systemd is not available or does not support autostart.
647
648         :param nodes: Nodes in the topology.
649         :param vpp_pkg_dir: Path to directory where VPP packages are stored.
650         :type nodes: dict
651         :type vpp_pkg_dir: str
652         :raises RuntimeError: If failed to remove or install VPP.
653         """
654         for node in nodes.values():
655             message = f"Failed to install VPP on host {node[u'host']}!"
656             if node[u"type"] == NodeType.DUT:
657                 command = u"ln -s /dev/null /etc/sysctl.d/80-vpp.conf || true"
658                 exec_cmd_no_error(node, command, sudo=True)
659
660                 command = u". /etc/lsb-release; echo \"${DISTRIB_ID}\""
661                 stdout, _ = exec_cmd_no_error(node, command)
662
663                 if stdout.strip() == u"Ubuntu":
664                     exec_cmd_no_error(
665                         node, u"apt-get purge -y '*vpp*' || true",
666                         timeout=120, sudo=True
667                     )
668                     # workaround to avoid installation of vpp-api-python
669                     exec_cmd_no_error(
670                         node, f"rm -f {vpp_pkg_dir}vpp-api-python.deb",
671                         timeout=120, sudo=True
672                     )
673                     exec_cmd_no_error(
674                         node, f"dpkg -i --force-all {vpp_pkg_dir}*.deb",
675                         timeout=120, sudo=True, message=message
676                     )
677                     exec_cmd_no_error(node, u"dpkg -l | grep vpp", sudo=True)
678                     if DUTSetup.running_in_container(node):
679                         DUTSetup.restart_service(node, Constants.VPP_UNIT)
680                 else:
681                     exec_cmd_no_error(
682                         node, u"yum -y remove '*vpp*' || true",
683                         timeout=120, sudo=True
684                     )
685                     # workaround to avoid installation of vpp-api-python
686                     exec_cmd_no_error(
687                         node, f"rm -f {vpp_pkg_dir}vpp-api-python.rpm",
688                         timeout=120, sudo=True
689                     )
690                     exec_cmd_no_error(
691                         node, f"rpm -ivh {vpp_pkg_dir}*.rpm",
692                         timeout=120, sudo=True, message=message
693                     )
694                     exec_cmd_no_error(node, u"rpm -qai '*vpp*'", sudo=True)
695                     DUTSetup.restart_service(node, Constants.VPP_UNIT)
696
697     @staticmethod
698     def running_in_container(node):
699         """This method tests if topology node is running inside container.
700
701         :param node: Topology node.
702         :type node: dict
703         :returns: True if running in docker container, false if not or failed
704             to detect.
705         :rtype: bool
706         """
707         command = u"fgrep docker /proc/1/cgroup"
708         message = u"Failed to get cgroup settings."
709         try:
710             exec_cmd_no_error(
711                 node, command, timeout=30, sudo=False, message=message
712             )
713         except RuntimeError:
714             return False
715         return True
716
717     @staticmethod
718     def get_docker_mergeddir(node, uuid):
719         """Get Docker overlay for MergedDir diff.
720
721         :param node: DUT node.
722         :param uuid: Docker UUID.
723         :type node: dict
724         :type uuid: str
725         :returns: Docker container MergedDir.
726         :rtype: str
727         :raises RuntimeError: If getting output failed.
728         """
729         command = f"docker inspect " \
730             f"--format='{{{{.GraphDriver.Data.MergedDir}}}}' {uuid}"
731         message = f"Failed to get directory of {uuid} on host {node[u'host']}"
732
733         stdout, _ = exec_cmd_no_error(node, command, sudo=True, message=message)
734         return stdout.strip()
735
736     @staticmethod
737     def get_hugepages_info(node, hugesize=None):
738         """Get number of huge pages in system.
739
740         :param node: Node in the topology.
741         :param hugesize: Size of hugepages. Default system huge size if None.
742         :type node: dict
743         :type hugesize: int
744         :returns: Number of huge pages in system.
745         :rtype: dict
746         :raises RuntimeError: If reading failed.
747         """
748         if not hugesize:
749             hugesize = "$(grep Hugepagesize /proc/meminfo | awk '{ print $2 }')"
750         command = f"cat /sys/kernel/mm/hugepages/hugepages-{hugesize}kB/*"
751         stdout, _ = exec_cmd_no_error(node, command)
752         try:
753             line = stdout.splitlines()
754             return {
755                 "free_hugepages": int(line[0]),
756                 "nr_hugepages": int(line[1]),
757                 "nr_hugepages_mempolicy": int(line[2]),
758                 "nr_overcommit_hugepages": int(line[3]),
759                 "resv_hugepages": int(line[4]),
760                 "surplus_hugepages": int(line[5])
761             }
762         except ValueError:
763             logger.trace(u"Reading huge pages information failed!")
764
765     @staticmethod
766     def check_huge_page(
767             node, huge_mnt, mem_size, hugesize=2048, allocate=False):
768         """Check if there is enough HugePages in system. If allocate is set to
769         true, try to allocate more HugePages.
770
771         :param node: Node in the topology.
772         :param huge_mnt: HugePage mount point.
773         :param mem_size: Reqeusted memory in MB.
774         :param hugesize: HugePage size in KB.
775         :param allocate: Whether to allocate more memory if not enough.
776         :type node: dict
777         :type huge_mnt: str
778         :type mem_size: int
779         :type hugesize: int
780         :type allocate: bool
781         :raises RuntimeError: Mounting hugetlbfs failed or not enough HugePages
782             or increasing map count failed.
783         """
784         # Get huge pages information.
785         hugepages = DUTSetup.get_hugepages_info(node, hugesize=hugesize)
786
787         # Check if hugepages requested are available on node.
788         if hugepages[u"nr_overcommit_hugepages"]:
789             # If overcommit is used, we need to know how many additional pages
790             # we can allocate
791             huge_available = hugepages[u"nr_overcommit_hugepages"] - \
792                 hugepages[u"surplus_hugepages"]
793         else:
794             # Fallbacking to free_hugepages which were used before to detect.
795             huge_available = hugepages[u"free_hugepages"]
796
797         if ((mem_size * 1024) // hugesize) > huge_available:
798             # If we want to allocate hugepage dynamically.
799             if allocate:
800                 huge_needed = ((mem_size * 1024) // hugesize) - huge_available
801                 huge_to_allocate = huge_needed + hugepages[u"nr_hugepages"]
802                 max_map_count = huge_to_allocate * 4
803                 # Check if huge pages mount point exist.
804                 try:
805                     exec_cmd_no_error(node, u"fgrep 'hugetlbfs' /proc/mounts")
806                 except RuntimeError:
807                     exec_cmd_no_error(node, f"mkdir -p {huge_mnt}", sudo=True)
808                     exec_cmd_no_error(
809                         node,
810                         f"mount -t hugetlbfs -o pagesize={hugesize}k none "
811                         f"{huge_mnt}",
812                         sudo=True)
813                 # Increase maximum number of memory map areas for process.
814                 exec_cmd_no_error(
815                     node,
816                     f"echo \"{max_map_count}\" | "
817                     f"sudo tee /proc/sys/vm/max_map_count",
818                     message=f"Increase map count failed on {node[u'host']}!"
819                 )
820                 # Increase hugepage count.
821                 exec_cmd_no_error(
822                     node,
823                     f"echo \"{huge_to_allocate}\" | "
824                     f"sudo tee /proc/sys/vm/nr_hugepages",
825                     message=f"Mount huge pages failed on {node[u'host']}!"
826                 )
827             # If we do not want to allocate dynamically end with error.
828             else:
829                 raise RuntimeError(
830                     f"Not enough availablehuge pages: {huge_available}!"
831                 )