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.
27 # There are different types of information. Can we handle them in a
28 # general way? There are:
32 # - Rate counters (per second)
34 # <<<mssql_counters>>>
35 # MSSQL_SQLEXPRESS:Buffer_Manager Buffer_cache_hit_ratio 12
36 # MSSQL_SQLEXPRESS:Databases master Data_File(s)_Size_(KB) 2304
37 # MSSQL_SQLEXPRESS:Databases master Transactions/sec 13733
38 # MSSQL_SQLEXPRESS:Databases master Percent_Log_Used 57
39 # MSSQL_SQLEXPRESS:Databases master Log_File(s)_Size_(KB)
42 inventory_mssql_counters_rules
= []
44 #TODO if not counters: raise
47 def inventory_mssql_counters_generic(parsed
, want_counters
, dflt
=None):
48 want_counters
= set(want_counters
)
49 for (obj
, instance
), node_data
in parsed
.iteritems():
50 for counters
in node_data
.values():
51 if not want_counters
.intersection(counters
.keys()):
53 yield "%s %s" % (obj
, instance
), dflt
56 # .--main----------------------------------------------------------------.
58 # | _ __ ___ __ _(_)_ __ |
59 # | | '_ ` _ \ / _` | | '_ \ |
60 # | | | | | | | (_| | | | | | |
61 # | |_| |_| |_|\__,_|_|_| |_| |
63 # +----------------------------------------------------------------------+
65 # '----------------------------------------------------------------------'
67 # Previously there was no main check, but we need it because
69 # - mssql_counters.transactions
70 # - mssql_counters.pageactivity
71 # - mssql_counters.locks
72 # will become cluster aware and thus all subchecks, too, because they share
73 # the same section. This main check is just a dummy with the benefit of the
77 def parse_mssql_counters_base(info
):
85 obj
, counter
, instance
= line
[:3]
86 if obj
.endswith(':Databases'):
93 values
= float(values
)
100 if counter
== "utc_time":
101 # mssql returns localized format. great! let's try ...
105 time
.strptime(" ".join(values
).split(".")[0], "%Y-%m-%d %H:%M:%S"))
109 values
= utc_mktime(time
.strptime(" ".join(values
), "%d.%m.%Y %H:%M:%S"))
113 data
= parsed
.setdefault((obj
, instance
), {})\
114 .setdefault(node_name
, {})
115 data
.setdefault(counter
, values
)
119 def inventory_mssql_counters_base(parsed
):
123 def check_mssql_counters_base(item
, params
, parsed
):
124 return 3, 'Not implemented check plugin'
127 check_info
['mssql_counters'] = {
128 'parse_function': parse_mssql_counters_base
,
129 'inventory_function': inventory_mssql_counters_base
,
130 'check_function': check_mssql_counters_base
,
131 'service_description': "MSSQL",
136 # .--Percentage based values---------------------------------------------.
138 # | | _ \ ___ _ __ ___ ___ _ __ | |_ __ _ __ _ ___ |
139 # | | |_) / _ \ '__/ __/ _ \ '_ \| __/ _` |/ _` |/ _ \ |
140 # | | __/ __/ | | (_| __/ | | | || (_| | (_| | __/ |
141 # | |_| \___|_| \___\___|_| |_|\__\__,_|\__, |\___| |
144 # | | |__ __ _ ___ ___ __| | __ ____ _| |_ _ ___ ___ |
145 # | | '_ \ / _` / __|/ _ \/ _` | \ \ / / _` | | | | |/ _ \/ __| |
146 # | | |_) | (_| \__ \ __/ (_| | \ V / (_| | | |_| | __/\__ \ |
147 # | |_.__/ \__,_|___/\___|\__,_| \_/ \__,_|_|\__,_|\___||___/ |
149 # '----------------------------------------------------------------------'
152 def inventory_mssql_counters_cache_hits(parsed
, want_counters
):
153 add_zero_based_services
= host_extra_conf_merged(host_name(), inventory_mssql_counters_rules
)\
154 .get('add_zero_based_services', False)
156 for (obj
, instance
), node_data
in parsed
.iteritems():
157 for counters
in node_data
.values():
158 for counter
in counters
.keys():
159 if counter
not in want_counters
:
162 if counters
.get('%s_base' % counter
, 0.0) == 0.0 \
163 and not add_zero_based_services
:
166 yield "%s %s %s" % (obj
, instance
, counter
), None
169 def check_mssql_counters_cache_hits(item
, params
, parsed
):
170 obj
, instance
, counter
= item
.split()
171 node_data
= parsed
.get((obj
, instance
), {})
173 for node_name
, counters
in node_data
.iteritems():
174 value
= counters
.get(counter
)
175 base
= counters
.get("%s_base" % counter
, 0)
177 if value
is None or base
is None:
178 # Assume general connection problem to the database, which is reported
179 # by the "X Instance" service and skip this check.
180 raise MKCounterWrapped("Failed to connect to database")
184 perc
= 100.0 * value
/ base
188 node_info
= "[%s] " % node_name
189 infotext
= "%s%s" % (node_info
, get_percent_human_readable(perc
))
192 #TODO: Previously params=None(=dflt) in inventory_mssql_counters
199 infotext
+= " (warn/crit below %s/%s)" % (warn
, crit
)
200 yield state
, infotext
, [(counter
, perc
)]
203 check_info
['mssql_counters.cache_hits'] = {
204 'inventory_function' : lambda parsed
: inventory_mssql_counters_cache_hits(parsed
,
205 ['cache_hit_ratio', 'log_cache_hit_ratio', 'buffer_cache_hit_ratio']),
206 'check_function' : check_mssql_counters_cache_hits
,
207 'service_description' : "MSSQL %s",
208 'has_perfdata' : True,
213 # .--Rates---------------------------------------------------------------.
215 # | | _ \ __ _| |_ ___ ___ |
216 # | | |_) / _` | __/ _ \/ __| |
217 # | | _ < (_| | || __/\__ \ |
218 # | |_| \_\__,_|\__\___||___/ |
220 # '----------------------------------------------------------------------'
222 # ---transactions---------------------------------------------------------
225 def check_mssql_counters_transactions(item
, params
, parsed
):
226 obj
, instance
= item
.split()
227 node_data
= parsed
.get((obj
, instance
), {})
229 if not any(node_data
.values()):
230 # Assume general connection problem to the database, which is reported
231 # by the "X Instance" service and skip this check.
232 raise MKCounterWrapped("Failed to connect to database")
234 for node_name
, counters
in node_data
.iteritems():
235 now
= counters
.get('utc_time')
241 node_info
= "[%s] " % node_name
243 for counter_key
, title
in [
244 ('transactions/sec', 'Transactions'),
245 ('write_transactions/sec', 'Write Transactions'),
246 ('tracked_transactions/sec', 'Tracked Transactions'),
248 value
= counters
.get(counter_key
)
252 rate
= get_rate("mssql_counters.transactions.%s.%s.%s" % (node_name
, item
, counter_key
),
254 infotext
= "%s%s: %.1f/s" % (node_info
, title
, rate
)
256 yield 0, infotext
, [(counter_key
, rate
)]
259 check_info
['mssql_counters.transactions'] = {
260 'inventory_function' : lambda parsed
: inventory_mssql_counters_generic(parsed
,
261 ['transactions/sec', 'write_transactions/sec', 'tracked_transactions/sec']),
262 'check_function' : check_mssql_counters_transactions
,
263 'service_description' : "MSSQL %s Transactions",
264 'has_perfdata' : True,
268 # ---locks----------------------------------------------------------------
271 def check_mssql_counters_locks(item
, params
, parsed
):
272 obj
, instance
= item
.split()
273 node_data
= parsed
.get((obj
, instance
), {})
275 if not any(node_data
.values()):
276 # Assume general connection problem to the database, which is reported
277 # by the "X Instance" service and skip this check.
278 raise MKCounterWrapped("Failed to connect to database")
280 for node_name
, counters
in node_data
.iteritems():
281 now
= counters
.get('utc_time')
287 node_info
= "[%s] " % node_name
289 for counter_key
, title
in [
290 ('lock_requests/sec', 'Requests'),
291 ('lock_timeouts/sec', 'Timeouts'),
292 ('number_of_deadlocks/sec', 'Deadlocks'),
293 ('lock_waits/sec', 'Waits'),
295 value
= counters
.get(counter_key
)
299 rate
= get_rate("mssql_counters.locks.%s.%s.%s" % (node_name
, item
, counter_key
), now
,
301 infotext
= "%s%s: %.1f/s" % (node_info
, title
, rate
)
305 warn
, crit
= params
.get(counter_key
, (None, None))
306 if crit
is not None and rate
>= crit
:
308 elif warn
is not None and rate
>= warn
:
311 infotext
+= " (warn/crit at %.1f/%.1f per second)" % (warn
, crit
)
313 yield state
, infotext
, [(counter_key
, rate
, warn
, crit
)]
316 check_info
['mssql_counters.locks'] = {
317 'inventory_function' : lambda parsed
: inventory_mssql_counters_generic(parsed
,
318 ['number_of_deadlocks/sec', 'lock_requests/sec', 'lock_timeouts/sec', 'lock_waits/sec'],
320 'check_function' : check_mssql_counters_locks
,
321 'service_description' : "MSSQL %s Locks",
322 'has_perfdata' : True,
323 'group' : 'mssql_counters_locks',
327 # ---sql stats------------------------------------------------------------
330 def inventory_mssql_counters_sqlstats(parsed
, want_counters
, dflt
=None):
331 for (obj
, instance
), node_data
in parsed
.iteritems():
332 for counters
in node_data
.values():
333 for counter
in counters
:
334 if counter
not in want_counters
:
336 yield "%s %s %s" % (obj
, instance
, counter
), dflt
339 def check_mssql_counters_sqlstats(item
, params
, parsed
):
340 obj
, instance
, counter
= item
.split()
341 node_data
= parsed
.get((obj
, instance
), {})
343 if not any(node_data
.values()):
344 # Assume general connection problem to the database, which is reported
345 # by the "X Instance" service and skip this check.
346 raise MKCounterWrapped("Failed to connect to database")
348 for node_name
, counters
in node_data
.iteritems():
349 value
= counters
.get(counter
)
353 now
= counters
.get('utc_time')
357 rate
= get_rate("mssql_counters.sqlstats.%s.%s.%s" % (node_name
, item
, counter
), now
, value
)
360 node_info
= "[%s] " % node_name
361 infotext
= "%s%.1f/sec" % (node_info
, rate
)
364 warn
, crit
= params
.get(counter
, (None, None))
365 if crit
is not None and rate
>= crit
:
367 elif warn
is not None and rate
>= warn
:
370 infotext
+= " (warn/crit at %.1f/%.1f per second)" % (warn
, crit
)
372 yield state
, infotext
, [(counter
, rate
, warn
, crit
)]
375 check_info
["mssql_counters.sqlstats"] = {
376 "inventory_function" : lambda parsed
: inventory_mssql_counters_sqlstats(parsed
,
377 ["batch_requests/sec", "sql_compilations/sec", "sql_re-compilations/sec"],
379 "check_function" : check_mssql_counters_sqlstats
,
380 "service_description" : "MSSQL %s",
381 "has_perfdata" : True,
382 "group" : "mssql_stats",
386 # ---page activity--------------------------------------------------------
389 def check_mssql_counters_pageactivity(item
, params
, parsed
):
390 obj
, instance
= item
.split()
391 node_data
= parsed
.get((obj
, instance
), {})
393 if not any(node_data
.values()):
394 # Assume general connection problem to the database, which is reported
395 # by the "X Instance" service and skip this check.
396 raise MKCounterWrapped("Failed to connect to database")
398 for node_name
, counters
in node_data
.iteritems():
399 now
= counters
.get('utc_time')
405 node_info
= "[%s] " % node_name
407 for counter_key
, title
in [
408 ("page_reads/sec", "Reads"),
409 ("page_writes/sec", "Writes"),
410 ("page_lookups/sec", "Lookups"),
412 value
= counters
.get(counter_key
)
416 rate
= get_rate("mssql_counters.pageactivity.%s.%s.%s" % (node_name
, item
, counter_key
),
418 infotext
= "%s%s: %.1f/s" % (node_info
, title
, rate
)
422 warn
, crit
= params
.get(counter_key
, (None, None))
423 if crit
is not None and rate
>= crit
:
425 elif warn
is not None and rate
>= warn
:
428 infotext
+= " (warn/crit at %.1f/%.1f per second)" % (warn
, crit
)
430 yield state
, infotext
, [(counter_key
, rate
, warn
, crit
)]
433 check_info
['mssql_counters.pageactivity'] = {
434 'inventory_function' : lambda parsed
: inventory_mssql_counters_generic(parsed
,
435 ["page_reads/sec", "page_writes/sec", "page_lookups/sec"],
437 'check_function' : check_mssql_counters_pageactivity
,
438 'service_description' : "MSSQL %s Page Activity",
439 'has_perfdata' : True,
440 'group' : "mssql_page_activity",
444 # ---locks per batch------------------------------------------------------
447 def inventory_mssql_counters_locks_per_batch(parsed
):
448 db_names
= [(obj
.split(":")[0], node_name
)
449 for (obj
, _instance
), node_data
in parsed
.iteritems() if ":" in obj
450 for node_name
in node_data
]
452 for db_name
, node_name
in db_names
:
453 if "lock_requests/sec" in parsed
.get(("%s:Locks" % db_name
, "_Total"), {})\
454 .get(node_name
, {}) \
455 and "batch_requests/sec" in parsed
.get(("%s:SQL_Statistics" % db_name
, "None"))\
460 def check_mssql_counters_locks_per_batch(item
, params
, parsed
):
461 data_locks_data
= parsed
.get(("%s:Locks" % item
, "_Total"), {})
462 data_stats_data
= parsed
.get(("%s:SQL_Statistics" % item
, "None"), {})
464 if not any(data_locks_data
.values() + data_stats_data
.values()):
465 # Assume general connection problem to the database, which is reported
466 # by the "X Instance" service and skip this check.
467 raise MKCounterWrapped("Failed to connect to database")
469 for node_name
in set(data_locks_data
.keys() + data_stats_data
.keys()):
470 data_locks
= data_locks_data
[node_name
]
471 data_stats
= data_stats_data
[node_name
]
472 now
= data_locks
.get('utc_time', data_stats
.get('utc_time'))
476 locks
= data_locks
["lock_requests/sec"]
477 batches
= data_stats
["batch_requests/sec"]
479 lock_rate
= get_rate("mssql_counters_locks_per_batch.%s.%s.locks" % (node_name
, item
), now
,
481 batch_rate
= get_rate("mssql_counters_locks_per_batch.%s.%s.batches" % (node_name
, item
),
487 lock_per_batch
= lock_rate
/ batch_rate
491 node_info
= "[%s] " % node_name
492 infotext
= "%s%.1f" % (node_info
, lock_per_batch
)
495 warn
, crit
= params
.get('locks_per_batch', (None, None))
496 if crit
is not None and lock_per_batch
>= crit
:
498 elif warn
is not None and lock_per_batch
>= warn
:
502 infotext
+= " (warn/crit at %.1f/%.1f per second)" % (warn
, crit
)
504 yield state
, infotext
, [("locks_per_batch", lock_per_batch
, warn
, crit
)]
507 check_info
["mssql_counters.locks_per_batch"] = {
508 "inventory_function": inventory_mssql_counters_locks_per_batch
,
509 "check_function": check_mssql_counters_locks_per_batch
,
510 "service_description": "MSSQL %s Locks per Batch",
511 "has_perfdata": True,
512 "group": "mssql_stats",
517 # .--File Sizes----------------------------------------------------------.
518 # | _____ _ _ ____ _ |
519 # | | ___(_) | ___ / ___|(_)_______ ___ |
520 # | | |_ | | |/ _ \ \___ \| |_ / _ \/ __| |
521 # | | _| | | | __/ ___) | |/ / __/\__ \ |
522 # | |_| |_|_|\___| |____/|_/___\___||___/ |
524 # '----------------------------------------------------------------------'
527 def check_mssql_file_sizes(item
, params
, parsed
):
528 obj
, instance
= item
.split()
529 node_data
= parsed
.get((obj
, instance
), {})
531 if not any(node_data
.values()):
532 # Assume general connection problem to the database, which is reported
533 # by the "X Instance" service and skip this check.
534 raise MKCounterWrapped("Failed to connect to database")
539 for node_name
, counters
in node_data
.iteritems():
542 node_info
= "[%s] " % node_name
544 log_files_size
= counters
.get("log_file(s)_size_(kb)")
545 for val_bytes
, key
, title
in [
546 (counters
.get("data_file(s)_size_(kb)"), "data_files", "Data files"),
547 (log_files_size
, "log_files", "Log files total"),
549 if val_bytes
is None:
552 val_bytes
= val_bytes
* 1024
553 infotext
= "%s%s: %s" % (node_info
, title
, get_bytes_human_readable(val_bytes
))
557 warn
, crit
= params
.get(key
, (None, None))
558 if crit
is not None and val_bytes
>= crit
:
560 elif warn
is not None and val_bytes
>= warn
:
563 infotext
+= " (warn/crit at %s/%s)" % (get_bytes_human_readable(warn
),
564 get_bytes_human_readable(crit
))
566 yield state
, infotext
, [(key
, val_bytes
, warn
, crit
)]
568 log_files_used
= counters
.get("log_file(s)_used_size_(kb)")
569 infotext
= "Log files used: %s" % get_bytes_human_readable(log_files_used
)
571 log_files_used_perc
= 100.0 * log_files_used
/ log_files_size
572 infotext
+= ", %s" % get_percent_human_readable(log_files_used_perc
)
573 except (TypeError, ZeroDivisionError):
574 log_files_used_perc
= None
576 warn
, crit
= params
.get("log_files_used", (None, None))
577 if isinstance(crit
, float) and log_files_used_perc
is not None:
578 log_files_used_value
= log_files_used_perc
579 readable_f
= get_percent_human_readable
580 elif isinstance(warn
, int):
581 log_files_used_value
= log_files_used
582 readable_f
= get_bytes_human_readable
588 if crit
is not None and log_files_used_value
>= crit
:
590 elif warn
is not None and log_files_used_value
>= warn
:
593 infotext
+= " (warn/crit at %s/%s)" % (readable_f(warn
), readable_f(crit
))
594 yield state
, infotext
597 check_info
['mssql_counters.file_sizes'] = {
598 'inventory_function' : lambda parsed
: inventory_mssql_counters_generic(parsed
,
599 ['data_file(s)_size_(kb)', 'log_file(s)_size_(kb)', 'log_file(s)_used_size_(kb)'],
601 'check_function' : check_mssql_file_sizes
,
602 'service_description' : "MSSQL %s File Sizes",
603 'has_perfdata' : True,
604 'group' : "mssql_file_sizes",