CSIT-1407 FIX vpp install after VPP changes
[csit.git] / resources / libraries / python / DUTSetup.py
1 # Copyright (c) 2018 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 robot.api import logger
17
18 from resources.libraries.python.constants import Constants
19 from resources.libraries.python.ssh import SSH, exec_cmd_no_error
20 from resources.libraries.python.topology import NodeType, Topology
21
22
23 class DUTSetup(object):
24     """Contains methods for setting up DUTs."""
25
26     @staticmethod
27     def get_service_logs(node, service):
28         """Get specific service unit logs from node.
29
30         :param node: Node in the topology.
31         :param service: Service unit name.
32         :type node: dict
33         :type service: str
34         """
35         if DUTSetup.running_in_container(node):
36             command = ('echo $(< /var/log/supervisord.log);'
37                        'echo $(< /tmp/*supervisor*.log)')
38         else:
39             command = ('journalctl --no-pager --unit={name} '
40                        '--since="$(echo `systemctl show -p '
41                        'ActiveEnterTimestamp {name}` | '
42                        'awk \'{{print $2 $3}}\')"'.
43                        format(name=service))
44         message = 'Node {host} failed to get logs from unit {name}'.\
45             format(host=node['host'], name=service)
46
47         exec_cmd_no_error(node, command, timeout=30, sudo=True,
48                           message=message)
49
50     @staticmethod
51     def get_service_logs_on_all_duts(nodes, service):
52         """Get specific service unit logs from all DUTs.
53
54         :param nodes: Nodes in the topology.
55         :param service: Service unit name.
56         :type nodes: dict
57         :type service: str
58         """
59         for node in nodes.values():
60             if node['type'] == NodeType.DUT:
61                 DUTSetup.get_service_logs(node, service)
62
63     @staticmethod
64     def start_service(node, service):
65         """Start up the named service on node.
66
67         :param node: Node in the topology.
68         :param service: Service unit name.
69         :type node: dict
70         :type service: str
71         """
72         if DUTSetup.running_in_container(node):
73             command = 'supervisorctl restart {name}'.format(name=service)
74         else:
75             command = 'service {name} restart'.format(name=service)
76         message = 'Node {host} failed to start service {name}'.\
77             format(host=node['host'], name=service)
78
79         exec_cmd_no_error(node, command, timeout=30, sudo=True, message=message)
80
81         DUTSetup.get_service_logs(node, service)
82
83     @staticmethod
84     def start_service_on_all_duts(nodes, service):
85         """Start up the named service on all DUTs.
86
87         :param node: Nodes in the topology.
88         :param service: Service unit name.
89         :type node: dict
90         :type service: str
91         """
92         for node in nodes.values():
93             if node['type'] == NodeType.DUT:
94                 DUTSetup.start_service(node, service)
95
96     @staticmethod
97     def stop_service(node, service):
98         """Stop 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 = 'supervisorctl stop {name}'.format(name=service)
107         else:
108             command = 'service {name} stop'.format(name=service)
109         message = 'Node {host} failed to stop service {name}'.\
110             format(host=node['host'], name=service)
111
112         exec_cmd_no_error(node, command, timeout=30, sudo=True, message=message)
113
114         DUTSetup.get_service_logs(node, service)
115
116     @staticmethod
117     def stop_service_on_all_duts(nodes, service):
118         """Stop the named service on all DUTs.
119
120         :param node: Nodes in the topology.
121         :param service: Service unit name.
122         :type node: dict
123         :type service: str
124         """
125         for node in nodes.values():
126             if node['type'] == NodeType.DUT:
127                 DUTSetup.stop_service(node, service)
128
129     @staticmethod
130     def setup_dut(node):
131         """Run script over SSH to setup the DUT node.
132
133         :param node: DUT node to set up.
134         :type node: dict
135
136         :raises Exception: If the DUT setup fails.
137         """
138         command = 'bash {0}/{1}/dut_setup.sh'.\
139             format(Constants.REMOTE_FW_DIR, Constants.RESOURCES_LIB_SH)
140         message = 'DUT test setup script failed at node {name}'.\
141             format(name=node['host'])
142
143         exec_cmd_no_error(node, command, timeout=120, sudo=True,
144                           message=message)
145
146     @staticmethod
147     def setup_all_duts(nodes):
148         """Run script over SSH to setup all DUT nodes.
149
150         :param nodes: Topology nodes.
151         :type nodes: dict
152         """
153         for node in nodes.values():
154             if node['type'] == NodeType.DUT:
155                 DUTSetup.setup_dut(node)
156
157     @staticmethod
158     def get_vpp_pid(node):
159         """Get PID of running VPP process.
160
161         :param node: DUT node.
162         :type node: dict
163         :returns: PID
164         :rtype: int
165         :raises RuntimeError: If it is not possible to get the PID.
166         """
167         ssh = SSH()
168         ssh.connect(node)
169
170         for i in range(3):
171             logger.trace('Try {}: Get VPP PID'.format(i))
172             ret_code, stdout, stderr = ssh.exec_command('pidof vpp')
173
174             if int(ret_code):
175                 raise RuntimeError('Not possible to get PID of VPP process '
176                                    'on node: {0}\n {1}'.
177                                    format(node['host'], stdout + stderr))
178
179             if len(stdout.splitlines()) == 1:
180                 return int(stdout)
181             elif not stdout.splitlines():
182                 logger.debug("No VPP PID found on node {0}".
183                              format(node['host']))
184                 continue
185             else:
186                 logger.debug("More then one VPP PID found on node {0}".
187                              format(node['host']))
188                 ret_list = list()
189                 for line in stdout.splitlines():
190                     ret_list.append(int(line))
191                 return ret_list
192
193         return None
194
195     @staticmethod
196     def get_vpp_pids(nodes):
197         """Get PID of running VPP process on all DUTs.
198
199         :param nodes: DUT nodes.
200         :type nodes: dict
201         :returns: PIDs
202         :rtype: dict
203         """
204         pids = dict()
205         for node in nodes.values():
206             if node['type'] == NodeType.DUT:
207                 pids[node['host']] = DUTSetup.get_vpp_pid(node)
208         return pids
209
210     @staticmethod
211     def crypto_device_verify(node, force_init=False, numvfs=32):
212         """Verify if Crypto QAT device virtual functions are initialized on all
213         DUTs. If parameter force initialization is set to True, then try to
214         initialize or remove VFs on QAT.
215
216         :param node: DUT node.
217         :param force_init: If True then try to initialize to specific value.
218         :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
219         :type node: dict
220         :type force_init: bool
221         :type numvfs: int
222         :returns: nothing
223         :raises RuntimeError: If QAT VFs are not created and force init is set
224                               to False.
225         """
226         pci_addr = Topology.get_cryptodev(node)
227         sriov_numvfs = DUTSetup.get_sriov_numvfs(node, pci_addr)
228
229         if sriov_numvfs != numvfs:
230             if force_init:
231                 # QAT is not initialized and we want to initialize with numvfs
232                 DUTSetup.crypto_device_init(node, numvfs)
233             else:
234                 raise RuntimeError('QAT device failed to create VFs on {host}'.
235                                    format(host=node['host']))
236
237     @staticmethod
238     def crypto_device_init(node, numvfs):
239         """Init Crypto QAT device virtual functions on DUT.
240
241         :param node: DUT node.
242         :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
243         :type node: dict
244         :type numvfs: int
245         :returns: nothing
246         :raises RuntimeError: If failed to stop VPP or QAT failed to initialize.
247         """
248         pci_addr = Topology.get_cryptodev(node)
249
250         # QAT device must be re-bound to kernel driver before initialization.
251         DUTSetup.verify_kernel_module(node, 'qat_dh895xcc', force_load=True)
252
253         # Stop VPP to prevent deadlock.
254         DUTSetup.stop_service(node, Constants.VPP_UNIT)
255
256         current_driver = DUTSetup.get_pci_dev_driver(
257             node, pci_addr.replace(':', r'\:'))
258         if current_driver is not None:
259             DUTSetup.pci_driver_unbind(node, pci_addr)
260
261         # Bind to kernel driver.
262         DUTSetup.pci_driver_bind(node, pci_addr, 'dh895xcc')
263
264         # Initialize QAT VFs.
265         if numvfs > 0:
266             DUTSetup.set_sriov_numvfs(node, pci_addr, numvfs)
267
268     @staticmethod
269     def get_virtfn_pci_addr(node, pf_pci_addr, vf_id):
270         """Get PCI address of Virtual Function.
271
272         :param node: DUT node.
273         :param pf_pci_addr: Physical Function PCI address.
274         :param vf_id: Virtual Function number.
275         :type node: dict
276         :type pf_pci_addr: str
277         :type vf_id: int
278         :returns: Virtual Function PCI address.
279         :rtype: int
280         :raises RuntimeError: If failed to get Virtual Function PCI address.
281         """
282         command = "sh -c "\
283             "'basename $(readlink /sys/bus/pci/devices/{pci}/virtfn{vf_id})'".\
284             format(pci=pf_pci_addr, vf_id=vf_id)
285         message = 'Failed to get virtual function PCI address.'
286
287         stdout, _ = exec_cmd_no_error(node, command, timeout=30, sudo=True,
288                                       message=message)
289
290         return stdout.strip()
291
292     @staticmethod
293     def get_sriov_numvfs(node, pf_pci_addr):
294         """Get number of SR-IOV VFs.
295
296         :param node: DUT node.
297         :param pf_pci_addr: Physical Function PCI device address.
298         :type node: dict
299         :type pf_pci_addr: str
300         :returns: Number of VFs.
301         :rtype: int
302         :raises RuntimeError: If PCI device is not SR-IOV capable.
303         """
304         command = 'cat /sys/bus/pci/devices/{pci}/sriov_numvfs'.\
305             format(pci=pf_pci_addr.replace(':', r'\:'))
306         message = 'PCI device {pci} is not a SR-IOV device.'.\
307             format(pci=pf_pci_addr)
308
309         for _ in range(3):
310             stdout, _ = exec_cmd_no_error(node, command, timeout=30, sudo=True,
311                                           message=message)
312             try:
313                 sriov_numvfs = int(stdout)
314             except ValueError:
315                 logger.trace('Reading sriov_numvfs info failed on {host}'.
316                              format(host=node['host']))
317             else:
318                 return sriov_numvfs
319
320     @staticmethod
321     def set_sriov_numvfs(node, pf_pci_addr, numvfs=0):
322         """Init or reset SR-IOV virtual functions by setting its number on PCI
323         device on DUT. Setting to zero removes all VFs.
324
325         :param node: DUT node.
326         :param pf_pci_addr: Physical Function PCI device address.
327         :param numvfs: Number of VFs to initialize, 0 - removes the VFs.
328         :type node: dict
329         :type pf_pci_addr: str
330         :type numvfs: int
331         :raises RuntimeError: Failed to create VFs on PCI.
332         """
333         command = "sh -c "\
334             "'echo {num} | tee /sys/bus/pci/devices/{pci}/sriov_numvfs'".\
335             format(num=numvfs, pci=pf_pci_addr.replace(':', r'\:'))
336         message = 'Failed to create {num} VFs on {pci} device on {host}'.\
337             format(num=numvfs, pci=pf_pci_addr, host=node['host'])
338
339         exec_cmd_no_error(node, command, timeout=120, sudo=True,
340                           message=message)
341
342     @staticmethod
343     def pci_driver_unbind(node, pci_addr):
344         """Unbind PCI device from current driver on node.
345
346         :param node: DUT node.
347         :param pci_addr: PCI device address.
348         :type node: dict
349         :type pci_addr: str
350         :raises RuntimeError: If PCI device unbind failed.
351         """
352         command = "sh -c "\
353             "'echo {pci} | tee /sys/bus/pci/devices/{pcie}/driver/unbind'".\
354             format(pci=pci_addr, pcie=pci_addr.replace(':', r'\:'))
355         message = 'Failed to unbind PCI device {pci} on {host}'.\
356             format(pci=pci_addr, host=node['host'])
357
358         exec_cmd_no_error(node, command, timeout=120, sudo=True,
359                           message=message)
360
361     @staticmethod
362     def pci_driver_bind(node, pci_addr, driver):
363         """Bind PCI device to driver on node.
364
365         :param node: DUT node.
366         :param pci_addr: PCI device address.
367         :param driver: Driver to bind.
368         :type node: dict
369         :type pci_addr: str
370         :type driver: str
371         :raises RuntimeError: If PCI device bind failed.
372         """
373         message = 'Failed to bind PCI device {pci} to {driver} on host {host}'.\
374             format(pci=pci_addr, driver=driver, host=node['host'])
375
376         command = "sh -c "\
377             "'echo {driver} | tee /sys/bus/pci/devices/{pci}/driver_override'".\
378             format(driver=driver, pci=pci_addr.replace(':', r'\:'))
379
380         exec_cmd_no_error(node, command, timeout=120, sudo=True,
381                           message=message)
382
383         command = "sh -c "\
384             "'echo {pci} | tee /sys/bus/pci/drivers/{driver}/bind'".\
385             format(pci=pci_addr, driver=driver)
386
387         exec_cmd_no_error(node, command, timeout=120, sudo=True,
388                           message=message)
389
390         command = "sh -c "\
391             "'echo  | tee /sys/bus/pci/devices/{pci}/driver_override'".\
392             format(pci=pci_addr.replace(':', r'\:'))
393
394         exec_cmd_no_error(node, command, timeout=120, sudo=True,
395                           message=message)
396
397     @staticmethod
398     def pci_vf_driver_unbind(node, pf_pci_addr, vf_id):
399         """Unbind Virtual Function from driver on node.
400
401         :param node: DUT node.
402         :param pf_pci_addr: PCI device address.
403         :param vf_id: Virtual Function ID.
404         :type node: dict
405         :type pf_pci_addr: str
406         :type vf_id: int
407         :raises RuntimeError: If Virtual Function unbind failed.
408         """
409         vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
410         vf_path = "/sys/bus/pci/devices/{pf_pci_addr}/virtfn{vf_id}".\
411             format(pf_pci_addr=pf_pci_addr.replace(':', r'\:'), vf_id=vf_id)
412
413         command = "sh -c "\
414             "'echo {vf_pci_addr} | tee {vf_path}/driver/unbind'".\
415             format(vf_pci_addr=vf_pci_addr, vf_path=vf_path)
416
417         message = 'Failed to unbind VF {vf_pci_addr} to on {host}'.\
418             format(vf_pci_addr=vf_pci_addr, host=node['host'])
419
420         exec_cmd_no_error(node, command, timeout=120, sudo=True,
421                           message=message)
422
423     @staticmethod
424     def pci_vf_driver_bind(node, pf_pci_addr, vf_id, driver):
425         """Bind Virtual Function to driver on node.
426
427         :param node: DUT node.
428         :param pf_pci_addr: PCI device address.
429         :param vf_id: Virtual Function ID.
430         :param driver: Driver to bind.
431         :type node: dict
432         :type pf_pci_addr: str
433         :type vf_id: int
434         :type driver: str
435         :raises RuntimeError: If PCI device bind failed.
436         """
437         vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
438         vf_path = "/sys/bus/pci/devices/{pf_pci_addr}/virtfn{vf_id}".\
439             format(pf_pci_addr=pf_pci_addr.replace(':', r'\:'), vf_id=vf_id)
440
441         message = 'Failed to bind VF {vf_pci_addr} to {driver} on {host}'.\
442             format(vf_pci_addr=vf_pci_addr, driver=driver, host=node['host'])
443
444         command = "sh -c "\
445             "'echo {driver} | tee {vf_path}/driver_override'".\
446             format(driver=driver, vf_path=vf_path)
447
448         exec_cmd_no_error(node, command, timeout=120, sudo=True,
449                           message=message)
450
451         command = "sh -c "\
452             "'echo {vf_pci_addr} | tee /sys/bus/pci/drivers/{driver}/bind'".\
453             format(vf_pci_addr=vf_pci_addr, driver=driver)
454
455         exec_cmd_no_error(node, command, timeout=120, sudo=True,
456                           message=message)
457
458         command = "sh -c "\
459             "'echo  | tee {vf_path}/driver_override'".\
460             format(vf_path=vf_path)
461
462         exec_cmd_no_error(node, command, timeout=120, sudo=True,
463                           message=message)
464
465     @staticmethod
466     def get_pci_dev_driver(node, pci_addr):
467         """Get current PCI device driver on node.
468
469         .. note::
470             # lspci -vmmks 0000:00:05.0
471             Slot:   00:05.0
472             Class:  Ethernet controller
473             Vendor: Red Hat, Inc
474             Device: Virtio network device
475             SVendor:        Red Hat, Inc
476             SDevice:        Device 0001
477             PhySlot:        5
478             Driver: virtio-pci
479
480         :param node: DUT node.
481         :param pci_addr: PCI device address.
482         :type node: dict
483         :type pci_addr: str
484         :returns: Driver or None
485         :raises RuntimeError: If PCI rescan or lspci command execution failed.
486         :raises RuntimeError: If it is not possible to get the interface driver
487             information from the node.
488         """
489         ssh = SSH()
490         ssh.connect(node)
491
492         for i in range(3):
493             logger.trace('Try number {0}: Get PCI device driver'.format(i))
494
495             cmd = 'lspci -vmmks {0}'.format(pci_addr)
496             ret_code, stdout, _ = ssh.exec_command(cmd)
497             if int(ret_code):
498                 raise RuntimeError("'{0}' failed on '{1}'"
499                                    .format(cmd, node['host']))
500
501             for line in stdout.splitlines():
502                 if not line:
503                     continue
504                 name = None
505                 value = None
506                 try:
507                     name, value = line.split("\t", 1)
508                 except ValueError:
509                     if name == "Driver:":
510                         return None
511                 if name == 'Driver:':
512                     return value
513
514             if i < 2:
515                 logger.trace('Driver for PCI device {} not found, executing '
516                              'pci rescan and retrying'.format(pci_addr))
517                 cmd = 'sh -c "echo 1 > /sys/bus/pci/rescan"'
518                 ret_code, _, _ = ssh.exec_command_sudo(cmd)
519                 if int(ret_code) != 0:
520                     raise RuntimeError("'{0}' failed on '{1}'"
521                                        .format(cmd, node['host']))
522
523         return None
524
525     @staticmethod
526     def verify_kernel_module(node, module, force_load=False):
527         """Verify if kernel module is loaded on node. If parameter force
528         load is set to True, then try to load the modules.
529
530         :param node: Node.
531         :param module: Module to verify.
532         :param force_load: If True then try to load module.
533         :type node: dict
534         :type module: str
535         :type force_load: bool
536         :raises RuntimeError: If module is not loaded or failed to load.
537         """
538         command = 'grep -w {module} /proc/modules'.format(module=module)
539         message = 'Kernel module {module} is not loaded on host {host}'.\
540             format(module=module, host=node['host'])
541
542         try:
543             exec_cmd_no_error(node, command, timeout=30, sudo=False,
544                               message=message)
545         except RuntimeError:
546             if force_load:
547                 # Module is not loaded and we want to load it
548                 DUTSetup.load_kernel_module(node, module)
549             else:
550                 raise
551
552     @staticmethod
553     def verify_kernel_module_on_all_duts(nodes, module, force_load=False):
554         """Verify if kernel module is loaded on all DUTs. If parameter force
555         load is set to True, then try to load the modules.
556
557         :param node: DUT nodes.
558         :param module: Module to verify.
559         :param force_load: If True then try to load module.
560         :type node: dict
561         :type module: str
562         :type force_load: bool
563         """
564         for node in nodes.values():
565             if node['type'] == NodeType.DUT:
566                 DUTSetup.verify_kernel_module(node, module, force_load)
567
568     @staticmethod
569     def verify_uio_driver_on_all_duts(nodes):
570         """Verify if uio driver kernel module is loaded on all DUTs. If module
571         is not present it will try to load it.
572
573         :param node: DUT nodes.
574         :type node: dict
575         """
576         for node in nodes.values():
577             if node['type'] == NodeType.DUT:
578                 uio_driver = Topology.get_uio_driver(node)
579                 DUTSetup.verify_kernel_module(node, uio_driver, force_load=True)
580
581     @staticmethod
582     def load_kernel_module(node, module):
583         """Load kernel module on node.
584
585         :param node: DUT node.
586         :param module: Module to load.
587         :type node: dict
588         :type module: str
589         :returns: nothing
590         :raises RuntimeError: If loading failed.
591         """
592         command = 'modprobe {module}'.format(module=module)
593         message = 'Failed to load {module} on host {host}'.\
594             format(module=module, host=node['host'])
595
596         exec_cmd_no_error(node, command, timeout=30, sudo=True, message=message)
597
598     @staticmethod
599     def install_vpp_on_all_duts(nodes, vpp_pkg_dir):
600         """Install VPP on all DUT nodes.
601
602         :param nodes: Nodes in the topology.
603         :param vpp_pkg_dir: Path to directory where VPP packages are stored.
604         :type nodes: dict
605         :type vpp_pkg_dir: str
606         :raises RuntimeError: If failed to remove or install VPP.
607         """
608         for node in nodes.values():
609             message='Failed to install VPP on {host}!'.format(host=node['host'])
610             if node['type'] == NodeType.DUT:
611                 command = '. /etc/lsb-release; echo "${DISTRIB_ID}"'
612                 stdout, _ = exec_cmd_no_error(node, command)
613
614                 if stdout.strip() == 'Ubuntu':
615                     exec_cmd_no_error(node, 'apt-get purge -y "*vpp*" || true',
616                                       timeout=120, sudo=True)
617                     exec_cmd_no_error(node, 'dpkg -i --force-all {dir}*.deb'.
618                         format(dir=vpp_pkg_dir), timeout=120, sudo=True,
619                         message=message)
620                     exec_cmd_no_error(node, 'dpkg -l | grep vpp', sudo=True)
621                 else:
622                     exec_cmd_no_error(node, 'yum -y remove "*vpp*" || true',
623                                       timeout=120, sudo=True)
624                     exec_cmd_no_error(node, 'rpm -ivh {dir}*.rpm'.\
625                         format(dir=vpp_pkg_dir), timeout=120, sudo=True,
626                         message=message)
627                     exec_cmd_no_error(node, 'rpm -qai *vpp*', sudo=True)
628
629     @staticmethod
630     def running_in_container(node):
631         """This method tests if topology node is running inside container.
632
633         :param node: Topology node.
634         :type node: dict
635         :returns: True if running in docker container, false if not or failed
636         to detect.
637         :rtype: bool
638         """
639         command = "fgrep docker /proc/1/cgroup"
640         message = 'Failed to get cgroup settings.'
641         try:
642             exec_cmd_no_error(node, command, timeout=30, sudo=False,
643                               message=message)
644         except RuntimeError:
645             return False
646         return True
647
648     @staticmethod
649     def get_huge_page_size(node):
650         """Get default size of huge pages in system.
651
652         :param node: Node in the topology.
653         :type node: dict
654         :returns: Default size of free huge pages in system.
655         :rtype: int
656         :raises RuntimeError: If reading failed for three times.
657         """
658         ssh = SSH()
659         ssh.connect(node)
660
661         for _ in range(3):
662             ret_code, stdout, _ = ssh.exec_command_sudo(
663                 "grep Hugepagesize /proc/meminfo | awk '{ print $2 }'")
664             if ret_code == 0:
665                 try:
666                     huge_size = int(stdout)
667                 except ValueError:
668                     logger.trace('Reading huge page size information failed')
669                 else:
670                     break
671         else:
672             raise RuntimeError('Getting huge page size information failed.')
673         return huge_size
674
675     @staticmethod
676     def get_huge_page_free(node, huge_size):
677         """Get number of free huge pages in system.
678
679         :param node: Node in the topology.
680         :param huge_size: Size of hugepages.
681         :type node: dict
682         :type huge_size: int
683         :returns: Number of free huge pages in system.
684         :rtype: int
685         :raises RuntimeError: If reading failed for three times.
686         """
687         # TODO: add numa aware option
688         ssh = SSH()
689         ssh.connect(node)
690
691         for _ in range(3):
692             ret_code, stdout, _ = ssh.exec_command_sudo(
693                 'cat /sys/kernel/mm/hugepages/hugepages-{0}kB/free_hugepages'.
694                 format(huge_size))
695             if ret_code == 0:
696                 try:
697                     huge_free = int(stdout)
698                 except ValueError:
699                     logger.trace('Reading free huge pages information failed')
700                 else:
701                     break
702         else:
703             raise RuntimeError('Getting free huge pages information failed.')
704         return huge_free
705
706     @staticmethod
707     def get_huge_page_total(node, huge_size):
708         """Get total number of huge pages in system.
709
710         :param node: Node in the topology.
711         :param huge_size: Size of hugepages.
712         :type node: dict
713         :type huge_size: int
714
715         :returns: Total number of huge pages in system.
716         :rtype: int
717         :raises RuntimeError: If reading failed for three times.
718         """
719         # TODO: add numa aware option
720         ssh = SSH()
721         ssh.connect(node)
722
723         for _ in range(3):
724             ret_code, stdout, _ = ssh.exec_command_sudo(
725                 'cat /sys/kernel/mm/hugepages/hugepages-{0}kB/nr_hugepages'.
726                 format(huge_size))
727             if ret_code == 0:
728                 try:
729                     huge_total = int(stdout)
730                 except ValueError:
731                     logger.trace('Reading total huge pages information failed')
732                 else:
733                     break
734         else:
735             raise RuntimeError('Getting total huge pages information failed.')
736         return huge_total
737
738     @staticmethod
739     def check_huge_page(node, huge_mnt, mem_size, allocate=False):
740         """Check if there is enough HugePages in system. If allocate is set to
741         true, try to allocate more HugePages.
742
743         :param node: Node in the topology.
744         :param huge_mnt: HugePage mount point.
745         :param mem_size: Requested memory in MB.
746         :param allocate: Whether to allocate more memory if not enough.
747         :type node: dict
748         :type huge_mnt: str
749         :type mem_size: str
750         :type allocate: bool
751
752         :raises RuntimeError: Mounting hugetlbfs failed or not enough HugePages
753         or increasing map count failed.
754         """
755         # TODO: split function into smaller parts.
756         ssh = SSH()
757         ssh.connect(node)
758
759         # Get huge pages information
760         huge_size = DUTSetup.get_huge_page_size(node)
761         huge_free = DUTSetup.get_huge_page_free(node, huge_size)
762         huge_total = DUTSetup.get_huge_page_total(node, huge_size)
763
764         # Check if memory reqested is available on host
765         if (mem_size * 1024) > (huge_free * huge_size):
766             # If we want to allocate hugepage dynamically
767             if allocate:
768                 mem_needed = (mem_size * 1024) - (huge_free * huge_size)
769                 huge_to_allocate = ((mem_needed / huge_size) * 2) + huge_total
770                 max_map_count = huge_to_allocate*4
771                 # Increase maximum number of memory map areas a process may have
772                 ret_code, _, _ = ssh.exec_command_sudo(
773                     'echo "{0}" | sudo tee /proc/sys/vm/max_map_count'.
774                     format(max_map_count))
775                 if int(ret_code) != 0:
776                     raise RuntimeError('Increase map count failed on {host}'.
777                                        format(host=node['host']))
778                 # Increase hugepage count
779                 ret_code, _, _ = ssh.exec_command_sudo(
780                     'echo "{0}" | sudo tee /proc/sys/vm/nr_hugepages'.
781                     format(huge_to_allocate))
782                 if int(ret_code) != 0:
783                     raise RuntimeError('Mount huge pages failed on {host}'.
784                                        format(host=node['host']))
785             # If we do not want to allocate dynamicaly end with error
786             else:
787                 raise RuntimeError('Not enough free huge pages: {0}, {1} MB'.
788                                    format(huge_free, huge_free * huge_size))
789         # Check if huge pages mount point exist
790         has_huge_mnt = False
791         ret_code, stdout, _ = ssh.exec_command('cat /proc/mounts')
792         if int(ret_code) == 0:
793             for line in stdout.splitlines():
794                 # Try to find something like:
795                 # none /mnt/huge hugetlbfs rw,relatime,pagesize=2048k 0 0
796                 mount = line.split()
797                 if mount[2] == 'hugetlbfs' and mount[1] == huge_mnt:
798                     has_huge_mnt = True
799                     break
800         # If huge page mount point not exist create one
801         if not has_huge_mnt:
802             ret_code, _, _ = ssh.exec_command_sudo(
803                 'mkdir -p {mnt}'.format(mnt=huge_mnt))
804             if int(ret_code) != 0:
805                 raise RuntimeError('Create mount dir failed on {host}'.
806                                    format(host=node['host']))
807             ret_code, _, _ = ssh.exec_command_sudo(
808                 'mount -t hugetlbfs -o pagesize=2048k none {mnt}'.
809                 format(mnt=huge_mnt))
810             if int(ret_code) != 0:
811                 raise RuntimeError('Mount huge pages failed on {host}'.
812                                    format(host=node['host']))