Name

global.EvtMgmtCalculateImpactedCis

Description

No description available

Script

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

  type: 'EvtMgmtCalculateImpactedCis',

  initialize: function() {
      // Classes
      this.impactManager = new SNC.ImpactManager();
      this.batchUtil = new SNC.BatchCommandsUtil();
      this.evtMgmtImpactedServiceGroups = new EvtMgmtImpactedServiceGroups();
      this.evtMgmtCommons = new EvtMgmtCommons();
      this.arrayUtil = new ArrayUtil();

      // Constants
      this.emImpactGraphModel = {
          TABLE: 'em_impacted_graph',
          PARENT_ID: 'parent_id',
          CHILD_ID: 'child_id',
          BUSINESS_SERVICE: 'business_service'
      };
      this.emImpactedCiModel = {
          TABLE: 'em_impacted_ci',
          ALERT_CI: 'alert_ci',
          IMPACTED_CI: 'impacted_ci'
      };
  },

  init: function() {
      // Properties
      this.isImpactedCisEnabled = gs.getProperty('evt_mgmt.impacted_cis.enable', 'true').toString() == 'true';
      this.isConnectedServicesEnabled = gs.getProperty('evt_mgmt.impacted_cis.enable_connected_services', 'true').toString() == 'true';
      this.maxCisLimit = gs.getProperty('evt_mgmt.impacted_cis.max_cis_limit', 10000);
      this.maxLevelsLimit = gs.getProperty('evt_mgmt.impacted_cis.max_levels', 10);
      this.bypassLimitsForServices = gs.getProperty('evt_mgmt.impacted_cis.bypass_limits_for_services', 'true').toString() == 'true';

      this.reset();
  },

  reset: function() {
      // Variables
      this.cisCount = 0;
      this.levelsCount = 0;
      this.impactedCis = {};
      this.impactedServices = {};
  },

  /**
   * Main function
   * Finds impacted CIs (cis, services and service groups) from a single given alert GlideRecord on a specific CI
   * @param alertGr - GlideRecord of an alert on a CI for which impact calculation is wanted
   */
  calculate: function(alertGr) {
      if (!(alertGr instanceof GlideRecord) || alertGr.getTableName() != 'em_alert') {
          this.evtMgmtCommons.addDebugLogNoPrefix(gs.getMessage("{0} script include: Parameter passed to calculate() must be GlideRecord from em_alert table", [this.type]));
          return;
      }
  	this.evtMgmtCommons.addDebugLogNoPrefix("****** Impacted CIs calculation starting... ************");

      // Init
      this.init();

      // Check impacted CIs property is enabled
      if (!this.isImpactedCisEnabled) {
          this.evtMgmtCommons.addDebugLogNoPrefix(gs.getMessage("{0} script include: Impact calculation was called although it is disabled", [this.type]));
          return;
      }

      // Add the child itself to impactd CIs
      this.alertCiSysId = alertGr.getValue('cmdb_ci');
      this.addToImpactedCis(this.alertCiSysId);

      // Clear existing records for the alert ci
      this.clearExistingRecords();

      // Run impact calculation
      this.calculateImpactedCis();
  },

  /**
   * Finds all impacted parents and services for the child of the alert
   */
  calculateImpactedCis: function() {
  	gs.log("Starting calculateImpactedCis");
      var children = {};
      var services = {
          current: '',
          next: {}
      };

      // Start from looking for the parents of the first CI
      children[this.alertCiSysId] = '';

      do {
          if (this.bypassLimitsForServices || !this.hasReachedMaxLevelsLimit()) {
              var parents = {};
  			this.levelsCount++;
  			this.evtMgmtCommons.addDebugLogNoPrefix("level=" + this.levelsCount);
              for (var child in children) {
                  var parentsGr = this.getParentsGr(child, services);
                  this.extractParents(parents, parentsGr, services);
              }
              children = this.getChildren(parents, services);
          }
          else {
              children = {};
          }
      }
      while (this.getLength(children));

      this.saveAndReset();
  },

  extractParents: function(parents, parentsGr, services) {
      while (parentsGr.next()) {
          if (!this.bypassLimitsForServices && this.hasReachedMaxCisLimit()) {
              // If limits have been reached then save stored data and force exit
              this.saveAndReset();
              return {};
          }
          var parent = parentsGr.getValue(this.emImpactGraphModel.PARENT_ID);
          var bs = parentsGr.getValue(this.emImpactGraphModel.BUSINESS_SERVICE);

          if (!services.current) {
              services.current = bs;
          }

          if (parent == bs) {
              // Add the service to the impacted SERVICES object
              this.addToImpactedServices(bs);
              // Continue traveling up the tree only if connected services is enabled, even if a limit was reached
              if (this.isConnectedServicesEnabled) {
                  if (!this.isLimitReached() || (this.isLimitReached() && this.bypassLimitsForServices)) {
                      if (!services.next.hasOwnProperty(bs)) {
                          services.next[bs] = {};
                      }
                      services.next[bs][bs] = {};
                  }
              }
          }
          else if (services.current != bs) {
              if (!services.next.hasOwnProperty(bs)) {
                  services.next[bs] = {};
              }
              services.next[bs][parent] = '';
          } else {
              // Keep adding impacted CIs only if the limits weren't reached
              if (!this.isLimitReached()) {
                  // Add the CI to the impacted CIS object
                  this.addToImpactedCis(parent);
              }
              parents[parent] = '';
          }
      }
  },

  getChildren: function(parents, services) {
      if (!this.bypassLimitsForServices && this.hasReachedMaxCisLimit()) {
          return {};
      }
      // Return parents as the children
      else if(parents && this.getLength(parents)) {
          return parents;
      }
      else {
          // Save to DB and reset objects
          this.saveAndReset();

          // Get children from the next service
          if (this.getLength(services.next)) {
              services.current = Object.keys(services.next).shift(); // Get the first BS in object and make it current
              var children = services.next[services.current]; // Get the children of that BS
              for (var child in children) {
                  if (child != services.current) {
                      this.addToImpactedCis(child);
                  }
              }
              delete(services.next[services.current]); // Remove the BS as it is no longer needed in next object
              return children; // Return the children
          } else {
              return {};
          }
      }
  },

  /**
   * Increases the levels counter by 1 and returns GlideRecords of the parent of the given CI
   * @param ci
   * @param [bs]
   * @returns {*}
   */
  getParentsGr: function(ci, services) {
      if (!services.current || (ci == services.current && this.isConnectedServicesEnabled)) {
          services.current = '';
          return this.impactManager.getParentsGrByCi(ci);
      } else {
          return this.impactManager.getParentsGrByCisAndBs(ci, services.current);
      }
  },

  /**
   * Adds the given ciSysId to the impacted cis object
   * @param ciSysId
   * @returns {*}
   */
  addToImpactedCis: function(ciSysId) {
      this.addImpact(this.impactedCis, ciSysId);
  },

  /**
   * Adds the given ciSysId to the impacted services object
   * @param ciSysId
   * @returns {*}
   */
  addToImpactedServices: function(ciSysId) {
      this.addImpact(this.impactedServices, ciSysId);
  },

  /**
   * If ciSysId doesn't exist in impactedObject: Adds the given ciSysId to the given impactedObject and returns ciSysId in an array
   * Else: return an empty array
   * @param impactedObject
   * @param ciSysId
   * @returns {*}
   */
  addImpact: function(impactedObject, ciSysId) {
      if (!impactedObject.hasOwnProperty(ciSysId)) {
          this.cisCount++;
          impactedObject[ciSysId] = '';
      }
  },

  /**
   * Saves the impacted CIs and clears the objects
   */
  saveAndReset: function() {
      this.saveResults();
      this.reset();
  },

  /**
   * Saves the impacted CIs in the DB
   */
  saveResults: function() {
      // Prepare results
  	this.evtMgmtCommons.addDebugLogNoPrefix("Starting saveResults()");
      var impactedCis = Object.keys(this.impactedCis); // CIs
  	this.evtMgmtCommons.addDebugLogNoPrefix("After 1st keys");
      var impactedServices = Object.keys(this.impactedServices); // Services
  	this.evtMgmtCommons.addDebugLogNoPrefix("After 2nd keys");
      var impactedServiceGroups = this.evtMgmtImpactedServiceGroups.getImpactedServiceGroups(impactedServices); // Service groups

      var allImpactedCis = this.arrayUtil.union(impactedCis, impactedServices, impactedServiceGroups);

      // Save results
      this.insertToDb(allImpactedCis);
  },

  insertToDb: function(ciArray) {
  	gs.log("Starting insertToDb()");
      // prepare JSON array for bulk insert
      var jsonArr = [];
      for (var i = 0; i < ciArray.length; i++) {
          this.addCiToImpactJson(ciArray[i], jsonArr);
      }
      // now - insert the new records
      var str = JSON.stringify(jsonArr);
      var domain = this.evtMgmtCommons.getCurrentDomainID();
      this.batchUtil.batchInsertMultiple(str, this.emImpactedCiModel.TABLE, domain);
  	this.evtMgmtCommons.addDebugLogNoPrefix("insertToDb(). After batchInsertMultiple");
  },

  /**
   * Removes all records of the alert ci from the em_impacted_ci table
   */
  clearExistingRecords: function() {
      // Delete old records for this alert CI
      var gr = new GlideRecord(this.emImpactedCiModel.TABLE);
      gr.addQuery(this.emImpactedCiModel.ALERT_CI, this.alertCiSysId);
      gr.query();
      gr.deleteMultiple();
  },

  addCiToImpactJson: function(impactedCi, jsonArr) {
      var jsonObj = {};
      if (this.alertCiSysId && impactedCi && (impactedCi != "null")) {
          jsonObj[this.emImpactedCiModel.ALERT_CI] = this.alertCiSysId;
          jsonObj[this.emImpactedCiModel.IMPACTED_CI] = impactedCi;
          jsonArr.push(jsonObj);
      }
  },

  /**
   * Returns a count of the number of elements in the given object
   * @param object
   * @returns {number}
   */
  getLength: function(object) {
      return Object.keys(object).length;
  },

  /**
   * Returns true if one of the limits is reached or about to be reached
   * @returns {boolean}
   */
  isLimitReached: function() {
      return this.hasReachedMaxLevelsLimit() || this.hasReachedMaxCisLimit();
  },

  /**
   * Returns true if level counter with the next level is going to be larger than the max levels
   * @returns {boolean}
   */
  hasReachedMaxLevelsLimit: function() {
      var maxLevelsLimit = parseInt(this.maxLevelsLimit, 10);
      if (this.levelsCount > maxLevelsLimit) {
          this.evtMgmtCommons.addDebugLogNoPrefix(gs.getMessage("{0} script include: Reached the max levels of {1}", [this.type, maxLevelsLimit]));
          return true;
      }
      return false;
  },

  /**
   * Returns true if the cis counter equals or larger than the max cis limit
   * @returns {boolean}
   */
  hasReachedMaxCisLimit: function() {
      var maxCisLimit = parseInt(this.maxCisLimit, 10);
      if (this.cisCount >= maxCisLimit) {
          this.evtMgmtCommons.addDebugLogNoPrefix(gs.getMessage("{0} script include: Reached the max CIs of {1}", [this.type, maxCisLimit]));
          return true;
      }
      return false;
  },

};

Sys ID

3953d56b6793230047c6e44d2685ef66

Offical Documentation

Official Docs: