diff options
author | Holger Weiss <holger@zedat.fu-berlin.de> | 2012-07-04 21:50:48 (GMT) |
---|---|---|
committer | Holger Weiss <holger@zedat.fu-berlin.de> | 2012-07-04 21:50:48 (GMT) |
commit | 800a86871305a482a6377e6835529e6abed34161 (patch) | |
tree | fd85f5a03721a231eadc17cae9af2e9501555f10 /plugins | |
parent | 49df5964eb6827eb3180f1dfc01c8efaa2859492 (diff) | |
parent | e056cc9d8279fdb76ffd77dfeaed4fb13f95cef0 (diff) | |
download | monitoring-plugins-800a86871305a482a6377e6835529e6abed34161.tar.gz |
Merge remote-tracking branch 'github/tokkee/sh/check_dbi'
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/Makefile.am | 4 | ||||
-rw-r--r-- | plugins/check_dbi.c | 817 | ||||
-rw-r--r-- | plugins/t/check_dbi.t | 103 |
3 files changed, 923 insertions, 1 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 3a2afc1..0eb0255 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am | |||
@@ -37,7 +37,7 @@ check_tcp_programs = check_ftp check_imap check_nntp check_pop \ | |||
37 | EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \ | 37 | EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \ |
38 | check_swap check_fping check_ldap check_game check_dig \ | 38 | check_swap check_fping check_ldap check_game check_dig \ |
39 | check_nagios check_by_ssh check_dns check_nt check_ide_smart \ | 39 | check_nagios check_by_ssh check_dns check_nt check_ide_smart \ |
40 | check_procs check_mysql_query check_apt | 40 | check_procs check_mysql_query check_apt check_dbi |
41 | 41 | ||
42 | EXTRA_DIST = t tests utils.c netutils.c sslutils.c popen.c utils.h netutils.h \ | 42 | EXTRA_DIST = t tests utils.c netutils.c sslutils.c popen.c utils.h netutils.h \ |
43 | popen.h common.h runcmd.c runcmd.h | 43 | popen.h common.h runcmd.c runcmd.h |
@@ -64,6 +64,7 @@ test-debug: | |||
64 | 64 | ||
65 | check_apt_LDADD = $(BASEOBJS) runcmd.o | 65 | check_apt_LDADD = $(BASEOBJS) runcmd.o |
66 | check_cluster_LDADD = $(BASEOBJS) | 66 | check_cluster_LDADD = $(BASEOBJS) |
67 | check_dbi_LDADD = $(NETLIBS) $(DBILIBS) | ||
67 | check_dig_LDADD = $(NETLIBS) runcmd.o | 68 | check_dig_LDADD = $(NETLIBS) runcmd.o |
68 | check_disk_LDADD = $(BASEOBJS) popen.o | 69 | check_disk_LDADD = $(BASEOBJS) popen.o |
69 | check_dns_LDADD = $(NETLIBS) runcmd.o | 70 | check_dns_LDADD = $(NETLIBS) runcmd.o |
@@ -109,6 +110,7 @@ urlize_LDADD = $(BASEOBJS) popen.o | |||
109 | 110 | ||
110 | check_apt_DEPENDENCIES = check_apt.c $(BASEOBJS) runcmd.o $(DEPLIBS) | 111 | check_apt_DEPENDENCIES = check_apt.c $(BASEOBJS) runcmd.o $(DEPLIBS) |
111 | check_cluster_DEPENDENCIES = check_cluster.c $(BASEOBJS) $(DEPLIBS) | 112 | check_cluster_DEPENDENCIES = check_cluster.c $(BASEOBJS) $(DEPLIBS) |
113 | check_dbi_DEPENDENCIES = check_dbi.c $(NETOBJS) $(DEPLIBS) | ||
112 | check_dig_DEPENDENCIES = check_dig.c $(NETOBJS) runcmd.o $(DEPLIBS) | 114 | check_dig_DEPENDENCIES = check_dig.c $(NETOBJS) runcmd.o $(DEPLIBS) |
113 | check_disk_DEPENDENCIES = check_disk.c $(BASEOBJS) popen.o $(DEPLIBS) | 115 | check_disk_DEPENDENCIES = check_disk.c $(BASEOBJS) popen.o $(DEPLIBS) |
114 | check_dns_DEPENDENCIES = check_dns.c $(NETOBJS) runcmd.o $(DEPLIBS) | 116 | check_dns_DEPENDENCIES = check_dns.c $(NETOBJS) runcmd.o $(DEPLIBS) |
diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c new file mode 100644 index 0000000..8c4a511 --- /dev/null +++ b/plugins/check_dbi.c | |||
@@ -0,0 +1,817 @@ | |||
1 | /***************************************************************************** | ||
2 | * | ||
3 | * Nagios check_dbi plugin | ||
4 | * | ||
5 | * License: GPL | ||
6 | * Copyright (c) 2011 Nagios Plugins Development Team | ||
7 | * Author: Sebastian 'tokkee' Harl <sh@teamix.net> | ||
8 | * | ||
9 | * Description: | ||
10 | * | ||
11 | * This file contains the check_dbi plugin | ||
12 | * | ||
13 | * Runs an arbitrary (SQL) command and checks the result. | ||
14 | * | ||
15 | * | ||
16 | * This program is free software: you can redistribute it and/or modify | ||
17 | * it under the terms of the GNU General Public License as published by | ||
18 | * the Free Software Foundation, either version 3 of the License, or | ||
19 | * (at your option) any later version. | ||
20 | * | ||
21 | * This program is distributed in the hope that it will be useful, | ||
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
24 | * GNU General Public License for more details. | ||
25 | * | ||
26 | * You should have received a copy of the GNU General Public License | ||
27 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
28 | * | ||
29 | * | ||
30 | *****************************************************************************/ | ||
31 | |||
32 | const char *progname = "check_dbi"; | ||
33 | const char *copyright = "2011"; | ||
34 | const char *email = "nagiosplug-devel@lists.sourceforge.net"; | ||
35 | |||
36 | #include "common.h" | ||
37 | #include "utils.h" | ||
38 | |||
39 | #include "netutils.h" | ||
40 | |||
41 | #include "regex.h" | ||
42 | |||
43 | /* required for NAN */ | ||
44 | #ifndef _ISOC99_SOURCE | ||
45 | #define _ISOC99_SOURCE | ||
46 | #endif | ||
47 | |||
48 | #include <assert.h> | ||
49 | #include <math.h> | ||
50 | |||
51 | #include <dbi/dbi.h> | ||
52 | |||
53 | #include <stdarg.h> | ||
54 | |||
55 | typedef enum { | ||
56 | METRIC_CONN_TIME, | ||
57 | METRIC_SERVER_VERSION, | ||
58 | METRIC_QUERY_RESULT, | ||
59 | METRIC_QUERY_TIME, | ||
60 | } np_dbi_metric_t; | ||
61 | |||
62 | typedef enum { | ||
63 | TYPE_NUMERIC, | ||
64 | TYPE_STRING, | ||
65 | } np_dbi_type_t; | ||
66 | |||
67 | typedef struct { | ||
68 | char *key; | ||
69 | char *value; | ||
70 | } driver_option_t; | ||
71 | |||
72 | char *host = NULL; | ||
73 | int verbose = 0; | ||
74 | |||
75 | char *warning_range = NULL; | ||
76 | char *critical_range = NULL; | ||
77 | thresholds *dbi_thresholds = NULL; | ||
78 | |||
79 | char *expect = NULL; | ||
80 | |||
81 | regex_t expect_re; | ||
82 | char *expect_re_str = NULL; | ||
83 | int expect_re_cflags = 0; | ||
84 | |||
85 | np_dbi_metric_t metric = METRIC_QUERY_RESULT; | ||
86 | np_dbi_type_t type = TYPE_NUMERIC; | ||
87 | |||
88 | char *np_dbi_driver = NULL; | ||
89 | driver_option_t *np_dbi_options = NULL; | ||
90 | int np_dbi_options_num = 0; | ||
91 | char *np_dbi_database = NULL; | ||
92 | char *np_dbi_query = NULL; | ||
93 | |||
94 | int process_arguments (int, char **); | ||
95 | int validate_arguments (void); | ||
96 | void print_usage (void); | ||
97 | void print_help (void); | ||
98 | |||
99 | double timediff (struct timeval, struct timeval); | ||
100 | |||
101 | void np_dbi_print_error (dbi_conn, char *, ...); | ||
102 | |||
103 | int do_query (dbi_conn, const char **, double *, double *); | ||
104 | |||
105 | int | ||
106 | main (int argc, char **argv) | ||
107 | { | ||
108 | int status = STATE_UNKNOWN; | ||
109 | |||
110 | dbi_driver driver; | ||
111 | dbi_conn conn; | ||
112 | |||
113 | unsigned int server_version; | ||
114 | |||
115 | struct timeval start_timeval, end_timeval; | ||
116 | double conn_time = 0.0; | ||
117 | double query_time = 0.0; | ||
118 | |||
119 | const char *query_val_str = NULL; | ||
120 | double query_val = 0.0; | ||
121 | |||
122 | int i; | ||
123 | |||
124 | setlocale (LC_ALL, ""); | ||
125 | bindtextdomain (PACKAGE, LOCALEDIR); | ||
126 | textdomain (PACKAGE); | ||
127 | |||
128 | /* Parse extra opts if any */ | ||
129 | argv = np_extra_opts (&argc, argv, progname); | ||
130 | |||
131 | if (process_arguments (argc, argv) == ERROR) | ||
132 | usage4 (_("Could not parse arguments")); | ||
133 | |||
134 | /* Set signal handling and alarm */ | ||
135 | if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { | ||
136 | usage4 (_("Cannot catch SIGALRM")); | ||
137 | } | ||
138 | alarm (timeout_interval); | ||
139 | |||
140 | if (verbose > 2) | ||
141 | printf ("Initializing DBI\n"); | ||
142 | |||
143 | if (dbi_initialize (NULL) < 0) { | ||
144 | printf ("UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n"); | ||
145 | return STATE_UNKNOWN; | ||
146 | } | ||
147 | |||
148 | if (verbose) | ||
149 | printf ("Opening DBI driver '%s'\n", np_dbi_driver); | ||
150 | |||
151 | driver = dbi_driver_open (np_dbi_driver); | ||
152 | if (! driver) { | ||
153 | printf ("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", | ||
154 | np_dbi_driver); | ||
155 | |||
156 | printf ("Known drivers:\n"); | ||
157 | for (driver = dbi_driver_list (NULL); driver; driver = dbi_driver_list (driver)) { | ||
158 | printf (" - %s\n", dbi_driver_get_name (driver)); | ||
159 | } | ||
160 | return STATE_UNKNOWN; | ||
161 | } | ||
162 | |||
163 | /* make a connection to the database */ | ||
164 | gettimeofday (&start_timeval, NULL); | ||
165 | |||
166 | conn = dbi_conn_open (driver); | ||
167 | if (! conn) { | ||
168 | printf ("UNKNOWN - failed top open connection object.\n"); | ||
169 | dbi_conn_close (conn); | ||
170 | return STATE_UNKNOWN; | ||
171 | } | ||
172 | |||
173 | for (i = 0; i < np_dbi_options_num; ++i) { | ||
174 | const char *opt; | ||
175 | |||
176 | if (verbose > 1) | ||
177 | printf ("Setting DBI driver option '%s' to '%s'\n", | ||
178 | np_dbi_options[i].key, np_dbi_options[i].value); | ||
179 | |||
180 | if (! dbi_conn_set_option (conn, np_dbi_options[i].key, np_dbi_options[i].value)) | ||
181 | continue; | ||
182 | /* else: status != 0 */ | ||
183 | |||
184 | np_dbi_print_error (conn, "UNKNOWN - failed to set option '%s' to '%s'", | ||
185 | np_dbi_options[i].key, np_dbi_options[i].value); | ||
186 | printf ("Known driver options:\n"); | ||
187 | |||
188 | for (opt = dbi_conn_get_option_list (conn, NULL); opt; | ||
189 | opt = dbi_conn_get_option_list (conn, opt)) { | ||
190 | printf (" - %s\n", opt); | ||
191 | } | ||
192 | dbi_conn_close (conn); | ||
193 | return STATE_UNKNOWN; | ||
194 | } | ||
195 | |||
196 | if (host) { | ||
197 | if (verbose > 1) | ||
198 | printf ("Setting DBI driver option 'host' to '%s'\n", host); | ||
199 | dbi_conn_set_option (conn, "host", host); | ||
200 | } | ||
201 | |||
202 | if (verbose) { | ||
203 | const char *dbname, *host; | ||
204 | |||
205 | dbname = dbi_conn_get_option (conn, "dbname"); | ||
206 | host = dbi_conn_get_option (conn, "host"); | ||
207 | |||
208 | if (! dbname) | ||
209 | dbname = "<unspecified>"; | ||
210 | if (! host) | ||
211 | host = "<unspecified>"; | ||
212 | |||
213 | printf ("Connecting to database '%s' at host '%s'\n", | ||
214 | dbname, host); | ||
215 | } | ||
216 | |||
217 | if (dbi_conn_connect (conn) < 0) { | ||
218 | np_dbi_print_error (conn, "UNKOWN - failed to connect to database"); | ||
219 | return STATE_UNKNOWN; | ||
220 | } | ||
221 | |||
222 | gettimeofday (&end_timeval, NULL); | ||
223 | conn_time = timediff (start_timeval, end_timeval); | ||
224 | |||
225 | server_version = dbi_conn_get_engine_version (conn); | ||
226 | if (verbose) | ||
227 | printf ("Connected to server version %u\n", server_version); | ||
228 | |||
229 | if (metric == METRIC_SERVER_VERSION) | ||
230 | status = get_status (server_version, dbi_thresholds); | ||
231 | |||
232 | if (verbose) | ||
233 | printf ("Time elapsed: %f\n", conn_time); | ||
234 | |||
235 | if (metric == METRIC_CONN_TIME) | ||
236 | status = get_status (conn_time, dbi_thresholds); | ||
237 | |||
238 | /* select a database */ | ||
239 | if (np_dbi_database) { | ||
240 | if (verbose > 1) | ||
241 | printf ("Selecting database '%s'\n", np_dbi_database); | ||
242 | |||
243 | if (dbi_conn_select_db (conn, np_dbi_database)) { | ||
244 | np_dbi_print_error (conn, "UNKOWN - failed to select database '%s'", | ||
245 | np_dbi_database); | ||
246 | return STATE_UNKNOWN; | ||
247 | } | ||
248 | } | ||
249 | |||
250 | if (np_dbi_query) { | ||
251 | /* execute query */ | ||
252 | status = do_query (conn, &query_val_str, &query_val, &query_time); | ||
253 | if (status != STATE_OK) | ||
254 | /* do_query prints an error message in this case */ | ||
255 | return status; | ||
256 | |||
257 | if (metric == METRIC_QUERY_RESULT) { | ||
258 | if (expect) { | ||
259 | if ((! query_val_str) || strcmp (query_val_str, expect)) | ||
260 | status = STATE_CRITICAL; | ||
261 | else | ||
262 | status = STATE_OK; | ||
263 | } | ||
264 | else if (expect_re_str) { | ||
265 | int err; | ||
266 | |||
267 | err = regexec (&expect_re, query_val_str, 0, NULL, /* flags = */ 0); | ||
268 | if (! err) | ||
269 | status = STATE_OK; | ||
270 | else if (err == REG_NOMATCH) | ||
271 | status = STATE_CRITICAL; | ||
272 | else { | ||
273 | char errmsg[1024]; | ||
274 | regerror (err, &expect_re, errmsg, sizeof (errmsg)); | ||
275 | printf ("ERROR - failed to execute regular expression: %s\n", | ||
276 | errmsg); | ||
277 | status = STATE_CRITICAL; | ||
278 | } | ||
279 | } | ||
280 | else | ||
281 | status = get_status (query_val, dbi_thresholds); | ||
282 | } | ||
283 | else if (metric == METRIC_QUERY_TIME) | ||
284 | status = get_status (query_time, dbi_thresholds); | ||
285 | } | ||
286 | |||
287 | if (verbose) | ||
288 | printf("Closing connection\n"); | ||
289 | dbi_conn_close (conn); | ||
290 | |||
291 | /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error | ||
292 | * which should have been reported and handled (abort) before | ||
293 | * ... unless we expected a string to be returned */ | ||
294 | assert ((metric != METRIC_QUERY_RESULT) || (! isnan (query_val)) | ||
295 | || (type == TYPE_STRING)); | ||
296 | |||
297 | assert ((type != TYPE_STRING) || (expect || expect_re_str)); | ||
298 | |||
299 | printf ("%s - connection time: %fs", state_text (status), conn_time); | ||
300 | if (np_dbi_query) { | ||
301 | if (type == TYPE_STRING) { | ||
302 | assert (expect || expect_re_str); | ||
303 | printf (", '%s' returned '%s' in %fs", np_dbi_query, | ||
304 | query_val_str ? query_val_str : "<nothing>", query_time); | ||
305 | if (status != STATE_OK) { | ||
306 | if (expect) | ||
307 | printf (" (expected '%s')", expect); | ||
308 | else if (expect_re_str) | ||
309 | printf (" (expected regex /%s/%s)", expect_re_str, | ||
310 | ((expect_re_cflags & REG_ICASE) ? "i" : "")); | ||
311 | } | ||
312 | } | ||
313 | else if (isnan (query_val)) | ||
314 | printf (", '%s' query execution time: %fs", np_dbi_query, query_time); | ||
315 | else | ||
316 | printf (", '%s' returned %f in %fs", np_dbi_query, query_val, query_time); | ||
317 | } | ||
318 | |||
319 | printf (" | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time, | ||
320 | ((metric == METRIC_CONN_TIME) && warning_range) ? warning_range : "", | ||
321 | ((metric == METRIC_CONN_TIME) && critical_range) ? critical_range : "", | ||
322 | server_version, | ||
323 | ((metric == METRIC_SERVER_VERSION) && warning_range) ? warning_range : "", | ||
324 | ((metric == METRIC_SERVER_VERSION) && critical_range) ? critical_range : ""); | ||
325 | if (np_dbi_query) { | ||
326 | if (! isnan (query_val)) /* this is also true when -e is used */ | ||
327 | printf (" query=%f;%s;%s;;", query_val, | ||
328 | ((metric == METRIC_QUERY_RESULT) && warning_range) ? warning_range : "", | ||
329 | ((metric == METRIC_QUERY_RESULT) && critical_range) ? critical_range : ""); | ||
330 | printf (" querytime=%fs;%s;%s;0;", query_time, | ||
331 | ((metric == METRIC_QUERY_TIME) && warning_range) ? warning_range : "", | ||
332 | ((metric == METRIC_QUERY_TIME) && critical_range) ? critical_range : ""); | ||
333 | } | ||
334 | printf ("\n"); | ||
335 | return status; | ||
336 | } | ||
337 | |||
338 | /* process command-line arguments */ | ||
339 | int | ||
340 | process_arguments (int argc, char **argv) | ||
341 | { | ||
342 | int c; | ||
343 | |||
344 | int option = 0; | ||
345 | static struct option longopts[] = { | ||
346 | STD_LONG_OPTS, | ||
347 | |||
348 | {"expect", required_argument, 0, 'e'}, | ||
349 | {"regex", required_argument, 0, 'r'}, | ||
350 | {"regexi", required_argument, 0, 'R'}, | ||
351 | {"metric", required_argument, 0, 'm'}, | ||
352 | {"driver", required_argument, 0, 'd'}, | ||
353 | {"option", required_argument, 0, 'o'}, | ||
354 | {"query", required_argument, 0, 'q'}, | ||
355 | {"database", required_argument, 0, 'D'}, | ||
356 | {0, 0, 0, 0} | ||
357 | }; | ||
358 | |||
359 | while (1) { | ||
360 | c = getopt_long (argc, argv, "Vvht:c:w:e:r:R:m:H:d:o:q:D:", | ||
361 | longopts, &option); | ||
362 | |||
363 | if (c == EOF) | ||
364 | break; | ||
365 | |||
366 | switch (c) { | ||
367 | case '?': /* usage */ | ||
368 | usage5 (); | ||
369 | case 'h': /* help */ | ||
370 | print_help (); | ||
371 | exit (STATE_OK); | ||
372 | case 'V': /* version */ | ||
373 | print_revision (progname, NP_VERSION); | ||
374 | exit (STATE_OK); | ||
375 | |||
376 | case 'c': /* critical range */ | ||
377 | critical_range = optarg; | ||
378 | type = TYPE_NUMERIC; | ||
379 | break; | ||
380 | case 'w': /* warning range */ | ||
381 | warning_range = optarg; | ||
382 | type = TYPE_NUMERIC; | ||
383 | break; | ||
384 | case 'e': | ||
385 | expect = optarg; | ||
386 | type = TYPE_STRING; | ||
387 | break; | ||
388 | case 'R': | ||
389 | expect_re_cflags = REG_ICASE; | ||
390 | /* fall through */ | ||
391 | case 'r': | ||
392 | { | ||
393 | int err; | ||
394 | |||
395 | expect_re_cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; | ||
396 | expect_re_str = optarg; | ||
397 | type = TYPE_STRING; | ||
398 | |||
399 | err = regcomp (&expect_re, expect_re_str, expect_re_cflags); | ||
400 | if (err) { | ||
401 | char errmsg[1024]; | ||
402 | regerror (err, &expect_re, errmsg, sizeof (errmsg)); | ||
403 | printf ("ERROR - failed to compile regular expression: %s\n", | ||
404 | errmsg); | ||
405 | return ERROR; | ||
406 | } | ||
407 | break; | ||
408 | } | ||
409 | |||
410 | case 'm': | ||
411 | if (! strcasecmp (optarg, "CONN_TIME")) | ||
412 | metric = METRIC_CONN_TIME; | ||
413 | else if (! strcasecmp (optarg, "SERVER_VERSION")) | ||
414 | metric = METRIC_SERVER_VERSION; | ||
415 | else if (! strcasecmp (optarg, "QUERY_RESULT")) | ||
416 | metric = METRIC_QUERY_RESULT; | ||
417 | else if (! strcasecmp (optarg, "QUERY_TIME")) | ||
418 | metric = METRIC_QUERY_TIME; | ||
419 | else | ||
420 | usage2 (_("Invalid metric"), optarg); | ||
421 | break; | ||
422 | case 't': /* timeout */ | ||
423 | if (!is_intnonneg (optarg)) | ||
424 | usage2 (_("Timeout interval must be a positive integer"), optarg); | ||
425 | else | ||
426 | timeout_interval = atoi (optarg); | ||
427 | |||
428 | case 'H': /* host */ | ||
429 | if (!is_host (optarg)) | ||
430 | usage2 (_("Invalid hostname/address"), optarg); | ||
431 | else | ||
432 | host = optarg; | ||
433 | break; | ||
434 | case 'v': | ||
435 | verbose++; | ||
436 | break; | ||
437 | |||
438 | case 'd': | ||
439 | np_dbi_driver = optarg; | ||
440 | break; | ||
441 | case 'o': | ||
442 | { | ||
443 | driver_option_t *new; | ||
444 | |||
445 | char *k, *v; | ||
446 | |||
447 | k = optarg; | ||
448 | v = strchr (k, (int)'='); | ||
449 | |||
450 | if (! v) | ||
451 | usage2 (_("Option must be '<key>=<value>'"), optarg); | ||
452 | |||
453 | *v = '\0'; | ||
454 | ++v; | ||
455 | |||
456 | new = realloc (np_dbi_options, | ||
457 | (np_dbi_options_num + 1) * sizeof (*new)); | ||
458 | if (! new) { | ||
459 | printf ("UNKOWN - failed to reallocate memory\n"); | ||
460 | exit (STATE_UNKNOWN); | ||
461 | } | ||
462 | |||
463 | np_dbi_options = new; | ||
464 | new = np_dbi_options + np_dbi_options_num; | ||
465 | ++np_dbi_options_num; | ||
466 | |||
467 | new->key = k; | ||
468 | new->value = v; | ||
469 | } | ||
470 | break; | ||
471 | case 'q': | ||
472 | np_dbi_query = optarg; | ||
473 | break; | ||
474 | case 'D': | ||
475 | np_dbi_database = optarg; | ||
476 | break; | ||
477 | } | ||
478 | } | ||
479 | |||
480 | set_thresholds (&dbi_thresholds, warning_range, critical_range); | ||
481 | |||
482 | return validate_arguments (); | ||
483 | } | ||
484 | |||
485 | int | ||
486 | validate_arguments () | ||
487 | { | ||
488 | if (! np_dbi_driver) | ||
489 | usage ("Must specify a DBI driver"); | ||
490 | |||
491 | if (((metric == METRIC_QUERY_RESULT) || (metric == METRIC_QUERY_TIME)) | ||
492 | && (! np_dbi_query)) | ||
493 | usage ("Must specify a query to execute (metric == QUERY_RESULT)"); | ||
494 | |||
495 | if ((metric != METRIC_CONN_TIME) | ||
496 | && (metric != METRIC_SERVER_VERSION) | ||
497 | && (metric != METRIC_QUERY_RESULT) | ||
498 | && (metric != METRIC_QUERY_TIME)) | ||
499 | usage ("Invalid metric specified"); | ||
500 | |||
501 | if (expect && (warning_range || critical_range || expect_re_str)) | ||
502 | usage ("Do not mix -e and -w/-c/-r/-R"); | ||
503 | |||
504 | if (expect_re_str && (warning_range || critical_range || expect)) | ||
505 | usage ("Do not mix -r/-R and -w/-c/-e"); | ||
506 | |||
507 | if (expect && (metric != METRIC_QUERY_RESULT)) | ||
508 | usage ("Option -e requires metric QUERY_RESULT"); | ||
509 | |||
510 | if (expect_re_str && (metric != METRIC_QUERY_RESULT)) | ||
511 | usage ("Options -r/-R require metric QUERY_RESULT"); | ||
512 | |||
513 | return OK; | ||
514 | } | ||
515 | |||
516 | void | ||
517 | print_help (void) | ||
518 | { | ||
519 | print_revision (progname, NP_VERSION); | ||
520 | |||
521 | printf (COPYRIGHT, copyright, email); | ||
522 | |||
523 | printf (_("This program connects to an (SQL) database using DBI and checks the\n" | ||
524 | "specified metric against threshold levels. The default metric is\n" | ||
525 | "the result of the specified query.\n")); | ||
526 | |||
527 | printf ("\n\n"); | ||
528 | |||
529 | print_usage (); | ||
530 | |||
531 | printf (UT_HELP_VRSN); | ||
532 | /* include this conditionally to avoid 'zero-length printf format string' | ||
533 | * compiler warnings */ | ||
534 | #ifdef NP_EXTRA_OPTS | ||
535 | printf (UT_EXTRA_OPTS); | ||
536 | #endif | ||
537 | printf ("\n"); | ||
538 | |||
539 | printf (" %s\n", "-d, --driver=STRING"); | ||
540 | printf (" %s\n", _("DBI driver to use")); | ||
541 | printf (" %s\n", "-o, --option=STRING"); | ||
542 | printf (" %s\n", _("DBI driver options")); | ||
543 | printf (" %s\n", "-q, --query=STRING"); | ||
544 | printf (" %s\n", _("query to execute")); | ||
545 | printf ("\n"); | ||
546 | |||
547 | printf (UT_WARN_CRIT_RANGE); | ||
548 | printf (" %s\n", "-e, --expect=STRING"); | ||
549 | printf (" %s\n", _("String to expect as query result")); | ||
550 | printf (" %s\n", _("Do not mix with -w, -c, -r, or -R!")); | ||
551 | printf (" %s\n", "-r, --regex=REGEX"); | ||
552 | printf (" %s\n", _("Extended POSIX regular expression to check query result against")); | ||
553 | printf (" %s\n", _("Do not mix with -w, -c, -e, or -R!")); | ||
554 | printf (" %s\n", "-R, --regexi=REGEX"); | ||
555 | printf (" %s\n", _("Case-insensitive extended POSIX regex to check query result against")); | ||
556 | printf (" %s\n", _("Do not mix with -w, -c, -e, or -r!")); | ||
557 | printf (" %s\n", "-m, --metric=METRIC"); | ||
558 | printf (" %s\n", _("Metric to check thresholds against. Available metrics:")); | ||
559 | printf (" CONN_TIME - %s\n", _("time used for setting up the database connection")); | ||
560 | printf (" QUERY_RESULT - %s\n", _("result (first column of first row) of the query")); | ||
561 | printf (" QUERY_TIME - %s\n", _("time used to execute the query")); | ||
562 | printf (" %s\n", _("(ignore the query result)")); | ||
563 | printf ("\n"); | ||
564 | |||
565 | printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); | ||
566 | |||
567 | printf (UT_VERBOSE); | ||
568 | |||
569 | printf ("\n"); | ||
570 | printf (" %s\n", _("A DBI driver (-d option) is required. If the specified metric operates")); | ||
571 | printf (" %s\n\n", _("on a query, one has to be specified (-q option).")); | ||
572 | |||
573 | printf (" %s\n", _("This plugin connects to an (SQL) database using libdbi and, optionally,")); | ||
574 | printf (" %s\n", _("executes the specified query. The first column of the first row of the")); | ||
575 | printf (" %s\n", _("result will be parsed and, in QUERY_RESULT mode, compared with the")); | ||
576 | printf (" %s\n", _("warning and critical ranges. The result from the query has to be numeric")); | ||
577 | printf (" %s\n\n", _("(strings representing numbers are fine).")); | ||
578 | |||
579 | printf (" %s\n", _("The number and type of required DBI driver options depends on the actual")); | ||
580 | printf (" %s\n", _("driver. See its documentation at http://libdbi-drivers.sourceforge.net/")); | ||
581 | printf (" %s\n\n", _("for details.")); | ||
582 | |||
583 | printf (" %s\n", _("Examples:")); | ||
584 | printf (" check_dbi -d pgsql -o username=postgres -m QUERY_RESULT \\\n"); | ||
585 | printf (" -q 'SELECT COUNT(*) FROM pg_stat_activity' -w 5 -c 10\n"); | ||
586 | printf (" Warning if more than five connections; critical if more than ten.\n\n"); | ||
587 | |||
588 | printf (" check_dbi -d mysql -H localhost -o username=user -o password=secret \\\n"); | ||
589 | printf (" -q 'SELECT COUNT(*) FROM logged_in_users -w 5:20 -c 0:50\n"); | ||
590 | printf (" Warning if less than 5 or more than 20 users are logged in; critical\n"); | ||
591 | printf (" if more than 50 users.\n\n"); | ||
592 | |||
593 | printf (" check_dbi -d firebird -o username=user -o password=secret -o dbname=foo \\\n"); | ||
594 | printf (" -m CONN_TIME -w 0.5 -c 2\n"); | ||
595 | printf (" Warning if connecting to the database takes more than half of a second;\n"); | ||
596 | printf (" critical if it takes more than 2 seconds.\n\n"); | ||
597 | |||
598 | printf (" check_dbi -d mysql -H localhost -o username=user \\\n"); | ||
599 | printf (" -q 'SELECT concat(@@version, \" \", @@version_comment)' \\\n"); | ||
600 | printf (" -r '^5\\.[01].*MySQL Enterprise Server'\n"); | ||
601 | printf (" Critical if the database server is not a MySQL enterprise server in either\n"); | ||
602 | printf (" version 5.0.x or 5.1.x.\n\n"); | ||
603 | |||
604 | printf (" check_dbi -d pgsql -u username=user -m SERVER_VERSION \\\n"); | ||
605 | printf (" -w 090000:090099 -c 090000:090199\n"); | ||
606 | printf (" Warn if the PostgreSQL server version is not 9.0.x; critical if the version\n"); | ||
607 | printf (" is less than 9.x or higher than 9.1.x.\n"); | ||
608 | |||
609 | printf (UT_SUPPORT); | ||
610 | } | ||
611 | |||
612 | void | ||
613 | print_usage (void) | ||
614 | { | ||
615 | printf ("%s\n", _("Usage:")); | ||
616 | printf ("%s -d <DBI driver> [-o <DBI driver option> [...]] [-q <query>]\n", progname); | ||
617 | printf (" [-H <host>] [-c <critical range>] [-w <warning range>] [-m <metric>]\n"); | ||
618 | printf (" [-e <string>] [-r|-R <regex>]\n"); | ||
619 | } | ||
620 | |||
621 | #define CHECK_IGNORE_ERROR(s) \ | ||
622 | do { \ | ||
623 | if (metric != METRIC_QUERY_RESULT) \ | ||
624 | return (s); \ | ||
625 | } while (0) | ||
626 | |||
627 | const char * | ||
628 | get_field_str (dbi_conn conn, dbi_result res, unsigned short field_type) | ||
629 | { | ||
630 | const char *str; | ||
631 | |||
632 | if (field_type != DBI_TYPE_STRING) { | ||
633 | printf ("CRITICAL - result value is not a string\n"); | ||
634 | return NULL; | ||
635 | } | ||
636 | |||
637 | str = dbi_result_get_string_idx (res, 1); | ||
638 | if ((! str) || (strcmp (str, "ERROR") == 0)) { | ||
639 | CHECK_IGNORE_ERROR (NULL); | ||
640 | np_dbi_print_error (conn, "CRITICAL - failed to fetch string value"); | ||
641 | return NULL; | ||
642 | } | ||
643 | |||
644 | if ((verbose && (type == TYPE_STRING)) || (verbose > 2)) | ||
645 | printf ("Query returned string '%s'\n", str); | ||
646 | return str; | ||
647 | } | ||
648 | |||
649 | double | ||
650 | get_field (dbi_conn conn, dbi_result res, unsigned short *field_type) | ||
651 | { | ||
652 | double val = NAN; | ||
653 | |||
654 | if (*field_type == DBI_TYPE_INTEGER) { | ||
655 | val = (double)dbi_result_get_longlong_idx (res, 1); | ||
656 | } | ||
657 | else if (*field_type == DBI_TYPE_DECIMAL) { | ||
658 | val = dbi_result_get_double_idx (res, 1); | ||
659 | } | ||
660 | else if (*field_type == DBI_TYPE_STRING) { | ||
661 | const char *val_str; | ||
662 | char *endptr = NULL; | ||
663 | |||
664 | val_str = get_field_str (conn, res, *field_type); | ||
665 | if (! val_str) { | ||
666 | CHECK_IGNORE_ERROR (NAN); | ||
667 | *field_type = DBI_TYPE_ERROR; | ||
668 | return NAN; | ||
669 | } | ||
670 | |||
671 | val = strtod (val_str, &endptr); | ||
672 | if (endptr == val_str) { | ||
673 | CHECK_IGNORE_ERROR (NAN); | ||
674 | printf ("CRITICAL - result value is not a numeric: %s\n", val_str); | ||
675 | *field_type = DBI_TYPE_ERROR; | ||
676 | return NAN; | ||
677 | } | ||
678 | else if ((endptr != NULL) && (*endptr != '\0')) { | ||
679 | if (verbose) | ||
680 | printf ("Garbage after value: %s\n", endptr); | ||
681 | } | ||
682 | } | ||
683 | else { | ||
684 | CHECK_IGNORE_ERROR (NAN); | ||
685 | printf ("CRITICAL - cannot parse value of type %s (%i)\n", | ||
686 | (*field_type == DBI_TYPE_BINARY) | ||
687 | ? "BINARY" | ||
688 | : (*field_type == DBI_TYPE_DATETIME) | ||
689 | ? "DATETIME" | ||
690 | : "<unknown>", | ||
691 | *field_type); | ||
692 | *field_type = DBI_TYPE_ERROR; | ||
693 | return NAN; | ||
694 | } | ||
695 | return val; | ||
696 | } | ||
697 | |||
698 | double | ||
699 | get_query_result (dbi_conn conn, dbi_result res, const char **res_val_str, double *res_val) | ||
700 | { | ||
701 | unsigned short field_type; | ||
702 | double val = NAN; | ||
703 | |||
704 | if (dbi_result_get_numrows (res) == DBI_ROW_ERROR) { | ||
705 | CHECK_IGNORE_ERROR (STATE_OK); | ||
706 | np_dbi_print_error (conn, "CRITICAL - failed to fetch rows"); | ||
707 | return STATE_CRITICAL; | ||
708 | } | ||
709 | |||
710 | if (dbi_result_get_numrows (res) < 1) { | ||
711 | CHECK_IGNORE_ERROR (STATE_OK); | ||
712 | printf ("WARNING - no rows returned\n"); | ||
713 | return STATE_WARNING; | ||
714 | } | ||
715 | |||
716 | if (dbi_result_get_numfields (res) == DBI_FIELD_ERROR) { | ||
717 | CHECK_IGNORE_ERROR (STATE_OK); | ||
718 | np_dbi_print_error (conn, "CRITICAL - failed to fetch fields"); | ||
719 | return STATE_CRITICAL; | ||
720 | } | ||
721 | |||
722 | if (dbi_result_get_numfields (res) < 1) { | ||
723 | CHECK_IGNORE_ERROR (STATE_OK); | ||
724 | printf ("WARNING - no fields returned\n"); | ||
725 | return STATE_WARNING; | ||
726 | } | ||
727 | |||
728 | if (dbi_result_first_row (res) != 1) { | ||
729 | CHECK_IGNORE_ERROR (STATE_OK); | ||
730 | np_dbi_print_error (conn, "CRITICAL - failed to fetch first row"); | ||
731 | return STATE_CRITICAL; | ||
732 | } | ||
733 | |||
734 | field_type = dbi_result_get_field_type_idx (res, 1); | ||
735 | if (field_type != DBI_TYPE_ERROR) { | ||
736 | if (type == TYPE_STRING) | ||
737 | /* the value will be freed in dbi_result_free */ | ||
738 | *res_val_str = strdup (get_field_str (conn, res, field_type)); | ||
739 | else | ||
740 | val = get_field (conn, res, &field_type); | ||
741 | } | ||
742 | |||
743 | *res_val = val; | ||
744 | |||
745 | if (field_type == DBI_TYPE_ERROR) { | ||
746 | CHECK_IGNORE_ERROR (STATE_OK); | ||
747 | np_dbi_print_error (conn, "CRITICAL - failed to fetch data"); | ||
748 | return STATE_CRITICAL; | ||
749 | } | ||
750 | |||
751 | dbi_result_free (res); | ||
752 | return STATE_OK; | ||
753 | } | ||
754 | |||
755 | #undef CHECK_IGNORE_ERROR | ||
756 | |||
757 | int | ||
758 | do_query (dbi_conn conn, const char **res_val_str, double *res_val, double *res_time) | ||
759 | { | ||
760 | dbi_result res; | ||
761 | |||
762 | struct timeval timeval_start, timeval_end; | ||
763 | int status = STATE_OK; | ||
764 | |||
765 | assert (np_dbi_query); | ||
766 | |||
767 | if (verbose) | ||
768 | printf ("Executing query '%s'\n", np_dbi_query); | ||
769 | |||
770 | gettimeofday (&timeval_start, NULL); | ||
771 | |||
772 | res = dbi_conn_query (conn, np_dbi_query); | ||
773 | if (! res) { | ||
774 | np_dbi_print_error (conn, "CRITICAL - failed to execute query '%s'", np_dbi_query); | ||
775 | return STATE_CRITICAL; | ||
776 | } | ||
777 | |||
778 | status = get_query_result (conn, res, res_val_str, res_val); | ||
779 | |||
780 | gettimeofday (&timeval_end, NULL); | ||
781 | *res_time = timediff (timeval_start, timeval_end); | ||
782 | |||
783 | if (verbose) | ||
784 | printf ("Time elapsed: %f\n", *res_time); | ||
785 | |||
786 | return status; | ||
787 | } | ||
788 | |||
789 | double | ||
790 | timediff (struct timeval start, struct timeval end) | ||
791 | { | ||
792 | double diff; | ||
793 | |||
794 | while (start.tv_usec > end.tv_usec) { | ||
795 | --end.tv_sec; | ||
796 | end.tv_usec += 1000000; | ||
797 | } | ||
798 | diff = (double)(end.tv_sec - start.tv_sec) | ||
799 | + (double)(end.tv_usec - start.tv_usec) / 1000000.0; | ||
800 | return diff; | ||
801 | } | ||
802 | |||
803 | void | ||
804 | np_dbi_print_error (dbi_conn conn, char *fmt, ...) | ||
805 | { | ||
806 | const char *errmsg = NULL; | ||
807 | va_list ap; | ||
808 | |||
809 | va_start (ap, fmt); | ||
810 | |||
811 | dbi_conn_error (conn, &errmsg); | ||
812 | vprintf (fmt, ap); | ||
813 | printf (": %s\n", errmsg); | ||
814 | |||
815 | va_end (ap); | ||
816 | } | ||
817 | |||
diff --git a/plugins/t/check_dbi.t b/plugins/t/check_dbi.t new file mode 100644 index 0000000..c24b5a8 --- /dev/null +++ b/plugins/t/check_dbi.t | |||
@@ -0,0 +1,103 @@ | |||
1 | #! /usr/bin/perl -w -I .. | ||
2 | # | ||
3 | # Database Server Tests via check_dbi | ||
4 | # | ||
5 | # | ||
6 | # Uses the 'sqlite3' DBD driver and command line utility. | ||
7 | |||
8 | use strict; | ||
9 | use Test::More; | ||
10 | use NPTest; | ||
11 | |||
12 | use File::Temp; | ||
13 | |||
14 | use vars qw($tests); | ||
15 | |||
16 | plan skip_all => "check_dbi not compiled" unless (-x "check_dbi"); | ||
17 | |||
18 | $tests = 20; | ||
19 | plan tests => $tests; | ||
20 | |||
21 | my $missing_driver_output = "failed to open DBI driver 'sqlite3'"; | ||
22 | |||
23 | my $bad_driver_output = "/failed to open DBI driver 'nodriver'/"; | ||
24 | my $conn_time_output = "/OK - connection time: [0-9\.]+s \|/"; | ||
25 | my $missing_query_output = "/Must specify a query to execute/"; | ||
26 | my $no_rows_output = "/WARNING - no rows returned/"; | ||
27 | my $not_numeric_output = "/CRITICAL - result value is not a numeric:/"; | ||
28 | my $query_time_output = "/OK - connection time: [0-9\.]+s, 'SELECT 1' returned 1.000000 in [0-9\.]+s \|/"; | ||
29 | my $syntax_error_output = "/CRITICAL - failed to execute query 'GET ALL FROM test': 1: near \"GET\": syntax error/"; | ||
30 | |||
31 | my $result; | ||
32 | |||
33 | SKIP: { | ||
34 | my $sqlite3 = qx(which sqlite3 2> /dev/null); | ||
35 | chomp($sqlite3); | ||
36 | |||
37 | skip "No Sqlite3 found", $tests unless $sqlite3; | ||
38 | |||
39 | my $sqlite3_check = qx(./check_dbi -d sqlite3 -q ''); | ||
40 | if ($sqlite3_check =~ m/$missing_driver_output/) { | ||
41 | skip "No 'sqlite3' DBD driver found", $tests; | ||
42 | } | ||
43 | |||
44 | my $fh = File::Temp->new( | ||
45 | TEMPLATE => "/tmp/check_dbi_sqlite3.XXXXXXX", | ||
46 | UNLINK => 1, | ||
47 | ); | ||
48 | my $filename = $fh->filename; | ||
49 | $filename =~ s/^\/tmp\///; | ||
50 | |||
51 | system("$sqlite3 /tmp/$filename 'CREATE TABLE test(a INT, b TEXT)'"); | ||
52 | system("$sqlite3 /tmp/$filename 'INSERT INTO test VALUES (1, \"text1\")'"); | ||
53 | system("$sqlite3 /tmp/$filename 'INSERT INTO test VALUES (2, \"text2\")'"); | ||
54 | |||
55 | my $check_cmd = "./check_dbi -d sqlite3 -o sqlite3_dbdir=/tmp -o dbname=$filename"; | ||
56 | |||
57 | $result = NPTest->testCmd("$check_cmd -q 'SELECT 1'"); | ||
58 | cmp_ok($result->return_code, '==', 0, "Sqlite3 login okay and can run query"); | ||
59 | |||
60 | $result = NPTest->testCmd("$check_cmd"); | ||
61 | cmp_ok($result->return_code, '==', 3, "Missing query parameter"); | ||
62 | like($result->output, $missing_query_output, "Missing query parameter error message"); | ||
63 | |||
64 | $result = NPTest->testCmd("$check_cmd -q 'GET ALL FROM test'"); | ||
65 | cmp_ok($result->return_code, '==', 2, "Invalid query"); | ||
66 | like($result->output, $syntax_error_output, "Syntax error message"); | ||
67 | |||
68 | $result = NPTest->testCmd("$check_cmd -q 'SELECT 2.71828' -w 2 -c 3"); | ||
69 | cmp_ok($result->return_code, '==', 1, "Got warning"); | ||
70 | |||
71 | $result = NPTest->testCmd("$check_cmd -q 'SELECT 3.1415' -w 2 -c 3"); | ||
72 | cmp_ok($result->return_code, '==', 2, "Got critical"); | ||
73 | |||
74 | $result = NPTest->testCmd("$check_cmd -q ''"); | ||
75 | cmp_ok($result->return_code, '==', 1, "No rows returned"); | ||
76 | like($result->output, $no_rows_output, "Now rows returned warning message"); | ||
77 | |||
78 | $result = NPTest->testCmd("$check_cmd -q 'SELECT b FROM test'"); | ||
79 | cmp_ok($result->return_code, '==', 2, "Value is not a numeric"); | ||
80 | like($result->output, $not_numeric_output, "Value is not a numeric error message"); | ||
81 | |||
82 | $result = NPTest->testCmd("$check_cmd -m QUERY_RESULT -q 'SELECT b FROM test' -e text1"); | ||
83 | cmp_ok($result->return_code, '==', 0, "Query result string comparison okay"); | ||
84 | |||
85 | $result = NPTest->testCmd("$check_cmd -q 'SELECT b FROM test' -r 'eXt[0-9]'"); | ||
86 | cmp_ok($result->return_code, '==', 2, "Query result case-insensitive regex failure"); | ||
87 | |||
88 | $result = NPTest->testCmd("$check_cmd -q 'SELECT b FROM test' -R 'eXt[0-9]'"); | ||
89 | cmp_ok($result->return_code, '==', 0, "Query result case-sensitive regex okay"); | ||
90 | |||
91 | $result = NPTest->testCmd("$check_cmd -m CONN_TIME -w 0.5 -c 0.7"); | ||
92 | cmp_ok($result->return_code, '==', 0, "CONN_TIME metric okay"); | ||
93 | like($result->output, $conn_time_output, "CONN_TIME metric output okay"); | ||
94 | |||
95 | $result = NPTest->testCmd("$check_cmd -m QUERY_TIME -q 'SELECT 1'"); | ||
96 | cmp_ok($result->return_code, '==', 0, "QUERY_TIME metric okay"); | ||
97 | like($result->output, $query_time_output, "QUERY_TIME metric output okay"); | ||
98 | |||
99 | $result = NPTest->testCmd("./check_dbi -d nodriver -q ''"); | ||
100 | cmp_ok($result->return_code, '==', 3, "Unknown DBI driver"); | ||
101 | like($result->output, $bad_driver_output, "Correct error message"); | ||
102 | } | ||
103 | |||