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