fix(Pylint): Small fixes
[csit.git] / resources / libraries / python / DUTSetup.py
1 # Copyright (c) 2021 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         cmd = f"test -f /sys/bus/pci/devices/{pf_pci_addr}/sriov_numvfs"
404         sriov_unsupported, _, _ = exec_cmd(node, cmd)
405         # if sriov_numvfs doesn't exist, then sriov_unsupported != 0
406         if int(sriov_unsupported):
407             if numvfs == 0:
408                 # sriov is not supported and we want 0 VFs
409                 # no need to do anything
410                 return
411
412             raise RuntimeError(
413                 f"Can't configure {numvfs} VFs on {pf_pci_addr} device "
414                 f"on {node[u'host']} since it doesn't support SR-IOV."
415             )
416
417         pci = pf_pci_addr.replace(u":", r"\:")
418         command = f"sh -c \"echo {numvfs} | " \
419             f"tee /sys/bus/pci/devices/{pci}/sriov_numvfs\""
420         message = f"Failed to create {numvfs} VFs on {pf_pci_addr} device " \
421             f"on {node[u'host']}"
422
423         exec_cmd_no_error(
424             node, command, timeout=120, sudo=True, message=message
425         )
426
427     @staticmethod
428     def pci_driver_unbind(node, pci_addr):
429         """Unbind PCI device from current driver on node.
430
431         :param node: DUT node.
432         :param pci_addr: PCI device address.
433         :type node: dict
434         :type pci_addr: str
435         :raises RuntimeError: If PCI device unbind failed.
436         """
437         pci = pci_addr.replace(u":", r"\:")
438         command = f"sh -c \"echo {pci_addr} | " \
439             f"tee /sys/bus/pci/devices/{pci}/driver/unbind\""
440         message = f"Failed to unbind PCI device {pci_addr} on {node[u'host']}"
441
442         exec_cmd_no_error(
443             node, command, timeout=120, sudo=True, message=message
444         )
445
446     @staticmethod
447     def pci_driver_unbind_list(node, *pci_addrs):
448         """Unbind PCI devices from current driver on node.
449
450         :param node: DUT node.
451         :param pci_addrs: PCI device addresses.
452         :type node: dict
453         :type pci_addrs: list
454         """
455         for pci_addr in pci_addrs:
456             DUTSetup.pci_driver_unbind(node, pci_addr)
457
458     @staticmethod
459     def pci_driver_bind(node, pci_addr, driver):
460         """Bind PCI device to driver on node.
461
462         :param node: DUT node.
463         :param pci_addr: PCI device address.
464         :param driver: Driver to bind.
465         :type node: dict
466         :type pci_addr: str
467         :type driver: str
468         :raises RuntimeError: If PCI device bind failed.
469         """
470         message = f"Failed to bind PCI device {pci_addr} to {driver} " \
471             f"on host {node[u'host']}"
472         pci = pci_addr.replace(u":", r"\:")
473         command = f"sh -c \"echo {driver} | " \
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         command = f"sh -c \"echo {pci_addr} | " \
481             f"tee /sys/bus/pci/drivers/{driver}/bind\""
482
483         exec_cmd_no_error(
484             node, command, timeout=120, sudo=True, message=message
485         )
486
487         command = f"sh -c \"echo  | " \
488             f"tee /sys/bus/pci/devices/{pci}/driver_override\""
489
490         exec_cmd_no_error(
491             node, command, timeout=120, sudo=True, message=message
492         )
493
494     @staticmethod
495     def pci_vf_driver_unbind(node, pf_pci_addr, vf_id):
496         """Unbind Virtual Function from driver on node.
497
498         :param node: DUT node.
499         :param pf_pci_addr: PCI device address.
500         :param vf_id: Virtual Function ID.
501         :type node: dict
502         :type pf_pci_addr: str
503         :type vf_id: int
504         :raises RuntimeError: If Virtual Function unbind failed.
505         """
506         vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
507         pf_pci = pf_pci_addr.replace(u":", r"\:")
508         vf_path = f"/sys/bus/pci/devices/{pf_pci}/virtfn{vf_id}"
509
510         command = f"sh -c \"echo {vf_pci_addr} | tee {vf_path}/driver/unbind\""
511         message = f"Failed to unbind VF {vf_pci_addr} on {node[u'host']}"
512
513         exec_cmd_no_error(
514             node, command, timeout=120, sudo=True, message=message
515         )
516
517     @staticmethod
518     def pci_vf_driver_bind(node, pf_pci_addr, vf_id, driver):
519         """Bind Virtual Function to driver on node.
520
521         :param node: DUT node.
522         :param pf_pci_addr: PCI device address.
523         :param vf_id: Virtual Function ID.
524         :param driver: Driver to bind.
525         :type node: dict
526         :type pf_pci_addr: str
527         :type vf_id: int
528         :type driver: str
529         :raises RuntimeError: If PCI device bind failed.
530         """
531         vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
532         pf_pci = pf_pci_addr.replace(u":", r'\:')
533         vf_path = f"/sys/bus/pci/devices/{pf_pci}/virtfn{vf_id}"
534
535         message = f"Failed to bind VF {vf_pci_addr} to {driver} " \
536             f"on {node[u'host']}"
537         command = f"sh -c \"echo {driver} | tee {vf_path}/driver_override\""
538
539         exec_cmd_no_error(
540             node, command, timeout=120, sudo=True, message=message
541         )
542
543         command = f"sh -c \"echo {vf_pci_addr} | " \
544             f"tee /sys/bus/pci/drivers/{driver}/bind\""
545
546         exec_cmd_no_error(
547             node, command, timeout=120, sudo=True, message=message
548         )
549
550         command = f"sh -c \"echo  | tee {vf_path}/driver_override\""
551
552         exec_cmd_no_error(
553             node, command, timeout=120, sudo=True, message=message
554         )
555
556     @staticmethod
557     def get_pci_dev_driver(node, pci_addr):
558         """Get current PCI device driver on node.
559
560         :param node: DUT node.
561         :param pci_addr: PCI device address.
562         :type node: dict
563         :type pci_addr: str
564         :returns: Driver or None
565         :raises RuntimeError: If it is not possible to get the interface driver
566             information from the node.
567         """
568         driver_path = f"/sys/bus/pci/devices/{pci_addr}/driver"
569         cmd = f"test -d {driver_path}"
570         ret_code, ret_val, _ = exec_cmd(node, cmd)
571         if int(ret_code):
572             # the directory doesn't exist which means the device is not bound
573             # to any driver
574             return None
575         cmd = f"basename $(readlink -f {driver_path})"
576         ret_val, _ = exec_cmd_no_error(node, cmd)
577         return ret_val.strip()
578
579     @staticmethod
580     def verify_kernel_module(node, module, force_load=False):
581         """Verify if kernel module is loaded on node. If parameter force
582         load is set to True, then try to load the modules.
583
584         :param node: Node.
585         :param module: Module to verify.
586         :param force_load: If True then try to load module.
587         :type node: dict
588         :type module: str
589         :type force_load: bool
590         :raises RuntimeError: If module is not loaded or failed to load.
591         """
592         command = f"grep -w {module} /proc/modules"
593         message = f"Kernel module {module} is not loaded " \
594             f"on host {node[u'host']}"
595
596         try:
597             exec_cmd_no_error(
598                 node, command, timeout=30, sudo=False, message=message
599             )
600         except RuntimeError:
601             if force_load:
602                 # Module is not loaded and we want to load it
603                 DUTSetup.load_kernel_module(node, module)
604             else:
605                 raise
606
607     @staticmethod
608     def verify_kernel_module_on_all_duts(nodes, module, force_load=False):
609         """Verify if kernel module is loaded on all DUTs. If parameter force
610         load is set to True, then try to load the modules.
611
612         :param nodes: DUT nodes.
613         :param module: Module to verify.
614         :param force_load: If True then try to load module.
615         :type nodes: dict
616         :type module: str
617         :type force_load: bool
618         """
619         for node in nodes.values():
620             if node[u"type"] == NodeType.DUT:
621                 DUTSetup.verify_kernel_module(node, module, force_load)
622
623     @staticmethod
624     def verify_uio_driver_on_all_duts(nodes):
625         """Verify if uio driver kernel module is loaded on all DUTs. If module
626         is not present it will try to load it.
627
628         :param nodes: DUT nodes.
629         :type nodes: dict
630         """
631         for node in nodes.values():
632             if node[u"type"] == NodeType.DUT:
633                 uio_driver = Topology.get_uio_driver(node)
634                 DUTSetup.verify_kernel_module(node, uio_driver, force_load=True)
635
636     @staticmethod
637     def load_kernel_module(node, module):
638         """Load kernel module on node.
639
640         :param node: DUT node.
641         :param module: Module to load.
642         :type node: dict
643         :type module: str
644         :returns: nothing
645         :raises RuntimeError: If loading failed.
646         """
647         command = f"modprobe {module}"
648         message = f"Failed to load {module} on host {node[u'host']}"
649
650         exec_cmd_no_error(node, command, timeout=30, sudo=True, message=message)
651
652     @staticmethod
653     def install_vpp_on_all_duts(nodes, vpp_pkg_dir):
654         """Install VPP on all DUT nodes. Start the VPP service in case of
655         systemd is not available or does not support autostart.
656
657         :param nodes: Nodes in the topology.
658         :param vpp_pkg_dir: Path to directory where VPP packages are stored.
659         :type nodes: dict
660         :type vpp_pkg_dir: str
661         :raises RuntimeError: If failed to remove or install VPP.
662         """
663         for node in nodes.values():
664             message = f"Failed to install VPP on host {node[u'host']}!"
665             if node[u"type"] == NodeType.DUT:
666                 command = u"ln -s /dev/null /etc/sysctl.d/80-vpp.conf || true"
667                 exec_cmd_no_error(node, command, sudo=True)
668
669                 command = u". /etc/lsb-release; echo \"${DISTRIB_ID}\""
670                 stdout, _ = exec_cmd_no_error(node, command)
671
672                 if stdout.strip() == u"Ubuntu":
673                     exec_cmd_no_error(
674                         node, u"apt-get purge -y '*vpp*' || true",
675                         timeout=120, sudo=True
676                     )
677                     # workaround to avoid installation of vpp-api-python
678                     exec_cmd_no_error(
679                         node, u"rm -f {vpp_pkg_dir}vpp-api-python.deb",
680                         timeout=120, sudo=True
681                     )
682                     exec_cmd_no_error(
683                         node, f"dpkg -i --force-all {vpp_pkg_dir}*.deb",
684                         timeout=120, sudo=True, message=message
685                     )
686                     exec_cmd_no_error(node, u"dpkg -l | grep vpp", sudo=True)
687                     if DUTSetup.running_in_container(node):
688                         DUTSetup.restart_service(node, Constants.VPP_UNIT)
689                 else:
690                     exec_cmd_no_error(
691                         node, u"yum -y remove '*vpp*' || true",
692                         timeout=120, sudo=True
693                     )
694                     # workaround to avoid installation of vpp-api-python
695                     exec_cmd_no_error(
696                         node, u"rm -f {vpp_pkg_dir}vpp-api-python.rpm",
697                         timeout=120, sudo=True
698                     )
699                     exec_cmd_no_error(
700                         node, f"rpm -ivh {vpp_pkg_dir}*.rpm",
701                         timeout=120, sudo=True, message=message
702                     )
703                     exec_cmd_no_error(node, u"rpm -qai '*vpp*'", sudo=True)
704                     DUTSetup.restart_service(node, Constants.VPP_UNIT)
705
706     @staticmethod
707     def running_in_container(node):
708         """This method tests if topology node is running inside container.
709
710         :param node: Topology node.
711         :type node: dict
712         :returns: True if running in docker container, false if not or failed
713             to detect.
714         :rtype: bool
715         """
716         command = u"fgrep docker /proc/1/cgroup"
717         message = u"Failed to get cgroup settings."
718         try:
719             exec_cmd_no_error(
720                 node, command, timeout=30, sudo=False, message=message
721             )
722         except RuntimeError:
723             return False
724         return True
725
726     @staticmethod
727     def get_docker_mergeddir(node, uuid):
728         """Get Docker overlay for MergedDir diff.
729
730         :param node: DUT node.
731         :param uuid: Docker UUID.
732         :type node: dict
733         :type uuid: str
734         :returns: Docker container MergedDir.
735         :rtype: str
736         :raises RuntimeError: If getting output failed.
737         """
738         command = f"docker inspect " \
739             f"--format='{{{{.GraphDriver.Data.MergedDir}}}}' {uuid}"
740         message = f"Failed to get directory of {uuid} on host {node[u'host']}"
741
742         stdout, _ = exec_cmd_no_error(node, command, sudo=True, message=message)
743         return stdout.strip()
744
745     @staticmethod
746     def get_hugepages_info(node, hugesize=None):
747         """Get number of huge pages in system.
748
749         :param node: Node in the topology.
750         :param hugesize: Size of hugepages. Default system huge size if None.
751         :type node: dict
752         :type hugesize: int
753         :returns: Number of huge pages in system.
754         :rtype: dict
755         :raises RuntimeError: If reading failed.
756         """
757         if not hugesize:
758             hugesize = "$(grep Hugepagesize /proc/meminfo | awk '{ print $2 }')"
759         command = f"cat /sys/kernel/mm/hugepages/hugepages-{hugesize}kB/*"
760         stdout, _ = exec_cmd_no_error(node, command)
761         try:
762             line = stdout.splitlines()
763             return {
764                 "free_hugepages": int(line[0]),
765                 "nr_hugepages": int(line[1]),
766                 "nr_hugepages_mempolicy": int(line[2]),
767                 "nr_overcommit_hugepages": int(line[3]),
768                 "resv_hugepages": int(line[4]),
769                 "surplus_hugepages": int(line[5])
770             }
771         except ValueError:
772             logger.trace(u"Reading huge pages information failed!")
773
774     @staticmethod
775     def check_huge_page(
776             node, huge_mnt, mem_size, hugesize=2048, allocate=False):
777         """Check if there is enough HugePages in system. If allocate is set to
778         true, try to allocate more HugePages.
779
780         :param node: Node in the topology.
781         :param huge_mnt: HugePage mount point.
782         :param mem_size: Reqeusted memory in MB.
783         :param hugesize: HugePage size in KB.
784         :param allocate: Whether to allocate more memory if not enough.
785         :type node: dict
786         :type huge_mnt: str
787         :type mem_size: int
788         :type hugesize: int
789         :type allocate: bool
790         :raises RuntimeError: Mounting hugetlbfs failed or not enough HugePages
791             or increasing map count failed.
792         """
793         # Get huge pages information.
794         hugepages = DUTSetup.get_hugepages_info(node, hugesize=hugesize)
795
796         # Check if hugepages requested are available on node.
797         if hugepages[u"nr_overcommit_hugepages"]:
798             # If overcommit is used, we need to know how many additional pages
799             # we can allocate
800             huge_available = hugepages[u"nr_overcommit_hugepages"] - \
801                 hugepages[u"surplus_hugepages"]
802         else:
803             # Fallbacking to free_hugepages which were used before to detect.
804             huge_available = hugepages[u"free_hugepages"]
805
806         if ((mem_size * 1024) // hugesize) > huge_available:
807             # If we want to allocate hugepage dynamically.
808             if allocate:
809                 huge_needed = ((mem_size * 1024) // hugesize) - huge_available
810                 huge_to_allocate = huge_needed + hugepages[u"nr_hugepages"]
811                 max_map_count = huge_to_allocate * 4
812                 # Check if huge pages mount point exist.
813                 try:
814                     exec_cmd_no_error(node, u"fgrep 'hugetlbfs' /proc/mounts")
815                 except RuntimeError:
816                     exec_cmd_no_error(node, f"mkdir -p {huge_mnt}", sudo=True)
817                     exec_cmd_no_error(
818                         node,
819                         f"mount -t hugetlbfs -o pagesize={hugesize}k none "
820                         f"{huge_mnt}",
821                         sudo=True)
822                 # Increase maximum number of memory map areas for process.
823                 exec_cmd_no_error(
824                     node,
825                     f"echo \"{max_map_count}\" | "
826                     f"sudo tee /proc/sys/vm/max_map_count",
827                     message=f"Increase map count failed on {node[u'host']}!"
828                 )
829                 # Increase hugepage count.
830                 exec_cmd_no_error(
831                     node,
832                     f"echo \"{huge_to_allocate}\" | "
833                     f"sudo tee /proc/sys/vm/nr_hugepages",
834                     message=f"Mount huge pages failed on {node[u'host']}!"
835                 )
836             # If we do not want to allocate dynamically end with error.
837             else:
838                 raise RuntimeError(
839                     f"Not enough availablehuge pages: {huge_available}!"
840                 )