/*****************************************************************************
 *
 * FLAPPING.C - State flap detection and handling routines for Nagios
 *
 * Copyright (c) 2001-2009 Ethan Galstad (egalstad@nagios.org)
 * Last Modified: 05-15-2009
 *
 * License:
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *****************************************************************************/

/*********** COMMON HEADER FILES ***********/

#include "../include/config.h"
#include "../include/common.h"
#include "../include/objects.h"
#include "../include/comments.h"
#include "../include/statusdata.h"
#include "../include/nagios.h"
#include "../include/broker.h"

extern int      interval_length;

extern int      enable_flap_detection;

extern double   low_service_flap_threshold;
extern double   high_service_flap_threshold;
extern double   low_host_flap_threshold;
extern double   high_host_flap_threshold;

extern host     *host_list;
extern service  *service_list;

extern unsigned long    modified_host_process_attributes;
extern unsigned long    modified_service_process_attributes;


/******************************************************************/
/******************** FLAP DETECTION FUNCTIONS ********************/
/******************************************************************/


/* detects service flapping */
void check_for_service_flapping(service *svc, int update, int allow_flapstart_notification){
	int update_history=TRUE;
	int is_flapping=FALSE;
	register int x=0;
	register int y=0;
	int last_state_history_value=STATE_OK;
	double curved_changes=0.0;
	double curved_percent_change=0.0;
	double low_threshold=0.0;
	double high_threshold=0.0;
	double low_curve_value=0.75;
	double high_curve_value=1.25;

	/* large install tweaks skips all flap detection logic - including state change calculation */


	log_debug_info(DEBUGL_FUNCTIONS,0,"check_for_service_flapping()\n");

	if(svc==NULL)
		return;

	log_debug_info(DEBUGL_FLAPPING,1,"Checking service '%s' on host '%s' for flapping...\n",svc->description,svc->host_name);

	/* if this is a soft service state and not a soft recovery, don't record this in the history */
	/* only hard states and soft recoveries get recorded for flap detection */
	if(svc->state_type==SOFT_STATE && svc->current_state!=STATE_OK)
		return;

	/* what threshold values should we use (global or service-specific)? */
	low_threshold=(svc->low_flap_threshold<=0.0)?low_service_flap_threshold:svc->low_flap_threshold;
	high_threshold=(svc->high_flap_threshold<=0.0)?high_service_flap_threshold:svc->high_flap_threshold;

	update_history=update;

	/* should we update state history for this state? */
	if(update_history==TRUE){

		if(svc->current_state==STATE_OK  && svc->flap_detection_on_ok==FALSE)
			update_history=FALSE;
		if(svc->current_state==STATE_WARNING && svc->flap_detection_on_warning==FALSE)
			update_history=FALSE;
		if(svc->current_state==STATE_UNKNOWN && svc->flap_detection_on_unknown==FALSE)
			update_history=FALSE;
		if(svc->current_state==STATE_CRITICAL && svc->flap_detection_on_critical==FALSE)
			update_history=FALSE;
	        }

	/* record current service state */
	if(update_history==TRUE){

		/* record the current state in the state history */
		svc->state_history[svc->state_history_index]=svc->current_state;

		/* increment state history index to next available slot */
		svc->state_history_index++;
		if(svc->state_history_index>=MAX_STATE_HISTORY_ENTRIES)
			svc->state_history_index=0;
	        }

	/* calculate overall and curved percent state changes */
	for(x=0,y=svc->state_history_index;x<MAX_STATE_HISTORY_ENTRIES;x++){

		if(x==0){
			last_state_history_value=svc->state_history[y];
			y++;
			if(y>=MAX_STATE_HISTORY_ENTRIES)
				y=0;
			continue;
		        }

		if(last_state_history_value!=svc->state_history[y])
			curved_changes+=(((double)(x-1)*(high_curve_value-low_curve_value))/((double)(MAX_STATE_HISTORY_ENTRIES-2)))+low_curve_value;

		last_state_history_value=svc->state_history[y];

		y++;
		if(y>=MAX_STATE_HISTORY_ENTRIES)
			y=0;
	        }

	/* calculate overall percent change in state */
	curved_percent_change=(double)(((double)curved_changes*100.0)/(double)(MAX_STATE_HISTORY_ENTRIES-1));

	svc->percent_state_change=curved_percent_change;

	log_debug_info(DEBUGL_FLAPPING,2,"LFT=%.2f, HFT=%.2f, CPC=%.2f, PSC=%.2f%%\n",low_threshold,high_threshold,curved_percent_change,curved_percent_change);


	/* don't do anything if we don't have flap detection enabled on a program-wide basis */
	if(enable_flap_detection==FALSE)
		return;

	/* don't do anything if we don't have flap detection enabled for this service */
	if(svc->flap_detection_enabled==FALSE)
		return;

	/* are we flapping, undecided, or what?... */

	/* we're undecided, so don't change the current flap state */
	if(curved_percent_change>low_threshold && curved_percent_change<high_threshold)
		return;

	/* we're below the lower bound, so we're not flapping */
	else if(curved_percent_change<=low_threshold)
		is_flapping=FALSE;
       
	/* else we're above the upper bound, so we are flapping */
	else if(curved_percent_change>=high_threshold)
		is_flapping=TRUE;

	log_debug_info(DEBUGL_FLAPPING,1,"Service %s flapping (%.2f%% state change).\n",(is_flapping==TRUE)?"is":"is not",curved_percent_change);

	/* did the service just start flapping? */
	if(is_flapping==TRUE && svc->is_flapping==FALSE)
		set_service_flap(svc,curved_percent_change,high_threshold,low_threshold,allow_flapstart_notification);

	/* did the service just stop flapping? */
	else if(is_flapping==FALSE && svc->is_flapping==TRUE)
		clear_service_flap(svc,curved_percent_change,high_threshold,low_threshold);

	return;
        }


/* detects host flapping */
void check_for_host_flapping(host *hst, int update, int actual_check, int allow_flapstart_notification){
	int update_history=TRUE;
	int is_flapping=FALSE;
	register int x=0;
	register int y=0;
	int last_state_history_value=HOST_UP;
	unsigned long wait_threshold=0L;
	double curved_changes=0.0;
	double curved_percent_change=0.0;
	time_t current_time=0L;
	double low_threshold=0.0;
	double high_threshold=0.0;
	double low_curve_value=0.75;
	double high_curve_value=1.25;


	log_debug_info(DEBUGL_FUNCTIONS,0,"check_for_host_flapping()\n");

	if(hst==NULL)
		return;

	log_debug_info(DEBUGL_FLAPPING,1,"Checking host '%s' for flapping...\n",hst->name);

	time(&current_time);

	/* period to wait for updating archived state info if we have no state change */
	if(hst->total_services==0)
		wait_threshold=hst->notification_interval*interval_length;
	else
		wait_threshold=(hst->total_service_check_interval*interval_length)/hst->total_services;

	update_history=update;

	/* should we update state history for this state? */
	if(update_history==TRUE){

		if(hst->current_state==HOST_UP  && hst->flap_detection_on_up==FALSE)
			update_history=FALSE;
		if(hst->current_state==HOST_DOWN && hst->flap_detection_on_down==FALSE)
			update_history=FALSE;
		if(hst->current_state==HOST_UNREACHABLE && hst->flap_detection_on_unreachable==FALSE)
			update_history=FALSE;
	        }

	/* if we didn't have an actual check, only update if we've waited long enough */
	if(update_history==TRUE && actual_check==FALSE && (current_time-hst->last_state_history_update)<wait_threshold){

		update_history=FALSE;

#ifdef REMOVED_05152009
		/* get the last recorded state */
		last_state_history_value=hst->state_history[(hst->state_history_index==0)?MAX_STATE_HISTORY_ENTRIES-1:hst->state_history_index-1];
#endif

		/* NOTE TO SELF 01/23/07 - does this ever evaluate to false?  I don't think so, which probably makes this code pointless... */
		/* if we haven't had a state change since our last recorded state, bail out */
#ifdef REMOVED_01232007
		if(last_state_history_value==hst->current_state)
			return;
#endif
	        }

	/* what thresholds should we use (global or host-specific)? */
	low_threshold=(hst->low_flap_threshold<=0.0)?low_host_flap_threshold:hst->low_flap_threshold;
	high_threshold=(hst->high_flap_threshold<=0.0)?high_host_flap_threshold:hst->high_flap_threshold;

	/* record current host state */
	if(update_history==TRUE){

		/* update the last record time */
		hst->last_state_history_update=current_time;

		/* record the current state in the state history */
		hst->state_history[hst->state_history_index]=hst->current_state;

		/* increment state history index to next available slot */
		hst->state_history_index++;
		if(hst->state_history_index>=MAX_STATE_HISTORY_ENTRIES)
			hst->state_history_index=0;
	        }

	/* calculate overall changes in state */
	for(x=0,y=hst->state_history_index;x<MAX_STATE_HISTORY_ENTRIES;x++){

		if(x==0){
			last_state_history_value=hst->state_history[y];
			y++;
			if(y>=MAX_STATE_HISTORY_ENTRIES)
				y=0;
			continue;
		        }

		if(last_state_history_value!=hst->state_history[y])
			curved_changes+=(((double)(x-1)*(high_curve_value-low_curve_value))/((double)(MAX_STATE_HISTORY_ENTRIES-2)))+low_curve_value;

		last_state_history_value=hst->state_history[y];

		y++;
		if(y>=MAX_STATE_HISTORY_ENTRIES)
			y=0;
	        }

	/* calculate overall percent change in state */
	curved_percent_change=(double)(((double)curved_changes*100.0)/(double)(MAX_STATE_HISTORY_ENTRIES-1));

	hst->percent_state_change=curved_percent_change;

	log_debug_info(DEBUGL_FLAPPING,2,"LFT=%.2f, HFT=%.2f, CPC=%.2f, PSC=%.2f%%\n",low_threshold,high_threshold,curved_percent_change,curved_percent_change);


	/* don't do anything if we don't have flap detection enabled on a program-wide basis */
	if(enable_flap_detection==FALSE)
		return;

	/* don't do anything if we don't have flap detection enabled for this host */
	if(hst->flap_detection_enabled==FALSE)
		return;

	/* are we flapping, undecided, or what?... */

	/* we're undecided, so don't change the current flap state */
	if(curved_percent_change>low_threshold && curved_percent_change<high_threshold)
		return;

	/* we're below the lower bound, so we're not flapping */
	else if(curved_percent_change<=low_threshold)
		is_flapping=FALSE;
       
	/* else we're above the upper bound, so we are flapping */
	else if(curved_percent_change>=high_threshold)
		is_flapping=TRUE;

	log_debug_info(DEBUGL_FLAPPING,1,"Host %s flapping (%.2f%% state change).\n",(is_flapping==TRUE)?"is":"is not",curved_percent_change);

	/* did the host just start flapping? */
	if(is_flapping==TRUE && hst->is_flapping==FALSE)
		set_host_flap(hst,curved_percent_change,high_threshold,low_threshold,allow_flapstart_notification);

	/* did the host just stop flapping? */
	else if(is_flapping==FALSE && hst->is_flapping==TRUE)
		clear_host_flap(hst,curved_percent_change,high_threshold,low_threshold);

	return;
        }


/******************************************************************/
/********************* FLAP HANDLING FUNCTIONS ********************/
/******************************************************************/


/* handles a service that is flapping */
void set_service_flap(service *svc, double percent_change, double high_threshold, double low_threshold, int allow_flapstart_notification){
	char *temp_buffer=NULL;

	log_debug_info(DEBUGL_FUNCTIONS,0,"set_service_flap()\n");

	if(svc==NULL)
		return;

	log_debug_info(DEBUGL_FLAPPING,1,"Service '%s' on host '%s' started flapping!\n",svc->description,svc->host_name);

	/* log a notice - this one is parsed by the history CGI */
	logit(NSLOG_RUNTIME_WARNING,FALSE,"SERVICE FLAPPING ALERT: %s;%s;STARTED; Service appears to have started flapping (%2.1f%% change >= %2.1f%% threshold)\n",svc->host_name,svc->description,percent_change,high_threshold);

	/* add a non-persistent comment to the service */
	asprintf(&temp_buffer,"Notifications for this service are being suppressed because it was detected as having been flapping between different states (%2.1f%% change >= %2.1f%% threshold).  When the service state stabilizes and the flapping stops, notifications will be re-enabled.",percent_change,high_threshold);
	add_new_service_comment(FLAPPING_COMMENT,svc->host_name,svc->description,time(NULL),"(Nagios Process)",temp_buffer,0,COMMENTSOURCE_INTERNAL,FALSE,(time_t)0,&(svc->flapping_comment_id));
	my_free(temp_buffer);

	/* set the flapping indicator */
	svc->is_flapping=TRUE;

#ifdef USE_EVENT_BROKER
	/* send data to event broker */
	broker_flapping_data(NEBTYPE_FLAPPING_START,NEBFLAG_NONE,NEBATTR_NONE,SERVICE_FLAPPING,svc,percent_change,high_threshold,low_threshold,NULL);
#endif

	/* see if we should check to send a recovery notification out when flapping stops */
	if(svc->current_state!=STATE_OK && svc->current_notification_number>0)
		svc->check_flapping_recovery_notification=TRUE;
	else
		svc->check_flapping_recovery_notification=FALSE;

	/* send a notification */
	if(allow_flapstart_notification==TRUE)
		service_notification(svc,NOTIFICATION_FLAPPINGSTART,NULL,NULL,NOTIFICATION_OPTION_NONE);

	return;
        }


/* handles a service that has stopped flapping */
void clear_service_flap(service *svc, double percent_change, double high_threshold, double low_threshold){

	log_debug_info(DEBUGL_FUNCTIONS,0,"clear_service_flap()\n");

	if(svc==NULL)
		return;

	log_debug_info(DEBUGL_FLAPPING,1,"Service '%s' on host '%s' stopped flapping.\n",svc->description,svc->host_name);

	/* log a notice - this one is parsed by the history CGI */
	logit(NSLOG_INFO_MESSAGE,FALSE,"SERVICE FLAPPING ALERT: %s;%s;STOPPED; Service appears to have stopped flapping (%2.1f%% change < %2.1f%% threshold)\n",svc->host_name,svc->description,percent_change,low_threshold);

	/* delete the comment we added earlier */
	if(svc->flapping_comment_id!=0)
		delete_service_comment(svc->flapping_comment_id);
	svc->flapping_comment_id=0;

	/* clear the flapping indicator */
	svc->is_flapping=FALSE;

#ifdef USE_EVENT_BROKER
	/* send data to event broker */
	broker_flapping_data(NEBTYPE_FLAPPING_STOP,NEBFLAG_NONE,NEBATTR_FLAPPING_STOP_NORMAL,SERVICE_FLAPPING,svc,percent_change,high_threshold,low_threshold,NULL);
#endif

	/* send a notification */
	service_notification(svc,NOTIFICATION_FLAPPINGSTOP,NULL,NULL,NOTIFICATION_OPTION_NONE);

	/* 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);

	/* clear the recovery notification flag */
	svc->check_flapping_recovery_notification=FALSE;

	return;
        }


/* handles a host that is flapping */
void set_host_flap(host *hst, double percent_change, double high_threshold, double low_threshold, int allow_flapstart_notification){
	char *temp_buffer=NULL;

	log_debug_info(DEBUGL_FUNCTIONS,0,"set_host_flap()\n");

	if(hst==NULL)
		return;

	log_debug_info(DEBUGL_FLAPPING,1,"Host '%s' started flapping!\n",hst->name);

	/* log a notice - this one is parsed by the history CGI */
	logit(NSLOG_RUNTIME_WARNING,FALSE,"HOST FLAPPING ALERT: %s;STARTED; Host appears to have started flapping (%2.1f%% change > %2.1f%% threshold)\n",hst->name,percent_change,high_threshold);

	/* add a non-persistent comment to the host */
	asprintf(&temp_buffer,"Notifications for this host are being suppressed because it was detected as having been flapping between different states (%2.1f%% change > %2.1f%% threshold).  When the host state stabilizes and the flapping stops, notifications will be re-enabled.",percent_change,high_threshold);
	add_new_host_comment(FLAPPING_COMMENT,hst->name,time(NULL),"(Nagios Process)",temp_buffer,0,COMMENTSOURCE_INTERNAL,FALSE,(time_t)0,&(hst->flapping_comment_id));
	my_free(temp_buffer);

	/* set the flapping indicator */
	hst->is_flapping=TRUE;

#ifdef USE_EVENT_BROKER
	/* send data to event broker */
	broker_flapping_data(NEBTYPE_FLAPPING_START,NEBFLAG_NONE,NEBATTR_NONE,HOST_FLAPPING,hst,percent_change,high_threshold,low_threshold,NULL);
#endif

	/* see if we should check to send a recovery notification out when flapping stops */
	if(hst->current_state!=HOST_UP && hst->current_notification_number>0)
		hst->check_flapping_recovery_notification=TRUE;
	else
		hst->check_flapping_recovery_notification=FALSE;

	/* send a notification */
	if(allow_flapstart_notification==TRUE)
		host_notification(hst,NOTIFICATION_FLAPPINGSTART,NULL,NULL,NOTIFICATION_OPTION_NONE);

	return;
        }


/* handles a host that has stopped flapping */
void clear_host_flap(host *hst, double percent_change, double high_threshold, double low_threshold){

	log_debug_info(DEBUGL_FUNCTIONS,0,"clear_host_flap()\n");

	if(hst==NULL)
		return;

	log_debug_info(DEBUGL_FLAPPING,1,"Host '%s' stopped flapping.\n",hst->name);

	/* log a notice - this one is parsed by the history CGI */
	logit(NSLOG_INFO_MESSAGE,FALSE,"HOST FLAPPING ALERT: %s;STOPPED; Host appears to have stopped flapping (%2.1f%% change < %2.1f%% threshold)\n",hst->name,percent_change,low_threshold);

	/* delete the comment we added earlier */
	if(hst->flapping_comment_id!=0)
		delete_host_comment(hst->flapping_comment_id);
	hst->flapping_comment_id=0;

	/* clear the flapping indicator */
	hst->is_flapping=FALSE;

#ifdef USE_EVENT_BROKER
	/* send data to event broker */
	broker_flapping_data(NEBTYPE_FLAPPING_STOP,NEBFLAG_NONE,NEBATTR_FLAPPING_STOP_NORMAL,HOST_FLAPPING,hst,percent_change,high_threshold,low_threshold,NULL);
#endif

	/* send a notification */
	host_notification(hst,NOTIFICATION_FLAPPINGSTOP,NULL,NULL,NOTIFICATION_OPTION_NONE);

	/* 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);

	/* clear the recovery notification flag */
	hst->check_flapping_recovery_notification=FALSE;

	return;
        }



/******************************************************************/
/***************** FLAP DETECTION STATUS FUNCTIONS ****************/
/******************************************************************/

/* enables flap detection on a program wide basis */
void enable_flap_detection_routines(void){
	host *temp_host=NULL;
	service *temp_service=NULL;
	unsigned long attr=MODATTR_FLAP_DETECTION_ENABLED;

	log_debug_info(DEBUGL_FUNCTIONS,0,"enable_flap_detection_routines()\n");

	/* bail out if we're already set */
	if(enable_flap_detection==TRUE)
		return;

	/* set the attribute modified flag */
	modified_host_process_attributes|=attr;
	modified_service_process_attributes|=attr;

	/* set flap detection flag */
	enable_flap_detection=TRUE;

#ifdef USE_EVENT_BROKER
	/* send data to event broker */
	broker_adaptive_program_data(NEBTYPE_ADAPTIVEPROGRAM_UPDATE,NEBFLAG_NONE,NEBATTR_NONE,CMD_NONE,attr,modified_host_process_attributes,attr,modified_service_process_attributes,NULL);
#endif

	/* update program status */
	update_program_status(FALSE);

	/* check for flapping */
	for(temp_host=host_list;temp_host!=NULL;temp_host=temp_host->next)
		check_for_host_flapping(temp_host,FALSE,FALSE,TRUE);
	for(temp_service=service_list;temp_service!=NULL;temp_service=temp_service->next)
		check_for_service_flapping(temp_service,FALSE,TRUE);

	return;
        }



/* disables flap detection on a program wide basis */
void disable_flap_detection_routines(void){
	host *temp_host=NULL;
	service *temp_service=NULL;
	unsigned long attr=MODATTR_FLAP_DETECTION_ENABLED;

	log_debug_info(DEBUGL_FUNCTIONS,0,"disable_flap_detection_routines()\n");

	/* bail out if we're already set */
	if(enable_flap_detection==FALSE)
		return;

	/* set the attribute modified flag */
	modified_host_process_attributes|=attr;
	modified_service_process_attributes|=attr;

	/* set flap detection flag */
	enable_flap_detection=FALSE;

#ifdef USE_EVENT_BROKER
	/* send data to event broker */
	broker_adaptive_program_data(NEBTYPE_ADAPTIVEPROGRAM_UPDATE,NEBFLAG_NONE,NEBATTR_NONE,CMD_NONE,attr,modified_host_process_attributes,attr,modified_service_process_attributes,NULL);
#endif

	/* update program status */
	update_program_status(FALSE);

	/* handle the details... */
	for(temp_host=host_list;temp_host!=NULL;temp_host=temp_host->next)
		handle_host_flap_detection_disabled(temp_host);
	for(temp_service=service_list;temp_service!=NULL;temp_service=temp_service->next)
		handle_service_flap_detection_disabled(temp_service);

	return;
        }



/* enables flap detection for a specific host */
void enable_host_flap_detection(host *hst){
	unsigned long attr=MODATTR_FLAP_DETECTION_ENABLED;

	log_debug_info(DEBUGL_FUNCTIONS,0,"enable_host_flap_detection()\n");

	if(hst==NULL)
		return;

	log_debug_info(DEBUGL_FLAPPING,1,"Enabling flap detection for host '%s'.\n",hst->name);

	/* nothing to do... */
	if(hst->flap_detection_enabled==TRUE)
		return;

	/* set the attribute modified flag */
	hst->modified_attributes|=attr;

	/* set the flap detection enabled flag */
	hst->flap_detection_enabled=TRUE;

#ifdef USE_EVENT_BROKER
	/* send data to event broker */
	broker_adaptive_host_data(NEBTYPE_ADAPTIVEHOST_UPDATE,NEBFLAG_NONE,NEBATTR_NONE,hst,CMD_NONE,attr,hst->modified_attributes,NULL);
#endif

	/* check for flapping */
	check_for_host_flapping(hst,FALSE,FALSE,TRUE);

	/* update host status */
	update_host_status(hst,FALSE);

	return;
        }



/* disables flap detection for a specific host */
void disable_host_flap_detection(host *hst){
	unsigned long attr=MODATTR_FLAP_DETECTION_ENABLED;

	log_debug_info(DEBUGL_FUNCTIONS,0,"disable_host_flap_detection()\n");

	if(hst==NULL)
		return;

	log_debug_info(DEBUGL_FLAPPING,1,"Disabling flap detection for host '%s'.\n",hst->name);

	/* nothing to do... */
	if(hst->flap_detection_enabled==FALSE)
		return;

	/* set the attribute modified flag */
	hst->modified_attributes|=attr;

	/* set the flap detection enabled flag */
	hst->flap_detection_enabled=FALSE;

#ifdef USE_EVENT_BROKER
	/* send data to event broker */
	broker_adaptive_host_data(NEBTYPE_ADAPTIVEHOST_UPDATE,NEBFLAG_NONE,NEBATTR_NONE,hst,CMD_NONE,attr,hst->modified_attributes,NULL);
#endif

	/* handle the details... */
	handle_host_flap_detection_disabled(hst);

	return;
        }


/* handles the details for a host when flap detection is disabled (globally or per-host) */
void handle_host_flap_detection_disabled(host *hst){

	log_debug_info(DEBUGL_FUNCTIONS,0,"handle_host_flap_detection_disabled()\n");

	if(hst==NULL)
		return;

	/* if the host was flapping, remove the flapping indicator */
	if(hst->is_flapping==TRUE){

		hst->is_flapping=FALSE;

		/* delete the original comment we added earlier */
		if(hst->flapping_comment_id!=0)
			delete_host_comment(hst->flapping_comment_id);
		hst->flapping_comment_id=0;

		/* log a notice - this one is parsed by the history CGI */
		logit(NSLOG_INFO_MESSAGE,FALSE,"HOST FLAPPING ALERT: %s;DISABLED; Flap detection has been disabled\n",hst->name);

#ifdef USE_EVENT_BROKER
		/* send data to event broker */
		broker_flapping_data(NEBTYPE_FLAPPING_STOP,NEBFLAG_NONE,NEBATTR_FLAPPING_STOP_DISABLED,HOST_FLAPPING,hst,hst->percent_state_change,0.0,0.0,NULL);
#endif

		/* send a notification */
		host_notification(hst,NOTIFICATION_FLAPPINGDISABLED,NULL,NULL,NOTIFICATION_OPTION_NONE);

		/* 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);

		/* clear the recovery notification flag */
		hst->check_flapping_recovery_notification=FALSE;
	        }

	/* update host status */
	update_host_status(hst,FALSE);

	return;
	}


/* enables flap detection for a specific service */
void enable_service_flap_detection(service *svc){
	unsigned long attr=MODATTR_FLAP_DETECTION_ENABLED;

	log_debug_info(DEBUGL_FUNCTIONS,0,"enable_service_flap_detection()\n");

	if(svc==NULL)
		return;

	log_debug_info(DEBUGL_FLAPPING,1,"Enabling flap detection for service '%s' on host '%s'.\n",svc->description,svc->host_name);

	/* nothing to do... */
	if(svc->flap_detection_enabled==TRUE)
		return;

	/* set the attribute modified flag */
	svc->modified_attributes|=attr;

	/* set the flap detection enabled flag */
	svc->flap_detection_enabled=TRUE;

#ifdef USE_EVENT_BROKER
	/* send data to event broker */
	broker_adaptive_service_data(NEBTYPE_ADAPTIVESERVICE_UPDATE,NEBFLAG_NONE,NEBATTR_NONE,svc,CMD_NONE,attr,svc->modified_attributes,NULL);
#endif

	/* check for flapping */
	check_for_service_flapping(svc,FALSE,TRUE);

	/* update service status */
	update_service_status(svc,FALSE);

	return;
        }



/* disables flap detection for a specific service */
void disable_service_flap_detection(service *svc){
	unsigned long attr=MODATTR_FLAP_DETECTION_ENABLED;

	log_debug_info(DEBUGL_FUNCTIONS,0,"disable_service_flap_detection()\n");

	if(svc==NULL)
		return;

	log_debug_info(DEBUGL_FLAPPING,1,"Disabling flap detection for service '%s' on host '%s'.\n",svc->description,svc->host_name);

	/* nothing to do... */
	if(svc->flap_detection_enabled==FALSE)
		return;

	/* set the attribute modified flag */
	svc->modified_attributes|=attr;

	/* set the flap detection enabled flag */
	svc->flap_detection_enabled=FALSE;

#ifdef USE_EVENT_BROKER
	/* send data to event broker */
	broker_adaptive_service_data(NEBTYPE_ADAPTIVESERVICE_UPDATE,NEBFLAG_NONE,NEBATTR_NONE,svc,CMD_NONE,attr,svc->modified_attributes,NULL);
#endif

	/* handle the details... */
	handle_service_flap_detection_disabled(svc);

	return;
        }


/* handles the details for a service when flap detection is disabled (globally or per-service) */
void handle_service_flap_detection_disabled(service *svc){

	log_debug_info(DEBUGL_FUNCTIONS,0,"handle_service_flap_detection_disabled()\n");

	if(svc==NULL)
		return;

	/* if the service was flapping, remove the flapping indicator */
	if(svc->is_flapping==TRUE){

		svc->is_flapping=FALSE;

		/* delete the original comment we added earlier */
		if(svc->flapping_comment_id!=0)
			delete_service_comment(svc->flapping_comment_id);
		svc->flapping_comment_id=0;

		/* log a notice - this one is parsed by the history CGI */
		logit(NSLOG_INFO_MESSAGE,FALSE,"SERVICE FLAPPING ALERT: %s;%s;DISABLED; Flap detection has been disabled\n",svc->host_name,svc->description);

#ifdef USE_EVENT_BROKER
		/* send data to event broker */
		broker_flapping_data(NEBTYPE_FLAPPING_STOP,NEBFLAG_NONE,NEBATTR_FLAPPING_STOP_DISABLED,SERVICE_FLAPPING,svc,svc->percent_state_change,0.0,0.0,NULL);
#endif

		/* send a notification */
		service_notification(svc,NOTIFICATION_FLAPPINGDISABLED,NULL,NULL,NOTIFICATION_OPTION_NONE);

		/* 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);

		/* clear the recovery notification flag */
		svc->check_flapping_recovery_notification=FALSE;
	        }

	/* update service status */
	update_service_status(svc,FALSE);

	return;
	}





