Name

sn_em_arm.EvtMgmtAlertMgmtProcess

Description

No description available

Script

gs.include('EvtMgmtCommons');
gs.include('EvtMgmtAlertMgmtCommons');
gs.include('SlowStepJSManager');

var EvtMgmtAlertMgmtProcess = Class.create();
EvtMgmtAlertMgmtProcess.prototype = {

  type: 'EvtMgmtAlertMgmtProcess',

  initialize: function(pSlowStepJSManager) {
      this.WORKFLOW_LINK = "/context_workflow.do?sysparm_sys_id=";
      this.SUBFLOW_LINK = "/$flow-designer.do?sysparm_nostack=true#/operations/context/";
      this.SUBFLOW_WRAPPER = 'sn_em_arm.arm_callback_wrapper';
      this.evtMgmtCommons = new global.EvtMgmtCommons();
      this.evtMgmtAlertMgmtMediator = new global.EvtMgmtAlertMgmtMediator();
      this.slowStepsManager = pSlowStepJSManager;
      this.evtMgmtAlertMgmtCommons = new EvtMgmtAlertMgmtCommons();
      this.evtMgmtAlertMgmtToolBox = new EvtMgmtAlertMgmtToolBox();
      this.fRuleAdditionalInfo = {};
      this.fApplicationServicesPerCI = {};
  	this.enableSynchronousSubflow = gs.getProperty("evt_mgmt.alert.management.enable_synchronous_subflow", 'false') == 'true';
      //constants:
      this.ADDITIONAL_INFO_TABLE = "sn_em_arm_additional_info";
      this.IMPACT_GRAPH_TABLE = "em_impact_graph";
      this.ALERT_MGMT_RULE = "alert_management_rule";
      this.SCRIPT_CONDITION = "condition_script";
      this.USE_SCRIPT = "use_script_condition";
      this.DEPENDENT_ON_RULE = "dependent_on_rule";
      this.ALERT_KEY_FIELD = "alert_key_field";
      this.ALERT_KEY_VALUE = "alert_key_value";
      this.IS_ADD_INFO_FIELD = "is_add_info_field";
      this.ALERT_ADDITIONAL_INFO = "additional_info";
      this.ALERT_KEY_IDENTIFIER_FIELD = gs.getProperty("sn_em_arm.alert_key_identifier_add_info_field", 'sn_integration_id');
      this.QUERY_IMPACT_GRAPH = gs.getProperty("sn_em_arm.evt_mgmt.query_impact_graph", 'true');
      this.MAX_APP_PER_CI = gs.getProperty("sn_em_arm.evt_mgmt.max_application_for_ci", 100);
      this.SYS_ID = "sys_id";
      this.CMDB_CI = "cmdb_ci";
      this.BUSINESS_SERVICE = "business_service";
      this.APPLIES_TO = "applies_to";
      this.APPLIES_TO_NONE = "none";
      this.APPLIES_TO_SERVICE = "service";
      this.APPLIES_TO_CI = "ci";
      this.APPLIES_TO_BOTH = "both";
      this.CHILD = "child_id";
      this.STATUS = "status";
  },

  runForAlert: function(alertGR, returnToPreviousDomain, manuallyRun, retrieveParams, executionsMap, perfCounters) {
      this.startStep(this.type + ": runForAlert- Handle domain");
      var originalDomain = this.evtMgmtCommons.getCurrentDomainID();
      this.evtMgmtCommons.changeDomain(this.evtMgmtCommons.getRecordDomain(alertGR));
      this.startStep(this.type + ": getReleveantAlertRules");
      var relevantRules = this.evtMgmtAlertMgmtMediator.getReleveantAlertRules();
      //sending one alert and the relevant rules for the this alert
      var manuallyActions = this.iterateOverRules(alertGR, relevantRules, manuallyRun, retrieveParams, executionsMap, perfCounters, false);
      this.startStep(this.type + ": runForAlert- returnToPreviousDomain");
      if (returnToPreviousDomain) {
          this.evtMgmtCommons.changeDomain(originalDomain);
      }
      this.startStep(this.type + ": end of runForAlert");
      return manuallyActions;
  },

  iterateOverRules: function(alertGR, ruleList, manuallyRun, retrieveParams, executionsMap, perfCounters, runDependentRules) {
      var lastExecutionGR = null;
      var lastSysId = [];
      var shouldExecuteActions = true;
      var result = {
          remediations: [],
          launchApplications: [],
          subflows: []
      };
      var ruleAdditionalInfo = this.loadAdditionalInfo(ruleList);
      var ruleAddInfoMap = ruleAdditionalInfo.ruleDataMap;
      var passthroughParams = JSON.stringify({
          retrieveParams: retrieveParams
      });
      var ruleAddInfoMapIsEmpty = this.evtMgmtAlertMgmtToolBox.isEmptyObject(ruleAddInfoMap);
      this.startStep(this.type + ": Iterate Over Rules");

      // check if this alert is a SRO alert
      var additionalInfo = alertGR.getValue(this.ALERT_ADDITIONAL_INFO);
      var additionalInfoJSON = this.getAddInfoFieldJsonForSRO(additionalInfo);
      var isSroAlert = this.isSroAlert(additionalInfoJSON);

      // Save the number of SRO alerts to sa_performance_statistics
      if (perfCounters) {
          perfCounters.sroAlertsCount += Number(isSroAlert) || 0;
      }

      for (var i = 0; i < ruleList.length; i++) {
          try {
              var rule = ruleList[i];
              var ruleSysId = rule.getSysId();
              var thisRuleInfo = ruleAddInfoMap[ruleSysId];
              if (!runDependentRules && !manuallyRun && thisRuleInfo && !gs.nil(thisRuleInfo.dependentOnRule) &&
                  this.hasActiveAncestor(ruleAdditionalInfo, thisRuleInfo.dependentOnRule)) {
                  // this is a dependent rule, and we're not running dependent rules now, unless all its ancestors are marked inactive
                  continue;
              }
              //reading the alert key field and value
              var keyField = thisRuleInfo ? thisRuleInfo["alertKeyField"] : undefined;
              var keyValue = undefined;
              var keyAppliesTo = undefined;
              var runTheRule = true;

              if (isSroAlert) {
                  if (ruleAddInfoMapIsEmpty)
                      break; // ruleAddInfoMap is empty, SRO alert can't run any regular rules, exiting rules loop                        
                  if (!keyField) // SRO alert can't run regular rule or even its dependencies, so move on to the next rule
                      continue;
              }
              if (thisRuleInfo) {
                  keyAppliesTo = thisRuleInfo["alertKeyAppliesTo"];
                  if (keyField) // the rule has a defined key field
                      keyValue = thisRuleInfo["alertKeyValue"];

                  runTheRule = this.matchAlertKeyValue(alertGR, thisRuleInfo, keyField, keyValue, keyAppliesTo, additionalInfoJSON);
              }
              // if there's an entry in the map for this rule, then it has dependencies
              var hasDependentRules = ruleAdditionalInfo.dependencyMap[ruleSysId];

              if (runTheRule) // the alert does not impact the rule AS 
              {
                  this.startStep(this.type + ": shouldRunRule");
                  if (this.shouldRunRule(alertGR, rule, manuallyRun)) {
                      this.startStep(this.type + ": getActions");

                      var ruleActions = this.getActions(rule, manuallyRun, retrieveParams, executionsMap, alertGR.sys_id);

                      this.startStep(this.type + ": getActionsNums");

                      var actionsNumber = ruleActions.remediations.length + ruleActions.subflows.length + ruleActions.launchApplications.length;

                      if (actionsNumber == 0) {
                          this.evtMgmtCommons.addDebugMessage("There are no actions to run, for " + this.getDebugPrefixMessage(alertGR, rule) + ".");
                          // no actions to execute, but still need to check stop processing other rules flag	
                          if (this.continueToOtherRules(rule)) {
                              // we do not do a callback in this situation, even if there are dependent rules, because it is possible for a race condition to cause a bug.
                              continue;
                          }
                          // need to proceed to check filter of this rule since stop processing other rules flag is on
                      }

                      this.startStep(this.type + ": checkFilter");

                      if (this.checkFilter(alertGR, rule, ruleAddInfoMap, perfCounters)) {

                          this.startStep(this.type + ": FilterMatched");

                          //If there are no valid actions, just check the continueToOtherRules flag 
                          if (actionsNumber != 0) {
                              if (manuallyRun) {
                                  this.startStep(this.type + ": union Results And Transform To Manual");
                                  this.unionResultsAndTransformToManual(result, ruleActions, alertGR);
                              } else {
                                  shouldExecuteActions = true;
                                  lastSysId = [];
                                  if (rule.runOnSelectiveUpdate()) {
                                      this.startStep(this.type + ": Did Last Alert Update Matched Filter");
                                      if (this.didLastAlertUpdateMatchedFilter(alertGR.sys_id, ruleSysId, executionsMap, lastSysId)) {
                                          shouldExecuteActions = false; //don't executeActions until last run doesn't fit 
                                          this.evtMgmtCommons.addDebugMessage("Not running actions for " + this.getDebugPrefixMessage(alertGR, rule) + ", rule is set to selective updates.");
                                      }
                                  }
                                  if (shouldExecuteActions) {
                                      this.startStep(this.type + ": Execute Actions");
                                      this.executeActions(alertGR, rule, ruleActions, false, hasDependentRules, passthroughParams);

                                      if (perfCounters) {
                                          perfCounters.remediationsCount += ruleActions.remediations.length;
                                          perfCounters.subFlowsCount += ruleActions.subflows.length;
                                      }
                                  }
                              }
                          }

                          // check if we should stop processing other rules
                          if (!this.continueToOtherRules(rule)) {
                              break;
                          }
                      } else { // rule filter condition is false. 
                          lastSysId = [];
                          if (rule.runOnSelectiveUpdate()) {
                              if (this.didLastAlertUpdateMatchedFilter(alertGR.sys_id, ruleSysId, executionsMap, lastSysId)) {
                                  this.startStep(this.type + ": update Execution Filter Matches False");
                                  this.updateExecutionFilterMatchesFalse(lastSysId[0]);
                                  this.evtMgmtCommons.addDebugMessage("Setting last filter match to false for " + this.getDebugPrefixMessage(alertGR, rule) + ", rule is set to selective updates.");
                              } else {
                                  this.evtMgmtCommons.addDebugMessage("Not setting last filter match to false since it is already false. For " + this.getDebugPrefixMessage(alertGR, rule) + ", rule is set to selective updates.");
                              }
                          }

                          // check for dependent rules that we may need to run, even though our condition didn't match
                          if (!manuallyRun && hasDependentRules) {
                              this.onRuleComplete(alertGR, ruleSysId, passthroughParams);
                          }
                      }
                  } else { // if (!this.shouldRunRule(alertGR, rule, manuallyRun))
                      // even if this rule does not have automatic actions, there may have been other automatic actions (erroneously?) set up as dependent on this rule
                      // In this case we should run the dependent actions
                      if (!manuallyRun && hasDependentRules) {
                          this.onRuleComplete(alertGR, ruleSysId, passthroughParams);
                      }
                  }
              } else {
                  if (hasDependentRules) { //This rule did not match but we need to see if the dependent rules match
                      this.onRuleComplete(alertGR, ruleSysId, passthroughParams);
                  }
              }

          } catch (e) {
              gs.error("Alert Management - Fail to process rule " + rule.getName() + " Alert:  " + alertGR.number + "( " + alertGR.sys_id + ") " + " Error Message: " + this.evtMgmtCommons.getExceptionMessage(e, true));
          }

          this.startStep(this.type + ": Finished Iterate Over Rule");
      }

      return result;
  },

  hasActiveAncestor: function(ruleAdditionalInfo, alertRuleId) {
      this.startStep(this.type + ": hasActiveAncestor (recursive function)");

      // checks if the specified rule is active. If not, recurse through its dependency ancestors
      if (ruleAdditionalInfo.activeRuleMap.hasOwnProperty(alertRuleId)) {
          return true;
      }
      // is this rule dependent on another rule? If so, recurse
      var thisRuleAddInfo = ruleAdditionalInfo.ruleDataMap[alertRuleId];
      if (thisRuleAddInfo && !gs.nil(thisRuleAddInfo.dependentOnRule)) {
          return this.hasActiveAncestor(ruleAdditionalInfo, thisRuleAddInfo.dependentOnRule);
      }
      // I am inactive, and I have no dependency ancestors, so:
      return false;
  },

  unionResultsAndTransformToManual: function(result, ruleActions, alertGR) {
      result.remediations = this.unionArrays(result.remediations, ruleActions.remediations);
      result.launchApplications = this.unionArrays(result.launchApplications, this.addAlertDetailsToLaunch(ruleActions.launchApplications, alertGR));
      result.subflows = this.unionArrays(result.subflows, ruleActions.subflows);
  },

  unionArrays: function(unionedActions, ruleActions) {
      if ((ruleActions) && (ruleActions.length > 0)) {
          if ((unionedActions) && (unionedActions.length > 0)) {
              for (i = 0; i < ruleActions.length; i++) {
                  unionedActions.push(ruleActions[i]);
              }
              return unionedActions;
          } else {
              return ruleActions; //no need to copy original is empty...
          }
      } else { //nothing todo
          return unionedActions;
      }
  },

  addAlertDetailsToLaunch: function(launchApplications, alertGR) {
      if ((launchApplications) && (launchApplications.length > 0)) {
          return this.evtMgmtAlertMgmtMediator.parseURLForTools(alertGR, launchApplications);
      }
      return launchApplications;
  },

  continueToOtherRules: function(rule) {
      return rule.searchAdditionalRules();
  },

  shouldRunRule: function(alertGR, rule, manuallyRun) {
      if (manuallyRun) {
          if (!rule.hasManualActions()) {
              return false;
          }
      } else {
          if (!rule.hasAutomaticActions()) {
              return false;
          }
      }
      return true;
      //run once check should go here as well
  },

  checkFilter: function(alertGR, rule, ruleAddInfoMap, perfCounters) {
      try {
          var alertFilter = rule.getAlertFilter();
          var returnValue = false;

          this.startStep(this.type + ': checkFilter - GlideFilter() call');
          // Take time before GlideFilter
          var t1 = new Date();

  		// A mediator function calling GlideFilter.checkRecord() was added to evtMgmtAlertMgmtMediator in Tokyo,
  		// to fix the "trend" condition not working in scoped app. Check if the function exists before calling it.
          var useMediatorToCheckFilter = typeof this.evtMgmtAlertMgmtMediator.checkRecord == 'function' ;
  		var isRuleGlideFilterMatch = useMediatorToCheckFilter ? this.evtMgmtAlertMgmtMediator.checkRecord(alertGR, alertFilter) :
  			GlideFilter.checkRecord(alertGR, alertFilter);

          var t2 = new Date(); // Take time after GlideFilter and before evaluateRuleScript
          var t3; // init var for taking time after evaluateRuleScript
          //we want to evaluate the script if exists only if the filter matches
          if (isRuleGlideFilterMatch) {
              this.startStep(this.type + ': checkFilter - evaluateRuleScript() call');

              returnValue = this.evaluateRuleScript(rule, ruleAddInfoMap, alertGR);

              t3 = new Date(); // take time before evaluateRuleScript
          }

          this.startStep(this.type + ': end checkFilter');

          if (perfCounters) {
              // Keep the max run time, related alert and rule name for getAlertFilter
              if (t2 - t1 >= perfCounters.maxDurationGlideFilterRule.value) {
                  perfCounters.maxDurationGlideFilterRule.value = t2 - t1;
                  perfCounters.maxDurationGlideFilterRule.alert = alertGR.getValue('number');
                  perfCounters.maxDurationGlideFilterRule.ruleName = rule.getName();
              }
              // Keep the max run time, related alert and rule name for evaluateRuleScript
              if (t3 && (t3 - t2 >= perfCounters.maxDurationEvaluateRuleScript.value)) {
                  perfCounters.maxDurationEvaluateRuleScript.value = t3 - t2;
                  perfCounters.maxDurationEvaluateRuleScript.alert = alertGR.getValue('number');
                  perfCounters.maxDurationEvaluateRuleScript.ruleName = rule.getName();
              }
          }

          return returnValue;
      } catch (e) {
          gs.error("Alert management rule: " + rule.getName() + " has invalid filter/script filter: " + alertFilter + ". Error is: " + this.evtMgmtCommons.getExceptionMessage(e, true));
          return false;
      }
  },


  getActions: function(rule, manuallyRun, retrieveParams, executionsMap, alertSysId) {
      var actions = {
          remediations: [],
          launchApplications: [],
          subflows: []
      };
      if (retrieveParams.retrieveRemediations) {
          actions.remediations = this.getRemediations(rule, manuallyRun, executionsMap, alertSysId);
      }
      if (retrieveParams.retrieveLaunchApplications) {
          actions.launchApplications = this.getLaunchApplications(rule, manuallyRun);
      }
      if (retrieveParams.retrieveSubflows) {
          actions.subflows = this.getSubflows(rule, manuallyRun, executionsMap, alertSysId);
      }
      return actions;
  },

  getRemediations: function(rule, manuallyRun, executionsMap, alertSysId) {
      //rule.getRemediations function expects automatic flag 

      this.startStep(this.type + ": getActions - getRemediations");

      var ruleRemediations = rule.getRemediations(!manuallyRun);

      this.startStep(this.type + ": getActions - checkLimits");

      var remediations = this.checkLimits(rule, manuallyRun, executionsMap, alertSysId, ruleRemediations);

      return remediations;
  },

  // filter out actions which reached their limits
  checkLimits: function(rule, manuallyRun, executionsMap, alertSysId, actions) {
      var actionObjects = [];
      var actionCounters = this.getActionCounters(rule.getSysId(), executionsMap, alertSysId);

      var actionsExist = this.evtMgmtAlertMgmtToolBox.isNotEmpty(actionCounters);

      var numOfExecutionsTillNow = 0; // assume first run unless told otherwise 
      //it's required to convert the native array to js array
      for (var i = 0; i < actions.length; i++) {
          if (!manuallyRun) {
              var limit = actions[i].getExecutionsLimit();
              if (actionsExist) {
                  var action = actions[i];
                  var actionSysId = action.getActionSysid();
                  numOfExecutionsTillNow = actionCounters[actionSysId];
                  if (numOfExecutionsTillNow) {
                      if (numOfExecutionsTillNow >= limit) {
                          continue;
                      }
                  } else {
                      numOfExecutionsTillNow = 0; // first run
                  }
              } else {
                  numOfExecutionsTillNow = 0; // assume first run unless told otherwise 
              }
          }
          // TODO: remove rule field (we have it already in action field)
          actionObjects.push({
              action: actions[i],
              numOfExecutions: Number(numOfExecutionsTillNow) + 1,
              rule: rule.getSysId(),
              ruleOrder: rule.getOrder(),
              ruleName: rule.getName()
          });
      }
      return actionObjects;
  },

  getActionCounters: function(ruleSysId, executionsMap, alertSysId) {
      var key = this.evtMgmtAlertMgmtCommons.createKeyWithRuleAndAlert(alertSysId, ruleSysId);
      return executionsMap && executionsMap[key] ? executionsMap[key] : null;
  },

  getLaunchApplications: function(rule, manuallyRun) {
      var launchApplications = [];

      this.startStep(this.type + ": getActions - getLaunchApplications");

      if (manuallyRun) { //launch applications should only be avilable for manual runs
          var ruleLaunchApplications = rule.getLaunchApplications();
          //it's required to convert the native array to js array
          for (var i = 0; i < ruleLaunchApplications.length; i++) {
              var launchObject = ruleLaunchApplications[i];
              launchApplications.push(launchObject);
          }
      }
      return launchApplications;
  },

  getSubflows: function(rule, manuallyRun, executionsMap, alertSysId) {

      this.startStep(this.type + ": getActions - getSubflows");

      var ruleSubflows = rule.getSubflows(!manuallyRun);

      this.startStep(this.type + ": getActions - getSubflows - checkLimits ");

      var subflows = this.checkLimits(rule, manuallyRun, executionsMap, alertSysId, ruleSubflows);

      return subflows;
  },

  executeActions: function(alertGR, rule, actions, manuallyRun, hasDependentRules, passthroughParams) {
      this.debugMessageExecuteActionsBefore(alertGR, rule, actions);
      //executing remediations:
      for (var i = 0; i < actions.remediations.length; i++) {
          this.debugMessageExecuteActionsInRemediations(alertGR, rule, actions, i);
          var remediation = actions.remediations[i];
          this.executeRemediationObj(alertGR, remediation, rule, manuallyRun);
      }

      if (actions.subflows.length > 0) {
          //executing subflows: [scopeOfSubflow].[subflowName]
          for (var j = 0; j < actions.subflows.length; j++) {
              // only use the callback function if this rule has dependent rules, it's not manually run, 
              // and this is the last subflow for this rule.
              var invokeWithCallback = hasDependentRules && !manuallyRun && j == actions.subflows.length - 1;
  			var sendSingleAlertGr = gs.getProperty('evt_mgmt.send_single_alert_to_subflow', 'true') == 'true';
              if (sendSingleAlertGr) {
                  var currAlertGr = new GlideRecord("em_alert");
                  currAlertGr.get(alertGR.sys_id);
                  this.executeSubflow(currAlertGr, actions.subflows[j], rule, manuallyRun, invokeWithCallback, passthroughParams);
              } else {
                  this.executeSubflow(alertGR, actions.subflows[j], rule, manuallyRun, invokeWithCallback, passthroughParams);
              }
          }
      }
      this.debugMessageExecuteActionsAfter(alertGR, rule);

  },

  executeSubflowManually: function(alertSysId, actionFullName, actionName, ruleSydId, actionId) {
      var alertGR = new GlideRecord('em_alert');
      alertGR.get(alertSysId);
      var ruleGR = new GlideRecord('em_alert_management_rule');
      ruleGR.get(ruleSydId);
      var contextId = this.callActivateSubflowAPI(actionFullName, ruleGR.name, ruleSydId, alertGR, true, undefined, undefined, actionId, actionName);
      return contextId;
  },

  executeSubflow: function(alertGR, actionObject, rule, manuallyRun, invokeWithCallback, passthroughParams) {
      var action = actionObject.action;
      var actionFullName = action.getReferenceExecutionName();
      var actionId = action.getActionSysid();
      var actionName = action.getReferenceActionDisplayName();
      var actionRefId = action.getReferenceActionID();
      var numOfExecutions = actionObject.numOfExecutions;
      var executionsLimit = actionObject.action.getExecutionsLimit();
      var contextId = this.callActivateSubflowAPI(actionFullName, rule.getName(), rule.getSysId(), alertGR, manuallyRun,
          numOfExecutions, executionsLimit, actionId, actionName, actionRefId, invokeWithCallback, passthroughParams);
  },

  callActivateSubflowAPI: function(actionFullName, ruleName, ruleSysId, alertGR, manuallyRun, numOfExecutions, executionsLimit,
      actionId, actionName, actionRefId, invokeWithCallback, passthroughParams) {

      var executionGR = this.createExectionRec();
      var inputs = {
          "ah_alertrulename": ruleName,
          "ah_alertruleid": ruleSysId,
          "ah_alertgr": alertGR,
          "ah_executionid": executionGR.getUniqueValue(),
          "ah_username": gs.getUserName(),
          "ah_userdisplayname": gs.getUserDisplayName()
      };
      if (invokeWithCallback) {
          inputs.ah_invokesubflow = actionRefId; // if we're invoking a wrapper, it's most reliable to use the subflow's id
          // the following parameter is passed directly through to the callback method
          inputs.ah_passthroughparams = passthroughParams;
      }
      try {
          var contextId;
          var actionToExecute = invokeWithCallback ? this.SUBFLOW_WRAPPER : actionFullName;
          if (gs.getProperty("evt_mgmt.alert.management.enable_legacy_whitelist_executeSubflow", 'false') == 'true') {
              contextId = this.evtMgmtAlertMgmtMediator.executeSubflow(actionToExecute, this.evtMgmtCommons.getRecordDomain(alertGR), inputs);
          } else if (this.enableSynchronousSubflow) {
               var startTimeForPerformence = new Date().getTime();
               contextId = this.evtMgmtAlertMgmtMediator.executeSubflowSync(actionToExecute, inputs);
               var duration = (new Date().getTime()) - startTimeForPerformence;
               this.evtMgmtCommons.addDebugLogNoPrefix('EvtMgmtAlertMgmtProcess synchronous callActivateSubflowAPI of ' + actionFullName + ' took ' + duration + ' ms');
          } else {
              contextId = this.evtMgmtAlertMgmtMediator.startSubflowSkipInputValidation(actionToExecute, inputs);
          }
          var logMessage = "";
          if (gs.nil(contextId)) {
              logMessage = this.getFailureExecutingLogMessage(manuallyRun, numOfExecutions, executionsLimit, gs.getMessage("Subflow '{0}' couldn't be started.", [actionName]));
          } else {
              logMessage = this.getSuccessExecutingLogMessage(manuallyRun, numOfExecutions, executionsLimit);
          }

          this.addExecutionRunForRecord(executionGR, alertGR.sys_id, ruleSysId, manuallyRun, contextId, actionName, "", this.SUBFLOW_LINK, logMessage, actionId);
          return contextId;
      } catch (error) {
          gs.error("Problem on running subflow " + actionFullName + " for alert " + alertGR.getUniqueValue() + ". Error is: " + this.evtMgmtCommons.getExceptionMessage(error, true));
          var logMessage2 = this.getFailureExecutingLogMessage(manuallyRun, numOfExecutions, executionsLimit, gs.getMessage("Subflow '{0}' couldn't be started: {1}.", [actionFullName, error]));
          this.addExecutionRunForRecord(executionGR, alertGR.sys_id, ruleSysId, manuallyRun, "", actionName, "", "", logMessage2, actionId);
      }
  },

  //called only when its automatic
  executeRemediationObj: function(alertGR, actionObject, rule, manuallyRun) {
      var refActionId = actionObject.action.getReferenceActionID();
      var actionName = actionObject.action.getWorkflowName();
      var actionId = actionObject.action.getActionSysid();
      var numOfExecutions = actionObject.numOfExecutions;
      var executionsLimit = actionObject.action.getExecutionsLimit();

      return this.executeRemediation(alertGR.sys_id, alertGR.cmdb_ci, rule.getSysId(), refActionId, actionName, manuallyRun, numOfExecutions, executionsLimit, actionId);
  },

  //called for automatic and manually
  executeRemediation: function(alertSysid, cmdbCi, ruleSysId, refActionId, actionName, manuallyRun, numOfExecutions, limit, actionId) {
      var taskId = this.evtMgmtAlertMgmtMediator.startRemediationReturnTaskId(alertSysid, cmdbCi, refActionId);
      var contextId = this.getContextByTask(taskId);
      var logMessage = "";

      if (gs.nil(taskId) || gs.nil(contextId)) {
          contextId = ""; //init context since it get a "null" value, not empty for worng tasks
          logMessage = this.getFailureExecutingLogMessage(manuallyRun, numOfExecutions, limit, gs.getMessage("Remediation '{0}' couldn't be started.", [actionName]));
          this.addExecutionRun(alertSysid, ruleSysId, manuallyRun, contextId, actionName, taskId, "", logMessage, actionId);

      } else {
          logMessage = this.getSuccessExecutingLogMessage(manuallyRun, numOfExecutions, limit);
          this.addExecutionRun(alertSysid, ruleSysId, manuallyRun, contextId, actionName, taskId, this.WORKFLOW_LINK, logMessage, actionId);

      }

      return contextId;
  },

  getSuccessExecutingLogMessage: function(manuallyRun, numOfExecutions, limit) {
      if (manuallyRun) {
          return gs.getMessage('Manual execution.');
      } else {
          return gs.getMessage('Automatic execution #{0} out of {1}.', [numOfExecutions + "", limit + ""]);
      }
  },

  getFailureExecutingLogMessage: function(manuallyRun, numOfExecutions, limit, failureMessage) {
      if (manuallyRun) {
          return gs.getMessage('Failure on manual execution. {0}', failureMessage);
      } else {
          return gs.getMessage('Failure on automatic execution #{0} out of {1}. {2}', [numOfExecutions + "", limit + "", failureMessage]);
      }
  },

  getContextByTask: function(taskId) {
      var gr = new GlideRecord("wf_context");
      gr.addQuery("id", taskId);
      gr.query();
      if (gr.next())
          return gr.getUniqueValue();
      return null;
  },

  addExecutionRun: function(alertId, ruleSysId, manuallyRun, contextId, actionName, taskId, linkPrefix, log, actionId) {
      return this.addExecutionRunForRecord(this.createExectionRec(), alertId, ruleSysId, manuallyRun, contextId, actionName, taskId, linkPrefix, log, actionId);
  },

  createExectionRec: function() {
      var executions = new GlideRecord("em_alert_management_execution");
      executions.setNewGuidValue(gs.generateGUID());
      
      if (this.enableSynchronousSubflow) {
          executions.insert();
      }
      
      return executions;
  },

  addExecutionRunForRecord: function(executions, alertId, ruleSysId, manuallyRun, contextId, actionName, taskId, linkPrefix, log, actionId) {

  	var update = false;
      if (this.enableSynchronousSubflow) {
          executions.addQuery(this.SYS_ID, executions.getValue(this.SYS_ID));
          executions.query();
          update = executions.next();
      }
      executions.management_rule = ruleSysId;
      executions.alert = alertId;
      executions.historical = false;
      executions.automatic_run = !manuallyRun;
      executions.action_name = actionName;
  	var task = executions.getValue('related_task');
      if (!this.enableSynchronousSubflow || gs.nil(task)) {
           executions.related_task = taskId;
      }
      executions.link = linkPrefix + contextId;
      executions.log = log;
      executions.rule_action = actionId;
      executions.filter_always_match = true;
      if (update) {
          executions.update();

      } else {
          executions.insert();

      }
  },

  didLastAlertUpdateMatchedFilter: function(alertId, ruleId, executionsMap, lastSysId) {
      var key = this.evtMgmtAlertMgmtCommons.createKeyWithRuleAndAlert(alertId, ruleId);
      var result = "false";

      if (this.evtMgmtCommons.isDebugEnabled()) { //Don't json stringify if not debugging
          this.evtMgmtCommons.addDebugMessage("In didLastAlertUpdateMatchedFilter Execution map is: " + JSON.stringify(executionsMap));
      }

      if (executionsMap) {
          if (executionsMap[key]) {
              result = executionsMap[key][this.evtMgmtAlertMgmtCommons.LAST_EXECUTION_FILTER_MATCH];
              lastSysId[0] = executionsMap[key][this.evtMgmtAlertMgmtCommons.LAST_EXECUTION_SYS_ID];
              this.evtMgmtCommons.addDebugMessage("In didLastAlertUpdateMatchedFilter() for rule " + ruleId + ", LAST_EXECUTION_FILTER_MATCH=" + result + ", LAST_EXECUTION_SYS_ID= " + lastSysId[0]);
          }
      }

      //It's a string, not boolean so we need to check it this way
      return result === "true";
  },

  updateExecutionFilterMatchesFalse: function(executionId) {

      var executions = new GlideRecord("em_alert_management_execution");
      if (executions.get(executionId)) {
          executions.filter_always_match = false;
          executions.update();
      }
  },

  getSubflowName: function(subflowFullName) {
      var subflowName = subflowFullName;
      if (subflowFullName.indexOf(".") > -1) {
          subflowName = subflowFullName.substring(subflowFullName.indexOf(".") + 1);
      }
      return subflowName;
  },

  debugMessageExecuteActionsBefore: function(alertGR, rule, actions) {
      this.evtMgmtCommons.addDebugMessage("Running " + this.getDebugPrefixMessage(alertGR, rule) + " with actions: " + actions.remediations + ".");
  },

  debugMessageExecuteActionsInRemediations: function(alertGR, rule, actions, i) {
      this.evtMgmtCommons.addDebugMessage("Running remediation for " + this.getDebugPrefixMessage(alertGR, rule) + " with remediation action: " + actions.remediations[i].action.getWorkflowId());
  },

  debugMessageExecuteActionsAfter: function(alertGR, rule) {
      this.evtMgmtCommons.addDebugMessage("Finished " + this.getDebugPrefixMessage(alertGR, rule));
  },

  getDebugPrefixMessage: function(alertGR, rule) {
      return "management rule " + rule.getName() + "(" + rule.getSysId() + ") for alert " + alertGR.number + "(" + alertGR.sys_id + ")";
  },

  loadAdditionalInfo: function(ruleList) {
      this.startStep(this.type + ": loadAdditionalInfo");

      //this object hods the additional info for the alert management rule,
      //ruleDataMap is a map that holds the records of the sn_em_arm_additional_info
      //in case the object is already is initialized in the correct domain, or there are no rules to run 
      //the function will return

      var ruleDataMap = {};

      var additionalInfoObj = {
          ruleDataMap: ruleDataMap
      };

      if (ruleList.length == 0)
          return additionalInfoObj;

      // the rule list only contains active rules. But it is possible that an active rule has been defined as dependent on an inactive rule.
      // In this case, the dependent rule should be be run immediately, even though the parent is inactive.
      // But we only know which rules are inactive by whether they *fail* to appear in the ruleList.
      // So here we convert ruleList into a map
      var activeRules = {};
      for (var i = 0; i < ruleList.length; i++) {
          activeRules[ruleList[i].getSysId()] = ruleList[i];
      }

      var domain = this.evtMgmtCommons.getCurrentDomainID();

      if (this.fRuleAdditionalInfo.hasOwnProperty(domain))
          return this.fRuleAdditionalInfo[domain];

      var dataGr = new GlideRecord(this.ADDITIONAL_INFO_TABLE);
      dataGr.orderBy("sys_created_on"); //if we have duplications we want the oldest
      dataGr.query();
      var dependencyMap = {};
      while (dataGr.next()) {
          var ruleSysId = dataGr.getValue(this.ALERT_MGMT_RULE);
          var obj = {
              ruleSysId: ruleSysId,
              useScript: dataGr.getValue(this.USE_SCRIPT),
              dependentOnRule: dataGr.getValue(this.DEPENDENT_ON_RULE),
              alertKeyField: dataGr.getValue(this.ALERT_KEY_FIELD),
              alertKeyValue: dataGr.getValue(this.ALERT_KEY_VALUE),
              alertKeyAppliesTo: dataGr.getValue(this.APPLIES_TO),
              isAddInfoField: JSON.parse(dataGr.getValue(this.IS_ADD_INFO_FIELD)),
              recordSysId: dataGr.getValue(this.SYS_ID)

          };
          if (ruleDataMap[ruleSysId]) { //Already have a record for this rule in the additional info table
              gs.error("Alert Management Rule: " + ruleSysId + " Already exists in the additional info table, ignoring this record: " + dataGr.getValue(this.SYS_ID));
          } else {
              ruleDataMap[ruleSysId] = obj;
          }

          if (!gs.nil(obj.dependentOnRule)) {
              // we have a dependency here, so store it in the dependencyt map
              if (!dependencyMap[obj.dependentOnRule]) {
                  dependencyMap[obj.dependentOnRule] = [];
              }
              dependencyMap[obj.dependentOnRule].push(ruleSysId);
          }
      }

      this.fRuleAdditionalInfo[domain] = additionalInfoObj;
      additionalInfoObj.dependencyMap = dependencyMap;
      additionalInfoObj.activeRuleMap = activeRules;

      return this.fRuleAdditionalInfo[domain];

  },

  evaluateRuleScript: function(rule, ruleAddInfoMap, alertGR) {
      this.startStep(this.type + ": evaluateRuleScript");

      //can happen in flows where AdditionalInfo was not initiated , for example - reopen alert
      if (!ruleAddInfoMap) {
          ruleAddInfoMap = this.initiateAddInfoMap();
      }

      var ruleSysId = rule.getSysId();
      var output = true;
      if (ruleAddInfoMap.hasOwnProperty(ruleSysId)) {
          if (ruleAddInfoMap[ruleSysId].useScript == 1) {
              this.startStep(this.type + ": evaluateRuleScript - query AI table");

              var sysId = ruleAddInfoMap[ruleSysId].recordSysId;
              var scriptGr = new GlideRecord(this.ADDITIONAL_INFO_TABLE);
              if (scriptGr.get(sysId)) {
                  this.startStep(this.type + ": evaluateRuleScript - evaluate script");

                  var evaluator = new GlideScopedEvaluator();
                  evaluator.putVariable('gr', alertGR);
                  output = evaluator.evaluateScript(scriptGr, this.SCRIPT_CONDITION);
              }
          }
      }
      if (gs.nil(output)) {
          gs.error("rule " + rule.getName() + " script evaluation has failed ");
          return false;
      }

      return output;
  },

  setStepManager: function(pSlowStepsManager) {
      this.slowStepsManager = pSlowStepsManager;
  },

  startStep: function(pStep) {
      if (typeof this.slowStepsManager !== "undefined") {
          this.slowStepsManager.startStep(pStep);
      }
  },

  onRuleComplete: function(alertGR, alertRuleId, passthroughParamsJson) {
      this.startStep(this.type + ": onRuleComplete - start");
      // the rule has finished running, now we can call its dependent rules

      // get the rules from the cache
      var relevantRules = this.evtMgmtAlertMgmtMediator.getReleveantAlertRules();
      var ruleAdditionalInfo = this.loadAdditionalInfo(relevantRules);
      var dependentRuleIds = ruleAdditionalInfo.dependencyMap[alertRuleId];
      if (!dependentRuleIds) {
          // this is a really far-fetched edge case, in case the dependency was removed while the rule was running, before the callback.
          return;
      }

      var dependentRules = this.addActiveDependentRules([], ruleAdditionalInfo, dependentRuleIds);
      // now we can loop back into iterateOverRules
      this.startStep(this.type + ": onRuleComplete - callback to iterateOverRules");
      // reload the execution map, just for this alert id
      var passthroughParams = JSON.parse(passthroughParamsJson);
      var map = this.evtMgmtAlertMgmtCommons.getAlertRuleExecutionMap([alertGR.sys_id]);
      this.iterateOverRules(alertGR, dependentRules, false, passthroughParams.retrieveParams, map, null, true);
  },

  addActiveDependentRules: function(dependentRules, ruleAdditionalInfo, dependentRuleIds) {
      this.startStep(this.type + ": addActiveDependentRules (recursive function)");

      for (var i = 0; i < dependentRuleIds.length; i++) {
          // if the rule is in the map, that's great. If it's not, that means it is inactive, and its dependencies should run immediately
          var ruleId = dependentRuleIds[i];
          var rule = ruleAdditionalInfo.activeRuleMap[ruleId];
          if (rule) {
              dependentRules.push(rule);
          } else {
              // I am inactive, so check if I have any dependencies, and if so, recurse
              var myDependentRuleIds = ruleAdditionalInfo.dependencyMap[ruleId];
              if (myDependentRuleIds) {
                  dependentRules = this.addActiveDependentRules(dependentRules, ruleAdditionalInfo, myDependentRuleIds);
              }
          }
      }
      return dependentRules;
  },

  getAddInfoFieldJsonForSRO: function(additionalInfo) {
      if (!additionalInfo)
          return undefined;

      // whether this is an SRO alert or not, we may need the additional info
      try {
          var additionalInfoJSON = JSON.parse(additionalInfo);
          if (!additionalInfoJSON)
              return undefined;
          return additionalInfoJSON;
      } catch (e) {
          gs.error("Alert Management - Fail to parse alert additional info:  " + additionalInfo + "; Error Message: " + this.evtMgmtCommons.getExceptionMessage(e, true));
          return undefined;
      }
  },

  // Returns a trimed keys array (or if allrady fetched the same key)
  getKeysListForSRO: function() {

      if (!this.SROKeys) {
          if (gs.nil(this.ALERT_KEY_IDENTIFIER_FIELD)) {
              this.SROKeys = [];
          } else {
              var keys = this.ALERT_KEY_IDENTIFIER_FIELD.split(",");
              for (var i = 0; i < keys.length; ++i) {
                  keys[i] = (keys[i]).trim();
              }
              this.SROKeys = keys;
          }
      }

      return this.SROKeys;

  },

  isSroAlert: function(additionalInfoJSON) {

      if (!additionalInfoJSON) // additionalInfoJSON undefined
          return false;

      var keys = this.getKeysListForSRO();

      for (var i = 0; i < keys.length; ++i) {
          if (!gs.nil(keys[i])) {
              if (additionalInfoJSON.hasOwnProperty((keys[i]))) {
                  return true;
              }
          }
      }

      return false;
  },

  initiateAddInfoMap: function() {
      this.startStep(this.type + ": initiateAddInfoMap");

      var relevantRules = this.evtMgmtAlertMgmtMediator.getReleveantAlertRules();
      var additionalInfo = this.loadAdditionalInfo(relevantRules);
      var ruleAddInfoMap = additionalInfo.ruleDataMap;
      return ruleAddInfoMap;


  },

  findApplicationService: function(alertGR) {
      var data = [];
      var query = this.QUERY_IMPACT_GRAPH;
      if (query == 'false')
          return data;

      this.startStep(this.type + ": findApplicationService - get CI");
      var alertCi = alertGR.getValue(this.CMDB_CI);
      try {
          if (alertCi) {
              data = this.fApplicationServicesPerCI[alertCi];
              if (data != undefined) {
                  return data;
              }
              // load the valid application services with this alert's CI in the impact graph, and cache them in fApplicationServicesPerCI
              data = [];
              this.startStep(this.type + ": findApplicationService - Query impact graph table");
              var gr = new GlideRecord(this.IMPACT_GRAPH_TABLE);
              gr.addQuery(this.CHILD, alertCi);
              gr.addQuery(this.STATUS, "Valid");
              gr.setLimit(this.MAX_APP_PER_CI);
              gr.query();

              this.startStep(this.type + ": findApplicationService - Loop through query results");
              while (gr.next()) {
                  data.push(gr.getValue(this.BUSINESS_SERVICE));
              }
              if (data.length >= this.MAX_APP_PER_CI) {
                  gs.error('EvtMgmtAlertMgmtProcess: findApplicationService ' + 'exceeded ' + this.MAX_APP_PER_CI + ' Application Services for CI ' + alertGR.getValue("cmdb_ci"));
              }
              this.fApplicationServicesPerCI[alertCi] = data;
          }
          return data;
      } finally {
          this.startStep(this.type + ": findApplicationService - end");
      }
  },

  matchAlertKeyValue: function(alertGR, thisRuleInfo, keyField, keyValue, keyAppliesTo, additionalInfoJSON) {
      var keyFieldMatches = true; //for all rules without alert_key_field
      var alertValue = "";
      if (keyField) {
          keyFieldMatches = false;
          if (keyField != this.CMDB_CI) {
              if (thisRuleInfo["isAddInfoField"] && additionalInfoJSON) {
                  alertValue = additionalInfoJSON[keyField]; //field in additional info
              } else {
                  alertValue = alertGR.getValue(keyField); //alert field
              }
              if (keyValue == alertValue)
                  keyFieldMatches = true;
          } else { //keyField == cmdb_ci
              alertValue = alertGR.getValue(keyField);

              if (keyAppliesTo != this.APPLIES_TO_CI) {
                  if (keyValue == alertValue)
                      keyFieldMatches = true; //the Alert's CI matches the rule's key value (AS)
              }
              if (keyAppliesTo != this.APPLIES_TO_SERVICE && (alertValue != keyValue)) {
                  var ApplicationServiceList = this.findApplicationService(alertGR);

                  for (var j = 0; j < ApplicationServiceList.length; j++) {
                      if (ApplicationServiceList[j] == keyValue) {
                          keyFieldMatches = true;
                          break;
                      }
                  }
              }
          }
      }

      return keyFieldMatches;
  }
};

Sys ID

1bf511aab7d920107c038229ce11a920

Offical Documentation

Official Docs: