diff --git a/Changelog b/Changelog
index ae07c8b36..b51d45850 100644
--- a/Changelog
+++ b/Changelog
@@ -5,6 +5,7 @@ Nagios Core 4 Change Log
4.5.7 - 2024-11-19
------------------
* Expand the custom variable macros to an empty string, if the custom variable does not exist (Andre Klärner)
+* Fix notification logic to not send a notification if a host and services parents are down (#873) (Dylan Anderson)
* Update Exfoliation theme (Dylan Anderson)
4.5.6 - 2024-10-08
diff --git a/base/checks.c b/base/checks.c
index aa2be2f68..e5fff720d 100644
--- a/base/checks.c
+++ b/base/checks.c
@@ -1064,8 +1064,8 @@ static inline void host_propagate_checks_to_immediate_parents(host * hst, int pa
log_debug_info(DEBUGL_CHECKS, 1, "Propagating checks to parent host(s)...\n");
for(temp_hostsmember = hst->parent_hosts; temp_hostsmember != NULL; temp_hostsmember = temp_hostsmember->next) {
parent_host = temp_hostsmember->host_ptr;
- if ((parent_host_up == TRUE && parent_host->current_state == HOST_UP)
- || ((parent_host_up == FALSE && parent_host->current_state != HOST_UP))) {
+ if ((parent_host_up == TRUE && parent_host->current_state != HOST_UP)
+ || ((parent_host_up == FALSE && parent_host->current_state == HOST_UP))) {
log_debug_info(DEBUGL_CHECKS, 1, "Check of parent host '%s' queued.\n", parent_host->name);
schedule_host_check(parent_host, current_time, CHECK_OPTION_DEPENDENCY_CHECK);
@@ -1208,6 +1208,8 @@ int handle_async_service_check_result(service *svc, check_result *cr)
host * hst = NULL;
+ int notification_type = NOTIFICATION_NORMAL;
+
log_debug_info(DEBUGL_FUNCTIONS, 0, "handle_async_service_check_result()\n");
if (svc == NULL) {
@@ -1590,10 +1592,16 @@ int handle_async_service_check_result(service *svc, check_result *cr)
handle_event = TRUE;
}
+ // Recovery notification was not sent
+ if(svc->notified_on != 0 && svc->current_state == STATE_OK && svc->state_type == HARD_STATE) {
+ notification_type = NOTIFICATION_RECOVERY;
+ send_notification = TRUE;
+ }
+
if (send_notification == TRUE) {
/* send notification */
- if (service_notification(svc, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE) == OK) {
+ if (service_notification(svc, notification_type, NULL, NULL, NOTIFICATION_OPTION_NONE) == OK) {
/* log state due to notification event when stalking_options N is set */
if (should_stalk_notifications(svc)) {
@@ -1602,13 +1610,6 @@ int handle_async_service_check_result(service *svc, check_result *cr)
}
}
- /* the service recovered, so reset the current notification number and state flags (after the recovery notification has gone out) */
- /* We don't want to reset notifications if the service is currently flapping because we want recovery notifications */
- if(svc->current_state == STATE_OK && svc->state_type == HARD_STATE && hard_state_change == TRUE && svc->is_flapping == FALSE) {
- svc->current_notification_number = 0;
- svc->notified_on = 0;
- }
-
if (obsess_over_services == TRUE) {
obsessive_compulsive_service_check_processor(svc);
}
@@ -1634,7 +1635,6 @@ int handle_async_service_check_result(service *svc, check_result *cr)
/* Reset attempts */
if (hard_state_change == TRUE) {
- svc->current_notification_number = 0;
svc->host_problem_at_last_check = FALSE;
}
@@ -2255,6 +2255,8 @@ int handle_async_host_check_result(host *hst, check_result *cr)
char * old_plugin_output = NULL;
+ int notification_type = NOTIFICATION_NORMAL;
+
log_debug_info(DEBUGL_FUNCTIONS, 0, "handle_async_host_check_result()\n");
if (is_valid_check_result_data(hst, cr) == FALSE) {
@@ -2482,10 +2484,16 @@ int handle_async_host_check_result(host *hst, check_result *cr)
hst->current_attempt = 1;
}
+ // Recovery notification was not sent
+ if(hst->notified_on != 0 && hst->current_state == HOST_UP && hst->state_type == HARD_STATE) {
+ notification_type = NOTIFICATION_RECOVERY;
+ send_notification = TRUE;
+ }
+
if (send_notification == TRUE) {
/* send notifications */
- if (host_notification(hst, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE) == OK) {
+ if (host_notification(hst, notification_type, NULL, NULL, NOTIFICATION_OPTION_NONE) == OK) {
/* log state due to notification event when stalking_options N is set */
if (should_stalk_notifications(hst)) {
@@ -2494,13 +2502,6 @@ int handle_async_host_check_result(host *hst, check_result *cr)
}
}
- /* the host recovered, so reset the current notification number and state flags (after the recovery notification has gone out) */
- /* We don't want to reset notifications if the host is currently flapping because we want recovery notifications */
- if(hst->current_state == HOST_UP && hst->state_type == HARD_STATE && hard_state_change == TRUE && hst->is_flapping == FALSE) {
- hst->current_notification_number = 0;
- hst->notified_on = 0;
- }
-
if (obsess_over_hosts == TRUE) {
obsessive_compulsive_host_check_processor(hst);
}
diff --git a/base/commands.c b/base/commands.c
index 09287a025..4b9a53833 100644
--- a/base/commands.c
+++ b/base/commands.c
@@ -5390,10 +5390,7 @@ void clear_host_flapping_state(host *hst) {
/* should we send a recovery notification? */
if (hst->current_state == HOST_UP && hst->check_flapping_recovery_notification == TRUE) {
- host_notification(hst, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE);
- /* Similar to what happens when a host recovers in handle_async_host_check_result */
- hst->current_notification_number = 0;
- hst->notified_on = 0;
+ host_notification(hst, NOTIFICATION_RECOVERY, NULL, NULL, NOTIFICATION_OPTION_NONE);
}
}
@@ -5450,10 +5447,7 @@ void clear_service_flapping_state(service *svc) {
/* should we send a recovery notification? */
if (svc->current_state == STATE_OK && svc->check_flapping_recovery_notification == TRUE) {
- service_notification(svc, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE);
- /* Similar to what happens when a service recovers in handle_async_service_check_result */
- svc->current_notification_number = 0;
- svc->notified_on = 0;
+ service_notification(svc, NOTIFICATION_RECOVERY, NULL, NULL, NOTIFICATION_OPTION_NONE);
}
}
diff --git a/base/flapping.c b/base/flapping.c
index ed13d463b..ce3677a69 100644
--- a/base/flapping.c
+++ b/base/flapping.c
@@ -375,10 +375,7 @@ void clear_service_flap(service *svc, double percent_change, double high_thresho
/* should we send a recovery notification? */
if(svc->check_flapping_recovery_notification == TRUE && svc->current_state == STATE_OK) {
- service_notification(svc, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE);
- /* Similar to what happens when a service recovers in handle_async_service_check_result */
- svc->current_notification_number = 0;
- svc->notified_on = 0;
+ service_notification(svc, NOTIFICATION_RECOVERY, NULL, NULL, NOTIFICATION_OPTION_NONE);
}
}
@@ -470,10 +467,7 @@ void clear_host_flap(host *hst, double percent_change, double high_threshold, do
/* should we send a recovery notification? */
if(hst->check_flapping_recovery_notification == TRUE && hst->current_state == HOST_UP) {
- host_notification(hst, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE);
- /* Similar to what happens when a host recovers in handle_async_host_check_result */
- hst->current_notification_number = 0;
- hst->notified_on = 0;
+ host_notification(hst, NOTIFICATION_RECOVERY, NULL, NULL, NOTIFICATION_OPTION_NONE);
}
}
@@ -663,10 +657,7 @@ void handle_host_flap_detection_disabled(host *hst) {
/* should we send a recovery notification? */
if(hst->check_flapping_recovery_notification == TRUE && hst->current_state == HOST_UP) {
- host_notification(hst, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE);
- /* Similar to what happens when a host recovers in handle_async_host_check_result */
- hst->current_notification_number = 0;
- hst->notified_on = 0;
+ host_notification(hst, NOTIFICATION_RECOVERY, NULL, NULL, NOTIFICATION_OPTION_NONE);
}
/* clear the recovery notification flag */
@@ -781,10 +772,7 @@ void handle_service_flap_detection_disabled(service *svc) {
/* should we send a recovery notification? */
if(svc->check_flapping_recovery_notification == TRUE && svc->current_state == STATE_OK) {
- service_notification(svc, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE);
- /* Similar to what happens when a service recovers in handle_async_service_check_result */
- svc->current_notification_number = 0;
- svc->notified_on = 0;
+ service_notification(svc, NOTIFICATION_RECOVERY, NULL, NULL, NOTIFICATION_OPTION_NONE);
}
/* clear the recovery notification flag */
diff --git a/base/notifications.c b/base/notifications.c
index 0d218b479..4e8786ea5 100644
--- a/base/notifications.c
+++ b/base/notifications.c
@@ -98,14 +98,19 @@ int service_notification(service *svc, int type, char *not_author, char *not_dat
/* check the viability of sending out a service notification */
if(check_service_notification_viability(svc, type, options) == ERROR) {
log_debug_info(DEBUGL_NOTIFICATIONS, 0, "Notification viability test failed. No notification will be sent out.\n");
+ // Recovery notification wasn't sent because they don't want it. Still have to modify values
+ if(type == NOTIFICATION_RECOVERY && ! flag_isset(svc->notification_options, OPT_RECOVERY)) {
+ svc->notified_on = 0;
+ svc->current_notification_number = 0;
+ }
/* Set next_notification time if we're in a downtime and
notification_interval = zero, otherwise if the service
doesn't recover after downtime ends, it will never send
the notification */
- if (svc->notification_interval == 0.0) {
- if (temp_host->scheduled_downtime_depth > 0 || svc->scheduled_downtime_depth > 0)
- svc->next_notification = current_time;
- }
+ if (svc->notification_interval == 0.0) {
+ if (temp_host->scheduled_downtime_depth > 0 || svc->scheduled_downtime_depth > 0)
+ svc->next_notification = current_time;
+ }
return ERROR;
}
@@ -212,7 +217,7 @@ int service_notification(service *svc, int type, char *not_author, char *not_dat
mac.x[MACRO_NOTIFICATIONTYPE] = "DOWNTIMECANCELLED";
else if(type == NOTIFICATION_CUSTOM)
mac.x[MACRO_NOTIFICATIONTYPE] = "CUSTOM";
- else if(svc->current_state == STATE_OK)
+ else if(type == NOTIFICATION_RECOVERY)
mac.x[MACRO_NOTIFICATIONTYPE] = "RECOVERY";
else
mac.x[MACRO_NOTIFICATIONTYPE] = "PROBLEM";
@@ -276,7 +281,7 @@ int service_notification(service *svc, int type, char *not_author, char *not_dat
clear_servicegroup_macros_r(&mac);
clear_datetime_macros_r(&mac);
- if(type == NOTIFICATION_NORMAL) {
+ if(type == NOTIFICATION_NORMAL || type == NOTIFICATION_RECOVERY) {
/* adjust last/next notification time and notification flags if we notified someone */
if(contacts_notified > 0) {
@@ -289,8 +294,14 @@ int service_notification(service *svc, int type, char *not_author, char *not_dat
/* update the last notification time for this service (this is needed for rescheduling later notifications) */
svc->last_notification = current_time;
- /* update notifications flags */
- add_notified_on(svc, svc->current_state);
+ /* update notifications flags, make sure to consider recovery */
+ if (type == NOTIFICATION_NORMAL) {
+ add_notified_on(svc, svc->current_state);
+ }
+ else {
+ svc->notified_on = 0;
+ svc->current_notification_number = 0;
+ }
}
/* we didn't end up notifying anyone */
@@ -603,6 +614,20 @@ int check_service_notification_viability(service *svc, int type, int options) {
return ERROR;
}
+ /* If any of the parents are down, don't notify */
+ if (temp_host->parent_hosts != NULL) {
+ hostsmember *temp_hostsmember = NULL;
+ host *parent_host = NULL;
+
+ for(temp_hostsmember = temp_host->parent_hosts; temp_hostsmember != NULL; temp_hostsmember = temp_hostsmember->next) {
+ parent_host = temp_hostsmember->host_ptr;
+ if (parent_host->current_state != HOST_UP) {
+ log_debug_info(DEBUGL_NOTIFICATIONS, 1, "At least one parent (%s) is down, so we won't notify about this service.\n", parent_host->name);
+ return ERROR;
+ }
+ }
+ }
+
/* don't notify if we haven't waited long enough since the last time (and the service is not marked as being volatile) */
if((current_time < svc->next_notification) && svc->is_volatile == FALSE) {
log_debug_info(DEBUGL_NOTIFICATIONS, 1, "We haven't waited long enough to re-notify contacts about this service.\n");
@@ -1065,6 +1090,11 @@ int host_notification(host *hst, int type, char *not_author, char *not_data, int
/* check viability of sending out a host notification */
if(check_host_notification_viability(hst, type, options) == ERROR) {
+ // Recovery notification wasn't sent because they don't want it. Still have to modify values
+ if(type == NOTIFICATION_RECOVERY && ! flag_isset(hst->notification_options, OPT_RECOVERY)) {
+ hst->notified_on = 0;
+ hst->current_notification_number = 0;
+ }
log_debug_info(DEBUGL_NOTIFICATIONS, 0, "Notification viability test failed. No notification will be sent out.\n");
return ERROR;
}
@@ -1169,7 +1199,7 @@ int host_notification(host *hst, int type, char *not_author, char *not_data, int
mac.x[MACRO_NOTIFICATIONTYPE] = "DOWNTIMECANCELLED";
else if(type == NOTIFICATION_CUSTOM)
mac.x[MACRO_NOTIFICATIONTYPE] = "CUSTOM";
- else if(hst->current_state == HOST_UP)
+ else if(type == NOTIFICATION_RECOVERY)
mac.x[MACRO_NOTIFICATIONTYPE] = "RECOVERY";
else
mac.x[MACRO_NOTIFICATIONTYPE] = "PROBLEM";
@@ -1233,7 +1263,7 @@ int host_notification(host *hst, int type, char *not_author, char *not_data, int
clear_hostgroup_macros_r(&mac);
clear_datetime_macros_r(&mac);
- if(type == NOTIFICATION_NORMAL) {
+ if(type == NOTIFICATION_NORMAL || type == NOTIFICATION_RECOVERY) {
/* adjust last/next notification time and notification flags if we notified someone */
if(contacts_notified > 0) {
@@ -1245,7 +1275,13 @@ int host_notification(host *hst, int type, char *not_author, char *not_data, int
hst->last_notification = current_time;
/* update notifications flags */
- add_notified_on(hst, hst->current_state);
+ if(type == NOTIFICATION_NORMAL) {
+ add_notified_on(hst, hst->current_state);
+ }
+ else {
+ hst->notified_on = 0;
+ hst->current_notification_number = 0;
+ }
log_debug_info(DEBUGL_NOTIFICATIONS, 0, "%d contacts were notified. Next possible notification time: %s", contacts_notified, ctime(&hst->next_notification));
}
@@ -1449,21 +1485,13 @@ int check_host_notification_viability(host *hst, int type, int options) {
}
/* see if we should notify about problems with this host */
- if((hst->notification_options & (1 << hst->current_state)) == FALSE) {
+ if(should_notify(hst) == FALSE) {
log_debug_info(DEBUGL_NOTIFICATIONS, 1, "We shouldn't notify about %s status for this host.\n", host_state_name(hst->current_state));
return ERROR;
}
- if(hst->current_state == HOST_UP) {
-
- if((hst->notification_options & OPT_RECOVERY) == FALSE) {
- log_debug_info(DEBUGL_NOTIFICATIONS, 1, "We shouldn't notify about RECOVERY states for this host.\n");
- return ERROR;
- }
- if(!hst->notified_on) {
- log_debug_info(DEBUGL_NOTIFICATIONS, 1, "We shouldn't notify about this recovery.\n");
- return ERROR;
- }
-
+ if(hst->current_state == HOST_UP && hst->notified_on == 0) {
+ log_debug_info(DEBUGL_NOTIFICATIONS, 1, "We shouldn't notify about this recovery.\n");
+ return ERROR;
}
/* see if enough time has elapsed for first notification (Mathias Sundman) */
@@ -1510,6 +1538,20 @@ int check_host_notification_viability(host *hst, int type, int options) {
return ERROR;
}
+ /* If any of the parents are down, don't notify */
+ if (hst->parent_hosts != NULL) {
+ hostsmember *temp_hostsmember = NULL;
+ host *parent_host = NULL;
+
+ for(temp_hostsmember = hst->parent_hosts; temp_hostsmember != NULL; temp_hostsmember = temp_hostsmember->next) {
+ parent_host = temp_hostsmember->host_ptr;
+ if (parent_host->current_state != HOST_UP) {
+ log_debug_info(DEBUGL_NOTIFICATIONS, 1, "At least one parent (%s) is down, so we won't notify about this host.\n", parent_host->name);
+ return ERROR;
+ }
+ }
+ }
+
return OK;
}
diff --git a/include/nagios.h b/include/nagios.h
index ac5ca793c..63504d0c4 100644
--- a/include/nagios.h
+++ b/include/nagios.h
@@ -326,6 +326,7 @@ extern struct load_control loadctl;
#define NOTIFICATION_DOWNTIMEEND 6
#define NOTIFICATION_DOWNTIMECANCELLED 7
#define NOTIFICATION_CUSTOM 8
+#define NOTIFICATION_RECOVERY 9
diff --git a/t-tap/.gitignore b/t-tap/.gitignore
index 5e2d4abd4..004481582 100644
--- a/t-tap/.gitignore
+++ b/t-tap/.gitignore
@@ -8,4 +8,5 @@ test_xsddefault
test_commands
test_downtime
test_strtoul
+test_notifications
*.dSYM
diff --git a/t-tap/Makefile.in b/t-tap/Makefile.in
index 1b3544fbb..5194552cd 100644
--- a/t-tap/Makefile.in
+++ b/t-tap/Makefile.in
@@ -24,6 +24,7 @@ TESTS += test_downtime
TESTS += test_nagios_config
TESTS += test_timeperiods
TESTS += test_macros
+TESTS += test_notifications
XSD_OBJS = $(BLD_CGI)/statusdata-cgi.o $(BLD_CGI)/xstatusdata-cgi.o
XSD_OBJS += $(BLD_CGI)/objects-cgi.o $(BLD_CGI)/xobjects-cgi.o
@@ -104,6 +105,9 @@ test_timeperiods: test_timeperiods.o $(TP_OBJS) $(TAPOBJ)
test_macros: test_macros.o $(TP_OBJS) $(TAPOBJ)
$(CC) $(CFLAGS) -o $@ $^ $(BLD_BASE)/checks.o $(BROKER_LDFLAGS) $(LDFLAGS) $(MATHLIBS) $(SOCKETLIBS) $(LIBS)
+test_notifications: test_notifications.o $(BLD_BASE)/notifications.o $(BLD_BASE)/checks.o $(BLD_BASE)/utils.o $(BLD_COMMON)/shared.o $(BLD_BASE)/objects-base.o $(TAPOBJ)
+ $(CC) $(CFLAGS) -o $@ $^ $(LIBS) $(MATHLIBS)
+
test_xsddefault: test_xsddefault.o $(XSD_OBJS) $(TAPOBJ)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
diff --git a/t-tap/stub_broker.c b/t-tap/stub_broker.c
index eed664648..6cd9649c5 100644
--- a/t-tap/stub_broker.c
+++ b/t-tap/stub_broker.c
@@ -42,3 +42,12 @@ void broker_timed_event(int type, int flags, int attr, timed_event *event, struc
void broker_flapping_data(int type, int flags, int attr, int flapping_type, void *data, double percent_change, double high_threshold, double low_threshold, struct timeval *timestamp)
{ }
+
+int broker_contact_notification_data(int type, int flags, int attr, int notification_type, int reason_type, struct timeval start_time, struct timeval end_time, void *data, contact *cntct, char *ack_author, char *ack_data, int escalated, struct timeval *timestamp)
+{ return OK; }
+
+int broker_contact_notification_method_data(int type, int flags, int attr, int notification_type, int reason_type, struct timeval start_time, struct timeval end_time, void *data, contact *cntct, char *cmd, char *ack_author, char *ack_data, int escalated, struct timeval *timestamp)
+{ return OK; }
+
+int broker_notification_data(int type, int flags, int attr, int notification_type, int reason_type, struct timeval start_time, struct timeval end_time, void *data, char *ack_author, char *ack_data, int escalated, int contacts_notified, struct timeval *timestamp)
+{ return OK; }
\ No newline at end of file
diff --git a/t-tap/stub_macros.c b/t-tap/stub_macros.c
index 0ba86737a..3a11c7a5e 100644
--- a/t-tap/stub_macros.c
+++ b/t-tap/stub_macros.c
@@ -29,3 +29,27 @@ int clear_argv_macros_r(nagios_macros *mac)
int set_all_macro_environment_vars_r(nagios_macros *mac, int set)
{ return OK; }
+
+int clear_datetime_macros_r(nagios_macros *mac)
+{ return OK; }
+
+int clear_hostgroup_macros_r(nagios_macros *mac)
+{ return OK; }
+
+int clear_contactgroup_macros_r(nagios_macros *mac)
+{ return OK; }
+
+int clear_contact_macros_r(nagios_macros *mac)
+{ return OK; }
+
+int clear_summary_macros_r(nagios_macros *mac)
+{ return OK; }
+
+int grab_contact_macros_r(nagios_macros *mac, contact *cntct)
+{ return OK; }
+
+int clear_service_macros_r(nagios_macros *mac)
+{ return OK; }
+
+int clear_servicegroup_macros_r(nagios_macros *mac)
+{ return OK; }
\ No newline at end of file
diff --git a/t-tap/stub_workers.c b/t-tap/stub_workers.c
index 6848108af..784e4d2f4 100644
--- a/t-tap/stub_workers.c
+++ b/t-tap/stub_workers.c
@@ -9,3 +9,6 @@ void wproc_reap(int jobs, int msecs)
int get_desired_workers(int desired_workers)
{ return 4; }
+
+int wproc_notify(char *cname, char *hname, char *sdesc, char *cmd, nagios_macros *mac)
+{ return OK; }
\ No newline at end of file
diff --git a/t-tap/test_notifications.c b/t-tap/test_notifications.c
new file mode 100644
index 000000000..41c6104d1
--- /dev/null
+++ b/t-tap/test_notifications.c
@@ -0,0 +1,549 @@
+/*****************************************************************************
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+*
+*****************************************************************************/
+
+#define TEST_NOTIFICATIONS_C
+#define NSCORE 1
+
+#include "config.h"
+
+#include
+#include
+
+#include "comments.h"
+#include "common.h"
+#include "statusdata.h"
+#include "downtime.h"
+#include "macros.h"
+#include "nagios.h"
+#include "broker.h"
+#include "perfdata.h"
+#include "../lib/lnag-utils.h"
+
+#include "tap.h"
+
+#include "stub_sehandlers.c"
+#include "stub_comments.c"
+#include "stub_perfdata.c"
+#include "stub_downtime.c"
+#include "stub_logging.c"
+#include "stub_broker.c"
+#include "stub_macros.c"
+#include "stub_workers.c"
+#include "stub_events.c"
+#include "stub_statusdata.c"
+#include "stub_flapping.c"
+#include "stub_nebmods.c"
+#include "stub_netutils.c"
+#include "stub_commands.c"
+#include "stub_xodtemplate.c"
+#include "stub_iobroker.c"
+
+#define HOST_NAME "hst1"
+#define HOST_ADDRESS "127.0.0.1"
+#define HOST_COMMAND "hst1_command"
+#define SERVICE_NAME "svc1"
+
+#define NO_SERVICE 0,0,NULL
+
+service * svc1 = NULL;
+host * hst1 = NULL;
+host * parent1 = NULL;
+host * parent2 = NULL;
+
+int c = 0;
+
+
+void free_host(host ** hst)
+{
+ if ((*hst) != NULL) {
+
+ my_free((*hst)->name);
+ my_free((*hst)->address);
+ my_free((*hst)->plugin_output);
+ my_free((*hst)->long_plugin_output);
+ my_free((*hst)->perf_data);
+ my_free((*hst)->check_command);
+
+ if ((*hst)->parent_hosts != NULL) {
+ my_free((*hst)->parent_hosts->next);
+ my_free((*hst)->parent_hosts);
+ }
+
+ my_free((*hst));
+ }
+}
+
+void free_parent1()
+{
+ free_host(&parent1);
+}
+
+void free_parent2()
+{
+ free_host(&parent2);
+}
+
+void free_hst1()
+{
+ free_host(&hst1);
+}
+
+void free_svc1()
+{
+ if (svc1 != NULL) {
+ my_free(svc1->host_name);
+ my_free(svc1->description);
+ my_free(svc1->plugin_output);
+ my_free(svc1->long_plugin_output);
+ my_free(svc1->perf_data);
+ my_free(svc1->check_command);
+ my_free(svc1);
+ }
+}
+
+void free_all()
+{
+ free_parent1();
+ free_parent2();
+ free_hst1();
+ free_svc1();
+}
+
+void adjust_host_output(char * output)
+{
+ my_free(hst1->plugin_output);
+ hst1->plugin_output = strdup(output);
+}
+
+void adjust_service_output(char * output)
+{
+ my_free(svc1->plugin_output);
+ svc1->plugin_output = strdup(output);
+}
+
+void create_objects(int host_state, int host_state_type, char * host_output, int service_state, int service_state_type, char * service_output)
+{
+ free_hst1();
+ free_svc1();
+
+ hst1 = (host *) calloc(1, sizeof(host));
+
+ if (service_output != NULL) {
+ svc1 = (service *) calloc(1, sizeof(service));
+ }
+
+ hst1->name = strdup(HOST_NAME);
+ hst1->address = strdup(HOST_ADDRESS);
+ hst1->check_command = strdup(HOST_COMMAND);
+ hst1->retry_interval = 1;
+ hst1->check_interval = 5;
+ hst1->current_attempt = 1;
+ hst1->max_attempts = 4;
+ hst1->check_options = CHECK_OPTION_NONE;
+ hst1->has_been_checked = TRUE;
+ hst1->last_state_change = (time_t) 0L;
+ hst1->last_hard_state_change = (time_t) 0L;
+ hst1->last_time_up = (time_t) 0L;
+ hst1->last_time_down = (time_t) 0L;
+ hst1->last_time_unreachable = (time_t) 0L;
+ hst1->last_notification = (time_t) 0L;
+ hst1->next_notification = (time_t) 0L;
+ hst1->last_check = (time_t) 0L;
+ hst1->next_check = (time_t) 0L;
+ hst1->should_be_scheduled = TRUE;
+ hst1->last_hard_state = STATE_UP;
+ hst1->notification_options = OPT_ALL;
+ hst1->notifications_enabled = TRUE;
+ hst1->event_handler_enabled = TRUE;
+ hst1->accept_passive_checks = TRUE;
+ hst1->initial_state = STATE_UP;
+ hst1->accept_passive_checks = TRUE;
+
+ if (service_output != NULL) {
+ svc1->host_name = strdup(HOST_NAME);
+ svc1->description = strdup(SERVICE_NAME);
+ svc1->host_ptr = hst1;
+ svc1->retry_interval = 1;
+ svc1->check_interval = 5;
+ svc1->current_attempt = 1;
+ svc1->max_attempts = 4;
+ svc1->check_options = CHECK_OPTION_NONE;
+ svc1->has_been_checked = TRUE;
+ svc1->last_state_change = (time_t) 0L;
+ svc1->last_hard_state_change = (time_t) 0L;
+ svc1->last_time_ok = (time_t) 0L;
+ svc1->last_time_warning = (time_t) 0L;
+ svc1->last_time_unknown = (time_t) 0L;
+ svc1->last_time_critical = (time_t) 0L;
+ svc1->last_notification = (time_t) 0L;
+ svc1->next_notification = (time_t) 0L;
+ svc1->last_check = (time_t) 0L;
+ svc1->next_check = (time_t) 0L;
+ svc1->host_problem_at_last_check = FALSE;
+ svc1->should_be_scheduled = TRUE;
+ svc1->last_hard_state = STATE_OK;
+ svc1->notification_options = OPT_ALL;
+ svc1->notifications_enabled = TRUE;
+ svc1->event_handler_enabled = TRUE;
+ svc1->accept_passive_checks = TRUE;
+ svc1->initial_state = STATE_OK;
+ svc1->accept_passive_checks = TRUE;
+ }
+
+ hst1->current_state = host_state;
+ hst1->state_type = host_state_type;
+ adjust_host_output(host_output);
+
+ if (service_output != NULL) {
+ svc1->current_state = service_state;
+ svc1->state_type = service_state_type;
+ adjust_service_output(service_output);
+ }
+}
+
+void setup_parent(host ** parent, const char * host_name)
+{
+ (*parent) = (host *) calloc(1, sizeof(host));
+
+ (*parent)->name = strdup(host_name);
+ (*parent)->address = strdup(HOST_ADDRESS);
+ (*parent)->check_command = strdup(HOST_COMMAND);
+ (*parent)->retry_interval = 1;
+ (*parent)->check_interval = 5;
+ (*parent)->current_attempt = 1;
+ (*parent)->max_attempts = 4;
+ (*parent)->check_options = CHECK_OPTION_NONE;
+ (*parent)->has_been_checked = TRUE;
+ (*parent)->current_state = STATE_DOWN;
+
+}
+
+void setup_parents()
+{
+ hostsmember * tmp_hsts = NULL;
+ tmp_hsts = (hostsmember *) calloc(1, sizeof(hostsmember));
+
+ setup_parent(&parent1, "Parent 1");
+ setup_parent(&parent2, "Parent 2");
+
+ tmp_hsts->host_ptr = parent1;
+ tmp_hsts->next = (hostsmember *) calloc(1, sizeof(hostsmember));
+ tmp_hsts->next->host_ptr = parent2;
+
+ hst1->parent_hosts = tmp_hsts;
+}
+
+void run_service_tests() {
+
+ int result = OK;
+
+ // debug_level = DEBUGL_NOTIFICATIONS;
+ // debug_verbosity = 2;
+
+ enable_notifications = FALSE;
+
+ // Forced notifications always pass
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok");
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_FORCED);
+ ok(result == OK, "Notification Forced - Service should notify");
+
+ // No notifications when notifications are disabled
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical");
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Notifications disabled - Service should NOT notify");
+
+ enable_notifications = TRUE;
+
+ // All parents are bad. Reminder that host parents are different than service parents.
+ create_objects(STATE_DOWN, HARD_STATE, "host down", STATE_CRITICAL, HARD_STATE, "service critical");
+ setup_parents();
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "All parents are down - Service should NOT notify");
+
+ // Not all parents are bad
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical");
+ setup_parents();
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Not all parents are down - Service should NOT notify");
+
+ free_all();
+
+ // Timeperiod checking
+
+ // Notifications disabled for service
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical");
+ svc1->notifications_enabled = FALSE;
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Service notifications disabled - Service should NOT notify");
+
+ // Custom notifications
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok");
+ result = check_service_notification_viability(svc1, NOTIFICATION_CUSTOM, NOTIFICATION_OPTION_NONE);
+ ok(result == OK, "Custom notification - Service should notify");
+
+ // Custom Notification in downtime
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok");
+ svc1->scheduled_downtime_depth = 1;
+ result = check_service_notification_viability(svc1, NOTIFICATION_CUSTOM, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Custom notification in downtime - Service should NOT notify");
+
+ // Acknowledgement
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical");
+ result = check_service_notification_viability(svc1, NOTIFICATION_ACKNOWLEDGEMENT, NOTIFICATION_OPTION_NONE);
+ ok(result == OK, "Acknowledgement - Service should notify");
+
+ // Acknowledgement while ok
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok");
+ result = check_service_notification_viability(svc1, NOTIFICATION_ACKNOWLEDGEMENT, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Acknowledgement while OK - Service should NOT notify");
+
+ // Service Flapping start
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok");
+ result = check_service_notification_viability(svc1, NOTIFICATION_FLAPPINGSTART, NOTIFICATION_OPTION_NONE);
+ ok(result == OK, "Flapping started - Service should notify");
+
+ // Service Flapping start while in downtime
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok");
+ svc1->scheduled_downtime_depth = 1;
+ result = check_service_notification_viability(svc1, NOTIFICATION_FLAPPINGSTART, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Flapping started in downtime - Service should NOT notify");
+
+ // Service downtime start
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok");
+ result = check_service_notification_viability(svc1, NOTIFICATION_DOWNTIMESTART, NOTIFICATION_OPTION_NONE);
+ ok(result == OK, "Downtime started - Service should notify");
+
+ // Service downtime start while in downtime (Sounds wrong)
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok");
+ svc1->scheduled_downtime_depth = 1;
+ result = check_service_notification_viability(svc1, NOTIFICATION_DOWNTIMESTART, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Downtime started in downtime - Service should NOT notify");
+
+ // Soft states don't get notifications
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, SOFT_STATE, "service critical");
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Host: up,hard - Service: critical,soft - Service should NOT notify");
+
+ // Problem has already been acknowledged
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical");
+ svc1->problem_has_been_acknowledged = TRUE;
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Service problem acknowledged - Service should NOT notify");
+
+ // Host and Service Dependencies
+
+ // Notification options for the service don't match
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical");
+ svc1->notification_options = OPT_NOTHING;
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Service options don't have critical - Service should NOT notify");
+
+ // Recovery notification
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok");
+ svc1->notified_on = 1;
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == OK, "Recovery notification - Service should notify");
+
+ // Recovery notification without matching error notification
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok");
+ svc1->notified_on = 0;
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Recovery notification without matching error - Service should NOT notify");
+
+ // Enough time between last notification
+
+ // Service is flapping
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical");
+ svc1->is_flapping = TRUE;
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Service is flapping - Service should NOT notify");
+
+ // Service in downtime
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical");
+ svc1->scheduled_downtime_depth = 1;
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Service is in downtime - Service should NOT notify");
+
+ // Parent host in downtime
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical");
+ hst1->scheduled_downtime_depth = 1;
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Parent host is in downtime - Service should NOT notify");
+
+ // Host is down, service shouldn't get notification
+ create_objects(STATE_DOWN, HARD_STATE, "host down", STATE_CRITICAL, HARD_STATE, "service critical");
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Host: down,hard - Service: critical,hard - Service should NOT notify");
+
+ // When you should be typically notified
+ create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical");
+ result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == OK, "Host: up,hard - Service: critical,hard - Service should notify");
+
+ // Some other thing with not notifying again too soon
+
+ free_all();
+}
+
+void run_host_tests() {
+ int result = OK;
+
+ // debug_level = DEBUGL_NOTIFICATIONS;
+ // debug_verbosity = 2;
+
+ enable_notifications = FALSE;
+
+ // Forced notifications always pass
+ create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE);
+ result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_FORCED);
+ ok(result == OK, "Notification Forced - Host should notify");
+
+ // No notifications when notifications are disabled
+ create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE);
+ result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Notifications disabled - Host should NOT notify");
+
+ enable_notifications = TRUE;
+
+ // Timeperiod checking
+
+ // Notifications disabled for host
+ create_objects(STATE_DOWN, HARD_STATE, "host down", NO_SERVICE);
+ hst1->notifications_enabled = FALSE;
+ result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Service notifications disabled - Host should NOT notify");
+
+ // Custom notifications
+ create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE);
+ result = check_host_notification_viability(hst1, NOTIFICATION_CUSTOM, NOTIFICATION_OPTION_NONE);
+ ok(result == OK, "Custom notification - Host should notify");
+
+ // Custom Notification in downtime
+ create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE);
+ hst1->scheduled_downtime_depth = 1;
+ result = check_host_notification_viability(hst1, NOTIFICATION_CUSTOM, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Custom notification in downtime - Host should NOT notify");
+
+ // Acknowledgement
+ create_objects(STATE_DOWN, HARD_STATE, "host down", NO_SERVICE);
+ result = check_host_notification_viability(hst1, NOTIFICATION_ACKNOWLEDGEMENT, NOTIFICATION_OPTION_NONE);
+ ok(result == OK, "Acknowledgement - Host should notify");
+
+ // Acknowledgement while ok
+ create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE);
+ result = check_host_notification_viability(hst1, NOTIFICATION_ACKNOWLEDGEMENT, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Acknowledgement while OK - Host should NOT notify");
+
+ // Host Flapping start
+ create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE);
+ result = check_host_notification_viability(hst1, NOTIFICATION_FLAPPINGSTART, NOTIFICATION_OPTION_NONE);
+ ok(result == OK, "Flapping started - Host should notify");
+
+ // Host Flapping start while in downtime
+ create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE);
+ hst1->scheduled_downtime_depth = 1;
+ result = check_host_notification_viability(hst1, NOTIFICATION_FLAPPINGSTART, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Flapping started in downtime - Host should NOT notify");
+
+ // Host downtime start
+ create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE);
+ result = check_host_notification_viability(hst1, NOTIFICATION_DOWNTIMESTART, NOTIFICATION_OPTION_NONE);
+ ok(result == OK, "Downtime started - Host should notify");
+
+ // Host downtime start while in downtime (Sounds wrong)
+ create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE);
+ hst1->scheduled_downtime_depth = 1;
+ result = check_host_notification_viability(hst1, NOTIFICATION_DOWNTIMESTART, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Downtime started in downtime - Host should NOT notify");
+
+ // Soft states don't get notifications
+ create_objects(STATE_DOWN, SOFT_STATE, "host down", NO_SERVICE);
+ result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Host: down,soft - Host should NOT notify");
+
+ // Problem has already been acknowledged
+ create_objects(STATE_DOWN, HARD_STATE, "host down", NO_SERVICE);
+ hst1->problem_has_been_acknowledged = TRUE;
+ result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Host problem acknowledged - Host should NOT notify");
+
+ // Host and Service Dependencies
+
+ // Notification options for the host don't match
+ create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE);
+ hst1->notification_options = OPT_NOTHING;
+ result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Host options don't have critical - Host should NOT notify");
+
+ // Recovery notification
+ create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE);
+ hst1->notified_on = 1;
+ result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == OK, "Recovery notification - Host should notify");
+
+ // Recovery notification without matching error notification
+ create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE);
+ hst1->notified_on = 0;
+ result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Recovery notification without matching error - Host should NOT notify");
+
+ // Enough time between last notification
+
+ // Host is flapping
+ create_objects(STATE_DOWN, HARD_STATE, "host down", NO_SERVICE);
+ hst1->is_flapping = TRUE;
+ result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Host is flapping - Host should NOT notify");
+
+ // Host in downtime
+ create_objects(STATE_DOWN, HARD_STATE, "host down", NO_SERVICE);
+ hst1->scheduled_downtime_depth = 1;
+ result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == ERROR, "Host is in downtime - Host should NOT notify");
+
+ // When you should be typically notified
+ create_objects(STATE_DOWN, HARD_STATE, "host down", NO_SERVICE);
+ result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE);
+ ok(result == OK, "Host: down,hard - Host should notify");
+
+ // Some other thing with not notifying again too soon
+
+ free_all();
+
+}
+
+int main(int argc, char **argv)
+{
+ char cwd[1024];
+ if (getcwd(cwd, sizeof(cwd)) != NULL) {
+ printf("\n\n********** cwd: %s\n\n", cwd);
+ } else {
+ printf("\n\n********** cwd error!\n\n");
+ }
+
+ time_t now = 0L;
+
+ plan_tests(42);
+
+ time(&now);
+
+ run_service_tests();
+ run_host_tests();
+
+ return exit_status();
+}
\ No newline at end of file