Licenses: Updated the list of licenses and added a PDF containing all license texts
[check_mk.git] / cmk_base / check_table.py
blob0f7ad9791914fe8b62885eacf9b35045370bb3ae
1 #!/usr/bin/python
2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
9 # | |
10 # | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
11 # +------------------------------------------------------------------+
13 # This file is part of Check_MK.
14 # The official homepage is at http://mathias-kettner.de/check_mk.
16 # check_mk is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by
18 # the Free Software Foundation in version 2. check_mk is distributed
19 # in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
20 # out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
21 # PARTICULAR PURPOSE. See the GNU General Public License for more de-
22 # tails. You should have received a copy of the GNU General Public
23 # License along with GNU Make; see the file COPYING. If not, write
24 # to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 # Boston, MA 02110-1301 USA.
26 """Code for computing the table of checks of hosts."""
28 from cmk.utils.exceptions import MKGeneralException
30 import cmk_base
31 import cmk_base.config as config
32 import cmk_base.item_state as item_state
33 import cmk_base.check_utils
34 import cmk_base.autochecks
35 import cmk_base.check_api_utils as check_api_utils
37 # TODO: Refactor this to OO. The check table needs to be an object.
40 # Returns check table for a specific host
41 # Format: (checkname, item) -> (params, description)
43 # filter_mode: None -> default, returns only checks for this host
44 # filter_mode: "only_clustered" -> returns only checks belonging to clusters
45 # filter_mode: "include_clustered" -> returns checks of own host, including clustered checks
46 def get_check_table(hostname,
47 remove_duplicates=False,
48 use_cache=True,
49 skip_autochecks=False,
50 filter_mode=None,
51 skip_ignored=True):
53 config_cache = config.get_config_cache()
54 host_config = config_cache.get_host_config(hostname)
56 if host_config.is_ping_host:
57 skip_autochecks = True
59 # speed up multiple lookup of same host
60 check_table_cache = config_cache.check_table_cache
61 table_cache_id = hostname, filter_mode
63 if not skip_autochecks and use_cache and table_cache_id in check_table_cache:
64 # TODO: The whole is_dual_host handling needs to be cleaned up. The duplicate checking
65 # needs to be done in all cases since a host can now have a lot of different data
66 # sources.
67 if remove_duplicates and host_config.is_dual_host:
68 return remove_duplicate_checks(check_table_cache[table_cache_id])
69 return check_table_cache[table_cache_id]
71 check_table = {}
73 single_host_checks = config_cache.single_host_checks
74 multi_host_checks = config_cache.multi_host_checks
76 hosttags = host_config.tags
78 # Just a local cache and its function
79 is_checkname_valid_cache = {}
81 def is_checkname_valid(checkname):
82 if checkname in is_checkname_valid_cache:
83 return is_checkname_valid_cache[checkname]
85 passed = True
86 if checkname not in config.check_info:
87 passed = False
89 # Skip SNMP checks for non SNMP hosts (might have been discovered before with other
90 # agent setting. Remove them without rediscovery). Same for agent based checks.
91 elif not host_config.is_snmp_host and config_cache.is_snmp_check(checkname) and \
92 (not host_config.has_management_board or host_config.management_protocol != "snmp"):
93 passed = False
95 elif not host_config.is_agent_host and config_cache.is_tcp_check(checkname):
96 passed = False
98 is_checkname_valid_cache[checkname] = passed
99 return passed
101 def handle_entry(entry):
102 num_elements = len(entry)
103 if num_elements == 3: # from autochecks
104 hostlist = hostname
105 checkname, item, params = entry
106 tags = []
107 elif num_elements == 4:
108 hostlist, checkname, item, params = entry
109 tags = []
110 elif num_elements == 5:
111 tags, hostlist, checkname, item, params = entry
112 if not isinstance(tags, list):
113 raise MKGeneralException(
114 "Invalid entry '%r' in check table. First entry must be list of host tags." %
115 (entry,))
117 else:
118 raise MKGeneralException(
119 "Invalid entry '%r' in check table. It has %d entries, but must have 4 or 5." %
120 (entry, len(entry)))
122 # hostlist list might be:
123 # 1. a plain hostname (string)
124 # 2. a list of hostnames (list of strings)
125 # Hostnames may be tagged. Tags are removed.
126 # In autochecks there are always single untagged hostnames. We optimize for that.
127 if isinstance(hostlist, str):
128 if hostlist != hostname:
129 return # optimize most common case: hostname mismatch
130 hostlist = [hostlist]
131 elif isinstance(hostlist[0], str):
132 pass # regular case: list of hostnames
133 elif hostlist != []:
134 raise MKGeneralException("Invalid entry '%r' in check table. Must be single hostname "
135 "or list of hostnames" % hostlist)
137 if not is_checkname_valid(checkname):
138 return
140 if config.hosttags_match_taglist(hosttags, tags) and \
141 config.in_extraconf_hostlist(hostlist, hostname):
142 descr = config.service_description(hostname, checkname, item)
143 if skip_ignored and config.service_ignored(hostname, checkname, descr):
144 return
146 if not host_config.part_of_clusters:
147 svc_is_mine = True
148 else:
149 svc_is_mine = hostname == config_cache.host_of_clustered_service(
150 hostname, descr, part_of_clusters=host_config.part_of_clusters)
152 if filter_mode is None and not svc_is_mine:
153 return
155 elif filter_mode == "only_clustered" and svc_is_mine:
156 return
158 deps = config.service_depends_on(hostname, descr)
159 check_table[(checkname, item)] = (params, descr, deps)
161 # Now process all entries that are specific to the host
162 # in search (single host) or that might match the host.
163 if not skip_autochecks:
164 for entry in config_cache.get_autochecks_of(hostname):
165 handle_entry(entry)
167 for entry in single_host_checks.get(hostname, []):
168 handle_entry(entry)
170 for entry in multi_host_checks:
171 handle_entry(entry)
173 # Now add checks a cluster might receive from its nodes
174 if host_config.is_cluster:
175 single_host_checks = cmk_base.config_cache.get_dict("single_host_checks")
177 for node in config.nodes_of(hostname):
178 node_checks = single_host_checks.get(node, [])
179 if not skip_autochecks:
180 node_checks = node_checks + config_cache.get_autochecks_of(node)
181 for entry in node_checks:
182 if len(entry) == 4:
183 entry = entry[1:] # drop hostname from single_host_checks
184 checkname, item, params = entry
185 descr = config.service_description(node, checkname, item)
186 if hostname == config_cache.host_of_clustered_service(node, descr):
187 cluster_params = config.compute_check_parameters(hostname, checkname, item,
188 params)
189 handle_entry((hostname, checkname, item, cluster_params))
191 # Remove dependencies to non-existing services
192 all_descr = set(
193 [descr for ((checkname, item), (params, descr, deps)) in check_table.iteritems()])
194 for (checkname, item), (params, descr, deps) in check_table.iteritems():
195 deeps = deps[:]
196 del deps[:]
197 for d in deeps:
198 if d in all_descr:
199 deps.append(d)
201 if not skip_autochecks and use_cache:
202 check_table_cache[table_cache_id] = check_table
204 if remove_duplicates:
205 return remove_duplicate_checks(check_table)
206 return check_table
209 def get_precompiled_check_table(hostname,
210 remove_duplicates=True,
211 filter_mode=None,
212 skip_ignored=True):
213 host_checks = get_sorted_check_table(
214 hostname, remove_duplicates, filter_mode=filter_mode, skip_ignored=skip_ignored)
215 precomp_table = []
216 for check_plugin_name, item, params, description, _unused_deps in host_checks:
217 # make these globals available to the precompile function
218 check_api_utils.set_service(check_plugin_name, description)
219 item_state.set_item_state_prefix(check_plugin_name, item)
221 params = get_precompiled_check_parameters(hostname, item, params, check_plugin_name)
222 precomp_table.append((check_plugin_name, item, params,
223 description)) # deps not needed while checking
224 return precomp_table
227 def get_precompiled_check_parameters(hostname, item, params, check_plugin_name):
228 precomp_func = config.precompile_params.get(check_plugin_name)
229 if precomp_func:
230 return precomp_func(hostname, item, params)
231 return params
234 def remove_duplicate_checks(check_table):
235 have_with_tcp = {}
236 have_with_snmp = {}
237 without_duplicates = {}
238 for key, value in check_table.iteritems():
239 checkname = key[0]
240 descr = value[1]
241 if cmk_base.check_utils.is_snmp_check(checkname):
242 if descr in have_with_tcp:
243 continue
244 have_with_snmp[descr] = key
245 else:
246 if descr in have_with_snmp:
247 snmp_key = have_with_snmp[descr]
248 del without_duplicates[snmp_key]
249 del have_with_snmp[descr]
250 have_with_tcp[descr] = key
251 without_duplicates[key] = value
252 return without_duplicates
255 # remove_duplicates: Automatically remove SNMP based checks
256 # if there already is a TCP based one with the same
257 # description. E.g: df vs hr_fs.
258 # TODO: Clean this up!
259 def get_sorted_check_table(hostname, remove_duplicates=False, filter_mode=None, skip_ignored=True):
260 # Convert from dictionary into simple tuple list. Then sort
261 # it according to the service dependencies.
262 unsorted = [(checkname, item, params, descr, deps)
263 for ((checkname, item), (params, descr, deps)) in get_check_table(
264 hostname,
265 remove_duplicates=remove_duplicates,
266 filter_mode=filter_mode,
267 skip_ignored=skip_ignored).items()]
269 unsorted.sort(key=lambda x: x[3])
271 ordered = []
272 while len(unsorted) > 0:
273 unsorted_descrs = set([entry[3] for entry in unsorted])
274 left = []
275 at_least_one_hit = False
276 for check in unsorted:
277 deps_fulfilled = True
278 for dep in check[4]: # deps
279 if dep in unsorted_descrs:
280 deps_fulfilled = False
281 break
282 if deps_fulfilled:
283 ordered.append(check)
284 at_least_one_hit = True
285 else:
286 left.append(check)
287 if len(left) == 0:
288 break
289 if not at_least_one_hit:
290 raise MKGeneralException("Cyclic service dependency of host %s. Problematic are: %s" %
291 (hostname, ",".join(unsorted_descrs)))
292 unsorted = left
293 return ordered