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