Name

global.EvtMgmtCloseDuplicateAlerts

Description

There might be some cases where alerts are created with the same message key. This script s purpose is to fix this situation, by closing the duplicate alerts and update their bound events accordingly.

Script

var EvtMgmtCloseDuplicateAlerts = Class.create();
EvtMgmtCloseDuplicateAlerts.prototype = {
  initialize: function() {
  	
  	SCRIPT_NAME = "EvtMgmtCloseDuplicateAlerts";
  	
  	this.minStartedUpgradeTimeSec = 2*60*60;
  	this.maxStartedUpgradeTimeDays = 2;
  	this.logLimit = 10;
  	this.isClosingDuplicate = true;
  },
  
  closeDuplicateAlerts: function(fromTime) {
  	
  	var now = new GlideDateTime();

  	if (fromTime != null) {
  		this.findDuplications(fromTime, now);
  		return;
  	}

  	var upgradeStartedTime = this.getUpgradeStartedTime();
  	if (upgradeStartedTime == null || upgradeStartedTime == "") {
  		gs.info("'{0}': upgradeStartedTime is not a valid date", SCRIPT_NAME);
  		return;
  	}
  
  	// use the last upgrade started time to calculate fromTime
  	var fromTimeGlide = this.getFromTime(upgradeStartedTime, now);
  	this.findDuplications(fromTimeGlide, now);
  },
  
  getAllOpenStates: function() {
  	
  	return ["Open", "Reopen", "Flapping"];
  },
  
  getUpgradeStartedTime: function() {
  	
  	var upgradeStartedTime = "";
  	
  	var upgradeGr = new GlideRecord('sys_upgrade_history');
  	upgradeGr.orderByDesc("upgrade_started");
  	upgradeGr.addQuery("from_version", "CONTAINS", "glide");
  	upgradeGr.addQuery("to_version", "CONTAINS", "glide");
  	upgradeGr.setLimit(1);
  	upgradeGr.query();
  	
  	if (upgradeGr.next())
  		upgradeStartedTime = upgradeGr.upgrade_started;
  			
  	return upgradeStartedTime;
  },
  
  getFromTime: function(upgradeStartedTime, nowGlideTime) {
  	
  	// if  secsAgo <= upgradeStartedTime <= daysAgo, then last upgrade time makes sense.
  	// Otherwise we return secsAgo or daysAgo as fromTime

  	var upgradeStartedGlideTime = new GlideDateTime(upgradeStartedTime);
  	
  	var secsAgo = new GlideDateTime(nowGlideTime);
  	var secs = gs.getProperty("evt_mgmt.min_started_upgrade_time_sec", this.minStartedUpgradeTimeSec);
  	var secsBack = -1*secs;
  	secsAgo.addSeconds(secsBack);
  	
  	var daysAgo = new GlideDateTime(nowGlideTime);
  	var days = gs.getProperty("evt_mgmt.max_started_upgrade_time_days", this.maxStartedUpgradeTimeDays);
  	var daysBack = -1*days;
  	daysAgo.addDays(daysBack);

  	var timeDiff = upgradeStartedGlideTime.compareTo(secsAgo);
  	if (timeDiff > 0)  // upgrade started less than two hours ago => too short time
  		return secsAgo;
  	
  	timeDiff = upgradeStartedGlideTime.compareTo(daysAgo);
  	if (timeDiff < 0)  // upgrade started more than two days ago => too long time
  		return daysAgo;
  	
  	return upgradeStartedTime;
  },
  
  updateTargetAlertEventCount: function(targetAlertId, eventCount) {

  	var alertGr = new GlideRecord('em_alert');
  	alertGr.addQuery("sys_id", targetAlertId);
  	alertGr.query();
  	
  	if (alertGr.next()) {
  		
  		alertGr.event_count = eventCount;
  		alertGr.update();
  	}
  },
  
  getWorkNoteMsg: function(alertNumber) {
  	
  	var workNoteMsg = 
  		gs.getMessage("This alert is a duplication of {0}. It was closed automatically by the script: '{1}'.",
  						[alertNumber, SCRIPT_NAME]);
  	
  	return workNoteMsg;
  },
  
  updateDuplicateAlert: function(alertDupGr, alertManager, targetAlertNumber, alertLogCount) {
  	
  	if (this.isClosingDuplicate && alertLogCount < this.logLimit) {
  		gs.info("'{0}': Closing a duplicate alert with sys_id: {1}", [SCRIPT_NAME, alertDupGr.getValue("sys_id")]);
  	}	
  	
  	alertDupGr.state = "Closed";
  	
  	alertDupGr.event_count = 0;

  	var workNoteMsg = this.getWorkNoteMsg(targetAlertNumber);
  	alertManager.updateWorkNotesOnAlert(alertDupGr, workNoteMsg);
  	
  	alertDupGr.update();
  	
  	return;
  },
  
  updateEvents: function(targetAlertId, curAlertId, fromTime, toTime) {
  	
  	// events that are bound to duplicate alerts get bound to the target alert

  	var evtDupGr = new GlideRecord('em_event');
  	evtDupGr.addQuery("sys_created_on", ">=", fromTime);
  	evtDupGr.addQuery("sys_created_on", "<=", toTime);
  	evtDupGr.addQuery("state", "Processed");
  	evtDupGr.addQuery("alert", curAlertId);
  	evtDupGr.query();
  	var eventsCount = evtDupGr.getRowCount();

  	if (targetAlertId == curAlertId)
  		return eventsCount;
  	
  	while (evtDupGr.next()) {

  		evtDupGr.alert = targetAlertId;
  		evtDupGr.update();
  	}
  	
  	return eventsCount;
  },
  
  handleCloseDuplicates: function(alertDupGr, alertManager, fromTime, toTime) {
  	
  	var targetAlertId = "";
  	var targetAlertNumber = "";
  	var totalEventCount = 0;
  	var alertLogCount = 0;
  	
  	if (alertDupGr.next()) {  // refer to the first alert as target

  		targetAlertNumber = alertDupGr.getValue("number");
  		targetAlertId = alertDupGr.getValue("sys_id");
  		totalEventCount +=  this.updateEvents(targetAlertId, targetAlertId, fromTime, toTime);
  	}

  	while (alertDupGr.next()) {  // refer to the rest as duplicate alerts

  		var alertDupId = alertDupGr.getValue("sys_id");
  		totalEventCount +=  this.updateEvents(targetAlertId, alertDupId, fromTime, toTime);
  		this.updateDuplicateAlert(alertDupGr, alertManager, targetAlertNumber, alertLogCount);
  		alertLogCount ++;
  	}

  	this.updateTargetAlertEventCount(targetAlertId, totalEventCount);
  },
  
  handlePrintDuplicates: function(msgKey, alertDupGr) {
  	
  	gs.info("The following alerts with duplicate message key - {0} are not closed:", msgKey);
  		while (alertDupGr.next()) {
  			gs.info(
  				"Alert Number: {0}, Alert sys_id: {1}", [alertDupGr.getValue("number"), alertDupGr.getValue("sys_id")]);
  		}
  },
  
  closeDuplications: function(alertManager, msgKey, fromTime, toTime) {
  		
  	var alertDupGr = new GlideRecord('em_alert');
  	alertDupGr.addQuery("sys_created_on", ">=", fromTime);
  	alertDupGr.addQuery("sys_created_on", "<=", toTime);
  	alertDupGr.addQuery("state", "IN", this.getAllOpenStates());
  	alertDupGr.addQuery("message_key", msgKey);
  	alertDupGr.query();  // query alerts with the same message_key
  	
  	if (!this.isClosingDuplicate) {
  		
  		this.handlePrintDuplicates(msgKey, alertDupGr);
  		
  	} else {
  	
  		this.handleCloseDuplicates(alertDupGr, alertManager, fromTime, toTime);
  	}
  },
  
  findDuplications: function(fromTime, toTime) {
  	
  	// find all duplicate alerts

  	var alertManager = new SNC.AlertManager();
  	var msgKeyLogCount = 0;
  	
  	var alertGr = new GlideAggregate('em_alert');
  	alertGr.addQuery("sys_created_on", ">=", fromTime);
  	alertGr.addQuery("sys_created_on", "<=", toTime);
  	alertGr.addQuery("state", "IN", this.getAllOpenStates());
  	alertGr.addAggregate('COUNT', 'message_key');
  	alertGr.groupBy('message_key');
  	alertGr.addHaving('COUNT', '>', '1');
  	alertGr.query();

  	// iterate over all groups of alerts with the same message_key
  	while (alertGr.next()) {
  		
  		var msgKey = alertGr.getValue("message_key");

  		if (this.isClosingDuplicate && msgKeyLogCount < this.logLimit) {
  			gs.info("'{0}': Found a duplicate message key: {1}", [SCRIPT_NAME, msgKey]);
  			msgKeyLogCount ++;			
  		}
  		
  		this.closeDuplications(alertManager, msgKey, fromTime, toTime);
  	}	
  },
  
  printDuplicateAlerts: function(fromTime) {
  	
  	this.isClosingDuplicate = false;
  	this.closeDuplicateAlerts(fromTime);
  },

  type: 'EvtMgmtCloseDuplicateAlerts'
};

Sys ID

c404c1c95b7e0010889dd01fb681c7c9

Offical Documentation

Official Docs: