2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
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
.regex
import regex
29 from cmk
.utils
.exceptions
import MKGeneralException
32 import cmk_base
.config
as config
33 import cmk_base
.item_state
as item_state
34 import cmk_base
.check_utils
35 import cmk_base
.autochecks
36 import cmk_base
.check_api_utils
as check_api_utils
38 # TODO: Refactor this to OO. The check table needs to be an object.
41 # Returns check table for a specific host
42 # Format: (checkname, item) -> (params, description)
44 # filter_mode: None -> default, returns only checks for this host
45 # filter_mode: "only_clustered" -> returns only checks belonging to clusters
46 # filter_mode: "include_clustered" -> returns checks of own host, including clustered checks
47 def get_check_table(hostname
,
48 remove_duplicates
=False,
51 skip_autochecks
=False,
54 if config
.is_ping_host(hostname
):
55 skip_autochecks
= True
57 # speed up multiple lookup of same host
58 check_table_cache
= cmk_base
.config_cache
.get_dict("check_tables")
59 table_cache_id
= hostname
, filter_mode
61 if not skip_autochecks
and use_cache
and table_cache_id
in check_table_cache
:
62 # TODO: The whole is_dual_host handling needs to be cleaned up. The duplicate checking
63 # needs to be done in all cases since a host can now have a lot of different data
65 if remove_duplicates
and config
.is_dual_host(hostname
):
66 return _remove_duplicate_checks(check_table_cache
[table_cache_id
])
67 return check_table_cache
[table_cache_id
]
71 single_host_checks
= cmk_base
.config_cache
.get_dict("single_host_checks")
72 multi_host_checks
= cmk_base
.config_cache
.get_list("multi_host_checks")
74 hosttags
= config
.tags_of_host(hostname
)
76 # Just a local cache and its function
77 is_checkname_valid_cache
= {}
79 def is_checkname_valid(checkname
):
80 the_id
= (hostname
, checkname
)
81 if the_id
in is_checkname_valid_cache
:
82 return is_checkname_valid_cache
[the_id
]
85 if checkname
not in config
.check_info
:
88 # Skip SNMP checks for non SNMP hosts (might have been discovered before with other
89 # agent setting. Remove them without rediscovery). Same for agent based checks.
90 elif not config
.is_snmp_host(hostname
) and cmk_base
.check_utils
.is_snmp_check(checkname
) and \
91 (not config
.has_management_board(hostname
) or config
.management_protocol_of(hostname
) != "snmp"):
94 elif not config
.is_agent_host(hostname
) and cmk_base
.check_utils
.is_tcp_check(checkname
):
97 is_checkname_valid_cache
[the_id
] = passed
100 def handle_entry(entry
):
101 num_elements
= len(entry
)
102 if num_elements
== 3: # from autochecks
104 checkname
, item
, params
= entry
106 elif num_elements
== 4:
107 hostlist
, checkname
, item
, params
= entry
109 elif num_elements
== 5:
110 tags
, hostlist
, checkname
, item
, params
= entry
111 if not isinstance(tags
, list):
112 raise MKGeneralException(
113 "Invalid entry '%r' in check table. First entry must be list of host tags." %
117 raise MKGeneralException(
118 "Invalid entry '%r' in check table. It has %d entries, but must have 4 or 5." %
121 # hostlist list might be:
122 # 1. a plain hostname (string)
123 # 2. a list of hostnames (list of strings)
124 # Hostnames may be tagged. Tags are removed.
125 # In autochecks there are always single untagged hostnames. We optimize for that.
126 if isinstance(hostlist
, str):
127 if hostlist
!= hostname
:
128 return # optimize most common case: hostname mismatch
129 hostlist
= [hostlist
]
130 elif isinstance(hostlist
[0], str):
131 pass # regular case: list of hostnames
133 raise MKGeneralException("Invalid entry '%r' in check table. Must be single hostname "
134 "or list of hostnames" % hostlist
)
136 if not is_checkname_valid(checkname
):
139 if config
.hosttags_match_taglist(hosttags
, tags
) and \
140 config
.in_extraconf_hostlist(hostlist
, hostname
):
141 descr
= config
.service_description(hostname
, checkname
, item
)
142 if skip_ignored
and config
.service_ignored(hostname
, checkname
, descr
):
145 svc_is_mine
= hostname
== config
.host_of_clustered_service(hostname
, descr
)
146 if filter_mode
is None and not svc_is_mine
:
149 elif filter_mode
== "only_clustered" and svc_is_mine
:
152 deps
= service_deps(hostname
, descr
)
153 check_table
[(checkname
, item
)] = (params
, descr
, deps
)
155 # Now process all entries that are specific to the host
156 # in search (single host) or that might match the host.
157 if not skip_autochecks
:
158 for entry
in cmk_base
.autochecks
.read_autochecks_of(hostname
, world
):
161 for entry
in single_host_checks
.get(hostname
, []):
164 for entry
in multi_host_checks
:
167 # Now add checks a cluster might receive from its nodes
168 if config
.is_cluster(hostname
):
169 single_host_checks
= cmk_base
.config_cache
.get_dict("single_host_checks")
171 for node
in config
.nodes_of(hostname
):
172 node_checks
= single_host_checks
.get(node
, [])
173 if not skip_autochecks
:
174 node_checks
= node_checks
+ cmk_base
.autochecks
.read_autochecks_of(node
, world
)
175 for entry
in node_checks
:
177 entry
= entry
[1:] # drop hostname from single_host_checks
178 checkname
, item
, params
= entry
179 descr
= config
.service_description(node
, checkname
, item
)
180 if hostname
== config
.host_of_clustered_service(node
, descr
):
181 cluster_params
= config
.compute_check_parameters(hostname
, checkname
, item
,
183 handle_entry((hostname
, checkname
, item
, cluster_params
))
185 # Remove dependencies to non-existing services
186 all_descr
= set([descr
for ((checkname
, item
), (params
, descr
, deps
)) in check_table
.items()])
187 for (checkname
, item
), (params
, descr
, deps
) in check_table
.items():
194 if not skip_autochecks
and use_cache
:
195 check_table_cache
[table_cache_id
] = check_table
197 if remove_duplicates
:
198 return _remove_duplicate_checks(check_table
)
202 def get_precompiled_check_table(hostname
,
203 remove_duplicates
=True,
207 host_checks
= get_sorted_check_table(
208 hostname
, remove_duplicates
, world
, filter_mode
=filter_mode
, skip_ignored
=skip_ignored
)
210 for check_plugin_name
, item
, params
, description
, _unused_deps
in host_checks
:
211 # make these globals available to the precompile function
212 check_api_utils
.set_service(check_plugin_name
, description
)
213 item_state
.set_item_state_prefix(check_plugin_name
, item
)
215 params
= get_precompiled_check_parameters(hostname
, item
, params
, check_plugin_name
)
216 precomp_table
.append((check_plugin_name
, item
, params
,
217 description
)) # deps not needed while checking
221 def get_precompiled_check_parameters(hostname
, item
, params
, check_plugin_name
):
222 precomp_func
= config
.precompile_params
.get(check_plugin_name
)
224 return precomp_func(hostname
, item
, params
)
228 # Return a list of services this services depends upon
229 # TODO: Make this use the generic "rulesets" functions
230 # TODO: Is this needed here? Investigate for what this is used for
231 def service_deps(hostname
, servicedesc
):
233 for entry
in config
.service_dependencies
:
234 entry
, rule_options
= config
.get_rule_options(entry
)
235 if rule_options
.get("disabled"):
239 depname
, hostlist
, patternlist
= entry
241 elif len(entry
) == 4:
242 depname
, tags
, hostlist
, patternlist
= entry
244 raise MKGeneralException("Invalid entry '%r' in service dependencies: "
245 "must have 3 or 4 entries" % entry
)
247 if config
.hosttags_match_taglist(config
.tags_of_host(hostname
), tags
) and \
248 config
.in_extraconf_hostlist(hostlist
, hostname
):
249 for pattern
in patternlist
:
250 matchobject
= regex(pattern
).search(servicedesc
)
253 item
= matchobject
.groups()[-1]
254 deps
.append(depname
% item
)
260 def _remove_duplicate_checks(check_table
):
263 without_duplicates
= {}
264 for key
, value
in check_table
.iteritems():
267 if cmk_base
.check_utils
.is_snmp_check(checkname
):
268 if descr
in have_with_tcp
:
270 have_with_snmp
[descr
] = key
272 if descr
in have_with_snmp
:
273 snmp_key
= have_with_snmp
[descr
]
274 del without_duplicates
[snmp_key
]
275 del have_with_snmp
[descr
]
276 have_with_tcp
[descr
] = key
277 without_duplicates
[key
] = value
278 return without_duplicates
281 # remove_duplicates: Automatically remove SNMP based checks
282 # if there already is a TCP based one with the same
283 # description. E.g: df vs hr_fs.
284 # TODO: Clean this up!
285 def get_sorted_check_table(hostname
,
286 remove_duplicates
=False,
290 # Convert from dictionary into simple tuple list. Then sort
291 # it according to the service dependencies.
292 unsorted
= [(checkname
, item
, params
, descr
, deps
)
293 for ((checkname
, item
), (params
, descr
, deps
)) in get_check_table(
295 remove_duplicates
=remove_duplicates
,
297 filter_mode
=filter_mode
,
298 skip_ignored
=skip_ignored
).items()]
300 unsorted
.sort(key
=lambda x
: x
[3])
303 while len(unsorted
) > 0:
304 unsorted_descrs
= set([entry
[3] for entry
in unsorted
])
306 at_least_one_hit
= False
307 for check
in unsorted
:
308 deps_fulfilled
= True
309 for dep
in check
[4]: # deps
310 if dep
in unsorted_descrs
:
311 deps_fulfilled
= False
314 ordered
.append(check
)
315 at_least_one_hit
= True
320 if not at_least_one_hit
:
321 raise MKGeneralException("Cyclic service dependency of host %s. Problematic are: %s" %
322 (hostname
, ",".join(unsorted_descrs
)))