504022381e4696355d17a07f41e3b9b56c2e585b
[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             else:
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         else:
576             cmd = f"basename $(readlink -f {driver_path})"
577             ret_val, _ = exec_cmd_no_error(node, cmd)
578             return ret_val.strip()
579
580     @staticmethod
581     def verify_kernel_module(node, module, force_load=False):
582         """Verify if kernel module is loaded on node. If parameter force
583         load is set to True, then try to load the modules.
584
585         :param node: Node.
586         :param module: Module to verify.
587         :param force_load: If True then try to load module.
588         :type node: dict
589         :type module: str
590         :type force_load: bool
591         :raises RuntimeError: If module is not loaded or failed to load.
592         """
593         command = f"grep -w {module} /proc/modules"
594         message = f"Kernel module {module} is not loaded " \
595             f"on host {node[u'host']}"
596
597         try:
598             exec_cmd_no_error(
599                 node, command, timeout=30, sudo=False, message=message
600             )
601         except RuntimeError:
602             if force_load:
603                 # Module is not loaded and we want to load it
604                 DUTSetup.load_kernel_module(node, module)
605             else:
606                 raise
607
608     @staticmethod
609     def verify_kernel_module_on_all_duts(nodes, module, force_load=False):
610         """Verify if kernel module is loaded on all DUTs. If parameter force
611         load is set to True, then try to load the modules.
612
613         :param nodes: DUT nodes.
614         :param module: Module to verify.
615         :param force_load: If True then try to load module.
616         :type nodes: dict
617         :type module: str
618         :type force_load: bool
619         """
620         for node in nodes.values():
621             if node[u"type"] == NodeType.DUT:
622                 DUTSetup.verify_kernel_module(node, module, force_load)
623
624     @staticmethod
625     def verify_uio_driver_on_all_duts(nodes):
626         """Verify if uio driver kernel module is loaded on all DUTs. If module
627         is not present it will try to load it.
628
629         :param nodes: DUT nodes.
630         :type nodes: dict
631         """
632         for node in nodes.values():
633             if node[u"type"] == NodeType.DUT:
634                 uio_driver = Topology.get_uio_driver(node)
635                 DUTSetup.verify_kernel_module(node, uio_driver, force_load=True)
636
637     @staticmethod
638     def load_kernel_module(node, module):
639         """Load kernel module on node.
640
641         :param node: DUT node.
642         :param module: Module to load.
643         :type node: dict
644         :type module: str
645         :returns: nothing
646         :raises RuntimeError: If loading failed.
647         """
648         command = f"modprobe {module}"
649         message = f"Failed to load {module} on host {node[u'host']}"
650
651         exec_cmd_no_error(node, command, timeout=30, sudo=True, message=message)
652
653     @staticmethod
654     def install_vpp_on_all_duts(nodes, vpp_pkg_dir):
655         """Install VPP on all DUT nodes. Start the VPP service in case of
656         systemd is not available or does not support autostart.
657
658         :param nodes: Nodes in the topology.
659         :param vpp_pkg_dir: Path to directory where VPP packages are stored.
660         :type nodes: dict
661         :type vpp_pkg_dir: str
662         :raises RuntimeError: If failed to remove or install VPP.
663         """
664         for node in nodes.values():
665             message = f"Failed to install VPP on host {node[u'host']}!"
666             if node[u"type"] == NodeType.DUT:
667                 command = u"ln -s /dev/null /etc/sysctl.d/80-vpp.conf || true"
668                 exec_cmd_no_error(node, command, sudo=True)
669
670                 command = u". /etc/lsb-release; echo \"${DISTRIB_ID}\""
671                 stdout, _ = exec_cmd_no_error(node, command)
672
673                 if stdout.strip() == u"Ubuntu":
674                     exec_cmd_no_error(
675                         node, u"apt-get purge -y '*vpp*' || true",
676                         timeout=120, sudo=True
677                     )
678                     # workaround to avoid installation of vpp-api-python
679                     exec_cmd_no_error(
680                         node, u"rm -f {vpp_pkg_dir}vpp-api-python.deb",
681                         timeout=120, sudo=True
682                     )
683                     exec_cmd_no_error(
684                         node, f"dpkg -i --force-all {vpp_pkg_dir}*.deb",
685                         timeout=120, sudo=True, message=message
686                     )
687                     exec_cmd_no_error(node, u"dpkg -l | grep vpp", sudo=True)
688                     if DUTSetup.running_in_container(node):
689                         DUTSetup.restart_service(node, Constants.VPP_UNIT)
690                 else:
691                     exec_cmd_no_error(
692                         node, u"yum -y remove '*vpp*' || true",
693                         timeout=120, sudo=True
694                     )
695                     # workaround to avoid installation of vpp-api-python
696                     exec_cmd_no_error(
697                         node, u"rm -f {vpp_pkg_dir}vpp-api-python.rpm",
698                         timeout=120, sudo=True
699                     )
700                     exec_cmd_no_error(
701                         node, f"rpm -ivh {vpp_pkg_dir}*.rpm",
702                         timeout=120, sudo=True, message=message
703                     )
704                     exec_cmd_no_error(node, u"rpm -qai '*vpp*'", sudo=True)
705                     DUTSetup.restart_service(node, Constants.VPP_UNIT)
706
707     @staticmethod
708     def running_in_container(node):
709         """This method tests if topology node is running inside container.
710
711         :param node: Topology node.
712         :type node: dict
713         :returns: True if running in docker container, false if not or failed
714             to detect.
715         :rtype: bool
716         """
717         command = u"fgrep docker /proc/1/cgroup"
718         message = u"Failed to get cgroup settings."
719         try:
720             exec_cmd_no_error(
721                 node, command, timeout=30, sudo=False, message=message
722             )
723         except RuntimeError:
724             return False
725         return True
726
727     @staticmethod
728     def get_docker_mergeddir(node, uuid):
729         """Get Docker overlay for MergedDir diff.
730
731         :param node: DUT node.
732         :param uuid: Docker UUID.
733         :type node: dict
734         :type uuid: str
735         :returns: Docker container MergedDir.
736         :rtype: str
737         :raises RuntimeError: If getting output failed.
738         """
739         command = f"docker inspect " \
740             f"--format='{{{{.GraphDriver.Data.MergedDir}}}}' {uuid}"
741         message = f"Failed to get directory of {uuid} on host {node[u'host']}"
742
743         stdout, _ = exec_cmd_no_error(node, command, sudo=True, message=message)
744         return stdout.strip()
745
746     @staticmethod
747     def get_hugepages_info(node, hugesize=None):
748         """Get number of huge pages in system.
749
750         :param node: Node in the topology.
751         :param hugesize: Size of hugepages. Default system huge size if None.
752         :type node: dict
753         :type hugesize: int
754         :returns: Number of huge pages in system.
755         :rtype: dict
756         :raises RuntimeError: If reading failed.
757         """
758         if not hugesize:
759             hugesize = "$(grep Hugepagesize /proc/meminfo | awk '{ print $2 }')"
760         command = f"cat /sys/kernel/mm/hugepages/hugepages-{hugesize}kB/*"
761         stdout, _ = exec_cmd_no_error(node, command)
762         try:
763             line = stdout.splitlines()
764             return {
765                 "free_hugepages": int(line[0]),
766                 "nr_hugepages": int(line[1]),
767                 "nr_hugepages_mempolicy": int(line[2]),
768                 "nr_overcommit_hugepages": int(line[3]),
769                 "resv_hugepages": int(line[4]),
770                 "surplus_hugepages": int(line[5])
771             }
772         except ValueError:
773             logger.trace(u"Reading huge pages information failed!")
774
775     @staticmethod
776     def check_huge_page(
777             node, huge_mnt, mem_size, hugesize=2048, allocate=False):
778         """Check if there is enough HugePages in system. If allocate is set to
779         true, try to allocate more HugePages.
780
781         :param node: Node in the topology.
782         :param huge_mnt: HugePage mount point.
783         :param mem_size: Reqeusted memory in MB.
784         :param hugesize: HugePage size in KB.
785         :param allocate: Whether to allocate more memory if not enough.
786         :type node: dict
787         :type huge_mnt: str
788         :type mem_size: int
789         :type hugesize: int
790         :type allocate: bool
791         :raises RuntimeError: Mounting hugetlbfs failed or not enough HugePages
792             or increasing map count failed.
793         """
794         # Get huge pages information.
795         hugepages = DUTSetup.get_hugepages_info(node, hugesize=hugesize)
796
797         # Check if hugepages requested are available on node.
798         if hugepages[u"nr_overcommit_hugepages"]:
799             # If overcommit is used, we need to know how many additional pages
800             # we can allocate
801             huge_available = hugepages[u"nr_overcommit_hugepages"] - \
802                 hugepages[u"surplus_hugepages"]
803         else:
804             # Fallbacking to free_hugepages which were used before to detect.
805             huge_available = hugepages[u"free_hugepages"]
806
807         if ((mem_size * 1024) // hugesize) > huge_available:
808             # If we want to allocate hugepage dynamically.
809             if allocate:
810                 huge_needed = ((mem_size * 1024) // hugesize) - huge_available
811                 huge_to_allocate = huge_needed + hugepages[u"nr_hugepages"]
812                 max_map_count = huge_to_allocate * 4
813                 # Check if huge pages mount point exist.
814                 try:
815                     exec_cmd_no_error(node, u"fgrep 'hugetlbfs' /proc/mounts")
816                 except RuntimeError:
817                     exec_cmd_no_error(node, f"mkdir -p {huge_mnt}", sudo=True)
818                     exec_cmd_no_error(
819                         node,
820                         f"mount -t hugetlbfs -o pagesize={hugesize}k none "
821                         f"{huge_mnt}",
822                         sudo=True)
823                 # Increase maximum number of memory map areas for process.
824                 exec_cmd_no_error(
825                     node,
826                     f"echo \"{max_map_count}\" | "
827                     f"sudo tee /proc/sys/vm/max_map_count",
828                     message=f"Increase map count failed on {node[u'host']}!"
829                 )
830                 # Increase hugepage count.
831                 exec_cmd_no_error(
832                     node,
833                     f"echo \"{huge_to_allocate}\" | "
834                     f"sudo tee /proc/sys/vm/nr_hugepages",
835                     message=f"Mount huge pages failed on {node[u'host']}!"
836                 )
837             # If we do not want to allocate dynamically end with error.
838             else:
839                 raise RuntimeError(
840                     f"Not enough availablehuge pages: {huge_available}!"
841                 )