Name

sn_em_ai.EvtMgmtAlertCrawlerUtils

Description

No description available

Script

var EvtMgmtAlertCrawlerUtils = Class.create();
EvtMgmtAlertCrawlerUtils.prototype = {
  initialize: function() {
      this.evtMgmtCommons = new global.EvtMgmtCommons();
      // we want to exclude secodaries when iterating over em_alert - secondaries will be dealt with on using the sn_em_ai_alert_queue table.
      this.EXCLUDE_SECONDARIES_QUERY = gs.getProperty("sn_em_ai.exclude_secodaried_query",
          "^correlation_groupNOT IN2,3^sn_extra_data.category=null^ORsn_extra_data.category!=3");
      this.ALERT_CRAWLER_TRIGGER = "alert_crawler_trigger";
      this.ALERT_PROCESSING_LIMIT_PROPERTY = 'sn_em_ai.alert_processing_query_limit';
      this.alertProcessingQueryLimit = parseInt(gs.getProperty(this.ALERT_PROCESSING_LIMIT_PROPERTY, 1000), 10) || 1000;
      this.changedAlertProcessingLimit = parseInt(gs.getProperty('sn_em_ai.changed_alerts_processing_limit', 1000), 10) || 1000;
      this.POPULATE_ALERT_CIS_PROPERTY = "sn_em_ai.populate_alert_cis_table_enabled";
      this.POPULATE_ALERT_SERVICES_PROPERTY = "sn_em_ai.populate_alert_services_table_enabled";
      this.POPULATE_ALERT_TAGS_PROPERTY = "sn_em_ai.populate_alert_tags_table_enabled";
      this.bulkLimit = parseInt(gs.getProperty("evt_mgmt.batchInsertSingleField.bulk_size", 500), 10) || 500; 

      //Constants
      this.SYS_CREATED_ON = "sys_created_on";
      this.CMDB_CI = 'cmdb_ci';
      this.DOMAIN = 'sys_domain';
      this.ALERT = 'alert';
      this.BUSINESS_SERVICE = 'business_service';
      this.NAME = "name";
      this.CLASS = "class";
      this.SYS_ID = "sys_id";
      this.EM_ALERT_TABLE = "em_alert";
      this.ALERT_TAGS = "sn_alert_tags";
      this.NUMBER = "number";
      this.IS_LA_ALERT = "is_la_alert";

      //sn_em_ai_alert_cis
      this.ALERT_CIS_TABLE = 'sn_em_ai_alert_cis';
      this.CI_NAME = 'cmdb_ci_name';
      this.CI_CLASS = 'cmdb_ci_sys_class_name';

      //sn_em_ai_alert_services
      this.ALERT_SERVICES_TABLE = 'sn_em_ai_alert_services';
      this.BS_NAME = 'business_service_name';
      this.BS_CLASS = 'business_service_sys_class_name';
      this.REFERENCE_CIS = 'reference_cmdb_cis';

      //sn_em_ai_agg_alert_queue
      this.ALERT_QUEUE_TABLE = 'sn_em_ai_agg_alert_queue';
      this.PARENT = 'parent';
      this.ADDITIONAL_INFO = 'additional_info';
      this.STATES = {
          PENDING: 'pending',
          PROCESSING: 'processing',
          COMPLETE: 'complete',
          FAILED: 'failed'
      };
      this.STATE = 'state';
      this.ACTIONS = {
          ALERT_ADDED_TO_GROUP: '1',
          ALERT_REMOVED_FROM_GROUP: '2',
          ALERT_TAGS_CHANGED: '3',
      };
      this.ACTION = "action";

      //em_impact_graph
      this.EM_IMPACT_GRAPH = "em_impact_graph";
      this.CHILD_ID = "child_id";
      this.PARENT_ID = "parent_id";
      this.STATUS = "status";

      //sn_em_ai_alert_tags
      this.ALERT_TAGS_TABLE = "sn_em_ai_alert_tags";
      this.TAG_NAME = "tag_name";
      this.TAG_VALUE = "tag_value";

      this.POPULATE_ALERT_TAGS = this.shouldPopulateAlertTags();
      this.POPULATE_ALERT_CIS = this.shouldPopulateTable(this.POPULATE_ALERT_CIS_PROPERTY, true, this.ALERT_CIS_TABLE);
      this.POPULATE_ALERT_SERVICES = this.shouldPopulateTable(this.POPULATE_ALERT_SERVICES_PROPERTY, true, this.ALERT_SERVICES_TABLE);

      // In memory objects saving the data that will be inserted to the utility tables in bulks, at the end of the job run.
      this.alertCisMap = {}; // Map of alerts with their configuration items
      this.cisExtraData = {}; // save the name and sys_class_name of each CI
      this.alertServicesMap = {}; // Map of alerts with their impacted services, and the refrence CI of each service.
      this.servicesExtraData = {}; // save the name and sys_class_name of each impacted service
      this.alertDomains = {}; // Map of alert and sys_domain
      this.alertTagsMap = {}; //Map of alerts and the key-value pair of the alert tags.
  },

  getHashDefaultEmpty: function(hashName) {
      var hashGr = this.evtMgmtCommons.getHashGr(hashName);

      if (!hashGr.next()) {
          // create on demand, with empty hash value
          hashGr = new GlideRecord('sa_hash');
          hashGr.name = hashName;
          hashGr.insert();
          return "";
      } else {
          return hashGr.getValue("hash");
      }
  },

  getAlertsBetweenTimes: function(alertProcessTimeForAlerts, lastJobRunTime) {
      var gr = new GlideRecord(this.EM_ALERT_TABLE);
      if (lastJobRunTime) {
          gr.addQuery(this.SYS_CREATED_ON, ">=", lastJobRunTime);
      }
      if (this.EXCLUDE_SECONDARIES_QUERY) {
          gr.addEncodedQuery(this.EXCLUDE_SECONDARIES_QUERY);
      }
      gr.addQuery(this.SYS_CREATED_ON, "<", alertProcessTimeForAlerts);

      if (this.alertProcessingQueryLimit > 0) { // unlimit the number of records by setting this property to 0 or less
          gr.setLimit(this.alertProcessingQueryLimit);
      }
      // here we are sorting by Created_On since the alerts should to process record in order
      gr.orderBy(this.SYS_CREATED_ON);
      gr.query();

      return gr;
  },

  getChangedAlerts: function(alertDelayTime) {
      var gr = new GlideRecord(this.ALERT_QUEUE_TABLE);
      gr.addQuery(this.STATE, this.STATES.PENDING);
      gr.addQuery(this.SYS_CREATED_ON, "<", alertDelayTime);
      if (this.changedAlertProcessingLimit > 0) { // unlimit the number of records by setting this property to 0 or less
          gr.setLimit(this.changedAlertProcessingLimit);
      }
      // here we are sorting by Created_On since the alerts should to process record in order
      gr.orderBy(this.SYS_CREATED_ON);
      gr.query();

      return gr;
  },

  addToAlertCIsTable: function(alertId, ciId, ciName, ciClass, alertDomain) {
      if (alertId) {
          if (ciId) { //If the record being added has a CI, need to remove any record with "empty" Ci value for this alert
              this.removeEmptyCiRecordIfExists(alertId);
          }
          var addRecord = ciId ? !this.doesCiExistForAlert(alertId, ciId) : !this.doesAlertExistInCiTable(alertId);
          if (addRecord) {
              this.addToAlertCisMap(alertId, ciId);
              this.addToAlertDomainMap(alertId, alertDomain);
              if (ciId) {
                  this.addToCisExtraData(ciId, ciName, ciClass);
              }
          }
      }
  },

  doesCiExistForAlert: function(alertId, ciId) {
      if (alertId && ciId) {
          //first check in the alertCisMap, if not found search the table
          if (this.alertCisMap?.[alertId] && this.alertCisMap?.[alertId].has(ciId)) {
              return true;
          }

          var gr = new GlideRecord(this.ALERT_CIS_TABLE);
          gr.addQuery(this.ALERT, alertId);
          if (ciId) {
              gr.addQuery(this.CMDB_CI, ciId);
          }
          gr.setLimit(1);
          gr.query();
          return gr.next();
      }
      return false;
  },

  doesAlertExistInCiTable: function(parentId) {
      //first check in the alertCisMap, if not found search the table
      if (this.alertCisMap?.[parentId]) {
          return true;
      }
      var gr = new GlideRecord(this.ALERT_CIS_TABLE);
      return gr.get(this.ALERT, parentId);
  },

  removeEmptyCiRecordIfExists: function(alertId) {
      //remove also from memory if exists
      if (this.alertCisMap?.[alertId]) {
          this.alertCisMap[alertId].delete("");
      }

      var gr = new GlideRecord(this.ALERT_CIS_TABLE);
      gr.addQuery(this.ALERT, alertId);
      gr.addNullQuery(this.CMDB_CI);
      gr.query();
      gr.deleteMultiple();
  },

  removeAllAlertRecords: function(alertId) {
      if (alertId) {
          this.evtMgmtCommons.addDebugLogNoPrefix(this.type + ': records for alert sys id: ' + alertId + ' are removed from ' + this.ALERT_CIS_TABLE + ', ' + this.ALERT_SERVICES_TABLE + ", and " + this.ALERT_TAGS_TABLE + ' tables.');

          if (this.POPULATE_ALERT_CIS) {
              delete this.alertCisMap[alertId];
              var gr = new GlideRecord(this.ALERT_CIS_TABLE);
              gr.addQuery(this.ALERT, alertId);
              gr.query();
              gr.deleteMultiple();
          }

          if (this.POPULATE_ALERT_SERVICES) {
              delete this.alertServicesMap[alertId];
              gr = new GlideRecord(this.ALERT_SERVICES_TABLE);
              gr.addQuery(this.ALERT, alertId);
              gr.query();
              gr.deleteMultiple();
          }

          if (this.POPULATE_ALERT_TAGS) {
              this.removeAlertTagsForAlert(alertId);
          }
      }
  },

  handleImpactedService: function(alertId, ciId, currAlertDomain, isGroupedAlert) {
      //check if CI was allready handled for the alert
      if (!this.doesCiExistForAlertService(alertId, ciId)) {
          var impactedServices = this.getImpactedServicesOfCi(ciId);
          if (impactedServices.length == 0) { // no impacted services
              if (!isGroupedAlert || !this.doesGroupHaveImpactedServices(alertId)) {
                  this.addToAlertServicesTable(alertId, "", "", "", currAlertDomain, ciId);
              }
          } else {
              for (var i = 0; i < impactedServices.length; i++) {
                  var impactedServiceObj = impactedServices[i];
                  this.addToAlertServicesTable(alertId, impactedServiceObj["sys_id"], impactedServiceObj["name"], impactedServiceObj["class"], currAlertDomain, ciId);
              }
          }
      }
  },

  addToAlertServicesTable: function(alertId, bsId, bsName, bsClass, alertDomain, referenceCi) {
      if (alertId) {
          //If the record being added has an impacted service, need to remove any record with "empty" service value for this alert
          if (bsId) {
              this.removeEmptyBsRecordIfExists(alertId);
          }

          // search alertServicesMap before searchin in table.
          if (this.alertServicesMap?.[alertId]?.[bsId]) { // if the service already exists for the alert - we should update the "reference ci" field.
              if (referenceCi) {
                  this.alertServicesMap[alertId][bsId].add(referenceCi);
              }
          } else { // search the table for the alert-service record, if doesn't exist then add a new record.
              var gr = new GlideRecord(this.ALERT_SERVICES_TABLE);
              gr.addQuery(this.ALERT, alertId);
              gr.addQuery(this.BUSINESS_SERVICE, bsId);
              gr.query();
              if (!gr.next()) { // add new record if doesn't exists
                  this.addToAlertServicesMap(alertId, bsId, referenceCi);
                  this.addToAlertDomainMap(alertId, alertDomain);
                  if (bsId) {
                      this.addToServicesExtraData(bsId, bsName, bsClass);
                  }
              } else if (referenceCi) { // add the CI to the reference Ci list
                  var referenceCiList = gr.getValue(this.REFERENCE_CIS) || "";
                  //make sure the CI doesn't exists in the list
                  if (referenceCiList.indexOf(referenceCi) <= -1) {
                      gr.setValue(this.REFERENCE_CIS, referenceCiList + "," + referenceCi);
                      gr.update();
                  }
              }
          }
      }
  },


  doesGroupHaveImpactedServices: function(parentId) {
      if (parentId) {
          //first check in the alertServicesMap, if not found search the table
          if (this.alertServicesMap?.[parentId] && (Object.keys(this.alertServicesMap?.[parentId]).length == 0)) {
              return true;
          }

          var gr = new GlideRecord(this.ALERT_SERVICES_TABLE);
          gr.addQuery(this.ALERT, parentId);
          gr.addNotNullQuery(this.BUSINESS_SERVICE);
          gr.setLimit(1);
          gr.query();
          return gr.next();
      }
      return false;
  },

  doesCiExistForAlertService: function(alertId, ciId) {
      if (alertId && ciId) {
          //first check in the alertServicesMap, if not found search the table
          if (this.alertServicesMap?.[alertId]) {
              var services = Object.keys(this.alertServicesMap[alertId]);
              for (var service of services) {
                  if (this.alertServicesMap[alertId][service].has(ciId)) {
                      return true;
                  }
              }
          }

          var gr = new GlideRecord(this.ALERT_SERVICES_TABLE);
          gr.addQuery(this.ALERT, alertId);
          gr.addQuery(this.REFERENCE_CIS, 'LIKE', ciId);
          gr.setLimit(1);
          gr.query();
          return gr.next();
      }
      return false;
  },

  removeEmptyBsRecordIfExists: function(alertId) {
      if (alertId) {
          //remove from memory if exists
          if (this.alertServicesMap?.[alertId]?.[""]) {
              delete this.alertServicesMap[alertId][""];
          }

          var gr = new GlideRecord(this.ALERT_SERVICES_TABLE);
          gr.addQuery(this.ALERT, alertId);
          gr.addNullQuery(this.BUSINESS_SERVICE);
          gr.query();
          gr.deleteMultiple();
      }
  },

  getImpactedServicesOfCi: function(ciId) {
      var impactedServices = [];
      if (ciId) {
          var impactGraphGr = new GlideAggregate(this.EM_IMPACT_GRAPH);
          impactGraphGr.addQuery(this.STATUS, "valid").addOrCondition(this.STATUS, null);
          impactGraphGr.addQuery(this.CHILD_ID, ciId);
          impactGraphGr.groupBy(this.BUSINESS_SERVICE);
          impactGraphGr.query();
          while (impactGraphGr.next()) {
              var bsId = impactGraphGr.getValue(this.BUSINESS_SERVICE);
              var bsClass = impactGraphGr.business_service.sys_class_name;
              var bsName = impactGraphGr.getDisplayValue(this.BUSINESS_SERVICE);
              var bsObj = {};
              bsObj[this.NAME] = bsName;
              bsObj[this.CLASS] = bsClass;
              bsObj[this.SYS_ID] = bsId;
              impactedServices.push(bsObj);
          }
      }
      return impactedServices;
  },

  addToAlertsQueue: function(alertId, parent, ciId, alertTags, actionType, additionalInfo, alertDomain) {
      if (alertId) {
          var gr = new GlideRecord(this.ALERT_QUEUE_TABLE);
          gr.setValue(this.ALERT, alertId);
          gr.setValue(this.PARENT, parent);
          gr.setValue(this.ALERT_TAGS, alertTags);
          gr.setValue(this.ACTION, actionType);
          gr.setValue(this.CMDB_CI, ciId);
          gr.setValue(this.STATE, this.STATES.PENDING);
          gr.setValue(this.ADDITIONAL_INFO, additionalInfo);
          gr.setValue(this.DOMAIN, alertDomain);
          gr.insert();
      }
  },

  addToAlertTagTable: function(alertId, tagName, tagValue, alertDomain) {
      if (alertId && tagName) {
          this.addToAlertTagsMap(alertId, tagName, tagValue);
          this.addToAlertDomainMap(alertId, alertDomain);
      }
  },

  removeAlertTagsForAlert: function(alertId) {
      if (alertId) {
          delete this.alertTagsMap[alertId];

          var gr = new GlideRecord(this.ALERT_TAGS_TABLE);
          gr.addQuery(this.ALERT, alertId);
          gr.query();
          gr.deleteMultiple();
      }
  },

  parseJsonReturnEmptyDefault(jsonStr) {
      try {
          var obj = JSON.parse(jsonStr);
          return obj;
      } catch (e) {
          return {};
      }
  },

  shouldPopulateAlertTags: function() {
      if (this.shouldPopulateTable(this.POPULATE_ALERT_TAGS_PROPERTY, true, this.ALERT_TAGS_TABLE)) {
          var alertState = gs.getProperty('evt_mgmt.alert_tags_state', 'true');
          if (alertState == 'true' || alertState == 'partial') { // check if "Alert tags" column exists in em_alert table
              var gr = new GlideRecord(this.EM_ALERT_TABLE);
              var element = gr.getElement(this.ALERT_TAGS);
              return (element != undefined);
          }
      }
      return false;
  },

  shouldPopulateTable: function(propertyName, defaultPropertyValue, tableName) {
      return ((gs.getProperty(propertyName, defaultPropertyValue) == "true") && gs.tableExists(tableName));
  },

  checkBatchInsertMultiple: function(jsonArr, tableName, jsonCounter, limit) {
      if (jsonCounter >= limit && jsonCounter > 0) {
          var batchUtilMediator;
          if (typeof new global.EvtMgmtAlertMgmtMediator().batchInsertMultiple == 'function') {
              batchUtilMediator = new global.EvtMgmtAlertMgmtMediator();
          } else {
              batchUtilMediator = new global.EMLicensingUtilWrapper();
          }
          batchUtilMediator.batchInsertMultiple(JSON.stringify(jsonArr), tableName, '');
  		
          jsonCounter = 0;
          jsonArr = [];
      }
      return [jsonCounter, jsonArr];
  },

  insertAlertCisBatchRecords: function() {
      var alertCisJsonArr = [];
      var alertCisCounter = 0;
      for (var alert in this.alertCisMap) {
          for (var ciId of this.alertCisMap[alert]) {
              var json = {
                  [this.ALERT]: alert,
                  [this.CMDB_CI]: ciId,
                  [this.CI_NAME]: this.cisExtraData?.[ciId]?.[this.CI_NAME] ?? "",
                  [this.CI_CLASS]: this.cisExtraData?.[ciId]?.[this.CI_CLASS] ?? "",
                  [this.DOMAIN]: this.alertDomains?.[alert] ?? "global",
              };
              alertCisJsonArr.push(json);
              alertCisCounter++;
              [alertCisCounter, alertCisJsonArr] = this.checkBatchInsertMultiple(alertCisJsonArr, this.ALERT_CIS_TABLE, alertCisCounter, this.bulkLimit);
          }
      }
      [alertCisCounter, alertCisJsonArr] = this.checkBatchInsertMultiple(alertCisJsonArr, this.ALERT_CIS_TABLE, alertCisCounter, 0);
  },

  insertAlertServicesBatchRecords: function() {
      var alertServicesJsonArr = [];
      var alertServicesCounter = 0;
      for (var alert in this.alertServicesMap) {
          for (var service in this.alertServicesMap[alert]) {
              var json = {
                  [this.ALERT]: alert,
                  [this.BUSINESS_SERVICE]: service,
                  [this.BS_NAME]: this.servicesExtraData?.[service]?.[this.BS_NAME] ?? "",
                  [this.BS_CLASS]: this.servicesExtraData?.[service]?.[this.BS_CLASS] ?? "",
                  [this.REFERENCE_CIS]: Array.from(this.alertServicesMap[alert][service]).join(","),
                  [this.DOMAIN]: this.alertDomains?.[alert] ?? "global",
              };
              alertServicesJsonArr.push(json);
              alertServicesCounter++;

              [alertServicesCounter, alertServicesJsonArr] = this.checkBatchInsertMultiple(alertServicesJsonArr, this.ALERT_SERVICES_TABLE, alertServicesCounter, this.bulkLimit);
          }
      }
  	[alertServicesCounter, alertServicesJsonArr] = this.checkBatchInsertMultiple(alertServicesJsonArr, this.ALERT_SERVICES_TABLE, alertServicesCounter, 0);
  },

  insertAlertTagsBatchRecords: function() {
      var alertTagsJsonArr = [];
      var alertTagsCounter = 0;
      for (var alert in this.alertTagsMap) {
          for (var tagName in this.alertTagsMap[alert]) {
              var json = {
                  [this.ALERT]: alert,
                  [this.TAG_NAME]: tagName,
                  [this.TAG_VALUE]: this.alertTagsMap?.[alert]?.[tagName] ?? "",
                  [this.DOMAIN]: this.alertDomains?.[alert] ?? "global",
              };
              alertTagsJsonArr.push(json);
              alertTagsCounter++;

              [alertTagsCounter, alertTagsJsonArr] = this.checkBatchInsertMultiple(alertTagsJsonArr, this.ALERT_TAGS_TABLE, alertTagsCounter, this.bulkLimit);
          }
      }
  	[alertTagsCounter, alertTagsJsonArr] = this.checkBatchInsertMultiple(alertTagsJsonArr, this.ALERT_TAGS_TABLE, alertTagsCounter, 0);
  },

  addToAlertCisMap: function(alertId, ciId) {
      ciId = ciId ?? "";
      if (!this.alertCisMap?.[alertId]) {
          this.alertCisMap[alertId] = new Set();
      }
      this.alertCisMap[alertId].add(ciId);
  },

  addToAlertDomainMap: function(alertId, domain) {
      if (!this.alertDomains?.[alertId]) {
          this.alertDomains[alertId] = domain;
      }
  },

  addToCisExtraData: function(ciId, ciName, ciClass) {
      if (!this.cisExtraData?.[ciId]) {
          var ciObj = {
              [this.CI_NAME]: String(ciName),
              [this.CI_CLASS]: String(ciClass)
          };
          this.cisExtraData[ciId] = ciObj;
      }
  },

  addToServicesExtraData: function(bsId, bsName, bsClass) {
      bsId = bsId ?? "";
      if (!this.servicesExtraData?.[bsId]) {
          var bsObj = {
              [this.BS_NAME]: String(bsName),
              [this.BS_CLASS]: String(bsClass)
          };
          this.servicesExtraData[bsId] = bsObj;
      }
  },

  addToAlertServicesMap: function(alertId, serviceId, ciId) {
      if (!this.alertServicesMap?.[alertId]) {
          this.alertServicesMap[alertId] = {};
      }
      if (!this.alertServicesMap?.[alertId]?.[serviceId]) {
          this.alertServicesMap[alertId][serviceId] = new Set();
      }
      this.alertServicesMap[alertId][serviceId].add(ciId);
  },

  addToAlertTagsMap: function(alertId, alertTag, value) {
      value = value ?? "";
      if (!this.alertTagsMap?.[alertId]) {
          this.alertTagsMap[alertId] = {};
      }
      this.alertTagsMap[alertId][alertTag] = value;
  },


  type: 'EvtMgmtAlertCrawlerUtils'
};

Sys ID

82506e7707f62510b34ce06b0fd30081

Offical Documentation

Official Docs: