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