Cleanup config.nodes_of
[check_mk.git] / checks / mknotifyd
blobd3ae4393f039091929323f3d6c73cfa0cae751b1
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.
27 # Example output from agent:
28 # <<<mknotifyd:sep(0)>>>
29 # [mysite]
30 # Version: 1.2.7i1
31 # Updated: 1425389753 (2015-03-03 14:35:53)
32 # Started: 1425388950 (2015-03-03 14:22:30, 803 sec ago)
33 # Configuration: 1425388950 (2015-03-03 14:22:30, 803 sec ago)
34 # Listening FD: 4
36 # Spool: New
37 # Count: 0
38 # Oldest:
40 # Spool: Deferred
41 # Count: 0
42 # Oldest:
44 # Spool: Corrupted
45 # Count: 4
46 # Oldest: 1425305956 (2015-03-02 15:19:16, 83797 sec ago)
48 # Connection: 127.0.0.1:46906
49 # Type: incoming
50 # State: established
51 # Since: 1425389490 (2015-03-03 14:31:30, 263 sec ago)
52 # Socket FD: 5
53 # HB. Interval: 10 sec
54 # LastHeartbeat: 1425389750 (2015-03-03 14:35:50, 3 sec ago)
55 # InputBuffer: 0 Bytes
56 # OutputBuffer: 0 Bytes
59 def parse_mknotifyd(info):
60 parsed = {}
61 for line in info:
62 if line[0].startswith('['):
63 site = line[0][1:-1]
64 site_entry = {
65 "spools": {},
66 "connections": {},
67 "queues": {},
69 sub_entry = site_entry
70 parsed[site] = site_entry
71 else:
72 varname, value = line[0].split(":", 1)
73 value = value.strip()
75 if varname == "Spool":
76 sub_entry = {}
77 site_entry["spools"][value] = sub_entry
79 elif varname == "Connection":
80 sub_entry = {}
81 site_entry["connections"][value] = sub_entry
83 elif varname == "Queue":
84 sub_entry = {}
85 site_entry["queues"][value] = sub_entry
87 else:
88 if value == "None":
89 value = None
90 elif value and varname == "Listening FD":
91 # May be the listening FD number or an error message
92 try:
93 value = int(value.split()[0])
94 except ValueError:
95 pass
96 elif value and varname not in [
97 "Type",
98 "State",
99 "Version",
100 "Status Message",
101 "Pending Acknowledgements",
102 "Connect Time",
104 value = int(value.split()[0])
105 elif varname == "Connect Time":
106 value = float(value.split()[0])
107 sub_entry[varname] = value
109 # Fixup names of the connections. For incoming connections the remote
110 # port is irrelevant. It changes randomly. But there might anyway be
111 # more than one connection from the same remote host, so we are forced
112 # to create artificial numbers if that is the case
113 for stats in parsed.itervalues():
114 remote_addresses = {}
115 for connection_name, connection in stats["connections"].items():
116 if connection["Type"] == "incoming":
117 remote_address = connection_name.split(":")[0]
118 remote_addresses.setdefault(remote_address, []).append(connection)
119 del stats["connections"][connection_name]
121 for address, connections in remote_addresses.items():
122 if len(connections) == 1:
123 stats["connections"][address] = connections[0]
124 else:
125 for nr, connection in enumerate(connections):
126 stats["connections"][address + "/" + str(nr + 1)] = connection
128 return parsed
131 # .--Spooler Status------------------------------------------------------.
132 # | ____ _ ____ _ _ |
133 # |/ ___| _ __ ___ ___ | | ___ _ __ / ___|| |_ __ _| |_ _ _ ___ |
134 # |\___ \| '_ \ / _ \ / _ \| |/ _ \ '__| \___ \| __/ _` | __| | | / __| |
135 # | ___) | |_) | (_) | (_) | | __/ | ___) | || (_| | |_| |_| \__ \ |
136 # ||____/| .__/ \___/ \___/|_|\___|_| |____/ \__\__,_|\__|\__,_|___/ |
137 # | |_| |
138 # +----------------------------------------------------------------------+
139 # | |
140 # '----------------------------------------------------------------------'
143 def inventory_mknotifyd(parsed):
144 return [(p, {}) for p in parsed]
147 def check_mknotifyd(item, _no_params, parsed):
148 if item not in parsed:
149 yield 2, "No status information, Spooler not running"
150 return
151 # There are dummy-entries created during the parsing. So the
152 # dict will never be completely empty. We check for Version
153 # because this should be always present in a valid state file.
154 elif not parsed[item].get("Version"):
155 yield 2, "The state file seems to be empty. It is very likely that the spooler is not working properly"
156 return
158 now = time.time()
159 stat = parsed[item]
160 version = stat["Version"]
162 # Output Version
163 yield 0, "Version: " + version, []
165 # Check age of status file. It's updated every 20 seconds
166 status_age = now - stat["Updated"]
167 if status_age > 90:
168 state = 2
169 infotext = "Status last updated %s ago, spooler seems crashed or busy" % get_age_human_readable(
170 status_age)
171 else:
172 state = 0
173 infotext = "Spooler running"
174 yield state, infotext, [('last_updated', status_age),
175 ('new_files', stat['spools']['New']['Count'])]
177 # Are there any corrupted files
178 corrupted = stat["spools"]["Corrupted"]
179 if corrupted["Count"]:
180 age = now - corrupted["Youngest"]
181 perf_data = [('corrupted_files', corrupted["Count"])]
182 yield 1, "%d corrupted files: youngest %s ago" % (corrupted["Count"],
183 get_age_human_readable(age)), perf_data
185 # Are there deferred files that are too old?
186 deferred = stat["spools"]["Deferred"]
187 if deferred["Count"]:
188 age = now - deferred["Oldest"]
189 count = deferred["Count"]
190 perf_data = [('deferred_age', age), ('deferred_files', deferred["Count"])]
191 if age > 5:
192 state = 1
193 elif age > 600:
194 state = 2
195 else:
196 state = 0
197 yield state, "%d deferred files: oldest %s ago" % (count,
198 get_age_human_readable(age)), perf_data
200 return
203 check_info["mknotifyd"] = {
204 "parse_function": parse_mknotifyd,
205 "inventory_function": inventory_mknotifyd,
206 "check_function": check_mknotifyd,
207 "has_perfdata": True,
208 "service_description": "OMD %s Notification Spooler",
211 # .--Connections---------------------------------------------------------.
212 # | ____ _ _ |
213 # | / ___|___ _ __ _ __ ___ ___| |_(_) ___ _ __ ___ |
214 # | | | / _ \| '_ \| '_ \ / _ \/ __| __| |/ _ \| '_ \/ __| |
215 # | | |__| (_) | | | | | | | __/ (__| |_| | (_) | | | \__ \ |
216 # | \____\___/|_| |_|_| |_|\___|\___|\__|_|\___/|_| |_|___/ |
217 # | |
218 # '----------------------------------------------------------------------'
221 def inventory_mknotifyd_connection(parsed):
222 for site_name, stats in parsed.items():
223 for connection_name in stats["connections"]:
224 yield site_name + "-" + connection_name, {}
227 def check_mknotifyd_connection(item, _no_params, parsed):
228 states = {
229 "established": (0, "Alive"),
230 "cooldown": (2, "Connection failed or terminated"),
231 "initial": (1, "Initialized"),
232 "connecting": (2, "Trying to connect"),
235 site_name, connection_name = item.split('-', 1)
236 if site_name not in parsed:
237 raise MKCounterWrapped("No status information about spooler available")
239 if connection_name in parsed[site_name]["connections"]:
240 connection = parsed[site_name]["connections"][connection_name]
242 # First check state
243 state, state_name = states[connection["State"]]
244 yield state, state_name
246 if "Status Message" in connection:
247 yield 0, connection["Status Message"]
249 # Show uptime
250 if connection["State"] == "established":
251 now = time.time()
252 age = now - connection["Since"]
253 yield 0, "Uptime: %s" % get_age_human_readable(age)
255 if "Connect Time" in connection:
256 yield 0, "Connect time: %.3f sec" % connection["Connect Time"]
258 # Stats
259 for what in ("Sent", "Received"):
260 num = connection["Notifications " + what]
261 if num:
262 yield 0, "%d Notifications %s" % (num, what.lower())
265 check_info["mknotifyd.connection"] = {
266 "inventory_function": inventory_mknotifyd_connection,
267 "check_function": check_mknotifyd_connection,
268 "service_description": "OMD %s Notify Connection",