Name

sn_agent.MonitoringConfig

Description

Monitoring helper functions

Script

var MonitoringConfig = Class.create();

MonitoringConfig.prototype = {
  CI_PARAM_KEY: '{{.labels.params_ci_',
  CI_PARAMS_TO_CHECK_PLACE_HOLDER: 'CI_PARAMS_TO_CHECK_PLACE_HOLDER',

  ENTRYPOINT_METRIC_RESOURCE_PARAM: "entrypoint_resource",
  ENTRYPOINT_METRIC_RESOURCE_USE_LOCATION_PROPERTY: "sn_itmon.entrypoint.monitoring.use_location",
  ENTRYPOINT_METRIC_RESOURCE_USE_HOSTNAME_PROPERTY: "sn_itmon.entrypoint.monitoring.use_hostname",
  IS_PROXY_CHECK: "is_proxy_check",
  PROXY_CHECK_CI_PARAM: ['ep_service_name', 'target_ip'],

  FORCE_SYNC_TIMESTAMP: "2000-01-01 00:00:00",
  currentPolicy: {},

  initialize: function() {
      this.agenstIdList = [];
      this.chunk_size = 100;
      this.forceMidConfigSync = false;
      this.domainInfo = new DomainInfo();
      this.policyStateUtil = new PolicyStateUtil();
      this.policyUpdateDomainSeparation = new PolicyUpdateDomainSeparation();
  },

  /*
   * split clients into those that should be newClients, full update, or update timestamp and status only.
   */
  classifyClientsAndUpdate: function(clients, mid, newClients, updateTimestamp, updateUpSince) {
      var clientsMap = {};
      var reCalcPolicyClients = false;
      // Build map of client names in the payload we got
      var i = 0;
      for (i = 0; i < clients.length; i++) {
          clientsMap[clients[i].agent_id] = clients[i];
          clients[i].name = "Agent_" + clients[i].name;
          this.agenstIdList.push(clients[i].agent_id);
      }

      var chunked = MonitoringUtils.chunkArray(this.agenstIdList, this.chunk_size);
      gs.debug('classifyClientsAndUpdate - number of chunks: ' + chunked.length);
      var existingAgents = {};
      var agentsInNewUpState = [];
      var numOfIpUpdates = 0;
      var timestamp = new GlideDateTime().getNumericValue();
      for (var chunk_index = 0; chunk_index < chunked.length; chunk_index++) {

          var chunkOfAgentIDs = chunked[chunk_index];
          var gr = new GlideRecord('sn_agent_ci_extended_info');
          gr.addQuery('agent_id', 'IN', chunkOfAgentIDs);
          gr.query();

          while (gr.next()) {
              //in case mid was changed, resend all configuration to the new mid
              if (gr.mid != mid) {
                  this.forceMidConfigSync = true;
              }
              //in case agent changed status from down to up, recalc policyClients, to include it in the relevant polices
              if (gr.status == 2) {
                  reCalcPolicyClients = true;
              }
              var agentId = '' + gr.agent_id;
              var agentInfoToUpdate = {};
              var agentKeepAlive = clientsMap[gr.agent_id];
              if (clientsMap[agentId]) {
                  /*Valdidation for backward compitability*/
                  if (agentKeepAlive.hasOwnProperty('version')) {
                      var currentAgentVersion = agentKeepAlive.version;
                      if (gr.agent_version != currentAgentVersion)
                          agentInfoToUpdate['agent_version'] = currentAgentVersion;
                  }

                  if (gr.ip_address != agentKeepAlive.address)
                      agentInfoToUpdate['ip_address'] = this.getPreferredIPAddress(agentKeepAlive);

                  if (gr.name != agentKeepAlive.name)
                      agentInfoToUpdate['name'] = agentKeepAlive.name;

                  // Check for param in payload to handle legacy MIDs
                  if (agentKeepAlive.hasOwnProperty('use_cloud_services')) {
                      var currentUseCloudServices = agentKeepAlive.use_cloud_services;
                      if (global.JSUtil.getBooleanValue(gr, "use_cloud_services") != currentUseCloudServices)
                          agentInfoToUpdate['use_cloud_services'] = currentUseCloudServices;
                  } else {
                      // If the agent is already marked as cloud services, but payload doesn't send the field
                      // it means it has now connected to an older MID, and we should mark it as non-cloud-services.
                      if (global.JSUtil.getBooleanValue(gr, "use_cloud_services") != false)
                          agentInfoToUpdate['use_cloud_services'] = false;
                  }

                  // update name,ip,version and mid
                  if (Object.keys(agentInfoToUpdate).length > 0)
                      this.updateAgentInfo(gr, agentInfoToUpdate);

                  var infoSysId = '' + gr.sys_id;
                  if (gr.status != '0') {
                      updateUpSince.push(infoSysId); // update upSince for agents that came alive
                      agentsInNewUpState.push(agentId); // agents that were not up and now are back up.
                      // handle a case where status changes from upgrad->up
                      if (gr.status == AgentKeepAlive.AGENT_UPGRADING) {
                          var acc = new AgentUpgradeUtil();
                          acc.agentUpgradeToUp(agentId, gr.is_windows);
                      }
                  } else {
                      updateTimestamp.push(infoSysId); //standart keepAlive
                  }
                  existingAgents[agentId] = true;

              } else
                  gs.debug('Agent ' + agentId + ' not found in map');
          }
      }
      // find agents which should be newClients
      for (i = 0; i < clients.length; i++) {
          var insert_client = clients[i];
          if (!existingAgents[insert_client.agent_id]) {
              agentsInNewUpState.push(insert_client.agent_id);
              newClients.push(insert_client);
          }
      }

      // If there are new agents or agents that came back online, generate Event Up
      if (agentsInNewUpState.length > 0) {
          AgentKeepAlive.chunkAndSendEvents(agentsInNewUpState, 'up');
      }

      if (reCalcPolicyClients) {
          (new PolicyClientsGeneratorNG()).getAndUpdateReCalcFilterFlag(true);
      }
  },

  handleDeltaUpdate: function(newConnections, expiredConnections, midSysId, useCloudServices) {
      if (!midSysId)
          gs.error('Unknown MID Server to update agents');

      // Baseline offset of 100 seconds. By default, keepalives comes every 60 seconds, add 40 second buffer for latency.
      var timeOffsetLength = 100;
      // Handle getting proper offset for Cloud Services agents
      if (useCloudServices) {
          // Time offset of the last KeepAlive payload; this should be aligned
          // with the "KafkaConsumerJob" sys_trigger repeat interval.
          var CLOUD_SERVICES_KEEPALIVE_BUFFER = parseInt(gs.getProperty('sn_itom_cloud_serv.cloud_services_keepalive_buffer', 30), 10);
  		// Cloud services always sends keepalives every 60 seconds.  Add an extra 15 seconds of buffer for Kafka latency.
          timeOffsetLength = 75 + CLOUD_SERVICES_KEEPALIVE_BUFFER;
      }
      // Handle proper configured offset for MID server case.
      else {
          // Time offset of the last KeepAlive payload; should be aligned with MID Property "sn_agent.sync.interval.sec"
          var BASE_KEEPALIVE_OFFSET = parseInt(gs.getProperty('sn_agent.agent_keep_alive.offset', 60), 10); // in seconds
          // Add some default buffer in the case of latency.
          timeOffsetLength = BASE_KEEPALIVE_OFFSET + 30;
      }

      var timeOffset = new GlideDateTime();
      timeOffset.addSeconds(-(timeOffsetLength)); // with some buffer

      // update last_refreshed for all currently Up agents that are not in the delta payload
      var gr = new GlideRecord('sn_agent_ci_extended_info');
      if (expiredConnections)
          gr.addQuery('agent_id', 'NOT IN', expiredConnections);
      gr.addQuery('last_refreshed', '>', timeOffset.getValue()); // handle case where agent disconnected in the full payload
      gr.addQuery('status', AgentKeepAlive.AGENT_UP);
      gr.addQuery('mid', midSysId);
      gr.setValue('last_refreshed', new GlideDateTime());
      gr.updateMultiple();

      // handle any new connections, just as with the full payload
      if (newConnections)
          this.updateClients(newConnections, midSysId);
  },

  getPreferredIPAddress: function(objMap) {
      var preferredVersion = AgentDiscoverySharedUtils.getPreferredIPVersion();

      // get IPv6 if it is preferred and exists
      if (preferredVersion == 6 && objMap.addressV6)
          return objMap.addressV6;

      // get IPv4
      if (objMap.address)
          return objMap.address;

      // if only IPv6 is available, get IPv6
      if (objMap.addressV6)
          return objMap.addressV6;

      return null;
  },

  //agent name or ip is found on both sn_agent_cmdb_ci_agent and sn_agent_ci_extended_info
  updateAgentInfo: function(extendedInfoRecord, agentInfoToUpdate) {
      var agentRecord = new GlideRecord("sn_agent_cmdb_ci_agent");
      agentRecord.addQuery("agent_extended_info", extendedInfoRecord.sys_id);
      agentRecord.query();
      agentRecord.next();
      Object.keys(agentInfoToUpdate).forEach(
          function(name) {
              if (name == '$objectName$' || name == '$objectNameNew$')
                  return;
              var val = agentInfoToUpdate[name];

              // Skip if we got null values
              if (val == null || val == undefined || val == 'undefined')
                  return;

              if (name == "ip_address") {
                  agentRecord.setValue("ip_address", val);
                  extendedInfoRecord.setValue("ip_address", val);
              } else if (name == "name") {
                  agentRecord.setValue("name", val);
                  extendedInfoRecord.setValue("name", val);
                  extendedInfoRecord.setValue("cmdb_ci", "");
              } else {
                  extendedInfoRecord.setValue(name, val);
              }
          });
      extendedInfoRecord.update();
      agentRecord.update();
  },

  updateClients: function(clients, mid) {
      //var startUpdateClients = (new Date()).getTime();
      var number_of_clients = 0;
      if (clients) {
          var newClients = [];
          var updateTimestamp = [];
          var updateUpSince = [];
          numOfIpUpdates = this.classifyClientsAndUpdate(clients, mid, newClients, updateTimestamp, updateUpSince);
          this.updateClientKeepAlive(updateTimestamp, updateUpSince, mid);
          //var endClassifications = (new Date()).getTime();
          //var endUpdate = (new Date()).getTime();
          //gs.debug("!!PERF!! Keep Alive update" + (endUpdate - endClassifications));
          this.insertNewClients(newClients, mid);
          //var endUpdateClients = (new Date()).getTime();
          //gs.debug("!!PERF!! Keep Alive insert" + (endUpdateClients - endUpdate));
          //gs.debug("!!PERF!! Keep Alive total" + (endUpdateClients - startUpdateClients));
      }

  },


  updateClientKeepAlive: function(updateTimestamp, updateUpSince, mid) {
      if (updateTimestamp.length == 0 && updateUpSince.length == 0)
          return;

      this.forceUpdateMidIfCacheExpired(updateUpSince, mid);

      var now = new GlideDateTime();
      gs.debug('updateTimestamp size to update: ' + updateTimestamp.length);
      var chunked = MonitoringUtils.chunkArray(updateTimestamp, this.chunk_size);
      gs.debug('number of chunks to updateTimestamp: ' + chunked.length);
      for (var i = 0; i < chunked.length; i++) {
          var chunk = chunked[i];
          var agentStatusGr = new GlideRecord('sn_agent_ci_extended_info');
          agentStatusGr.addQuery('sys_id', 'IN', chunk);
          agentStatusGr.setValue('mid', mid);
          agentStatusGr.setValue('sys_updated_on', now);
          agentStatusGr.setValue('last_refreshed', now);
          agentStatusGr.setWorkflow(false); //disable business rules so updateMultiple will work in bulk
          agentStatusGr.updateMultiple();
      }

      //update Status and up Since
      gs.debug('updateUpSince size to update: ' + updateUpSince.length);
      chunked = MonitoringUtils.chunkArray(updateUpSince, this.chunk_size);
      gs.debug('number of chunks to updateUpSince: ' + chunked.length);
      for (i = 0; i < chunked.length; i++) {
          chunk = chunked[i];
          agentStatusGr = new GlideRecord('sn_agent_ci_extended_info');
          agentStatusGr.addQuery('sys_id', 'IN', chunk);
          agentStatusGr.setValue('mid', mid);
          agentStatusGr.setValue('status', '0');
          agentStatusGr.setValue('up_since', now);
          agentStatusGr.setValue('sys_updated_on', now);
          agentStatusGr.setValue('last_refreshed', now);
          agentStatusGr.setWorkflow(false); //disable business rules so updateMultiple will work in bulk
          agentStatusGr.updateMultiple();
      }
  },

  forceUpdateMidIfCacheExpired: function(upAgents, midSysId) {
      var midInfoGr = new GlideRecord('sn_agent_mid_info');
      if (!midInfoGr.get('mid', midSysId)) {
          return;
      }

      var midAgentCacheExperationSec = 259200; // Default for 3 days.
      // If this is a MID and not a POD - try and take the MID property
      if (!global.JSUtil.getBooleanValue(midInfoGr, "use_cloud_services")) {
          var gr = new GlideRecord('ecc_agent_property');
          gr.addQuery('name', 'sn_agent.mid.agent_expiration_sec');
          gr.orderBy('ecc_agent');
          gr.query();

          while (gr.next()) {
              var midFound = gr.getValue('ecc_agent') == midSysId;
              if (!gr.getValue('ecc_agent') || midFound) {
                  midAgentCacheExperationSec = Number(gr.getValue('value'));
                  if (midFound)
                      break; // found the mid we wanted;
              }
          }
          if (!midAgentCacheExperationSec || midAgentCacheExperationSec <= 0)
              midAgentCacheExperationSec = 259200;
      } else {
          // If this is a cloud services POD, set expiration to three minutes
          // We want to cycle non-connected agent info out of memory quickly
          // because agents are unlikely to connect to the same pod again.
          midAgentCacheExperationSec = 180;
      }

      midAgentCacheExperationSec = midAgentCacheExperationSec - 10; // compensate for network lag. 10 seconds standard diviation for 3 days is ok
      var cacheExpireTime = new GlideDateTime();
      cacheExpireTime.addSeconds(-1 * midAgentCacheExperationSec);
      var agentsGr = new GlideRecord('sn_agent_ci_extended_info');
      agentsGr.addQuery('sys_id', upAgents);
      agentsGr.addQuery('last_refreshed', '<=', cacheExpireTime);
      agentsGr.setLimit(1);
      agentsGr.query();
      if (!agentsGr.hasNext()) {
          return;
      }
      // We have at least 1 agents that is now up but the MID cache expired and this agent does NOT have its configuration cached - force update the MID.

      // Forcinf the MID to get the configuration in order to restore the agent's cache
      midInfoGr.setValue('force_config_sync', '1');
      midInfoGr.update();
  },

  insertNewClients: function(clients, mid) {
      if (clients.length == 0)
          return;

      for (var i = 0; i < clients.length; i++) {
          var currentClient = clients[i];

          var now = new GlideDateTime();
          var gr = new GlideRecord('sn_agent_ci_extended_info');
          gr.setValue("status", "0");
          gr.setValue("mid", mid);
          gr.setValue("up_since", now);
          gr.setValue("name", currentClient.name);
          gr.setValue("agent_id", currentClient.agent_id);
          if (currentClient.address && currentClient.address != 'undefined')
              gr.setValue("ip_address", currentClient.address);
          gr.setValue("is_windows", currentClient.isWindows);
          if (currentClient.use_cloud_services) {
              gr.setValue("use_cloud_services", true);
              gr.setValue("agent_registration", this.getAgentRegistration(currentClient.agent_id));
          }

          // DEF0220355 prevent immediately uninstalled agent from being "up" forever
          gr.setValue('last_refreshed', now);

          var infoSysId = gr.insert();

          gr = new GlideRecord('sn_agent_cmdb_ci_agent');
          gr.setValue("name", currentClient.name);
          gr.setValue("agent_id", currentClient.agent_id);
          if (currentClient.address && currentClient.address != 'undefined')
              gr.setValue("ip_address", currentClient.address);
          gr.setValue("agent_extended_info", infoSysId);
          gr.insert();
      }
  },

  splitIn: function(ids, callback, splitSize) {
      splitSize = splitSize ? splitSize : 500;
      for (var i = 0; i * splitSize < ids.length; i++) {
          callback(ids.slice(i * splitSize, (i + 1) * splitSize));
      }
  },

  sanitizeCommand: function(current) {
      var agentSanitizeCheckCommand = new sn_agent.AgentSanitizeCheckCommand();
      return agentSanitizeCheckCommand.sanitize(current, false);
  },

  addStatusChangeThresholds: function(chgr, check) {
      var checkTypeId = chgr.check_script_type;
      if (!checkTypeId) {
          //if we get here it means this is check instance, so dot walk to check definition
          checkTypeId = chgr.check_def.check_script_type;
      }

      if (checkTypeId == "a3a00b53532a330034b8ddeeff7b123d") {
          if (chgr.getValue("event_status_change_threshold"))
              check["event_status_change_threshold"] = chgr.getValue("event_status_change_threshold");
          else
              check["event_status_change_threshold"] = 1;

          if (chgr.getValue("event_status_repair_threshold"))
              check["event_status_repair_threshold"] = chgr.getValue("event_status_repair_threshold");
          else
              check["event_status_repair_threshold"] = 1;
      }
  },

  createCheckInstance: function(polGr, checkDefSysId, params) {
      var gr = new GlideRecord('sn_agent_check_def');
      if (gr.get(checkDefSysId)) {
          //handle check
          var check_instance = new GlideRecord('sn_agent_check');
          check_instance.initialize();
          check_instance.name = gr.name;
          check_instance.command = gr.command;
          check_instance.check_def = gr.sys_id;
          check_instance.monitoring_policy = polGr.sys_id;
          check_instance.is_interval = gr.is_interval;
          check_instance.interval = gr.interval;
          check_instance.is_cron = gr.is_cron;
          check_instance.cron_expressions = gr.cron_expressions;
          check_instance.active = gr.active;
          check_instance.timeout = gr.timeout;
          check_instance.pre_condition = gr.pre_condition;
          check_instance.check_json = gr.check_json;
          check_instance.label = gr.label;
          check_instance.command_prefix = gr.command_prefix;
          check_instance.auto_generate = gr.auto_generate;
          check_instance.cred_failure_regex = gr.cred_failure_regex;
          check_instance.background = gr.background;
          check_instance.exec_mode = gr.exec_mode;
          if (gr.getValue("check_script_type") == "a3a00b53532a330034b8ddeeff7b123d") {
              if (gr.getValue("event_status_change_threshold"))
                  check_instance.event_status_change_threshold = gr.getValue("event_status_change_threshold");
              else
                  check_instance.event_status_change_threshold = 1;

              if (gr.getValue("event_status_repair_threshold"))
                  check_instance.event_status_repair_threshold = gr.getValue("event_status_repair_threshold");
              else
                  check_instance.event_status_repair_threshold = 1;
          }


          var checkId = check_instance.insert();
          // Policy params
          var pol_params = {};
          var ppGr = new GlideRecord('sn_agent_policy_param');
          ppGr.addQuery('policy', polGr.sys_id);
          ppGr.query();
          while (ppGr.next()) {
              pol_params['' + ppGr.name] = '' + ppGr.value;
          }

          //handle params
          var param_def = new GlideRecord('sn_agent_check_param_def');
          param_def.addQuery('check_def', checkDefSysId);
          param_def.query();
          var param_instance;
          var isCheckInstanceActive = true;
          while (param_def.next()) {
              param_instance = new GlideRecord('sn_agent_check_param');
              param_instance.initialize();
              param_instance.label = param_def.label;
              param_instance.name = param_def.name;
              param_instance.value = param_def.default_value;
              if (params && params["" + param_def.name] != undefined) {
                  param_instance.value = params["" + param_def.name];
                  delete params["" + param_def.name];
              } else if (!gs.nil(pol_params["" + param_def.name])) {
                  param_instance.value = pol_params["" + param_def.name];
              }
              param_instance.mandatory = param_def.mandatory;
              param_instance.order = param_def.order;
              param_instance.validation_regex = param_def.validation_regex;
              param_instance.active = param_def.active;
              param_instance.check = checkId;
              param_instance.flag = param_def.flag;
              param_instance.value_required = param_def.value_required;
              isCheckInstanceActive = (param_instance.mandatory && param_instance.value == '') ? false : true;
              param_instance.insert();
          }
          if (params) {
              for (var k in params) {
                  param_instance = new GlideRecord('sn_agent_check_param');
                  param_instance.initialize();
                  param_instance.name = k;
                  param_instance.value = params[k];
                  param_instance.check = checkId;
                  param_instance.insert();
              }
          }

          // handle secure parameters
          var sec_param_def = new GlideRecord('sn_agent_check_secure_param_def');
          sec_param_def.addQuery('check_def', checkDefSysId);
          sec_param_def.query();
          var sp_instance = new GlideRecord('sn_agent_check_secure_param');
          while (sec_param_def.next()) {
              sp_instance.initialize();
              sp_instance.label = sec_param_def.label;
              sp_instance.description = sec_param_def.description;
              sp_instance.name = sec_param_def.name;
              sp_instance.order = sec_param_def.order;
              sp_instance.active = sec_param_def.active;
              sp_instance.check = checkId;
              sp_instance.insert();
          }
      }

      if (!isCheckInstanceActive) {
          check_instance = new GlideRecord('sn_agent_check');
          if (check_instance.get(checkId)) {
              check_instance.setValue('active', false);
              check_instance.update();
              gs.addErrorMessage(gs.getMessage("The check {0}:{1} has mandatory parameters which are missing values. Therefore, the check instance is set as inactive.", [check_instance.name, checkId]));
          }
      }
  },

  sendProbe: function(command, id, mid, payload, priority, agentCorrelator) {
      mid = mid ? mid : "mid.server.*";
      if (!mid.startsWith("mid.server.")) {
          gs.error("invalid 'mid' (mid server name) argument passed to sn_agent.MonitoringConfig.sendProbe. got {0} which isn't prefixed by 'mid.server.'.", mid);
          return;
      }
      midName = mid.substring("mid.server.".length); // 'mid' variable should is in the form of "mid.server.MID_NAME". we need "MID_NAME".
      // fetch mid(s) domain(s) and set the domain of the ecc queue record we'll create with the mid's domain so he will be able to query for it.
      // If we are launching probe for all MIDs (.*) or specifically for Cloud Services Agents (acc-cnc)
      var launchCloudServices = midName == "*" || midName.startsWith("acc-cnc");
      var midGr = new GlideRecord("ecc_agent");
      if (midName == "*") { // all mids
          midsWithAccSysIds = [];
          var accExtGr = new GlideRecord("sn_agent_ext_context");
          accExtGr.addQuery("status", "Started");
          accExtGr.query();
          while (accExtGr.next())
              midsWithAccSysIds.push(accExtGr.getValue("executing_on"));
          midGr.addQuery("sys_id", "IN", midsWithAccSysIds.join(","));
      } else { // specific mid
          midGr.addQuery("name", midName);
      }
      midGr.addQuery("status", "Up");
      midGr.addQuery("validated", "true");
      midGr.query();
      if (!midGr.hasNext() && !launchCloudServices) {
          gs.error("invalid 'mid' (mid server name) argument passed to sn_agent.MonitoringConfig.sendProbe. got {0} which doesn't match any existing up and validated mid server running an ACC WebSocket Extension.", mid);
          return;
      }
      while (midGr.next()) {
          this.launchMIDProbe(midGr, command, id, payload, priority, agentCorrelator);
      }

      if (launchCloudServices)
          this.launchCloudServicesProbe(midName, command, id, payload, priority, agentCorrelator);
  },

  launchMIDProbe: function(midGr, command, id, payload, priority, agentCorrelator) {
      var gr = new GlideRecord('ecc_queue');
      gr.initialize();
      gr.setValue("sys_domain", midGr.getValue("sys_domain"));
      gr.setValue("agent", "mid.server." + midGr.getValue("name"));
      gr.setValue('skip_sensor', true);
      gr.setValue('source', command);
      gr.setValue('name', id);
      if (payload)
          gr.setValue('payload', payload);
      gr.setValue('topic', 'MonitoringProbe');
      gr.setValue('queue', 'output');
      gr.setValue('state', 'ready');
      if (!priority)
          priority = '2'; //Standard
      gr.setValue('priority', priority);
      if (!agentCorrelator)
          agentCorrelator = '';
      gr.setValue('agent_correlator', agentCorrelator);
      gr.insert();
  },

  launchCloudServicesProbe: function(podName, command, id, payload, priority, agentCorrelator) {
      var gr = new GlideRecord('ecc_queue');
      gr.initialize();
      // TODO: Deal with domain separation somehow
      gr.setValue("sys_domain", "global");
      gr.setValue("agent", "mid.server.acc-cnc");
      gr.setValue('skip_sensor', true);
      gr.setValue('source', command);
      gr.setValue('name', id);
      if (payload) {
          if (podName == "" || podName == "*")
              gr.setValue('payload', "pod: all_pods, " + payload);
          else
              gr.setValue('payload', "pod: " + podName.substring(ACCConstants.CLOUD_SERVICES_POD_PREFIX.length) + ", " + payload);
      }
      gr.setValue('topic', 'MonitoringProbe');
      gr.setValue('queue', 'output');
      gr.setValue('state', 'ready');
      if (!priority)
          priority = '2'; //Standard
      gr.setValue('priority', priority);
      if (!agentCorrelator)
          agentCorrelator = '';
      gr.setValue('agent_correlator', agentCorrelator);
      gr.insert();
  },

  updateMidUpdateTime: function(lut, mid, useCloudServices) {
      var midInfoGR = new GlideRecord("sn_agent_mid_info");
      midInfoGR.addQuery("mid", mid);
      midInfoGR.query();
      if (!midInfoGR.next()) {
          midInfoGR.setValue("mid", mid);
          if (useCloudServices) {
              midInfoGR.setValue("use_cloud_services", useCloudServices)
          }
      }

      midInfoGR.setValue("last_update_time", lut);
      //only if true update value, otherwise it overiddes the value if it was set to true by other flow
      if (this.forceMidConfigSync) {
          midInfoGR.setValue("force_config_sync", this.forceMidConfigSync);
      }
      midInfoGR.update();
  },

  updateDuplicatedAgents: function(agentIds, isDuplicate) {
      if (agentIds.length > 0) {
          var gr = new GlideRecord('sn_agent_ci_extended_info');
          gr.addQuery('agent_id', 'IN', agentIds.join(","));
          gr.addQuery('is_duplicate', !isDuplicate);
          gr.setValue('is_duplicate', isDuplicate);
          if (isDuplicate == true)
              gr.setValue('cmdb_ci', "");
          gr.setWorkflow(false);
          gr.updateMultiple();
      }
  },

  sendConfigPublishProbe: function(policies, midName) {
      //send configuration probe only in case actual delta was detected
      if (!policies || policies.length < 1 || !midName)
          return;

      var timestamp = "" + new GlideDateTime();
      var updates = {
          timestamp: timestamp,
          policies: policies
      };
      var payload = (new MonitoringSync()).getPayload(JSON.stringify(updates));

      this.sendProbe('config_publish', 'config_publish', "mid.server." + midName, payload);
  },

  getOrphanPolicy: function(agentsWithNoPolicies) {
      var orphanAgentsPolicy = {
          name: "",
          id: ACCConstants.IDLE_POLICY_NAME,
          interval: "",
          "auto_binding": "false",
          clients_cis: {}
      };
      for (var agent in agentsWithNoPolicies) {
          var agentId = agentsWithNoPolicies[agent];
          orphanAgentsPolicy['clients_cis'][agentId] = {};
      }

      return orphanAgentsPolicy;
  },

  handleMidParamsOnChecks: function(policies, mid, midLastConfigSyncTime) {
      var uniqueCheckTypesSysIds = {};

      for (var policyIdx in policies) {
          var policy = policies[policyIdx];
          var checks = policy["checks"];
          for (var checkIdx in checks) {
              var check = checks[checkIdx];
              if (check["check_type_id"]) {
                  uniqueCheckTypesSysIds[check["check_type_id"]] = true; // mark as exist
              }
          }
      }
      var checksTypeIds = Object.keys(uniqueCheckTypesSysIds);
      if (checksTypeIds.length < 1)
          return;

      var midEnrichGr = new GlideRecord("sn_agent_mid_enrichment_for_check");
      midEnrichGr.addQuery("check_type", "IN", checksTypeIds);
      midEnrichGr.query();

      var checkTypesToValues = {};
      var evaluator = new GlideScopedEvaluator();
      evaluator.putVariable('midId', mid); //pass current mid id
      evaluator.putVariable('midLastConfigSyncTime', midLastConfigSyncTime); //pass mid's acc last config sync time
      while (midEnrichGr.next()) {
          var midInfoContainer = evaluator.evaluateScript(midEnrichGr, 'mid_enrich_script');
          if (midInfoContainer) {
              var valuesToAdd = checkTypesToValues[midEnrichGr.check_type];
              if (!valuesToAdd) {
                  valuesToAdd = [];
                  checkTypesToValues[midEnrichGr.check_type] = valuesToAdd;
              }
              valuesToAdd.push(midInfoContainer);
          }
      }

      for (var policyIdx in policies) {
          var policy = policies[policyIdx];
          var checks = policy["checks"];
          for (var checkIdx in checks) {
              var check = checks[checkIdx];
              for (var checkType in checkTypesToValues) {
                  if (check["check_type_id"] == checkType) {
                      if (!check["params"]) {
                          check["params"] = {};
                      }
                      var midValuesLst = checkTypesToValues[checkType];
                      for (var valIdx in midValuesLst) {
                          var infoContainer = midValuesLst[valIdx];
                          check["params"][infoContainer.key] = infoContainer.value;
                          check["command"] = check["command"] + " " + infoContainer.flag + " {{.labels.params_" + infoContainer.key + "}}";
                      }
                  }
              }
          }
      }
  },

  syncPoliciesToMid: function() {
      var p = new PolicyClientsGeneratorNG(this.policyStateUtil);
      // Make sure all Policies are up to date.
      p.syncClients();
      // Send probes to MID servers / updates to C&C PODs.
      this.syncPolicies();
      this.runInitialHostDataCollection();
  },

  syncPolicies: function() {
      // ensure we are in global domain before getting the leaf domains
      var currentDomainId = this.domainInfo.getCurrentDomain();
      if (this.policyUpdateDomainSeparation.isDomainSeparationPluginActive && currentDomainId != 'global')
          this.policyUpdateDomainSeparation.changeSessionDomain('global');

      var leafDomainIds = this.policyUpdateDomainSeparation.getAgentLeafDomains();
      try {
          for (var i = 0; i < leafDomainIds.length; i++) {
              var leafDomainId = leafDomainIds[i];
              gs.debug("Running syncClients for domain " + leafDomainId);
              currentDomainId = this.domainInfo.getCurrentDomain();
              if (this.policyUpdateDomainSeparation.isDomainSeparationPluginActive && leafDomainId != currentDomainId)
                  this.policyUpdateDomainSeparation.changeSessionDomain(leafDomainId);

              var midInfoGr = new GlideRecord('sn_agent_mid_info');
              midInfoGr.query();

              var upStatusMids = 0;
              while (midInfoGr.next()) {
                  /*
                  	MID sends its last_update_time alongside the agents' keepalive
                  	If a MID has 0 agents up - then we will not get the last_update_time of the mid and we will keep
                  	Sending the policies to the MID.
                  	At the first agent up for a MID - we will send the full policies configuration.
                  */
                  if (!this.atLeastOneUpAgent(midInfoGr.getValue('mid'))) {
                      gs.debug('MonitoringConfig syncPolicies: no agents up for mid info record ' + midInfoGr.getUniqueValue() + ', skipping this mid info record');
                      continue;
                  }

                  var useCloudServices = global.JSUtil.getBooleanValue(midInfoGr, "use_cloud_services");
                  // handle MID case
                  if (!useCloudServices && midInfoGr.mid.status != 'Up') {
                      gs.debug('MonitoringConfig syncPolicies: mid info ' + midInfoGr.getUniqueValue() + ' is not using cloud services and not up, skipping this mid info record');
                      continue;
                  }

                  upStatusMids++;

                  var handlerLastUpdate = midInfoGr.getValue('last_update_time');
                  // this is a force sync if the mid info record is not marked as force sync, AND we have a last update time, AND that is not a reserved time for marking force (backward compatibility)
                  var isForcedSync = !(midInfoGr.getValue('force_config_sync') == '0' && handlerLastUpdate && handlerLastUpdate != this.FORCE_SYNC_TIMESTAMP);

                  var policyGr = new GlideRecord('sn_agent_policy');
                  // If this is not force sync only take the updated policies - otherwise take all policies for that mid.
                  if (!isForcedSync) {
                      /*
                      publish status = 5 is ready to publish.
                       Mostly the last_calc_time would be effected by the change to ready to publish, but in some cases we only want to sent
                       the policy to the MID server without changing the last_calc_time.
                      Example: adding a new proxy agents: we want to add more records to the table and send to the MID - but we would
                       not want to effect the detection of new CI
                      */
                      var policyStateQuery = policyGr.addJoinQuery('sn_agent_policy_state', 'sys_id', 'policy');
                      policyStateQuery.addCondition('last_calc_time', '>', handlerLastUpdate).addOrCondition('publish_status', '5');
                  }
                  policyGr.addActiveQuery();
                  policyGr.addQuery('is_draft', '0');
                  policyGr.query();

                  if (useCloudServices) {
                      // handle Cloud Services case
                      var pod = midInfoGr.getValue('mid');
                      // this function handles sending the signed policies and signed agents,
                      // as well as sending the idle policy if needed (analogous to the orphan policy in the MID flow)
                      (new ConfigPublishPayload()).sendConfigPublishPayloadsForHandler(pod, this, policyGr, isForcedSync, handlerLastUpdate);
                      // Mark as done
                      this.disableForceSync(isForcedSync, midInfoGr);
                      continue;
                  }

                  var policies = [];
                  while (policyGr.next()) {
                      // If the MID does NOT have any agents for this policy, we will get an undefined object.
                      policy = this.getPolicyJson(policyGr, midInfoGr.getValue('mid'));
                      if (policy && policy.clients_cis && Object.keys(policy.clients_cis).length > 0)
                          policies.push(policy);
                  }

                  // Add MID argument to commands if they use check type with MID script.
                  this.handleMidParamsOnChecks(policies, midInfoGr.getValue('mid'), handlerLastUpdate);

                  // If this was a forced sync - put agents without a policy in idle policy
                  if (isForcedSync) {
                      var agentsForMidWithoutPolicies = [];
                      var agentsForMidWithPolicies = [];

                      for (var policyIndex = 0; policyIndex < policies.length; policyIndex++) {
                          var policy = policies[policyIndex];
                          for (var client in policy.clients_cis)
                              agentsForMidWithPolicies.push(client);
                      }

                      var agentsGr = new GlideRecord('sn_agent_ci_extended_info');
                      if (agentsForMidWithPolicies.length > 0)
                          agentsGr.addQuery('agent_id', 'NOT IN', agentsForMidWithPolicies);
                      agentsGr.addQuery('mid', midInfoGr.getValue('mid'));
                      agentsGr.query();
                      while (agentsGr.next()) {
                          agentsForMidWithoutPolicies.push(agentsGr.getValue('agent_id'));
                      }

                      if (agentsForMidWithoutPolicies.length > 0) {
                          var orphanPolicy = this.getOrphanPolicy(agentsForMidWithoutPolicies);
                          policies.push(orphanPolicy);
                      }
                  }

                  this.sendConfigPublishProbe(policies, midInfoGr.mid.name);
                  // Mark as done
                  this.disableForceSync(isForcedSync, midInfoGr);
              }

              this.setPoliciesToCurrectPublishedStatus();
          }

          if (this.policyUpdateDomainSeparation.isDomainSeparationPluginActive)
              this.policyUpdateDomainSeparation.updatePolicies();
      } finally {
          // reset the domain to global if we changed domains during config synch
          currentDomainId = this.domainInfo.getCurrentDomain();
          if (this.policyUpdateDomainSeparation.isDomainSeparationPluginActive && currentDomainId != 'global')
              this.policyUpdateDomainSeparation.changeSessionDomain('global');
      }
  },

  /**
   * Disables force sync on the passed the sn_agent_mid_info record
   *
   * Parameters:
   *     isForcedSync - true if the current policy sync is a force sync, false otherwise
   *     midInfoGr - a GlideRecord pointing to a sn_agent_mid_info record
   *
   * Return:
   *     none
   */
  disableForceSync: function(isForcedSync, midInfoGr) {
      if (!isForcedSync || !midInfoGr)
          return;

      midInfoGr.setValue('force_config_sync', '0');
      midInfoGr.update();
  },

  setPoliciesToCurrectPublishedStatus: function() {
      var policyGr = new GlideRecord('sn_agent_policy');
      policyGr.setWorkflow(false);
      policyGr.addActiveQuery();
      policyGr.addQuery('is_draft', '0');
      var policyStateQuery = policyGr.addJoinQuery('sn_agent_policy_state', 'sys_id', 'policy');
      policyStateQuery.addCondition('publish_status', '5');
      policyGr.query();

      var draftUtil = new PolicyDraftUtils();

      while (policyGr.next()) {
          var publishStatus = "1"; // Published.
          var draftPolicyGr = new GlideRecord('sn_agent_policy');
          draftPolicyGr.addQuery('related_policy', policyGr.getUniqueValue());
          draftPolicyGr.addQuery('is_draft', '1');
          draftPolicyGr.addActiveQuery();
          draftPolicyGr.query();
          if (draftPolicyGr.next()) {
              // Can be Published or Published* (diff between the published policy and the draft policy)
              var draftPublishStatus = this.policyStateUtil.getPolicyStatePublishStatus(draftPolicyGr);
              if (draftPublishStatus == '1' || draftPublishStatus == '2')
                  publishStatus = draftPublishStatus;
              else {
                  if (draftUtil.isDraftDifferentFromPublished(draftPolicyGr))
                      publishStatus = "2"; // published but different from draft status.

                  this.policyStateUtil.updatePolicyStatePublishStatus(draftPolicyGr, publishStatus);
                  if (this.policyUpdateDomainSeparation.isDomainSeparationPluginActive)
                      this.policyUpdateDomainSeparation.updatePolicyStatusMap(draftPolicyGr, publishStatus);
                  else {
                      draftPolicyGr.setValue('publish_status', publishStatus);
                      draftPolicyGr.update();
                  }
              }
          }

          this.policyStateUtil.updatePolicyStatePublishStatus(policyGr, publishStatus);
          if (this.policyUpdateDomainSeparation.isDomainSeparationPluginActive)
              this.policyUpdateDomainSeparation.updatePolicyStatusMap(policyGr, publishStatus);
          else {
              policyGr.setValue('publish_status', publishStatus);
              policyGr.update();
          }
      }
  },

  atLeastOneUpAgent: function(mid) {
      if (!mid)
          return false;

      var agentsGr = new GlideRecord('sn_agent_ci_extended_info');
      agentsGr.addQuery('mid', mid);
      agentsGr.addQuery('status', '0'); //up
      agentsGr.setLimit(1);
      agentsGr.query();

      return agentsGr.hasNext();
  },

  /* This method loads to memory the policy structure as we will send it to the MID
  	Expected results:
  	{
          "name": "<policy name>",
          "interval": "60",
          "cron_expressions": "0 0 0,1,2,3,4,5,6,7,17,18,19,20,21,22,23 ? * *|0 * 8-16 ? * *",
          "id": "d3b9b2aed445d510e76096c40f8ab07c",
          "auto_binding": "true",
          "clients_cis": { (will be filled in later) },
          "checks": [
              {
                  "name": "<check name>",
                  "command": "echo {{.labels.params_ci_name}}",
                  "interval": "60",
                  "timeout": "60",
                  "id": "7afd37a2d489d510e76096c40f8ab09c",
                  "exec_mode": "execv",
                  "params_background": false,
                  "check_instance_id": "7afd37a2d489d510e76096c40f8ab09c",
                  "check_type_id": "a32f261967133300b7b72dbd2685eff9",
                  "params": {
                      "last_updated": "2022-08-04 21:51:29"
                  }
  				(THERE COULD BE MORE ATTRIBUTE NOT SHOWING HERE)
              }
  			...
          ]
      }

  	input : policy glide record that points to the policy record we are building
  */
  loadPolicyToMemory: function(policyGr, payloadVersion) {
      this.currentPolicy = {};

      if (!policyGr.getUniqueValue()) {
          gs.error(gs.getMessage("MonitoringConfig: in loadPolicyToMemory(): could not load policy to memory as there was a problem getting values from the policy glide record."));
          return;
      }

      this.currentPolicy.name = policyGr.getValue('name');
      this.currentPolicy.interval = policyGr.getValue('interval');
      this.currentPolicy.cron_expressions = ""; // Will fill this later
      this.currentPolicy.id = policyGr.getUniqueValue();
      this.currentPolicy.auto_binding = policyGr.getValue('auto_binding') == '1';
      // ci information is populated within the agent object instead of the policy object for payload version 2
      // so only populate clients_cis for version 1 payload
      if (!payloadVersion || payloadVersion < 2)
          this.currentPolicy.clients_cis = {};

      var grChecks = new GlideRecord('sn_agent_check');
      grChecks.addQuery('monitoring_policy', policyGr.getUniqueValue());
      grChecks.addActiveQuery();
      grChecks.query();

      this.currentPolicy.checks = [];
      var credId, credAliasId = "";
      if (policyGr.credential_alias)
          credAliasId = "" + policyGr.credential_alias.id;
      if (policyGr.cred_alias)
          credId = MonitoringUtils.getCredIdFromCredName(policyGr.cred_alias);
      var accUtils = new ACCConfigUtils();
      var mc = new MonitoringConfig();

      var allCronExp = [];
      if (policyGr.getValue('cron_expressions'))
          allCronExp = policyGr.getValue('cron_expressions').split(',');

      while (grChecks.next()) {
          try {
              var checkObj = accUtils.getCheckJson(grChecks, credId, credAliasId, payloadVersion);
              var command = grChecks.getValue('command');

              checkObj["check_instance_id"] = "" + grChecks.sys_id;
              if (grChecks.check_def && grChecks.check_def.check_script_type) {
                  checkObj["check_type_id"] = "" + grChecks.check_def.check_script_type;
                  //DO WE NEED to collect all check types uniquely here ???
                  if (grChecks.check_def.check_script_type.mid_script)
                      checkObj["mid_script"] = "" + grChecks.check_def.check_script_type.mid_script.name;
              }

              //try taking cron expressions from check
              var cronExpressions = grChecks.cron_expressions;
              if (cronExpressions)
                  allCronExp.push(cronExpressions);

              //if cron expressions and interval are empty on check, try taking cron expressions from policy
              if (gs.nil(cronExpressions) && gs.nil(checkObj["interval"])) {
                  cronExpressions = this.currentPolicy["cron_expressions"];
              }
              // Will go over this later and grab the cron expressions
              if (!gs.nil(cronExpressions)) {
                  checkObj.cron_ref = cronExpressions;
              } else {
                  //if we have no cron expressions on check and policy and interval on check is empty, take interval from policy
                  if (gs.nil(checkObj["interval"])) {
                      checkObj["interval"] = this.currentPolicy["interval"];
                  }
              }

              mc.addStatusChangeThresholds(grChecks, checkObj);

              // if this check is using a CI parameter - keep it per policy, we will fill the
              // only calculate ci params for the check if using payload version 1
              if ((!payloadVersion || payloadVersion < 2) && command.indexOf(this.CI_PARAM_KEY) > 0) {
                  /* Structure will be
                  	policy.clients_cis.CI_PARAMS_TO_CHECK_PLACE_HOLDER[<check sys id>] = array of ci param keys
                  */
                  if (!this.currentPolicy.clients_cis[this.CI_PARAMS_TO_CHECK_PLACE_HOLDER]) {
                      this.currentPolicy.clients_cis[this.CI_PARAMS_TO_CHECK_PLACE_HOLDER] = {};
                  }
                  this.currentPolicy.clients_cis[this.CI_PARAMS_TO_CHECK_PLACE_HOLDER][grChecks.getUniqueValue()] = [];
                  var ciParams = this.currentPolicy.clients_cis[this.CI_PARAMS_TO_CHECK_PLACE_HOLDER][grChecks.getUniqueValue()];
                  // Get all ci params keys using regex
                  var myRegexp = new RegExp(this.CI_PARAM_KEY + "(.*?)}}", "gm");
                  var match = myRegexp.exec(command);
                  while (match != null) {
                      if (match[1]) {
                          ciParams.push(match[1]); // will be filled per CI later.
                      }
                      match = myRegexp.exec(command);
                  }
              }

              mc.addParam(checkObj, "last_updated", grChecks.getValue('sys_updated_on'));
              this.currentPolicy.checks.push(checkObj);
          } catch (e) {
              gs.error(gs.getMessage("MonitoringConfig: Skipping check: {0} ({1}) invalid json, error: {2}", [grChecks.name, grChecks.sys_id, e]));
          }
      }

      // If nothing to do with cron - return.
      if (!allCronExp || allCronExp.length < 1) {
          delete this.currentPolicy.cron_expressions;
          return;
      }

      // Add cron expressions
      var grCron = new GlideRecord("sn_agent_cron_expression");
      grCron.addQuery("sys_id", allCronExp);
      grCron.query();
      var cronExpMap = {};
      while (grCron.next()) {
          cronExpMap[grCron.getUniqueValue()] = grCron.cron_expression;
      }

      // We could not create a cron expression map; nothing to replace with.
      var isCronExpMap = Object.keys(cronExpMap).length != 0;
      var p;
      if (isCronExpMap)
          p = new PolicyClientsGeneratorNG();

      for (var i = 0; i < this.currentPolicy.checks.length; i++) {
          var check = this.currentPolicy.checks[i];
          if (!check.cron_ref)
              continue;

          if (isCronExpMap)
              mc.addParam(check, "cron_expressions", p.replaceCronReferenceWithExpression(check.cron_ref, cronExpMap));
          delete check.cron_ref; // No need for it now
      }

      if (isCronExpMap)
          this.currentPolicy.cron_expressions = p.replaceCronReferenceWithExpression(policyGr.getValue('cron_expressions'), cronExpMap);
  },

  isProxyPolicy: function(policyGr) {
      return policyGr.single_proxy_agent || policyGr.proxy_advanced || policyGr.proxy_script_advanced || policyGr.proxy_cluster;
  },

  /*
  	This method returns a policy structure with agents (policy.clients_cis) and CI params per check, if needed.
  	Input: policyGr - a GR point to the current policy
  			midId - the mid sys id to create the policy for (it will be sent as an ecc queue)
  */
  getPolicyJson: function(policyGr, midId) {
      if (!policyGr || !policyGr.sys_id)
          return undefined;

      var policyMonitoredCIsGr = new GlideRecord('sn_agent_policy_monitored_cis');
      policyMonitoredCIsGr.addQuery('policy', policyGr.getValue('sys_id'));
      policyMonitoredCIsGr.addNotNullQuery('monitored_ci');
      policyMonitoredCIsGr.addNotNullQuery('agent_id');
      if (midId)
          policyMonitoredCIsGr.addQuery('agent_id.mid', midId);
      policyMonitoredCIsGr.setCategory('acc-policy-calc');
      policyMonitoredCIsGr.query();
      if (!policyMonitoredCIsGr.hasNext())
          return undefined;

      this.loadPolicyToMemory(policyGr);

      var ciParamsToPopulate = false;
      var policy = this.currentPolicy;

      if (!policy.clients_cis)
          policy.clients_cis = {};

      if (policy.clients_cis[this.CI_PARAMS_TO_CHECK_PLACE_HOLDER])
          ciParamsToPopulate = true;

      var isProxyPolicy = this.isProxyPolicy(policyGr);


      while (policyMonitoredCIsGr.next()) {
          var agentId = policyMonitoredCIsGr.getValue('agent_id');
          var monitoredCi = policyMonitoredCIsGr.getValue('monitored_ci');

          if (!policy.clients_cis[agentId])
              policy.clients_cis[agentId] = {};
          if (!policy.clients_cis[agentId][monitoredCi])
              policy.clients_cis[agentId][monitoredCi] = {};

          // If this policy does not contain any checks that has CI params - continue
          if (!ciParamsToPopulate)
              continue;

          // Prepare a db CI param map ({key: value})
          var dbCiParams = policyMonitoredCIsGr.getValue('ci_params');
          if (!dbCiParams)
              continue;

          var arrDbCiParams = dbCiParams.split(',');
          var dbCiParamsMap = {};

          for (var i = 0; i < arrDbCiParams.length; i++) {
              if (!arrDbCiParams[i])
                  continue;

              var index = arrDbCiParams[i].indexOf('=');

              // We had a string without the "=" sign; add it as a key with no value, if it does not exist
              if (index < 0) {
                  if (!dbCiParamsMap[arrDbCiParams[i]])
                      dbCiParamsMap[arrDbCiParams[i]] = "";
                  continue;
              }

              var key = arrDbCiParams[i].substring(0, index);
              var value = "";
              if (index > 0) // check that we do have a value, otherwise we will have an empty string
                  value = arrDbCiParams[i].substring(index + 1);
              dbCiParamsMap[key] = value;
          }

          // Per check find the correct CI Params
          for (var checkSysId in policy.clients_cis[this.CI_PARAMS_TO_CHECK_PLACE_HOLDER]) {
              // Create the structure for the CI Params
              policy.clients_cis[agentId][monitoredCi][checkSysId] = {};
              policy.clients_cis[agentId][monitoredCi][checkSysId]["ci"] = {};

              // Go over the CI params for the current check
              var arrCiParamsKeysForCheck = policy.clients_cis[this.CI_PARAMS_TO_CHECK_PLACE_HOLDER][checkSysId];
              for (var j = 0; j < arrCiParamsKeysForCheck.length; j++) {
                  var ciParamKey = arrCiParamsKeysForCheck[j];
                  var ciParamValue = dbCiParamsMap[ciParamKey];
                  policy.clients_cis[agentId][monitoredCi][checkSysId]["ci"][ciParamKey] = ciParamValue;
              }

              if (isProxyPolicy) {
                  this.addProxyCheckCiParamsValues(policy.clients_cis[agentId][monitoredCi][checkSysId], dbCiParamsMap, agentId);
              }
          }
      }

      // No need for the place holder
      delete policy.clients_cis[this.CI_PARAMS_TO_CHECK_PLACE_HOLDER];

      return policy;
  },


  /*
   * Query the agent registration table for certificate information. When agent is MID-less, agent should be registered
   * with servicenow instance before reaching instance.
   */
  getAgentRegistration: function(agentId) {
      var regGr = new GlideRecord("sn_agent_agent_registration");
      regGr.addQuery("agent_id", agentId);
      regGr.addQuery("is_validated", true);
      regGr.query();
      if (!regGr.next())
          return "";
      return regGr.getUniqueValue();
  },

  addProxyCheckCiParamsValues: function(checkObj, valuesParamsMap, agentId) {
      for (var i = 0; i < this.PROXY_CHECK_CI_PARAM.length; i++) {
          var proxyParamVal = valuesParamsMap[this.PROXY_CHECK_CI_PARAM[i]];
          if (proxyParamVal)
              checkObj[this.PROXY_CHECK_CI_PARAM[i]] = proxyParamVal;
      }

      var entrypointMetricResourceName = this.getEntrypointMetricsResourceName(agentId);
      // in case resource name is indeed nil (very unlikely...), MID should put the raw agent client name (which should resolve to the agent host name) as resource
      if (!gs.nil(entrypointMetricResourceName))
          checkObj[this.ENTRYPOINT_METRIC_RESOURCE_PARAM] = entrypointMetricResourceName;
      // Mark this as proxy check
      checkObj[this.IS_PROXY_CHECK] = true;
  },

  getEntrypointMetricsResourceName: function(agentId) {
      var agentCiGr = new GlideRecord("sn_agent_cmdb_ci_agent");
      if (!agentCiGr.get("agent_id", agentId)) {
          gs.error(gs.getMessage("PolicyClientsGeneratorNG.getEntrypointMetricsResourceName: ERROR: no agent with agent_id == {0}", agentId));
          return undefined;
      }
      var hostname = null;
      var location = null;
      var agentHostGr = new GlideRecord("cmdb_ci");
      if (!agentHostGr.get(agentCiGr.agent_extended_info.cmdb_ci)) {
          gs.error(gs.getMessage("PolicyClientsGeneratorNG.getEntrypointMetricsResourceName: ERROR: agent with agent_id == {0} doesn't have a valid host CI", agentId));
          return undefined;
      }
      hostname = agentHostGr.getValue("name");
      var useHostname = gs.getProperty(this.ENTRYPOINT_METRIC_RESOURCE_USE_HOSTNAME_PROPERTY, "true") === "true";
      var useLocation = gs.getProperty(this.ENTRYPOINT_METRIC_RESOURCE_USE_LOCATION_PROPERTY, "true") === "true";
      if (useLocation) {
          // try taking location from agent host CI. Fallback to location on agent CI
          if (!gs.nil(agentHostGr.getValue("location")))
              location = agentHostGr.getElement("location").getDisplayValue();
          else if (!gs.nil(agentCiGr.getValue("location")))
              location = agentCiGr.getElement("location").getDisplayValue();
          if (!gs.nil(location))
              return useHostname ? "From host: " + hostname + ", location: " + location : "From location: " + location;
          else {
              gs.warn(gs.getMessage("PolicyClientsGeneratorNG.getEntrypointMetricsResourceName: WARNING: property specifies to use location but it is nil. Falling back to hostname"));
          }
      }
      if (useHostname)
          return "From host: " + hostname;
      else {
          gs.warn(gs.getMessage("PolicyClientsGeneratorNG.getEntrypointMetricsResourceName: WARNING: location is nil (becasue property specifies to not use it or because it is not present on agent and agent host CIs) and property specifies to not use hostname. Hostname reported by agent websocket connection will be used"));
      }
      // the metrics parser script on the MID will use the hostname returned by the agent websocket connection as the hostname
      return undefined;
  },

  addParam: function(object, paramName, paramVal) {
      if (!object["params"]) {
          object["params"] = {};
      }

      object["params"][paramName] = paramVal;
  },

  runInitialHostDataCollection: function() {
      var AGENTS_IDS = 'clients_cis';

      // Get all agents that ...
      var agentGr = new GlideRecord('sn_agent_ci_extended_info');
      agentGr.addQuery('status', 'IN', '0,1'); // 0=Up, 1=Warning
      // was never discovered
      agentGr.addNullQuery('host_data'); // mimic old behavior.
      agentGr.addNullQuery('cmdb_ci');
      // And we have all the data needed
      agentGr.addQuery('is_duplicate', 'false');
      agentGr.addNotNullQuery('mid');
      agentGr.addNotNullQuery('agent_id');
      agentGr.orderBy('mid');
      agentGr.query();

      // Now that we have all new, undiscovered, agents - we need to arrange them per MID.
      var agentsToRefreshPerMid = {};
      var runDiscovery = false;

      while (agentGr.next()) {
          var midName;
          if (global.JSUtil.getBooleanValue(agentGr, "use_cloud_services"))
              midName = ACCConstants.CLOUD_SERVICES_POD_PREFIX + agentGr.mid;
          else
              midName = agentGr.mid.name;

          if (!midName) {
              gs.warn(gs.getMessage("ACC initial discovery: MID name is empty - will continue to next record. sn_agent_ci_extended_info sys id={0}", agentGr.getUniqueValue()));
              continue;
          }

          // If there is no object for the specific MID name, create one.
          if (!agentsToRefreshPerMid[midName]) {
              agentsToRefreshPerMid[midName] = {};
              agentsToRefreshPerMid[midName][AGENTS_IDS] = {};
          }


          agentsToRefreshPerMid[midName][AGENTS_IDS][agentGr.getValue('agent_id')] = {};
          // put in the sn_agent_ci_extended_info sys_id as the "monitored ci" since we don't have one yet
          agentsToRefreshPerMid[midName][AGENTS_IDS][agentGr.getValue('agent_id')][agentGr.getUniqueValue()] = {};
          if (!runDiscovery)
              runDiscovery = true;
      }

      if (!runDiscovery) {
          gs.debug(gs.getMessage("ACC initial discovery: Found no agents to run host data collection."));
          return;
      }

      var adsu = new AgentDiscoverySharedUtils();
      // Run discovery and set the host_data status
      adsu.runDiscoveryCheckForAgentPerMid(agentsToRefreshPerMid);
  },

  type: 'MonitoringConfig'
};

Sys ID

6c62a3f0c305130039a3553a81d3ae88

Offical Documentation

Official Docs: