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