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

©2016 FD.io a Linux Foundation Collaborative Project. All Rights Reserved.
Linux Foundation is a registered trademark of The Linux Foundation. Linux is a registered trademark of Linus Torvalds.
Please see our privacy policy and terms of use.