diff options
Diffstat (limited to 'plugins')
| -rw-r--r-- | plugins/check_dbi.c | 715 | ||||
| -rw-r--r-- | plugins/check_dbi.d/config.h | 31 | ||||
| -rw-r--r-- | plugins/t/check_dbi.t | 10 |
3 files changed, 432 insertions, 324 deletions
diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c index 468ded31..9bc68eb3 100644 --- a/plugins/check_dbi.c +++ b/plugins/check_dbi.c | |||
| @@ -34,6 +34,10 @@ const char *copyright = "2011-2024"; | |||
| 34 | const char *email = "devel@monitoring-plugins.org"; | 34 | const char *email = "devel@monitoring-plugins.org"; |
| 35 | 35 | ||
| 36 | #include "../lib/monitoringplug.h" | 36 | #include "../lib/monitoringplug.h" |
| 37 | #include "thresholds.h" | ||
| 38 | #include "perfdata.h" | ||
| 39 | #include "output.h" | ||
| 40 | #include "states.h" | ||
| 37 | #include "check_dbi.d/config.h" | 41 | #include "check_dbi.d/config.h" |
| 38 | #include "common.h" | 42 | #include "common.h" |
| 39 | #include "utils.h" | 43 | #include "utils.h" |
| @@ -63,7 +67,6 @@ typedef struct { | |||
| 63 | } check_dbi_config_wrapper; | 67 | } check_dbi_config_wrapper; |
| 64 | 68 | ||
| 65 | static check_dbi_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); | 69 | static check_dbi_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); |
| 66 | static check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper /*config_wrapper*/); | ||
| 67 | void print_usage(void); | 70 | void print_usage(void); |
| 68 | static void print_help(void); | 71 | static void print_help(void); |
| 69 | 72 | ||
| @@ -71,26 +74,18 @@ static double timediff(struct timeval /*start*/, struct timeval /*end*/); | |||
| 71 | 74 | ||
| 72 | static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...); | 75 | static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...); |
| 73 | 76 | ||
| 74 | static mp_state_enum do_query(dbi_conn /*conn*/, const char ** /*res_val_str*/, | 77 | typedef struct { |
| 75 | double * /*res_val*/, double * /*res_time*/, mp_dbi_metric /*metric*/, | 78 | char *result_string; |
| 76 | mp_dbi_type /*type*/, char * /*np_dbi_query*/); | 79 | double result_number; |
| 80 | double query_duration; | ||
| 81 | int error_code; | ||
| 82 | const char *error_string; | ||
| 83 | mp_state_enum query_processing_status; | ||
| 84 | } do_query_result; | ||
| 85 | static do_query_result do_query(dbi_conn conn, check_dbi_metric metric, check_dbi_type type, | ||
| 86 | char *query); | ||
| 77 | 87 | ||
| 78 | int main(int argc, char **argv) { | 88 | int main(int argc, char **argv) { |
| 79 | int status = STATE_UNKNOWN; | ||
| 80 | |||
| 81 | dbi_driver driver; | ||
| 82 | dbi_conn conn; | ||
| 83 | |||
| 84 | unsigned int server_version; | ||
| 85 | |||
| 86 | struct timeval start_timeval; | ||
| 87 | struct timeval end_timeval; | ||
| 88 | double conn_time = 0.0; | ||
| 89 | double query_time = 0.0; | ||
| 90 | |||
| 91 | const char *query_val_str = NULL; | ||
| 92 | double query_val = 0.0; | ||
| 93 | |||
| 94 | setlocale(LC_ALL, ""); | 89 | setlocale(LC_ALL, ""); |
| 95 | bindtextdomain(PACKAGE, LOCALEDIR); | 90 | bindtextdomain(PACKAGE, LOCALEDIR); |
| 96 | textdomain(PACKAGE); | 91 | textdomain(PACKAGE); |
| @@ -106,6 +101,10 @@ int main(int argc, char **argv) { | |||
| 106 | 101 | ||
| 107 | const check_dbi_config config = tmp.config; | 102 | const check_dbi_config config = tmp.config; |
| 108 | 103 | ||
| 104 | if (config.output_format_is_set) { | ||
| 105 | mp_set_format(config.output_format); | ||
| 106 | } | ||
| 107 | |||
| 109 | /* Set signal handling and alarm */ | 108 | /* Set signal handling and alarm */ |
| 110 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { | 109 | if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { |
| 111 | usage4(_("Cannot catch SIGALRM")); | 110 | usage4(_("Cannot catch SIGALRM")); |
| @@ -116,44 +115,46 @@ int main(int argc, char **argv) { | |||
| 116 | printf("Initializing DBI\n"); | 115 | printf("Initializing DBI\n"); |
| 117 | } | 116 | } |
| 118 | 117 | ||
| 119 | dbi_inst *instance_p = {0}; | 118 | dbi_inst instance_p = NULL; |
| 120 | 119 | if (dbi_initialize_r(NULL, &instance_p) < 0) { | |
| 121 | if (dbi_initialize_r(NULL, instance_p) < 0) { | 120 | printf("failed to initialize DBI; possibly you don't have any drivers installed.\n"); |
| 122 | printf( | 121 | exit(STATE_UNKNOWN); |
| 123 | "UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n"); | ||
| 124 | return STATE_UNKNOWN; | ||
| 125 | } | 122 | } |
| 126 | 123 | ||
| 124 | // Try to prevent libdbi from printing stuff on stderr | ||
| 125 | // Who thought that would be a good idea anyway? | ||
| 126 | dbi_set_verbosity_r(0, instance_p); | ||
| 127 | |||
| 127 | if (instance_p == NULL) { | 128 | if (instance_p == NULL) { |
| 128 | printf("UNKNOWN - failed to initialize DBI.\n"); | 129 | printf("failed to initialize DBI.\n"); |
| 129 | return STATE_UNKNOWN; | 130 | exit(STATE_UNKNOWN); |
| 130 | } | 131 | } |
| 131 | 132 | ||
| 132 | if (verbose) { | 133 | if (verbose) { |
| 133 | printf("Opening DBI driver '%s'\n", config.dbi_driver); | 134 | printf("Opening DBI driver '%s'\n", config.dbi_driver); |
| 134 | } | 135 | } |
| 135 | 136 | ||
| 136 | driver = dbi_driver_open_r(config.dbi_driver, instance_p); | 137 | dbi_driver driver = dbi_driver_open_r(config.dbi_driver, instance_p); |
| 137 | if (!driver) { | 138 | if (!driver) { |
| 138 | printf("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", | 139 | printf("failed to open DBI driver '%s'; possibly it's not installed.\n", config.dbi_driver); |
| 139 | config.dbi_driver); | ||
| 140 | 140 | ||
| 141 | printf("Known drivers:\n"); | 141 | printf("Known drivers:\n"); |
| 142 | for (driver = dbi_driver_list_r(NULL, instance_p); driver; | 142 | for (driver = dbi_driver_list_r(NULL, instance_p); driver; |
| 143 | driver = dbi_driver_list_r(driver, instance_p)) { | 143 | driver = dbi_driver_list_r(driver, instance_p)) { |
| 144 | printf(" - %s\n", dbi_driver_get_name(driver)); | 144 | printf(" - %s\n", dbi_driver_get_name(driver)); |
| 145 | } | 145 | } |
| 146 | return STATE_UNKNOWN; | 146 | exit(STATE_UNKNOWN); |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | /* make a connection to the database */ | 149 | /* make a connection to the database */ |
| 150 | struct timeval start_timeval; | ||
| 150 | gettimeofday(&start_timeval, NULL); | 151 | gettimeofday(&start_timeval, NULL); |
| 151 | 152 | ||
| 152 | conn = dbi_conn_open(driver); | 153 | dbi_conn conn = dbi_conn_open(driver); |
| 153 | if (!conn) { | 154 | if (!conn) { |
| 154 | printf("UNKNOWN - failed top open connection object.\n"); | 155 | printf("UNKNOWN - failed top open connection object.\n"); |
| 155 | dbi_conn_close(conn); | 156 | dbi_conn_close(conn); |
| 156 | return STATE_UNKNOWN; | 157 | exit(STATE_UNKNOWN); |
| 157 | } | 158 | } |
| 158 | 159 | ||
| 159 | for (size_t i = 0; i < config.dbi_options_num; ++i) { | 160 | for (size_t i = 0; i < config.dbi_options_num; ++i) { |
| @@ -167,10 +168,10 @@ int main(int argc, char **argv) { | |||
| 167 | if (!dbi_conn_set_option(conn, config.dbi_options[i].key, config.dbi_options[i].value)) { | 168 | if (!dbi_conn_set_option(conn, config.dbi_options[i].key, config.dbi_options[i].value)) { |
| 168 | continue; | 169 | continue; |
| 169 | } | 170 | } |
| 170 | /* else: status != 0 */ | ||
| 171 | 171 | ||
| 172 | np_dbi_print_error(conn, "UNKNOWN - failed to set option '%s' to '%s'", | 172 | // Failing to set option |
| 173 | config.dbi_options[i].key, config.dbi_options[i].value); | 173 | np_dbi_print_error(conn, "failed to set option '%s' to '%s'", config.dbi_options[i].key, |
| 174 | config.dbi_options[i].value); | ||
| 174 | printf("Known driver options:\n"); | 175 | printf("Known driver options:\n"); |
| 175 | 176 | ||
| 176 | for (opt = dbi_conn_get_option_list(conn, NULL); opt; | 177 | for (opt = dbi_conn_get_option_list(conn, NULL); opt; |
| @@ -178,7 +179,7 @@ int main(int argc, char **argv) { | |||
| 178 | printf(" - %s\n", opt); | 179 | printf(" - %s\n", opt); |
| 179 | } | 180 | } |
| 180 | dbi_conn_close(conn); | 181 | dbi_conn_close(conn); |
| 181 | return STATE_UNKNOWN; | 182 | exit(STATE_UNKNOWN); |
| 182 | } | 183 | } |
| 183 | 184 | ||
| 184 | if (config.host) { | 185 | if (config.host) { |
| @@ -206,80 +207,216 @@ int main(int argc, char **argv) { | |||
| 206 | } | 207 | } |
| 207 | 208 | ||
| 208 | if (dbi_conn_connect(conn) < 0) { | 209 | if (dbi_conn_connect(conn) < 0) { |
| 209 | np_dbi_print_error(conn, "UNKNOWN - failed to connect to database"); | 210 | np_dbi_print_error(conn, "failed to connect to database"); |
| 210 | return STATE_UNKNOWN; | 211 | exit(STATE_UNKNOWN); |
| 211 | } | 212 | } |
| 212 | 213 | ||
| 214 | struct timeval end_timeval; | ||
| 213 | gettimeofday(&end_timeval, NULL); | 215 | gettimeofday(&end_timeval, NULL); |
| 214 | conn_time = timediff(start_timeval, end_timeval); | 216 | double conn_time = timediff(start_timeval, end_timeval); |
| 215 | |||
| 216 | server_version = dbi_conn_get_engine_version(conn); | ||
| 217 | if (verbose) { | 217 | if (verbose) { |
| 218 | printf("Connected to server version %u\n", server_version); | 218 | printf("Time elapsed: %f\n", conn_time); |
| 219 | } | 219 | } |
| 220 | 220 | ||
| 221 | if (config.metric == METRIC_SERVER_VERSION) { | 221 | mp_check overall = mp_check_init(); |
| 222 | status = get_status(server_version, config.dbi_thresholds); | 222 | |
| 223 | mp_subcheck sc_connection_time = mp_subcheck_init(); | ||
| 224 | sc_connection_time = mp_set_subcheck_default_state(sc_connection_time, STATE_OK); | ||
| 225 | xasprintf(&sc_connection_time.output, "Connection time: %f", conn_time); | ||
| 226 | |||
| 227 | mp_perfdata pd_conn_duration = perfdata_init(); | ||
| 228 | pd_conn_duration.label = "conntime"; | ||
| 229 | pd_conn_duration = mp_set_pd_value(pd_conn_duration, conn_time); | ||
| 230 | |||
| 231 | if (config.metric == METRIC_CONN_TIME) { | ||
| 232 | pd_conn_duration = mp_pd_set_thresholds(pd_conn_duration, config.thresholds); | ||
| 233 | mp_state_enum status = mp_get_pd_status(pd_conn_duration); | ||
| 234 | sc_connection_time = mp_set_subcheck_state(sc_connection_time, status); | ||
| 235 | if (status != STATE_OK) { | ||
| 236 | xasprintf(&sc_connection_time.output, "%s violates thresholds", | ||
| 237 | sc_connection_time.output); | ||
| 238 | } | ||
| 223 | } | 239 | } |
| 224 | 240 | ||
| 241 | mp_add_perfdata_to_subcheck(&sc_connection_time, pd_conn_duration); | ||
| 242 | mp_add_subcheck_to_check(&overall, sc_connection_time); | ||
| 243 | |||
| 244 | unsigned int server_version = dbi_conn_get_engine_version(conn); | ||
| 225 | if (verbose) { | 245 | if (verbose) { |
| 226 | printf("Time elapsed: %f\n", conn_time); | 246 | printf("Connected to server version %u\n", server_version); |
| 227 | } | 247 | } |
| 228 | 248 | ||
| 229 | if (config.metric == METRIC_CONN_TIME) { | 249 | mp_subcheck sc_server_version = mp_subcheck_init(); |
| 230 | status = get_status(conn_time, config.dbi_thresholds); | 250 | sc_server_version = mp_set_subcheck_default_state(sc_server_version, STATE_OK); |
| 231 | } | 251 | xasprintf(&sc_server_version.output, "Connected to server version %u", server_version); |
| 252 | |||
| 253 | if (config.metric == METRIC_SERVER_VERSION) { | ||
| 254 | mp_perfdata pd_server_version = perfdata_init(); | ||
| 255 | pd_server_version = mp_set_pd_value(pd_server_version, server_version); | ||
| 256 | pd_server_version = mp_pd_set_thresholds(pd_server_version, config.thresholds); | ||
| 257 | mp_state_enum status = mp_get_pd_status(pd_server_version); | ||
| 258 | mp_add_perfdata_to_subcheck(&sc_server_version, pd_server_version); | ||
| 259 | |||
| 260 | sc_server_version = mp_set_subcheck_state(sc_server_version, status); | ||
| 261 | |||
| 262 | if (status != STATE_OK) { | ||
| 263 | xasprintf(&sc_server_version.output, "%s violates thresholds", | ||
| 264 | sc_server_version.output); | ||
| 265 | } | ||
| 266 | }; | ||
| 267 | mp_add_subcheck_to_check(&overall, sc_server_version); | ||
| 232 | 268 | ||
| 233 | /* select a database */ | 269 | /* select a database */ |
| 234 | if (config.dbi_database) { | 270 | if (config.database) { |
| 235 | if (verbose > 1) { | 271 | if (verbose > 1) { |
| 236 | printf("Selecting database '%s'\n", config.dbi_database); | 272 | printf("Selecting database '%s'\n", config.database); |
| 237 | } | 273 | } |
| 238 | 274 | ||
| 239 | if (dbi_conn_select_db(conn, config.dbi_database)) { | 275 | mp_subcheck sc_select_db = mp_subcheck_init(); |
| 240 | np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", | 276 | sc_select_db = mp_set_subcheck_default_state(sc_select_db, STATE_OK); |
| 241 | config.dbi_database); | 277 | |
| 242 | return STATE_UNKNOWN; | 278 | if (dbi_conn_select_db(conn, config.database)) { |
| 279 | np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", config.database); | ||
| 280 | exit(STATE_UNKNOWN); | ||
| 281 | } else { | ||
| 282 | mp_add_subcheck_to_check(&overall, sc_select_db); | ||
| 243 | } | 283 | } |
| 244 | } | 284 | } |
| 245 | 285 | ||
| 246 | if (config.dbi_query) { | 286 | // Do a query (if configured) |
| 287 | if (config.query) { | ||
| 288 | mp_subcheck sc_query = mp_subcheck_init(); | ||
| 289 | sc_query = mp_set_subcheck_default_state(sc_query, STATE_UNKNOWN); | ||
| 290 | |||
| 247 | /* execute query */ | 291 | /* execute query */ |
| 248 | status = do_query(conn, &query_val_str, &query_val, &query_time, config.metric, config.type, | 292 | do_query_result query_res = do_query(conn, config.metric, config.type, config.query); |
| 249 | config.dbi_query); | 293 | |
| 250 | if (status != STATE_OK) { | 294 | if (query_res.error_code != 0) { |
| 251 | /* do_query prints an error message in this case */ | 295 | xasprintf(&sc_query.output, "Query failed: %s", query_res.error_string); |
| 252 | return status; | 296 | sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); |
| 253 | } | 297 | } else if (query_res.query_processing_status != STATE_OK) { |
| 298 | if (query_res.error_string) { | ||
| 299 | xasprintf(&sc_query.output, "Failed to process query: %s", query_res.error_string); | ||
| 300 | } else { | ||
| 301 | xasprintf(&sc_query.output, "Failed to process query"); | ||
| 302 | } | ||
| 303 | sc_query = mp_set_subcheck_state(sc_query, query_res.query_processing_status); | ||
| 304 | } else { | ||
| 305 | // query succeeded in general | ||
| 306 | xasprintf(&sc_query.output, "Query '%s' succeeded", config.query); | ||
| 307 | |||
| 308 | // that's a OK by default now | ||
| 309 | sc_query = mp_set_subcheck_default_state(sc_query, STATE_OK); | ||
| 310 | |||
| 311 | // query duration first | ||
| 312 | mp_perfdata pd_query_duration = perfdata_init(); | ||
| 313 | pd_query_duration = mp_set_pd_value(pd_query_duration, query_res.query_duration); | ||
| 314 | pd_query_duration.label = "querytime"; | ||
| 315 | if (config.metric == METRIC_QUERY_TIME) { | ||
| 316 | pd_query_duration = mp_pd_set_thresholds(pd_query_duration, config.thresholds); | ||
| 317 | } | ||
| 318 | |||
| 319 | mp_add_perfdata_to_subcheck(&sc_query, pd_query_duration); | ||
| 254 | 320 | ||
| 255 | if (config.metric == METRIC_QUERY_RESULT) { | 321 | if (config.metric == METRIC_QUERY_RESULT) { |
| 256 | if (config.expect) { | 322 | if (config.expect) { |
| 257 | if ((!query_val_str) || strcmp(query_val_str, config.expect)) { | 323 | if ((!query_res.result_string) || |
| 258 | status = STATE_CRITICAL; | 324 | strcmp(query_res.result_string, config.expect)) { |
| 325 | xasprintf(&sc_query.output, "Found string '%s' in query result", | ||
| 326 | config.expect); | ||
| 327 | sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); | ||
| 328 | } else { | ||
| 329 | xasprintf(&sc_query.output, "Did not find string '%s' in query result", | ||
| 330 | config.expect); | ||
| 331 | sc_query = mp_set_subcheck_state(sc_query, STATE_OK); | ||
| 332 | } | ||
| 333 | } else if (config.expect_re_str) { | ||
| 334 | int comp_err; | ||
| 335 | regex_t expect_re = {}; | ||
| 336 | comp_err = regcomp(&expect_re, config.expect_re_str, config.expect_re_cflags); | ||
| 337 | if (comp_err != 0) { | ||
| 338 | // TODO error, failed to compile regex | ||
| 339 | // TODO move this to config sanitatisation | ||
| 340 | printf("Failed to compile regex from string '%s'", config.expect_re_str); | ||
| 341 | exit(STATE_UNKNOWN); | ||
| 342 | } | ||
| 343 | |||
| 344 | int err = | ||
| 345 | regexec(&expect_re, query_res.result_string, 0, NULL, /* flags = */ 0); | ||
| 346 | if (!err) { | ||
| 347 | sc_query = mp_set_subcheck_state(sc_query, STATE_OK); | ||
| 348 | xasprintf(&sc_query.output, "Found regular expression '%s' in query result", | ||
| 349 | config.expect_re_str); | ||
| 350 | } else if (err == REG_NOMATCH) { | ||
| 351 | sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); | ||
| 352 | xasprintf(&sc_query.output, | ||
| 353 | "Did not find regular expression '%s' in query result", | ||
| 354 | config.expect_re_str); | ||
| 355 | } else { | ||
| 356 | char errmsg[1024]; | ||
| 357 | regerror(err, &expect_re, errmsg, sizeof(errmsg)); | ||
| 358 | xasprintf(&sc_query.output, | ||
| 359 | "ERROR - failed to execute regular expression: %s\n", errmsg); | ||
| 360 | sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); | ||
| 361 | } | ||
| 259 | } else { | 362 | } else { |
| 260 | status = STATE_OK; | 363 | // no string matching |
| 364 | if (isnan(query_res.result_number)) { | ||
| 365 | // The query result is not a number, but no string checking was configured | ||
| 366 | // so we expected a number | ||
| 367 | // this is a CRITICAL | ||
| 368 | xasprintf(&sc_query.output, "Query '%s' result is not numeric", | ||
| 369 | config.query); | ||
| 370 | sc_query = mp_set_subcheck_state(sc_query, STATE_CRITICAL); | ||
| 371 | |||
| 372 | } else { | ||
| 373 | |||
| 374 | mp_perfdata pd_query_val = perfdata_init(); | ||
| 375 | pd_query_val = mp_set_pd_value(pd_query_val, query_res.result_number); | ||
| 376 | pd_query_val.label = "query"; | ||
| 377 | pd_query_val = mp_pd_set_thresholds(pd_query_val, config.thresholds); | ||
| 378 | |||
| 379 | mp_add_perfdata_to_subcheck(&sc_query, pd_query_val); | ||
| 380 | mp_state_enum query_numerical_result = mp_get_pd_status(pd_query_val); | ||
| 381 | |||
| 382 | sc_query = mp_set_subcheck_state(sc_query, query_numerical_result); | ||
| 383 | // TODO set pd thresholds | ||
| 384 | // if (config.dbi_thresholds->warning) { | ||
| 385 | // pd_query_val.warn= config.dbi_thresholds->warning | ||
| 386 | // } else { | ||
| 387 | // } | ||
| 388 | |||
| 389 | if (query_numerical_result == STATE_OK) { | ||
| 390 | xasprintf(&sc_query.output, | ||
| 391 | "Query result '%f' is within given thresholds", | ||
| 392 | query_res.result_number); | ||
| 393 | } else { | ||
| 394 | xasprintf(&sc_query.output, | ||
| 395 | "Query result '%f' violates the given thresholds", | ||
| 396 | query_res.result_number); | ||
| 397 | } | ||
| 398 | } | ||
| 261 | } | 399 | } |
| 262 | } else if (config.expect_re_str) { | 400 | } else if (config.metric == METRIC_QUERY_TIME) { |
| 263 | int err; | 401 | mp_state_enum query_time_status = mp_get_pd_status(pd_query_duration); |
| 264 | 402 | mp_set_subcheck_state(sc_query, query_time_status); | |
| 265 | regex_t expect_re = {}; | 403 | |
| 266 | err = regexec(&expect_re, query_val_str, 0, NULL, /* flags = */ 0); | 404 | if (query_time_status == STATE_OK) { |
| 267 | if (!err) { | 405 | xasprintf(&sc_query.output, "Query duration '%f' is within given thresholds", |
| 268 | status = STATE_OK; | 406 | query_res.query_duration); |
| 269 | } else if (err == REG_NOMATCH) { | ||
| 270 | status = STATE_CRITICAL; | ||
| 271 | } else { | 407 | } else { |
| 272 | char errmsg[1024]; | 408 | xasprintf(&sc_query.output, "Query duration '%f' violates the given thresholds", |
| 273 | regerror(err, &expect_re, errmsg, sizeof(errmsg)); | 409 | query_res.query_duration); |
| 274 | printf("ERROR - failed to execute regular expression: %s\n", errmsg); | ||
| 275 | status = STATE_CRITICAL; | ||
| 276 | } | 410 | } |
| 277 | } else { | 411 | } else { |
| 278 | status = get_status(query_val, config.dbi_thresholds); | 412 | /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error |
| 413 | * which should have been reported and handled (abort) before | ||
| 414 | * ... unless we expected a string to be returned */ | ||
| 415 | assert((!isnan(query_res.result_number)) || (config.type == TYPE_STRING)); | ||
| 279 | } | 416 | } |
| 280 | } else if (config.metric == METRIC_QUERY_TIME) { | ||
| 281 | status = get_status(query_time, config.dbi_thresholds); | ||
| 282 | } | 417 | } |
| 418 | |||
| 419 | mp_add_subcheck_to_check(&overall, sc_query); | ||
| 283 | } | 420 | } |
| 284 | 421 | ||
| 285 | if (verbose) { | 422 | if (verbose) { |
| @@ -287,71 +424,17 @@ int main(int argc, char **argv) { | |||
| 287 | } | 424 | } |
| 288 | dbi_conn_close(conn); | 425 | dbi_conn_close(conn); |
| 289 | 426 | ||
| 290 | /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error | 427 | mp_exit(overall); |
| 291 | * which should have been reported and handled (abort) before | ||
| 292 | * ... unless we expected a string to be returned */ | ||
| 293 | assert((config.metric != METRIC_QUERY_RESULT) || (!isnan(query_val)) || | ||
| 294 | (config.type == TYPE_STRING)); | ||
| 295 | |||
| 296 | assert((config.type != TYPE_STRING) || (config.expect || config.expect_re_str)); | ||
| 297 | |||
| 298 | printf("%s - connection time: %fs", state_text(status), conn_time); | ||
| 299 | if (config.dbi_query) { | ||
| 300 | if (config.type == TYPE_STRING) { | ||
| 301 | assert(config.expect || config.expect_re_str); | ||
| 302 | printf(", '%s' returned '%s' in %fs", config.dbi_query, | ||
| 303 | query_val_str ? query_val_str : "<nothing>", query_time); | ||
| 304 | if (status != STATE_OK) { | ||
| 305 | if (config.expect) { | ||
| 306 | printf(" (expected '%s')", config.expect); | ||
| 307 | } else if (config.expect_re_str) { | ||
| 308 | printf(" (expected regex /%s/%s)", config.expect_re_str, | ||
| 309 | ((config.expect_re_cflags & REG_ICASE) ? "i" : "")); | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } else if (isnan(query_val)) { | ||
| 313 | printf(", '%s' query execution time: %fs", config.dbi_query, query_time); | ||
| 314 | } else { | ||
| 315 | printf(", '%s' returned %f in %fs", config.dbi_query, query_val, query_time); | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | printf( | ||
| 320 | " | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time, | ||
| 321 | ((config.metric == METRIC_CONN_TIME) && config.warning_range) ? config.warning_range : "", | ||
| 322 | ((config.metric == METRIC_CONN_TIME) && config.critical_range) ? config.critical_range : "", | ||
| 323 | server_version, | ||
| 324 | ((config.metric == METRIC_SERVER_VERSION) && config.warning_range) ? config.warning_range | ||
| 325 | : "", | ||
| 326 | ((config.metric == METRIC_SERVER_VERSION) && config.critical_range) ? config.critical_range | ||
| 327 | : ""); | ||
| 328 | if (config.dbi_query) { | ||
| 329 | if (!isnan(query_val)) { /* this is also true when -e is used */ | ||
| 330 | printf(" query=%f;%s;%s;;", query_val, | ||
| 331 | ((config.metric == METRIC_QUERY_RESULT) && config.warning_range) | ||
| 332 | ? config.warning_range | ||
| 333 | : "", | ||
| 334 | ((config.metric == METRIC_QUERY_RESULT) && config.critical_range) | ||
| 335 | ? config.critical_range | ||
| 336 | : ""); | ||
| 337 | } | ||
| 338 | printf(" querytime=%fs;%s;%s;0;", query_time, | ||
| 339 | ((config.metric == METRIC_QUERY_TIME) && config.warning_range) ? config.warning_range | ||
| 340 | : "", | ||
| 341 | ((config.metric == METRIC_QUERY_TIME) && config.critical_range) | ||
| 342 | ? config.critical_range | ||
| 343 | : ""); | ||
| 344 | } | ||
| 345 | printf("\n"); | ||
| 346 | return status; | ||
| 347 | } | 428 | } |
| 348 | 429 | ||
| 349 | /* process command-line arguments */ | 430 | /* process command-line arguments */ |
| 350 | check_dbi_config_wrapper process_arguments(int argc, char **argv) { | 431 | check_dbi_config_wrapper process_arguments(int argc, char **argv) { |
| 432 | enum { | ||
| 433 | output_format_index = CHAR_MAX + 1, | ||
| 434 | }; | ||
| 351 | 435 | ||
| 352 | int option = 0; | 436 | int option = 0; |
| 353 | static struct option longopts[] = {STD_LONG_OPTS, | 437 | static struct option longopts[] = {STD_LONG_OPTS, |
| 354 | |||
| 355 | {"expect", required_argument, 0, 'e'}, | 438 | {"expect", required_argument, 0, 'e'}, |
| 356 | {"regex", required_argument, 0, 'r'}, | 439 | {"regex", required_argument, 0, 'r'}, |
| 357 | {"regexi", required_argument, 0, 'R'}, | 440 | {"regexi", required_argument, 0, 'R'}, |
| @@ -360,6 +443,7 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { | |||
| 360 | {"option", required_argument, 0, 'o'}, | 443 | {"option", required_argument, 0, 'o'}, |
| 361 | {"query", required_argument, 0, 'q'}, | 444 | {"query", required_argument, 0, 'q'}, |
| 362 | {"database", required_argument, 0, 'D'}, | 445 | {"database", required_argument, 0, 'D'}, |
| 446 | {"output-format", required_argument, 0, output_format_index}, | ||
| 363 | {0, 0, 0, 0}}; | 447 | {0, 0, 0, 0}}; |
| 364 | 448 | ||
| 365 | check_dbi_config_wrapper result = { | 449 | check_dbi_config_wrapper result = { |
| @@ -384,14 +468,22 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { | |||
| 384 | print_revision(progname, NP_VERSION); | 468 | print_revision(progname, NP_VERSION); |
| 385 | exit(STATE_UNKNOWN); | 469 | exit(STATE_UNKNOWN); |
| 386 | 470 | ||
| 387 | case 'c': /* critical range */ | 471 | case 'c': /* critical range */ { |
| 388 | result.config.critical_range = optarg; | 472 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 473 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 474 | die(STATE_UNKNOWN, "failed to parse critical threshold"); | ||
| 475 | } | ||
| 476 | result.config.thresholds = mp_thresholds_set_crit(result.config.thresholds, tmp.range); | ||
| 389 | result.config.type = TYPE_NUMERIC; | 477 | result.config.type = TYPE_NUMERIC; |
| 390 | break; | 478 | } break; |
| 391 | case 'w': /* warning range */ | 479 | case 'w': /* warning range */ { |
| 392 | result.config.warning_range = optarg; | 480 | mp_range_parsed tmp = mp_parse_range_string(optarg); |
| 481 | if (tmp.error != MP_PARSING_SUCCES) { | ||
| 482 | die(STATE_UNKNOWN, "failed to parse warning threshold"); | ||
| 483 | } | ||
| 484 | result.config.thresholds = mp_thresholds_set_warn(result.config.thresholds, tmp.range); | ||
| 393 | result.config.type = TYPE_NUMERIC; | 485 | result.config.type = TYPE_NUMERIC; |
| 394 | break; | 486 | } break; |
| 395 | case 'e': | 487 | case 'e': |
| 396 | result.config.expect = optarg; | 488 | result.config.expect = optarg; |
| 397 | result.config.type = TYPE_STRING; | 489 | result.config.type = TYPE_STRING; |
| @@ -418,7 +510,6 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { | |||
| 418 | } | 510 | } |
| 419 | break; | 511 | break; |
| 420 | } | 512 | } |
| 421 | |||
| 422 | case 'm': | 513 | case 'm': |
| 423 | if (!strcasecmp(optarg, "CONN_TIME")) { | 514 | if (!strcasecmp(optarg, "CONN_TIME")) { |
| 424 | result.config.metric = METRIC_CONN_TIME; | 515 | result.config.metric = METRIC_CONN_TIME; |
| @@ -438,7 +529,6 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { | |||
| 438 | } else { | 529 | } else { |
| 439 | timeout_interval = atoi(optarg); | 530 | timeout_interval = atoi(optarg); |
| 440 | } | 531 | } |
| 441 | |||
| 442 | break; | 532 | break; |
| 443 | case 'H': /* host */ | 533 | case 'H': /* host */ |
| 444 | if (!is_host(optarg)) { | 534 | if (!is_host(optarg)) { |
| @@ -450,7 +540,6 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { | |||
| 450 | case 'v': | 540 | case 'v': |
| 451 | verbose++; | 541 | verbose++; |
| 452 | break; | 542 | break; |
| 453 | |||
| 454 | case 'd': | 543 | case 'd': |
| 455 | result.config.dbi_driver = optarg; | 544 | result.config.dbi_driver = optarg; |
| 456 | break; | 545 | break; |
| @@ -482,61 +571,68 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { | |||
| 482 | new->value = value; | 571 | new->value = value; |
| 483 | } break; | 572 | } break; |
| 484 | case 'q': | 573 | case 'q': |
| 485 | result.config.dbi_query = optarg; | 574 | result.config.query = optarg; |
| 486 | break; | 575 | break; |
| 487 | case 'D': | 576 | case 'D': |
| 488 | result.config.dbi_database = optarg; | 577 | result.config.database = optarg; |
| 578 | break; | ||
| 579 | case output_format_index: { | ||
| 580 | parsed_output_format parser = mp_parse_output_format(optarg); | ||
| 581 | if (!parser.parsing_success) { | ||
| 582 | // TODO List all available formats here, maybe add anothoer usage function | ||
| 583 | printf("Invalid output format: %s\n", optarg); | ||
| 584 | exit(STATE_UNKNOWN); | ||
| 585 | } | ||
| 586 | |||
| 587 | result.config.output_format_is_set = true; | ||
| 588 | result.config.output_format = parser.output_format; | ||
| 489 | break; | 589 | break; |
| 490 | } | 590 | } |
| 591 | } | ||
| 491 | } | 592 | } |
| 492 | 593 | ||
| 493 | set_thresholds(&result.config.dbi_thresholds, result.config.warning_range, | 594 | if (!result.config.dbi_driver) { |
| 494 | result.config.critical_range); | ||
| 495 | |||
| 496 | return validate_arguments(result); | ||
| 497 | } | ||
| 498 | |||
| 499 | check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper config_wrapper) { | ||
| 500 | if (!config_wrapper.config.dbi_driver) { | ||
| 501 | usage("Must specify a DBI driver"); | 595 | usage("Must specify a DBI driver"); |
| 502 | } | 596 | } |
| 503 | 597 | ||
| 504 | if (((config_wrapper.config.metric == METRIC_QUERY_RESULT) || | 598 | if (((result.config.metric == METRIC_QUERY_RESULT) || |
| 505 | (config_wrapper.config.metric == METRIC_QUERY_TIME)) && | 599 | (result.config.metric == METRIC_QUERY_TIME)) && |
| 506 | (!config_wrapper.config.dbi_query)) { | 600 | (!result.config.query)) { |
| 507 | usage("Must specify a query to execute (metric == QUERY_RESULT)"); | 601 | usage("Must specify a query to execute (metric == QUERY_RESULT)"); |
| 508 | } | 602 | } |
| 509 | 603 | ||
| 510 | if ((config_wrapper.config.metric != METRIC_CONN_TIME) && | 604 | if ((result.config.metric != METRIC_CONN_TIME) && |
| 511 | (config_wrapper.config.metric != METRIC_SERVER_VERSION) && | 605 | (result.config.metric != METRIC_SERVER_VERSION) && |
| 512 | (config_wrapper.config.metric != METRIC_QUERY_RESULT) && | 606 | (result.config.metric != METRIC_QUERY_RESULT) && |
| 513 | (config_wrapper.config.metric != METRIC_QUERY_TIME)) { | 607 | (result.config.metric != METRIC_QUERY_TIME)) { |
| 514 | usage("Invalid metric specified"); | 608 | usage("Invalid metric specified"); |
| 515 | } | 609 | } |
| 516 | 610 | ||
| 517 | if (config_wrapper.config.expect && | 611 | if (result.config.expect && |
| 518 | (config_wrapper.config.warning_range || config_wrapper.config.critical_range || | 612 | (result.config.thresholds.warning_is_set || result.config.thresholds.critical_is_set || |
| 519 | config_wrapper.config.expect_re_str)) { | 613 | result.config.expect_re_str)) { |
| 520 | usage("Do not mix -e and -w/-c/-r/-R"); | 614 | usage("Do not mix -e and -w/-c/-r/-R"); |
| 521 | } | 615 | } |
| 522 | 616 | ||
| 523 | if (config_wrapper.config.expect_re_str && | 617 | if (result.config.expect_re_str && |
| 524 | (config_wrapper.config.warning_range || config_wrapper.config.critical_range || | 618 | (result.config.thresholds.warning_is_set || result.config.thresholds.critical_is_set || |
| 525 | config_wrapper.config.expect)) { | 619 | result.config.expect)) { |
| 526 | usage("Do not mix -r/-R and -w/-c/-e"); | 620 | usage("Do not mix -r/-R and -w/-c/-e"); |
| 527 | } | 621 | } |
| 528 | 622 | ||
| 529 | if (config_wrapper.config.expect && (config_wrapper.config.metric != METRIC_QUERY_RESULT)) { | 623 | if (result.config.expect && (result.config.metric != METRIC_QUERY_RESULT)) { |
| 530 | usage("Option -e requires metric QUERY_RESULT"); | 624 | usage("Option -e requires metric QUERY_RESULT"); |
| 531 | } | 625 | } |
| 532 | 626 | ||
| 533 | if (config_wrapper.config.expect_re_str && | 627 | if (result.config.expect_re_str && (result.config.metric != METRIC_QUERY_RESULT)) { |
| 534 | (config_wrapper.config.metric != METRIC_QUERY_RESULT)) { | ||
| 535 | usage("Options -r/-R require metric QUERY_RESULT"); | 628 | usage("Options -r/-R require metric QUERY_RESULT"); |
| 536 | } | 629 | } |
| 537 | 630 | ||
| 538 | config_wrapper.errorcode = OK; | 631 | if (result.config.type == TYPE_STRING) { |
| 539 | return config_wrapper; | 632 | assert(result.config.expect || result.config.expect_re_str); |
| 633 | } | ||
| 634 | |||
| 635 | return result; | ||
| 540 | } | 636 | } |
| 541 | 637 | ||
| 542 | void print_help(void) { | 638 | void print_help(void) { |
| @@ -642,21 +738,12 @@ void print_usage(void) { | |||
| 642 | printf(" [-e <string>] [-r|-R <regex>]\n"); | 738 | printf(" [-e <string>] [-r|-R <regex>]\n"); |
| 643 | } | 739 | } |
| 644 | 740 | ||
| 645 | const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_type, | 741 | const char *get_field_str(dbi_result res, check_dbi_metric metric, check_dbi_type type) { |
| 646 | mp_dbi_metric metric, mp_dbi_type type) { | 742 | const char *str = dbi_result_get_string_idx(res, 1); |
| 647 | const char *str; | ||
| 648 | |||
| 649 | if (field_type != DBI_TYPE_STRING) { | ||
| 650 | printf("CRITICAL - result value is not a string\n"); | ||
| 651 | return NULL; | ||
| 652 | } | ||
| 653 | |||
| 654 | str = dbi_result_get_string_idx(res, 1); | ||
| 655 | if ((!str) || (strcmp(str, "ERROR") == 0)) { | 743 | if ((!str) || (strcmp(str, "ERROR") == 0)) { |
| 656 | if (metric != METRIC_QUERY_RESULT) { | 744 | if (metric != METRIC_QUERY_RESULT) { |
| 657 | return NULL; | 745 | return NULL; |
| 658 | } | 746 | } |
| 659 | np_dbi_print_error(conn, "CRITICAL - failed to fetch string value"); | ||
| 660 | return NULL; | 747 | return NULL; |
| 661 | } | 748 | } |
| 662 | 749 | ||
| @@ -666,36 +753,50 @@ const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_ty | |||
| 666 | return str; | 753 | return str; |
| 667 | } | 754 | } |
| 668 | 755 | ||
| 669 | double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_dbi_metric metric, | 756 | typedef struct { |
| 670 | mp_dbi_type type) { | 757 | double value; |
| 671 | double val = NAN; | 758 | int error_code; |
| 759 | int dbi_error_code; // not sure if useful | ||
| 760 | } get_field_wrapper; | ||
| 761 | get_field_wrapper get_field(dbi_result res, check_dbi_metric metric, check_dbi_type type) { | ||
| 762 | |||
| 763 | unsigned short field_type = dbi_result_get_field_type_idx(res, 1); | ||
| 764 | get_field_wrapper result = { | ||
| 765 | .value = NAN, | ||
| 766 | .error_code = OK, | ||
| 767 | }; | ||
| 672 | 768 | ||
| 673 | if (*field_type == DBI_TYPE_INTEGER) { | 769 | if (field_type == DBI_TYPE_INTEGER) { |
| 674 | val = (double)dbi_result_get_longlong_idx(res, 1); | 770 | result.value = (double)dbi_result_get_longlong_idx(res, 1); |
| 675 | } else if (*field_type == DBI_TYPE_DECIMAL) { | 771 | } else if (field_type == DBI_TYPE_DECIMAL) { |
| 676 | val = dbi_result_get_double_idx(res, 1); | 772 | result.value = dbi_result_get_double_idx(res, 1); |
| 677 | } else if (*field_type == DBI_TYPE_STRING) { | 773 | } else if (field_type == DBI_TYPE_STRING) { |
| 678 | const char *val_str; | 774 | const char *val_str; |
| 679 | char *endptr = NULL; | 775 | char *endptr = NULL; |
| 680 | 776 | ||
| 681 | val_str = get_field_str(conn, res, *field_type, metric, type); | 777 | val_str = get_field_str(res, metric, type); |
| 682 | if (!val_str) { | 778 | if (!val_str) { |
| 683 | if (metric != METRIC_QUERY_RESULT) { | 779 | result.error_code = ERROR; |
| 684 | return NAN; | 780 | field_type = DBI_TYPE_ERROR; |
| 685 | } | 781 | return result; |
| 686 | *field_type = DBI_TYPE_ERROR; | ||
| 687 | return NAN; | ||
| 688 | } | 782 | } |
| 689 | 783 | ||
| 690 | val = strtod(val_str, &endptr); | 784 | result.value = strtod(val_str, &endptr); |
| 691 | if (endptr == val_str) { | 785 | if (endptr == val_str) { |
| 692 | if (metric != METRIC_QUERY_RESULT) { | 786 | if (metric != METRIC_QUERY_RESULT) { |
| 693 | return NAN; | 787 | result.error_code = ERROR; |
| 788 | return result; | ||
| 694 | } | 789 | } |
| 695 | printf("CRITICAL - result value is not a numeric: %s\n", val_str); | 790 | |
| 696 | *field_type = DBI_TYPE_ERROR; | 791 | if (verbose) { |
| 697 | return NAN; | 792 | printf("CRITICAL - result value is not a numeric: %s\n", val_str); |
| 793 | } | ||
| 794 | |||
| 795 | field_type = DBI_TYPE_ERROR; | ||
| 796 | result.error_code = ERROR; | ||
| 797 | return result; | ||
| 698 | } | 798 | } |
| 799 | |||
| 699 | if ((endptr != NULL) && (*endptr != '\0')) { | 800 | if ((endptr != NULL) && (*endptr != '\0')) { |
| 700 | if (verbose) { | 801 | if (verbose) { |
| 701 | printf("Garbage after value: %s\n", endptr); | 802 | printf("Garbage after value: %s\n", endptr); |
| @@ -703,123 +804,127 @@ double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_d | |||
| 703 | } | 804 | } |
| 704 | } else { | 805 | } else { |
| 705 | if (metric != METRIC_QUERY_RESULT) { | 806 | if (metric != METRIC_QUERY_RESULT) { |
| 706 | return NAN; | 807 | result.error_code = ERROR; |
| 808 | return result; | ||
| 707 | } | 809 | } |
| 708 | printf("CRITICAL - cannot parse value of type %s (%i)\n", | 810 | // printf("CRITICAL - cannot parse value of type %s (%i)\n", |
| 709 | (*field_type == DBI_TYPE_BINARY) ? "BINARY" | 811 | // (*field_type == DBI_TYPE_BINARY) ? "BINARY" |
| 710 | : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME" | 812 | // : (*field_type == DBI_TYPE_DATETIME) ? "DATETIME" |
| 711 | : "<unknown>", | 813 | // : "<unknown>", |
| 712 | *field_type); | 814 | // *field_type); |
| 713 | *field_type = DBI_TYPE_ERROR; | 815 | field_type = DBI_TYPE_ERROR; |
| 714 | return NAN; | 816 | result.error_code = ERROR; |
| 715 | } | 817 | } |
| 716 | return val; | 818 | return result; |
| 717 | } | 819 | } |
| 718 | 820 | ||
| 719 | mp_state_enum get_query_result(dbi_conn conn, dbi_result res, const char **res_val_str, | 821 | static do_query_result do_query(dbi_conn conn, check_dbi_metric metric, check_dbi_type type, |
| 720 | double *res_val, mp_dbi_metric metric, mp_dbi_type type) { | 822 | char *query) { |
| 721 | unsigned short field_type; | 823 | assert(query); |
| 722 | double val = NAN; | ||
| 723 | |||
| 724 | if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) { | ||
| 725 | if (metric != METRIC_QUERY_RESULT) { | ||
| 726 | return STATE_OK; | ||
| 727 | } | ||
| 728 | np_dbi_print_error(conn, "CRITICAL - failed to fetch rows"); | ||
| 729 | return STATE_CRITICAL; | ||
| 730 | } | ||
| 731 | 824 | ||
| 732 | if (dbi_result_get_numrows(res) < 1) { | 825 | if (verbose) { |
| 733 | if (metric != METRIC_QUERY_RESULT) { | 826 | printf("Executing query '%s'\n", query); |
| 734 | return STATE_OK; | ||
| 735 | } | ||
| 736 | printf("WARNING - no rows returned\n"); | ||
| 737 | return STATE_WARNING; | ||
| 738 | } | ||
| 739 | |||
| 740 | if (dbi_result_get_numfields(res) == DBI_FIELD_ERROR) { | ||
| 741 | if (metric != METRIC_QUERY_RESULT) { | ||
| 742 | return STATE_OK; | ||
| 743 | } | ||
| 744 | np_dbi_print_error(conn, "CRITICAL - failed to fetch fields"); | ||
| 745 | return STATE_CRITICAL; | ||
| 746 | } | ||
| 747 | |||
| 748 | if (dbi_result_get_numfields(res) < 1) { | ||
| 749 | if (metric != METRIC_QUERY_RESULT) { | ||
| 750 | return STATE_OK; | ||
| 751 | } | ||
| 752 | printf("WARNING - no fields returned\n"); | ||
| 753 | return STATE_WARNING; | ||
| 754 | } | ||
| 755 | |||
| 756 | if (dbi_result_first_row(res) != 1) { | ||
| 757 | if (metric != METRIC_QUERY_RESULT) { | ||
| 758 | return STATE_OK; | ||
| 759 | } | ||
| 760 | np_dbi_print_error(conn, "CRITICAL - failed to fetch first row"); | ||
| 761 | return STATE_CRITICAL; | ||
| 762 | } | 827 | } |
| 763 | 828 | ||
| 764 | field_type = dbi_result_get_field_type_idx(res, 1); | 829 | do_query_result result = { |
| 765 | if (field_type != DBI_TYPE_ERROR) { | 830 | .query_duration = 0, |
| 766 | if (type == TYPE_STRING) { | 831 | .result_string = NULL, |
| 767 | /* the value will be freed in dbi_result_free */ | 832 | .result_number = 0, |
| 768 | *res_val_str = strdup(get_field_str(conn, res, field_type, metric, type)); | 833 | .error_code = 0, |
| 769 | } else { | 834 | .query_processing_status = STATE_UNKNOWN, |
| 770 | val = get_field(conn, res, &field_type, metric, type); | 835 | }; |
| 771 | } | ||
| 772 | } | ||
| 773 | 836 | ||
| 774 | *res_val = val; | 837 | struct timeval timeval_start; |
| 838 | gettimeofday(&timeval_start, NULL); | ||
| 775 | 839 | ||
| 776 | if (field_type == DBI_TYPE_ERROR) { | 840 | dbi_result res = dbi_conn_query(conn, query); |
| 777 | if (metric != METRIC_QUERY_RESULT) { | 841 | if (!res) { |
| 778 | return STATE_OK; | 842 | dbi_conn_error(conn, &result.error_string); |
| 779 | } | 843 | result.error_code = 1; |
| 780 | np_dbi_print_error(conn, "CRITICAL - failed to fetch data"); | 844 | return result; |
| 781 | return STATE_CRITICAL; | ||
| 782 | } | 845 | } |
| 783 | 846 | ||
| 784 | dbi_result_free(res); | ||
| 785 | return STATE_OK; | ||
| 786 | } | ||
| 787 | |||
| 788 | mp_state_enum do_query(dbi_conn conn, const char **res_val_str, double *res_val, double *res_time, | ||
| 789 | mp_dbi_metric metric, mp_dbi_type type, char *np_dbi_query) { | ||
| 790 | dbi_result res; | ||
| 791 | |||
| 792 | struct timeval timeval_start; | ||
| 793 | struct timeval timeval_end; | 847 | struct timeval timeval_end; |
| 794 | mp_state_enum status = STATE_OK; | 848 | gettimeofday(&timeval_end, NULL); |
| 795 | 849 | result.query_duration = timediff(timeval_start, timeval_end); | |
| 796 | assert(np_dbi_query); | ||
| 797 | 850 | ||
| 798 | if (verbose) { | 851 | if (verbose) { |
| 799 | printf("Executing query '%s'\n", np_dbi_query); | 852 | printf("Query duration: %f\n", result.query_duration); |
| 800 | } | 853 | } |
| 801 | 854 | ||
| 802 | gettimeofday(&timeval_start, NULL); | 855 | // Default state is OK, all error will be set explicitly |
| 856 | mp_state_enum query_processing_state = STATE_OK; | ||
| 857 | { | ||
| 803 | 858 | ||
| 804 | res = dbi_conn_query(conn, np_dbi_query); | 859 | if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) { |
| 805 | if (!res) { | 860 | if (metric != METRIC_QUERY_RESULT) { |
| 806 | np_dbi_print_error(conn, "CRITICAL - failed to execute query '%s'", np_dbi_query); | 861 | query_processing_state = STATE_OK; |
| 807 | return STATE_CRITICAL; | 862 | } else { |
| 863 | dbi_conn_error(conn, &result.error_string); | ||
| 864 | query_processing_state = STATE_CRITICAL; | ||
| 865 | } | ||
| 866 | } else if (dbi_result_get_numrows(res) < 1) { | ||
| 867 | if (metric != METRIC_QUERY_RESULT) { | ||
| 868 | query_processing_state = STATE_OK; | ||
| 869 | } else { | ||
| 870 | result.error_string = "no rows returned"; | ||
| 871 | // printf("WARNING - no rows returned\n"); | ||
| 872 | query_processing_state = STATE_WARNING; | ||
| 873 | } | ||
| 874 | } else if (dbi_result_get_numfields(res) == DBI_FIELD_ERROR) { | ||
| 875 | if (metric != METRIC_QUERY_RESULT) { | ||
| 876 | query_processing_state = STATE_OK; | ||
| 877 | } else { | ||
| 878 | dbi_conn_error(conn, &result.error_string); | ||
| 879 | // np_dbi_print_error(conn, "CRITICAL - failed to fetch fields"); | ||
| 880 | query_processing_state = STATE_CRITICAL; | ||
| 881 | } | ||
| 882 | } else if (dbi_result_get_numfields(res) < 1) { | ||
| 883 | if (metric != METRIC_QUERY_RESULT) { | ||
| 884 | query_processing_state = STATE_OK; | ||
| 885 | } else { | ||
| 886 | result.error_string = "no fields returned"; | ||
| 887 | // printf("WARNING - no fields returned\n"); | ||
| 888 | query_processing_state = STATE_WARNING; | ||
| 889 | } | ||
| 890 | } else if (dbi_result_first_row(res) != 1) { | ||
| 891 | if (metric != METRIC_QUERY_RESULT) { | ||
| 892 | query_processing_state = STATE_OK; | ||
| 893 | } else { | ||
| 894 | dbi_conn_error(conn, &result.error_string); | ||
| 895 | // np_dbi_print_error(conn, "CRITICAL - failed to fetch first row"); | ||
| 896 | query_processing_state = STATE_CRITICAL; | ||
| 897 | } | ||
| 898 | } else { | ||
| 899 | unsigned short field_type = dbi_result_get_field_type_idx(res, 1); | ||
| 900 | if (field_type != DBI_TYPE_ERROR) { | ||
| 901 | if (type == TYPE_STRING) { | ||
| 902 | result.result_string = strdup(get_field_str(res, metric, type)); | ||
| 903 | } else { | ||
| 904 | get_field_wrapper gfw = get_field(res, metric, type); | ||
| 905 | result.result_number = gfw.value; | ||
| 906 | } | ||
| 907 | } else { | ||
| 908 | // Error when retrieving the field, that is OK if the Query result is not of | ||
| 909 | // interest | ||
| 910 | if (metric != METRIC_QUERY_RESULT) { | ||
| 911 | query_processing_state = STATE_OK; | ||
| 912 | } else { | ||
| 913 | dbi_conn_error(conn, &result.error_string); | ||
| 914 | // np_dbi_print_error(conn, "CRITICAL - failed to fetch data"); | ||
| 915 | query_processing_state = STATE_CRITICAL; | ||
| 916 | } | ||
| 917 | } | ||
| 918 | } | ||
| 808 | } | 919 | } |
| 920 | dbi_result_free(res); | ||
| 809 | 921 | ||
| 810 | status = get_query_result(conn, res, res_val_str, res_val, metric, type); | 922 | result.query_processing_status = query_processing_state; |
| 811 | |||
| 812 | gettimeofday(&timeval_end, NULL); | ||
| 813 | *res_time = timediff(timeval_start, timeval_end); | ||
| 814 | |||
| 815 | if (verbose) { | ||
| 816 | printf("Time elapsed: %f\n", *res_time); | ||
| 817 | } | ||
| 818 | 923 | ||
| 819 | return status; | 924 | return result; |
| 820 | } | 925 | } |
| 821 | 926 | ||
| 822 | double timediff(struct timeval start, struct timeval end) { | 927 | static double timediff(struct timeval start, struct timeval end) { |
| 823 | double diff; | 928 | double diff; |
| 824 | 929 | ||
| 825 | while (start.tv_usec > end.tv_usec) { | 930 | while (start.tv_usec > end.tv_usec) { |
| @@ -830,7 +935,7 @@ double timediff(struct timeval start, struct timeval end) { | |||
| 830 | return diff; | 935 | return diff; |
| 831 | } | 936 | } |
| 832 | 937 | ||
| 833 | void np_dbi_print_error(dbi_conn conn, char *fmt, ...) { | 938 | static void np_dbi_print_error(dbi_conn conn, char *fmt, ...) { |
| 834 | const char *errmsg = NULL; | 939 | const char *errmsg = NULL; |
| 835 | va_list ap; | 940 | va_list ap; |
| 836 | 941 | ||
diff --git a/plugins/check_dbi.d/config.h b/plugins/check_dbi.d/config.h index f6f0d7b3..25d74a12 100644 --- a/plugins/check_dbi.d/config.h +++ b/plugins/check_dbi.d/config.h | |||
| @@ -3,18 +3,19 @@ | |||
| 3 | #include "../../config.h" | 3 | #include "../../config.h" |
| 4 | #include <stddef.h> | 4 | #include <stddef.h> |
| 5 | #include "../../lib/monitoringplug.h" | 5 | #include "../../lib/monitoringplug.h" |
| 6 | #include "thresholds.h" | ||
| 6 | 7 | ||
| 7 | typedef enum { | 8 | typedef enum { |
| 8 | METRIC_CONN_TIME, | 9 | METRIC_CONN_TIME, |
| 9 | METRIC_SERVER_VERSION, | 10 | METRIC_SERVER_VERSION, |
| 10 | METRIC_QUERY_RESULT, | 11 | METRIC_QUERY_RESULT, |
| 11 | METRIC_QUERY_TIME, | 12 | METRIC_QUERY_TIME, |
| 12 | } mp_dbi_metric; | 13 | } check_dbi_metric; |
| 13 | 14 | ||
| 14 | typedef enum { | 15 | typedef enum { |
| 15 | TYPE_NUMERIC, | 16 | TYPE_NUMERIC, |
| 16 | TYPE_STRING, | 17 | TYPE_STRING, |
| 17 | } mp_dbi_type; | 18 | } check_dbi_type; |
| 18 | 19 | ||
| 19 | typedef struct { | 20 | typedef struct { |
| 20 | char *key; | 21 | char *key; |
| @@ -24,20 +25,22 @@ typedef struct { | |||
| 24 | typedef struct { | 25 | typedef struct { |
| 25 | char *dbi_driver; | 26 | char *dbi_driver; |
| 26 | char *host; | 27 | char *host; |
| 28 | |||
| 27 | driver_option_t *dbi_options; | 29 | driver_option_t *dbi_options; |
| 28 | size_t dbi_options_num; | 30 | size_t dbi_options_num; |
| 29 | char *dbi_database; | 31 | |
| 30 | char *dbi_query; | 32 | char *database; |
| 33 | char *query; | ||
| 31 | 34 | ||
| 32 | char *expect; | 35 | char *expect; |
| 33 | char *expect_re_str; | 36 | char *expect_re_str; |
| 34 | int expect_re_cflags; | 37 | int expect_re_cflags; |
| 35 | mp_dbi_metric metric; | 38 | check_dbi_metric metric; |
| 36 | mp_dbi_type type; | 39 | check_dbi_type type; |
| 37 | char *warning_range; | 40 | mp_thresholds thresholds; |
| 38 | char *critical_range; | ||
| 39 | thresholds *dbi_thresholds; | ||
| 40 | 41 | ||
| 42 | bool output_format_is_set; | ||
| 43 | mp_output_format output_format; | ||
| 41 | } check_dbi_config; | 44 | } check_dbi_config; |
| 42 | 45 | ||
| 43 | check_dbi_config check_dbi_config_init() { | 46 | check_dbi_config check_dbi_config_init() { |
| @@ -46,8 +49,8 @@ check_dbi_config check_dbi_config_init() { | |||
| 46 | .host = NULL, | 49 | .host = NULL, |
| 47 | .dbi_options = NULL, | 50 | .dbi_options = NULL, |
| 48 | .dbi_options_num = 0, | 51 | .dbi_options_num = 0, |
| 49 | .dbi_database = NULL, | 52 | .database = NULL, |
| 50 | .dbi_query = NULL, | 53 | .query = NULL, |
| 51 | 54 | ||
| 52 | .expect = NULL, | 55 | .expect = NULL, |
| 53 | .expect_re_str = NULL, | 56 | .expect_re_str = NULL, |
| @@ -55,9 +58,9 @@ check_dbi_config check_dbi_config_init() { | |||
| 55 | .metric = METRIC_QUERY_RESULT, | 58 | .metric = METRIC_QUERY_RESULT, |
| 56 | .type = TYPE_NUMERIC, | 59 | .type = TYPE_NUMERIC, |
| 57 | 60 | ||
| 58 | .warning_range = NULL, | 61 | .thresholds = mp_thresholds_init(), |
| 59 | .critical_range = NULL, | 62 | |
| 60 | .dbi_thresholds = NULL, | 63 | .output_format_is_set = false, |
| 61 | }; | 64 | }; |
| 62 | return tmp; | 65 | return tmp; |
| 63 | } | 66 | } |
diff --git a/plugins/t/check_dbi.t b/plugins/t/check_dbi.t index c24b5a8c..75444de4 100644 --- a/plugins/t/check_dbi.t +++ b/plugins/t/check_dbi.t | |||
| @@ -21,12 +21,12 @@ plan tests => $tests; | |||
| 21 | my $missing_driver_output = "failed to open DBI driver 'sqlite3'"; | 21 | my $missing_driver_output = "failed to open DBI driver 'sqlite3'"; |
| 22 | 22 | ||
| 23 | my $bad_driver_output = "/failed to open DBI driver 'nodriver'/"; | 23 | my $bad_driver_output = "/failed to open DBI driver 'nodriver'/"; |
| 24 | my $conn_time_output = "/OK - connection time: [0-9\.]+s \|/"; | 24 | my $conn_time_output = "/connection time: [0-9\.]+s \|/"; |
| 25 | my $missing_query_output = "/Must specify a query to execute/"; | 25 | my $missing_query_output = "/Must specify a query to execute/"; |
| 26 | my $no_rows_output = "/WARNING - no rows returned/"; | 26 | my $no_rows_output = "/no rows returned/"; |
| 27 | my $not_numeric_output = "/CRITICAL - result value is not a numeric:/"; | 27 | my $not_numeric_output = "/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 \|/"; | 28 | my $query_time_output = "/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/"; | 29 | my $syntax_error_output = "/1: near \"GET\": syntax error/"; |
| 30 | 30 | ||
| 31 | my $result; | 31 | my $result; |
| 32 | 32 | ||
