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