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
.exceptions
import MKGeneralException
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,
49 skip_autochecks
=False,
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
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
]
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
]
86 if checkname
not in config
.check_info
:
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"):
95 elif not host_config
.is_agent_host
and config_cache
.is_tcp_check(checkname
):
98 is_checkname_valid_cache
[checkname
] = passed
101 def handle_entry(entry
):
102 num_elements
= len(entry
)
103 if num_elements
== 3: # from autochecks
105 checkname
, item
, params
= entry
107 elif num_elements
== 4:
108 hostlist
, checkname
, item
, params
= entry
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." %
118 raise MKGeneralException(
119 "Invalid entry '%r' in check table. It has %d entries, but must have 4 or 5." %
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
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
):
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
):
146 if not host_config
.part_of_clusters
:
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
:
155 elif filter_mode
== "only_clustered" and svc_is_mine
:
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
):
167 for entry
in single_host_checks
.get(hostname
, []):
170 for entry
in multi_host_checks
:
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
:
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
,
189 handle_entry((hostname
, checkname
, item
, cluster_params
))
191 # Remove dependencies to non-existing services
193 [descr
for ((checkname
, item
), (params
, descr
, deps
)) in check_table
.iteritems()])
194 for (checkname
, item
), (params
, descr
, deps
) in check_table
.iteritems():
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
)
209 def get_precompiled_check_table(hostname
,
210 remove_duplicates
=True,
213 host_checks
= get_sorted_check_table(
214 hostname
, remove_duplicates
, filter_mode
=filter_mode
, skip_ignored
=skip_ignored
)
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
227 def get_precompiled_check_parameters(hostname
, item
, params
, check_plugin_name
):
228 precomp_func
= config
.precompile_params
.get(check_plugin_name
)
230 return precomp_func(hostname
, item
, params
)
234 def remove_duplicate_checks(check_table
):
237 without_duplicates
= {}
238 for key
, value
in check_table
.iteritems():
241 if cmk_base
.check_utils
.is_snmp_check(checkname
):
242 if descr
in have_with_tcp
:
244 have_with_snmp
[descr
] = key
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(
265 remove_duplicates
=remove_duplicates
,
266 filter_mode
=filter_mode
,
267 skip_ignored
=skip_ignored
).items()]
269 unsorted
.sort(key
=lambda x
: x
[3])
272 while len(unsorted
) > 0:
273 unsorted_descrs
= set([entry
[3] for entry
in unsorted
])
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
283 ordered
.append(check
)
284 at_least_one_hit
= True
289 if not at_least_one_hit
:
290 raise MKGeneralException("Cyclic service dependency of host %s. Problematic are: %s" %
291 (hostname
, ",".join(unsorted_descrs
)))