Name

global.ProcessGroupService

Description

Handling all the process clustering logic taken from ML platform

Script

var ProcessGroupService = Class.create();

ProcessGroupService.prototype = {
  // class constants
  INSTANCE_ANALYSIS: 'Instance Analysis', // group name suggestion identifier/source
  MAX_CLUSTER_MEMBERS_PROPERTY: 'process.clustering.max_cluster_members', //system property name for max number of supported cluster members
  MAX_CLUSTER_PAGINATION_PROPERTY: 'process.clustering.max_clusters_in_page',
  PROCESS_SA_HASH_CLUSTER_SEQ_IDENTIFIER: 'process_grouping_last_update_sequence',
  PROCESS_SA_HASH_MEMBER_SEQ_IDENTIFIER: 'process_grouping_last_member_update_sequence',
  MAX_CLUSTER_MEMBERS_DEFAULT: 100, // defualt value for max number of supported cluster members per page
  MAX_CLUSTER_PAGES: 10000, // max number of cluster pages to retrieve (infinite loop protection)   
  SUGGESTED_CLASS_NAME_PREFIX: 'u_cmdb_ci_',
  MAX_CLUSTERS_PAGINATION_LIMIT_DEFAULT: 100, // default value for max number of clusters to process per result retrieval iteration/page
  INITIAL_SEQUENCE_DEFAULT: 1,
  MIN_CLUSTER_MEMBERS_DEFAULT: 2, // minimum cluster members count. Cluster below this count will be excluded from AFP group creation
  MIN_CLUSTER_MEMBERS_PROPERTY: 'process.clustering.min_cluster_members_in_group',
  SA_HASH: 'sa_hash',
  SA_HASH_NAME: 'name',
  SA_HASH_VALUE: 'hash',
  MAX_KEY_WORDS_IN_GROUP_NAME: 3, // max top frequency key words from cluster concept field to construct process group name

  initialize: function() {
      var bulkChunk = GlideProperties.getInt("afp.bucket_map_batch_size", 500);
      this.bulkSize = bulkChunk;

      this.batchUtil = new SNC.BatchCommandsUtil();
      this.maxProcessSize = GlideProperties.getInt("process.clustering.max.regex.group.size", 100);
  },

  type: 'ProcessGroupService',

  /**
   * Create suggested CMDB class based on given cluster group name 
   */
  getSuggestedCMDBClass: function(suggestedName) {

      return this.SUGGESTED_CLASS_NAME_PREFIX + suggestedName;
  },

  /**
   * Fetch MLSolution clustering results over cmdb_running_process table
   */
  clusterProcessGroups: function() {

      var solution_name = this.createOrUpdateSolution();
      gs.debug('ProcessGroupService: solution_name=' + solution_name);
      if (solution_name == 'undefined' || solution_name == "") {
          gs.debug('ProcessGroupService.clusterProcessGroups: solution ceation failed. solution_name=' + solution_name);
          return;
      }
      try {
          var solutioAPI = sn_ml.ClusteringSolutionStore.get(solution_name);
          gs.debug("ProcessGroupService: Found Active solution name=" + solutioAPI.getName());
          this.solutionVerAPI = solutioAPI.getActiveVersion();
          gs.debug('ProcessGroupService: solutionVersion=' + this.solutionVerAPI);

      } catch (e) {
          gs.warn("ProcessGroupService: failed to get solution from ClusteringSolutionStore, exception=" + e);
          return;
      }

      gs.debug("ProcessGroupService: solution status json=" +
          this.solutionVerAPI.getStatus() +
          ", version=" +
          this.solutionVerAPI.getVersionNumber());

      this.groupSVC = new SNC.ProcessGroupJS();
      if (this.groupSVC == null || this.groupSVC == "undefined") {
          gs.error("SNC.ProcessGroupJS failed, aborting process grouping operation");
          gs.debug("SNC.ProcessGroupJS failed, aborting process grouping operation");
          return;
      }
      this.createGroups();
      //this.deleteGroupsWithNoMembers();
  },

  /**
   * Create solution in case running for the first time
   * otherwise update the solution with the next batch of records
   */
  createOrUpdateSolution: function() {
      var util = new ApplicationsFingerprintUpdateUtil();
      return util.submitUpdate();
  },

  /**
   * Create processes group records in cmdb_process_groups table based on clustering results
   */
  createGroups: function() {

      var last_update = this.getLastSavedSequence();
      //var last_member_update = this.getLastSavedMemberSequence();
      var last_member_update = 0;
      if (last_update > this.INITIAL_SEQUENCE_DEFAULT) last_update++;
      gs.debug("ProcessGroupService: last sequence from hash=" + last_update);
      this.loadSysProperties();

      var cluster_info_options = {};
      cluster_info_options.limit = Number(this.max_clusters_in_page); // max number of clusters per retrieval iteration
      cluster_info_options.sequenceSince = last_update++;
      cluster_info_options.outputFieldNames = ['cluster_id', 'group_by', 'cluster_size', 'cluster_concept', 'insert_sequence'];

      // iterate over all clusters pages
      var cluster_page_counter = 0;
      while (last_update > 0 && cluster_page_counter++ < this.MAX_CLUSTER_PAGES) {
          gs.debug("ProcessGroupService: cluster_page_counter=" + cluster_page_counter);
          gs.debug("ProcessGroupService: incremented sequence from last_update=" + last_update);
          last_update = this.retrieveClusters(cluster_info_options, last_member_update);
          cluster_info_options.sequenceSince = last_update + 1;
      }
  },

  loadSysProperties: function() {
      this.max_clusters_in_page = GlideProperties.get(this.MAX_CLUSTER_PAGINATION_PROPERTY,
          this.MAX_CLUSTERS_PAGINATION_LIMIT_DEFAULT);
      gs.debug("ProcessGroupService sys_property: max_clusters_in_page=" + this.max_clusters_in_page);

      this.max_cluster_members_sample = GlideProperties.get(this.MAX_CLUSTER_MEMBERS_PROPERTY,
          this.MAX_CLUSTER_MEMBERS_DEFAULT);
      gs.debug("ProcessGroupService sys_property: max_cluster_members=" + this.max_cluster_members_sample);

      this.min_cluster_members_in_group = GlideProperties.get(this.MIN_CLUSTER_MEMBERS_PROPERTY,
          this.MIN_CLUSTER_MEMBERS_DEFAULT);
      gs.debug("ProcessGroupService sys_property: min_cluster_members_in_group=" + this.min_cluster_members_in_group);
  },

  /**
   * retrieve and analyze clusters for given page
   */
  retrieveClusters: function(cluster_info_options, last_member_update) {

      //cluster_info_options.clusterId = 1; 
      gs.debug("ProcessGroupService: calling getClusterInfo with options=" + JSON.stringify(cluster_info_options, null, 2));
      // Retrieve clusters according to the provided sequence number and options
      var json_res = JSON.parse(this.solutionVerAPI.getClusterInfo(cluster_info_options));
      //gs.debug("ProcessGroupService: getClusterInfo json=" + JSON.stringify(json_res, null, 2));
      if (json_res == null || json_res == '[]' || json_res == 'undefined' || json_res == '') {
          gs.debug("ProcessGroupService.retrieveClusters: getClusterInfo returned empty result, breaking pagination. json result=" + JSON.stringify(json_res, null, 2));
          return 0;
      }
      var last_update = 0;
      // iterate over clustered process groups 
      gs.debug("ProcessGroupService: processing clusters in page. number of clusters=" + Object.keys(json_res).length);
      // iterate over process members in currrent batch and under the given cluster
      var counter = 0;
      var processCounter = 0;

      var clusterJsonArr = [];
      var processJsonArr = [];

      for (var key in json_res) {
          gs.debug("ProcessGroupService.retrieveClusters: processing new cluster_id=" + json_res[key].cluster_id);
          gs.debug("ProcessGroupService.retrieveClusters: processing new cluster. group_by=" + json_res[key].group_by);
          gs.debug("ProcessGroupService.retrieveClusters: json_res[key].cluster_size=" + json_res[key].cluster_size);
          gs.debug("ProcessGroupService.retrieveClusters: json_res[key].cluster_concept=" + json_res[key].cluster_concept);
          last_update = Number(json_res[key].insert_sequence);

          // exclude clusters that are smaller than minimum and continue to next cluster in page
          if (Number(json_res[key].cluster_size) < this.min_cluster_members_in_group) {
              gs.warn("ProcessGroupService: current cluster=" +
                  json_res[key].group_by +
                  " has no minimum members count, cluster is excluded from AFP creation. cluster_size=" +
                  json_res[key].cluster_size +
                  " min_cluster_members_in_group=" +
                  this.min_cluster_members_in_group);
              continue;
          }
          var cluster_params = {};
          var clean_group_name = this.cleanGroupName(json_res, key);
          gs.debug("ProcessGroupService: clean_group_name=" + clean_group_name);
          var cluster_record = this.createClusterRecord(json_res[key].cluster_id, clean_group_name);
          cluster_params.json_res_in_key = json_res[key];
          cluster_params.clean_group_name = clean_group_name;
          // retrieve and analyze cluster members sample
          var members_rec = this.retrieveMembersSample(json_res, key);
          //gs.debug(">>>>>>>>>ProcessGroupService: cluster members json=" + JSON.stringify(members_rec.json_member_res, null, 2));

          // create cmdb_process_groups only after cmdb_process_to_group records have been created
          // regex extraction and accuracy calculation need to accure post group and mapping creation and requires 2nd upgrade 
          if (members_rec.json_member_res == null ||
              members_rec.members_count == 0 ||
              members_rec.json_member_res == '[]' ||
              members_rec.json_member_res == '') {

              gs.warn("ProcessGroupService: getClusterAssignments returned empty/invalid results, continue to next cluster");
              continue;
          }

          // record does not exist and will be inserted for the first time
          if (cluster_record.insert_cluster) {
              this.insertClusterRecord(cluster_record, members_rec.process_name, json_res[key].cluster_size);
          }

          processCounter = this.createGroup2ProcMappingRecord(cluster_record.cluster_sysid, members_rec.json_member_res, processJsonArr, processCounter);

          // update the next sequence for the next cluster iteration
          gs.debug("ProcessGroupService: last_update from current clusters page=" + last_update);
          var clusterJSON = this.updateClusterRecord(cluster_record, cluster_params);
          if (Object.keys(clusterJSON).length > 0) {
              clusterJsonArr.push(clusterJSON);
              counter++;
          }

          if (counter >= this.bulkSize) {
              this.batchUtil.batchUpdateMultiple(JSON.stringify(clusterJsonArr), 'cmdb_process_groups', '');
              counter = 0;
              clusterJsonArr = [];
          }

      }
      if (counter > 0) {
          this.batchUtil.batchUpdateMultiple(JSON.stringify(clusterJsonArr), 'cmdb_process_groups', '');
      }
      if (processCounter > 0) {
          this.batchUtil.batchInsertMultiple(JSON.stringify(processJsonArr), 'cmdb_process_to_group', '');
      }

      this.setLastSavedSequence(last_update);
      return last_update;
  },

  cleanGroupName: function(json_res, key) {
      return (json_res[key].group_by.replace(/\W+/g, "_"));
  },

  /**
   * retrieve and analyze clusters members sample for the first page up to max defined members results
   * this method should be called once for every cluster
   */
  retrieveMembersSample: function(json_res, key) {
      // build getClusterAssignments options
      var options = {};
      // set options to retrieve only members that are part of current cluster using its id and group_by values
      options.clusterId = json_res[key].cluster_id;
      options.groupBy = json_res[key].group_by;
      //options.top_n_per_cluster = Number(max_cluster_members); // this is relevant for k-mince algorithms only: request solution to return this max value for results per cluster
      // get getClusterAssignments response for the first page up to max defined number of members
      options.limit = Number(this.max_cluster_members_sample); // set limit for max number of process members to retreive as sample
      options.sequenceSince = this.INITIAL_SEQUENCE_DEFAULT; // intial sqeuence
      options.outputFieldNames = ['cluster_id', 'group_by', 'insert_sequence', 'rec_sys_id'];

      // for each cluster: retrieve cluster members from solution API
      // configure optional parameters for getClusterAssignments
      gs.debug("ProcessGroupService: calling getClusterAssignments with options=" + JSON.stringify(options, null, 2));
      var json_member_res = JSON.parse(this.solutionVerAPI.getClusterAssignments(options));
      var members_count = Object.keys(json_member_res).length;
      gs.debug("ProcessGroupService: members_count from json=" + members_count);

      // get process name from the first member record
      var process_name = json_member_res[0].group_by;
      gs.debug("get process name from the first member record. process_name=" + process_name);
      if (process_name == null || process_name == 'undefined' || process_name == '') {
          // process is invalid - continue to the next process
          gs.error("ProcessGroupService: process name in cluster members for cluster=" +
              options.groupBy +
              "id=" +
              options.clusterId +
              " is invalid - skipping to the next cluster processes . process_name=" +
              process_name);
          json_member_res = null;
      }
      return {
          json_member_res: json_member_res,
          process_name: process_name,
          members_count: members_count
      };
  },

  deleteClusterRecord: function(cluster_record) {
      var cluster_gr = new GlideRecord("cmdb_process_groups");
      //cluster_gr.initialize();
      cluster_gr.addQuery("cluster_id", cluster_record.uniq_cluster_id);
      cluster_gr.query();
      if (cluster_gr.next()) {
          if (cluster_gr.deleteRecord()) {
              gs.log("Partial recored for: " + cluster_record.uniq_cluster_id + " delted successfully from cmdb_process_groups");
          } else {
              gs.warn("Failed to delete partial record=" + cluster_record.uniq_cluster_id + " from cmdb_process_groups");
          }
      } else {
          gs.warn("Failed to delete partial record=" + cluster_record.uniq_cluster_id + " from cmdb_process_groups. record does not exist");
      }
  },
  /**
   * Update existing cluster record into cmdb_process_groups
   * @param {*} cluster_record 
   * @param {*} params 
   */
  updateClusterRecord: function(cluster_record, params) {
      //gs.debug("ProcessGroupService: updateClusterRecord cluster_record=" + JSON.stringify(cluster_record, null, 2));
      // position current glide record to curret group position for the upcoming update (after all members are inserted)
      var jsonObj = {};
      var cluster_gr = new GlideRecord("cmdb_process_groups");
      cluster_gr.initialize();
      cluster_gr.addQuery("cluster_id", cluster_record.uniq_cluster_id);
      cluster_gr.query();
      if (cluster_gr.next()) {
          jsonObj['sys_id'] = cluster_gr.getUniqueValue();
          gs.debug("ProcessGroupService.updateClusterRecord: found existing cluster, now updaing. cluster_id=" + cluster_record.uniq_cluster_id);
          jsonObj['cluster_id'] = cluster_record.uniq_cluster_id;
          try {
              var cluster_regex;
              var regex_accuracy;
              
  			var paramsForCluster = this.getclusterProcessParameters(cluster_record.cluster_sysid);
              if (paramsForCluster.length > 1) {
                  cluster_regex = this.processUtils.createRegex(paramsForCluster);
                  regex_accuracy = this.groupSVC.getGroupAccuracy(cluster_record.cluster_sysid, cluster_regex);
              }
          } catch (e) {
              gs.error("ProcessGroupService: regex extraction and accuracy calulation failed, regex related fields will not get populated. exception=" + e);
          }
          var extended_group_name = this.removeBlacklistedTags(params.json_res_in_key.cluster_concept.replace(/\W+/g, "_"));
          extended_group_name = this.cleanTags(params.clean_group_name, extended_group_name);

          gs.debug("ProcessGroupService: extended_group_name after cleaning=" + extended_group_name);

          var potential_server = this.groupSVC.calculateIsPotentialServer(cluster_record.cluster_sysid);
          gs.debug("ProcessGroupService: calculateIsPotentialServer=" + potential_server);
          gs.debug("ProcessGroupService: extractRegexForGroup=" + cluster_regex);
          gs.debug("ProcessGroupService: getGroupAccuracy=" + regex_accuracy);

          // remove process name from extended group name to build the new group name with no duplicates
          var re = new RegExp(params.clean_group_name, "g");
          var filtered_extended_group_name = extended_group_name.replace(re, "");
          var name_keys = filtered_extended_group_name.split("_");
          gs.debug("ProcessGroupService: before clean name_keys=" + name_keys);
          // remove process name from suggested group name keys to prevent duplicates
          name_keys = this.removeDuplicates(params.clean_group_name, name_keys);
          // remove duplicate key words
          var filtered_keys = name_keys.filter(function(item, index) {
              if (name_keys.indexOf(item) == index)
                  return item;
          });
          gs.debug("ProcessGroupService: clean filtered_keys=" + filtered_keys);
          gs.debug("ProcessGroupService: name_keys.length=" + filtered_keys.length);
          if (filtered_keys.length > 0)
              jsonObj['suggested_name'] = params.clean_group_name + "_" + filtered_keys.slice(0, this.MAX_KEY_WORDS_IN_GROUP_NAME).join("_");
          else
              jsonObj['suggested_name'] = params.clean_group_name;
          gs.debug("ProcessGroupService: cluster_gr.suggested_name=" + jsonObj['suggested_name']);
          jsonObj['suggested_by'] = this.INSTANCE_ANALYSIS;
          //based on suggested name - construct cmdb class name
          var suggested_cmdb_class = this.getSuggestedCMDBClass(jsonObj['suggested_name']);
          jsonObj['suggested_cmdb_class'] = suggested_cmdb_class;
          gs.debug("ProcessGroupService: suggested_class=" + jsonObj['suggested_cmdb_class']);
          jsonObj['pattern_name'] = jsonObj['suggested_name'];
          gs.debug("ProcessGroupService: pattern_name=" + jsonObj['pattern_name']);
          jsonObj['suggested_rule'] = cluster_regex;
          if (regex_accuracy) {
              jsonObj['regex_accuracy'] = regex_accuracy + '';
          }
          jsonObj['process_count'] = params.json_res_in_key.cluster_size;
          gs.debug("ProcessGroupService: cluster size to be used as AFP group size=" + cluster_gr.process_count);
          jsonObj['potential_server'] = potential_server;
          jsonObj['extended_group_name'] = extended_group_name;
          jsonObj['main_process'] = params.process_name;
          return jsonObj;
      } else {
          gs.error("ProcessGroupService.updateClusterRecord: error positioning glide record to curret process group");
      }
  },

  getclusterProcessParameters: function(clusterID) {
      var params = [];
      var processGroupGR = new GlideRecord('cmdb_process_to_group');
      processGroupGR.addQuery('cluster_group', clusterID);
      processGroupGR.setLimit(10 * this.maxProcessSize);
      processGroupGR.query();
      while (processGroupGR.next()) {
          var param = processGroupGR.process.parameters;
          if (param) {
              params.push(param);
          }
      }
      return params;
  },

  /**
   * Remove duplicate process name key words from the the cluster concept key words 
   * @param {*} proc_name process name
   * @param {*} name_keys keys array
   */
  removeDuplicates: function(proc_name, name_keys) {
      gs.debug("proc_name=" + proc_name);
      gs.debug("name_keys=" + name_keys);
      var filtered = name_keys.filter(function(element) {
          return element != proc_name;
      });

      return filtered;
  },

  insertClusterRecord: function(cluster_record, process_name, cluster_size) {
      // make a differnt cluster insert incase cluster is a new record - this should only accure once per cluster 
      // the 3 attributes below are needed to be set before extracting regex and calculating its accuracy
      var cluster_gr = new GlideRecord("cmdb_process_groups");
      cluster_gr.initialize();
      gs.debug("ProcessGroupService: inserting new cluster record. cluster_record.uniq_cluster_id=" + cluster_record.uniq_cluster_id);
      cluster_gr.cluster_id = cluster_record.uniq_cluster_id;
      cluster_gr.main_process = process_name;
      cluster_gr.process_count = cluster_size;
      cluster_record.cluster_sysid = cluster_gr.insert();
      gs.debug("sys_id after inserting new cluster=" + cluster_record.cluster_sysid)

      cluster_record.insert_cluster = false;
  },
  /**
   * Create new glide record to prepare for insert or update in cmdb_process_groups for current cluster
   * */
  createClusterRecord: function(input_cluster_id, clean_group_name) {

      // combine group_by with cluster sequential id to create unique cluster id
      var uniq_cluster_id = this.buildUniqClusterId(clean_group_name, input_cluster_id);
      gs.debug("ProcessGroupService: unique_cluster_id=" + uniq_cluster_id);
      // if cluster does not exist perfrom first insert in order to obtain sys_id for "group to process" mapping
      var insert_cluster = false;

      var proc_gr = this.isClusterExist(uniq_cluster_id);
      if (proc_gr != null) {
          //cluster exist - insert_cluster flag will remain flase indicating that cluster will need to only be updated 
          var cluster_sysid = proc_gr.getValue("sys_id");
          gs.debug("ProcessGroupService: cluster=" + uniq_cluster_id + " exists - will only update. cluster_sysid=" + cluster_sysid);
          //var sys_id = existing_cluster_sysid;
          //sys_id = proc_gr.update();
      } else {
          insert_cluster = true;
          gs.debug("ProcessGroupService: cluster=" + uniq_cluster_id + " does not exists - will create new cluster record. cluster_sysid=" + cluster_sysid);
      }
      return {
          insert_cluster: insert_cluster,
          proc_gr: proc_gr,
          cluster_sysid: cluster_sysid,
          uniq_cluster_id: uniq_cluster_id
      };
  },

  isClusterExist: function(cluster_id) {
      var cluster_gr = new GlideRecord("cmdb_process_groups");
      cluster_gr.addQuery("cluster_id", cluster_id);
      cluster_gr.query();

      //var sysid = null;
      if (cluster_gr.next()) {
          gs.debug("ProcessGroupService: Cluster already exist, id=" + cluster_id);
          //sysid = cluster_gr.getValue("sys_id");
          return cluster_gr;
      }
      return null;
  },

  /**
   * Construct a uniq cluster id from cluster group name and numeric group id
   * seperated by '_'
   */
  buildUniqClusterId: function(group_by, cluster_id) {

      var cluster_id = group_by + "_" + cluster_id;
      gs.debug("ProcessGroupService: *** unique cluster_id=" + cluster_id);
      return cluster_id;
  },
  /**
   * Create mapping between cluster group and processes (aka cluster members)
   * mapping will be created in cmdb_process_to_group table
   */
  createGroup2ProcMappingRecord: function(clusterSysId, jsonMembers, processJsonArr, processCounter) {

      gs.debug("ProcessGroupService.createGroup:creating Group to Proc Mapping for cluster sys_id=" + clusterSysId);
      // iterate over process members in currrent batch and under the given cluster
      var process_in_page_counter = 0;
      for (var proc_member in jsonMembers) {
          var jsonObj = {};
          if (process_in_page_counter++ >= this.MAX_CLUSTER_MEMBERS_DEFAULT) {
              break;
          }
          var process_sysid = jsonMembers[proc_member].rec_sys_id;

          if (!this.isProcessExist(process_sysid)) {
              jsonObj['cluster_group'] = clusterSysId;
              jsonObj['process'] = process_sysid;
              if (Object.keys(jsonObj).length > 0) {
                  processJsonArr.push(jsonObj);
                  processCounter++;
              }
          }

          if (processCounter >= this.bulkSize) {
              this.batchUtil.batchInsertMultiple(JSON.stringify(processJsonArr), 'cmdb_process_to_group', '');
              processCounter = 0;
              processJsonArr = [];
          }
      }
      return processCounter;
  },

  /**
   * retrun true if process has reference in cmdb_ci_appl table otherwise false
   */
  isProcessDiscovered: function(proc_sysid) {
      // check if it contains in the cmdb_ci_appl
      var applGR = new GlideRecord("cmdb_ci_appl");
      applGR.addQuery("running_process", proc_sysid);
      applGR.query();
      if (applGR.next()) {
          return true;
      }
      return false;
  },

  isProcessExist: function(proc_sysid) {
      var proc_gr = new GlideRecord("cmdb_process_to_group");
      proc_gr.addQuery("process", proc_sysid);
      proc_gr.query();

      //var sysid = null;
      if (proc_gr.next()) {
          gs.debug("Process(cluster member) already exist id=" + proc_sysid);
          //sysid = proc_gr.getValue("sys_id");
          return true;
      }
      return false;
  },

  getSaHashGR: function(hash_name) {
      var saHash = new GlideRecord(this.SA_HASH);
      saHash.addQuery(this.SA_HASH_NAME, hash_name);
      saHash.query();
      return saHash;
  },

  getLastSavedSequence: function() {
      var saHash = this.getSaHashGR(this.PROCESS_SA_HASH_CLUSTER_SEQ_IDENTIFIER);
      var seq = "";
      if (saHash.next()) {
          seq = saHash.getValue(this.SA_HASH_VALUE);
          gs.debug("hash sequence=" + seq);
      } else {
          gs.debug("ProcessGroupService: Could not find " + this.PROCESS_SA_HASH_CLUSTER_SEQ_IDENTIFIER + " in sa_hash table");
      }
      if (seq == null || seq == "") {
          return this.INITIAL_SEQUENCE_DEFAULT;
      } else {
          return seq;
      }
  },

  cleanTags: function(group_by, extended_group_name) {
      extended_group_name = extended_group_name.replace(/__+/g, "");
      extended_group_name = group_by + "_" + extended_group_name;
      extended_group_name = extended_group_name.replace(/_+$/g, "");
      gs.debug("ProcessGroupService: extended_group_name after cleaning=" + extended_group_name);
      return extended_group_name;
  },

  removeBlacklistedTags: function(extended_group_name) {

      extended_group_name = extended_group_name.replace(/jar/g, "");
      extended_group_name = extended_group_name.replace(/svchost/g, "");
      extended_group_name = extended_group_name.replace(/java/g, "");
      extended_group_name = extended_group_name.replace(/grep/g, "");
      extended_group_name = extended_group_name.replace(/path/g, "");
      extended_group_name = extended_group_name.replace(/runtime/g, "");
      extended_group_name = extended_group_name.replace(/program/g, "");
      extended_group_name = extended_group_name.replace(/address/g, "");
      extended_group_name = extended_group_name.replace(/\./g, "");
      extended_group_name = extended_group_name.replace(/binary/g, "");
      extended_group_name = extended_group_name.replace(/uuid/g, "");
      extended_group_name = extended_group_name.replace(/print/g, "");
      extended_group_name = extended_group_name.replace(/root/g, "");
      extended_group_name = extended_group_name.replace(/init/g, "");
      extended_group_name = extended_group_name.replace(/home/g, "");
      extended_group_name = extended_group_name.replace(/temp/g, "");
      extended_group_name = extended_group_name.replace(/runc/g, "");
      extended_group_name = extended_group_name.replace(/serverdll/g, "");
      extended_group_name = extended_group_name.replace(/containerd/g, "");
      extended_group_name = extended_group_name.replace(/tmp/g, "");
      extended_group_name = extended_group_name.replace(/dir/g, "");
      extended_group_name = extended_group_name.replace(/ppid/g, "");
      extended_group_name = extended_group_name.replace(/pid/g, "");
      extended_group_name = extended_group_name.replace(/file/g, "");
      return extended_group_name;
  },

  setLastSavedSequence: function(lastUpdatedProcess) {
      if (lastUpdatedProcess == null || lastUpdatedProcess.trim().isEmpty()) {
          return;
      }
      var saHash = this.getSaHashGR(this.PROCESS_SA_HASH_CLUSTER_SEQ_IDENTIFIER);

      if (!saHash.next()) {
          saHash = new GlideRecord(this.SA_HASH);
          saHash.initialize();
          saHash.setValue(this.SA_HASH_NAME, this.PROCESS_SA_HASH_CLUSTER_SEQ_IDENTIFIER);
          saHash.setValue(this.SA_HASH_VALUE, lastUpdatedProcess);
          saHash.insert();

      } else {
          saHash.setValue(this.SA_HASH_VALUE, lastUpdatedProcess);
          saHash.update();
      }
  },

  getLastSavedMemberSequence: function() {
      var saHash = this.getSaHashGR(this.PROCESS_SA_HASH_MEMBER_SEQ_IDENTIFIER);
      var seq = "";
      if (saHash.next()) {
          seq = saHash.getValue(this.SA_HASH_VALUE);
          gs.debug("hash sequence=" + seq);
      } else {
          gs.debug("ProcessGroupService: Could not find " + this.PROCESS_SA_HASH_MEMBER_SEQ_IDENTIFIER + " in sa_hash table");
      }
      if (seq == null || seq == "") {
          return this.INITIAL_SEQUENCE_DEFAULT;
      } else {
          return seq;
      }
  },
  deleteGroupsWithNoMembers: function() {
      var gr = new GlideRecord("cmdb_process_groups");
      gr.addQuery("process_count", 0);
      gr.query();
      gs.debug("ProcessGroupService: Deleting " + gr.getRowCount() + " groups that had no members");
      gr.deleteMultiple();
  },
  setLastSavedMemberSequence: function(lastUpdatedProcess) {
      if (lastUpdatedProcess == null || lastUpdatedProcess.trim().isEmpty()) {
          return;
      }
      var saHash = this.getSaHashGR(this.PROCESS_SA_HASH_MEMBER_SEQ_IDENTIFIER);

      if (!saHash.next()) {
          saHash = new GlideRecord(this.SA_HASH);
          saHash.initialize();
          saHash.setValue(this.SA_HASH_NAME, this.PROCESS_SA_HASH_MEMBER_SEQ_IDENTIFIER);
          saHash.setValue(this.SA_HASH_VALUE, lastUpdatedProcess);
          saHash.insert();

      } else {
          saHash.setValue(this.SA_HASH_VALUE, lastUpdatedProcess);
          saHash.update();
      }
  }
};

Sys ID

bf7dc8d873aa40105ad2db37aef6a78e

Offical Documentation

Official Docs: