3d40ffa25d14359a221fd0c1c31a3f06f95245da
[csit.git] / resources / libraries / python / CoreDumpUtil.py
1 # Copyright (c) 2019 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 """Core dump library."""
15
16 from resources.libraries.python.Constants import Constants
17 from resources.libraries.python.DUTSetup import DUTSetup
18 from resources.libraries.python.LimitUtil import LimitUtil
19 from resources.libraries.python.SysctlUtil import SysctlUtil
20 from resources.libraries.python.ssh import exec_cmd_no_error
21 from resources.libraries.python.topology import NodeType
22
23 __all__ = [u"CoreDumpUtil"]
24
25
26 class CoreDumpUtil:
27     """Class contains methods for processing core dumps."""
28
29     # Use one instance of class for all tests. If the functionality should
30     # be enabled per suite or per test case, change the scope to "TEST SUITE" or
31     # "TEST CASE" respectively.
32     ROBOT_LIBRARY_SCOPE = u"GLOBAL"
33
34     def __init__(self):
35         """Initialize CoreDumpUtil class."""
36         # Corekeeper is configured.
37         self._corekeeper_configured = False
38         # Enable setting core limit for process. This can be used to prevent
39         # library to further set the core limit for unwanted behavior.
40         self._core_limit_enabled = True
41
42     def set_core_limit_enabled(self):
43         """Enable setting of core limit for PID."""
44         self._core_limit_enabled = True
45
46     def set_core_limit_disabled(self):
47         """Disable setting of core limit for PID."""
48         self._core_limit_enabled = False
49
50     def is_core_limit_enabled(self):
51         """Check if core limit is set for process.
52
53         :returns: True if core limit is set for process.
54         :rtype: bool
55         """
56         return self._corekeeper_configured and self._core_limit_enabled
57
58     def setup_corekeeper_on_all_nodes(self, nodes):
59         """Setup core dumps system wide on all nodes.
60
61         :param nodes: Nodes in the topology.
62         :type nodes: dict
63         """
64         for node in nodes.values():
65             # Any binary which normally would not be dumped is dumped anyway,
66             # but only if the "core_pattern" kernel sysctl is set to either a
67             # pipe handler or a fully qualified path. (For more details on this
68             # limitation, see CVE-2006-2451.) This mode is appropriate when
69             # administrators are attempting to debug problems in a normal
70             # environment, and either have a core dump pipe handler that knows
71             # to treat privileged core dumps with care, or specific directory
72             # defined for catching core dumps. If a core dump happens without a
73             # pipe handler or fully qualified path, a message will be emitted to
74             # syslog warning about the lack of a correct setting.
75             SysctlUtil.set_sysctl_value(node, u"fs.suid_dumpable", 2)
76
77             # Specify a core dumpfile pattern name (for the output filename).
78             # %p    pid
79             # %u    uid (in initial user namespace)
80             # %g    gid (in initial user namespace)
81             # %s    signal number
82             # %t    UNIX time of dump
83             # %h    hostname
84             # %e    executable filename (may be shortened)
85             SysctlUtil.set_sysctl_value(
86                 node, u"kernel.core_pattern", Constants.KERNEL_CORE_PATTERN
87             )
88
89         self._corekeeper_configured = True
90
91     @staticmethod
92     def enable_coredump_limit(node, pid):
93         """Enable coredump for PID(s) by setting no core limits.
94
95         :param node: Node in the topology.
96         :param pid: Process ID(s) to set core dump limit to unlimited.
97         :type node: dict
98         :type pid: list or int
99         """
100         if isinstance(pid, list):
101             for item in pid:
102                 LimitUtil.set_pid_limit(node, item, u"core", u"unlimited")
103                 LimitUtil.get_pid_limit(node, item)
104         else:
105             LimitUtil.set_pid_limit(node, pid, u"core", u"unlimited")
106             LimitUtil.get_pid_limit(node, pid)
107
108     def enable_coredump_limit_vpp(self, node):
109         """Enable coredump for VPP PID by setting no core limits on DUT
110         if setting of core limit by this library is enabled.
111
112         :param node: DUT Node in the topology.
113         :type node: dict
114         """
115         if node['type'] == NodeType.DUT and self.is_core_limit_enabled():
116             vpp_pid = DUTSetup.get_vpp_pid(node)
117             self.enable_coredump_limit(node, vpp_pid)
118
119     def enable_coredump_limit_vpp_on_all_duts(self, nodes):
120         """Enable coredump for all VPP PIDs by setting no core limits on all
121         DUTs if setting of core limit by this library is enabled.
122
123         :param nodes: Nodes in the topology.
124         :type nodes: dict
125         """
126         for node in nodes.values():
127             self.enable_coredump_limit_vpp(node)
128
129     def get_core_files_on_all_nodes(self, nodes, disable_on_success=True):
130         """Process all core files and remove the original core files on all
131         nodes.
132
133         :param nodes: Nodes in the topology.
134         :param disable_on_success: If True, disable setting of core limit by
135             this instance of library. Default: True
136         :type nodes: dict
137         :type disable_on_success: bool
138         """
139         for node in nodes.values():
140             if node[u"type"] == NodeType.DUT:
141                 command = (
142                     f"for f in {Constants.CORE_DUMP_DIR}/*.core; do "
143                     f"sudo gdb /usr/bin/vpp ${{f}} "
144                     f"-ex 'source -v {Constants.REMOTE_FW_DIR}"
145                     f"/resources/tools/scripts/gdb-commands' -ex quit; "
146                     f"sudo rm -f ${{f}}; done"
147                 )
148                 try:
149                     exec_cmd_no_error(node, command, timeout=3600)
150                     if disable_on_success:
151                         self.set_core_limit_disabled()
152                 except RuntimeError:
153                     # If compress was not successful ignore error and skip further
154                     # processing.
155                     continue