Name
sn_agent.PolicyClientsGenerator
Description
No description available
Script
var PolicyClientsGenerator = Class.create();
PolicyClientsGenerator.prototype = {
DOCKER_PARENT_TABLE: 'cmdb_ci_docker_container',
HOST_PARENT_TABLE: 'cmdb_ci_computer',
APP_PARENT_TABLE: 'cmdb_ci_appl',
AGENT_PARENT_TABLE: 'sn_agent_cmdb_ci_agent',
LIMIT_TO_DECIDE_SHOULD_ADD_AGENTS: 1000,
DEFAULT_LIMIT_TO_DECIDE_SHOULD_ADD_AGENTS: 1000,
MAX_NUMBER_OF_AGENTS_HOSTS_TO_ADD_TO_QUERY: 200000,
MAX_NUMBER_OF_MONITORED_CIS_TO_PROCESS: 400000,
DEFAULT_MAX_NUMBER_OF_MONITORED_CIS: 400000,
MIN_RATIO_TO_ADD_AGENTS_HOSTS_TO_QUERY: 0.6,
// Status for monitored CIs query
QUERY_SUCCESSFULLY: -1,
QUERY_FAILED: -2,
QUERY_WITH_TOO_MANY_MONITORED_CIS: -3,
QUERY_WITH_ZERO_CIS: -4,
initialize: function() {
this.base64CheckParamsUtil = new Base64CheckParamsUtil();
this.MAX_NUMBER_OF_MONITORED_CIS_TO_PROCESS = Number(gs.getProperty('sn_agent.max_number_of_monitored_cis_per_policy', this.DEFAULT_MAX_NUMBER_OF_MONITORED_CIS));
if (!this.MAX_NUMBER_OF_MONITORED_CIS_TO_PROCESS)
this.MAX_NUMBER_OF_MONITORED_CIS_TO_PROCESS = this.DEFAULT_MAX_NUMBER_OF_MONITORED_CIS;
this.LIMIT_TO_DECIDE_SHOULD_ADD_AGENTS = Number(gs.getProperty('sn_agent.max_num_of_monitored_ci_to_process_with_no_agents_association', this.DEFAULT_LIMIT_TO_DECIDE_SHOULD_ADD_AGENTS));
if (!this.LIMIT_TO_DECIDE_SHOULD_ADD_AGENTS)
this.LIMIT_TO_DECIDE_SHOULD_ADD_AGENTS = this.DEFAULT_LIMIT_TO_DECIDE_SHOULD_ADD_AGENTS;
},
//define the database view for http entrypoints
ENTRYPOINT_DB_VIEW: 'sn_itmon_http_entrypoint',
CI_PARAM_KEY: '{{.labels.params_ci_',
SERVICE_INFO_PREFIX: 'rel_service_',
ENTRYPOINT_METRIC_RESOURCE_PARAM: "entrypoint_resource",
ENTRY_POINT_METRIC_RESOURCE_USE_LOCATION_PROPERTY: "sn_itmon.entrypoint.monitoring.use_location",
ENTRY_POINT_METRIC_RESOURCE_USE_HOSTNAME_PROPERTY: "sn_itmon.entrypoint.monitoring.use_hostname",
IS_PROXY_CHECK: "is_proxy_check",
resetPoliciesToClientsForMidSync: function(policiesSysIds) {
// Mark the policy to clients to now - this will cause the policies to sync down to the MID server
var filterResultGr = new GlideRecord('sn_agent_policy_clients');
filterResultGr.addQuery("policy", policiesSysIds);
filterResultGr.setValue("sys_updated_on", new GlideDateTime());
filterResultGr.updateMultiple();
},
/*
iterate over policies and calculate their checksum based on the CIs they cover. if different from the previous value, update the
result clients json and the timestamp on the policy so that we will propogate new values to MIDs.
*/
syncClients: function() {
var shouldReCalc = this.getAndUpdateReCalcFilterFlag(false) == "true";
var now = new GlideDateTime();
var currentDomain = new sn_agent.DomainInfo().getCurrentDomain();
//if discovery flag wasn't raised, check if we are reaching interval limit
if (!shouldReCalc) {
var hashGr = new GlideRecord("sn_agent_flags");
hashGr.addQuery("name", "mon_filters_last_sync_time");
hashGr.addQuery("sys_domain", currentDomain);
hashGr.query();
if (hashGr.next()) {
var lastRun = new GlideDateTime(hashGr.getValue("hash"));
shouldReCalc = (now.getNumericValue() - lastRun.getNumericValue()) > gs.getProperty("sn_agent.sync_filters_interval_min", 10) * 60000;
} else {
//recalc if never recalced before...
shouldReCalc = true;
}
}
// If nothing to do - check forcerecalc required for any policies and exit.
if (!shouldReCalc) {
//Recalc is not required, check whether force recalculation is required for any policies.
//STRY53093659: for this story distribution of CIs among proxy agent is done. If any proxy agent went down, force recalc will be set to true for that policy that uses the down proxy agent.
this.performForceReCalcIfRequired();
return;
}
//As recalc is true, the policy client json will be calculated any how, so will set force_recalc flag to false as policy client json are getting calculated
var forceRecalcClientsGr = new GlideRecord('sn_agent_policy_clients');
forceRecalcClientsGr.addQuery("force_recalc", "true");
forceRecalcClientsGr.setValue("force_recalc", false);
forceRecalcClientsGr.updateMultiple();
this.updateLastForceRecalcSyncTime(now);
var filterResultMap = {};
var filterResultGr = new GlideRecord('sn_agent_policy_clients');
filterResultGr.addNotNullQuery("filter_result_hash");
filterResultGr.query();
while (filterResultGr.next()) {
var hashValue = filterResultGr.getValue("filter_result_hash");
//making sure we are not storing corrupted data in map
if (!hashValue)
hashValue = "0.0";
filterResultMap[filterResultGr.policy] = hashValue;
}
var policyGr = undefined;
var cmdbCiToAgentMap = this.getCmdbCiToAgentMap();
if (Object.keys(cmdbCiToAgentMap).length < 1) {
gs.warn(gs.getMessage("PolicyClientsGenerator: No active agents found on the instance. Skipping syncClients()"));
policyGr = this.getActivePublishedPoliciesOrdered();
policyGr.query();
// Write empty values for all policies.
while (policyGr.next()) {
this.updatePolicyClientsResults({}, policyGr, filterResultMap[policyGr.getValue('sys_id')], false);
}
// Make sure we update the last sync time
this.updateLastSyncTime(now);
return;
}
var policyToCheckToSpecialComm = {};
var proxyPolicyToNonCiCheckIds = {};
this.fillPolicyToChecksMap(policyToCheckToSpecialComm, proxyPolicyToNonCiCheckIds);
var parentToChildrenMap = {};
var childToAgentsListMap = {};
policyGr = this.getActivePublishedPoliciesOrdered();
policyGr.query();
while (policyGr.next()) {
var prevHash = filterResultMap[policyGr.sys_id];
var policySysId = policyGr.getValue("sys_id");
try {
this.updatePolicyClientsJson(policyGr, prevHash, policyToCheckToSpecialComm[policySysId], proxyPolicyToNonCiCheckIds[policySysId], cmdbCiToAgentMap, parentToChildrenMap, childToAgentsListMap);
} catch (e) {
gs.error(gs.getMessage("PolicyClientsGenerator: Could not update Policy Clients for policy={0}. Error={1}", [policyGr.name, e]));
continue;
}
}
this.updateLastSyncTime(now);
},
performForceReCalcIfRequired: function() {
//check whether force recalculation is required for any policies.
//STRY53093659: for this story distribution of CIs among proxy agent is done. If any proxy agent went down, force recalc will be set to true for that policy that uses the down proxy agent.
//If we see any issue in force recalc for customers then we can ask them to create sn_agent.policy_force_recalc_required system property with value false.
if (gs.getProperty("sn_agent.policy_force_recalc_required", "true") == 'false')
return;
var now = new GlideDateTime();
var currentDomain = new sn_agent.DomainInfo().getCurrentDomain();
var shouldForceReCalc = false;
var forceRecalcGr = new GlideRecord("sn_agent_flags");
forceRecalcGr.addQuery("name", "mon_filters_last_force_recalc_sync_time");
forceRecalcGr.addQuery("sys_domain", currentDomain);
forceRecalcGr.query();
if (forceRecalcGr.next()) {
var lastForceRun = new GlideDateTime(forceRecalcGr.getValue("hash"));
shouldForceReCalc = (now.getNumericValue() - lastForceRun.getNumericValue()) > gs.getProperty("sn_agent.policy_force_recalc_interval_min", 2) * 60000;
} else {
//recalc if never force recalced before...
shouldForceReCalc = true;
}
if (shouldForceReCalc) {
var forceRecalcClientsGr = new GlideRecord('sn_agent_policy_clients');
forceRecalcClientsGr.addQuery("policy.active", "true");
forceRecalcClientsGr.addQuery("force_recalc", "true");
forceRecalcClientsGr.query();
while (forceRecalcClientsGr.next()) {
var agentPolicyGr = new GlideRecord("sn_agent_policy");
agentPolicyGr.addQuery("sys_id", forceRecalcClientsGr.policy);
agentPolicyGr.query();
if (agentPolicyGr.next()) {
gs.eventQueue("sn_agent.update_policy_clients_json", agentPolicyGr, false);
gs.debug(gs.getMessage("PolicyClientsGenerator: performForceReCalcIfRequired: Force recalc triggered for policy {0}", agentPolicyGr.name));
forceRecalcClientsGr.setValue("force_recalc", false);
forceRecalcClientsGr.update();
}
}
this.updateLastForceRecalcSyncTime(now);
}
},
getActivePublishedPoliciesOrdered: function() {
var policyGr = new GlideRecord('sn_agent_policy');
//calculate the filter only for active and published policies
policyGr.addActiveQuery();
policyGr.addEncodedQuery("is_draft=false");
policyGr.orderByDesc("published_parent");
policyGr.orderBy("order");
return policyGr;
},
updateLastSyncTime: function(now) {
var syncTimeHashGr = new GlideRecord("sn_agent_flags");
syncTimeHashGr.addQuery("name", "mon_filters_last_sync_time");
syncTimeHashGr.query();
if (!syncTimeHashGr.next())
syncTimeHashGr.setValue("name", "mon_filters_last_sync_time");
syncTimeHashGr.setValue("hash", now);
//this will insert or update
syncTimeHashGr.update();
},
updateLastForceRecalcSyncTime: function(now) {
var syncTimeHashGr = new GlideRecord("sn_agent_flags");
syncTimeHashGr.addQuery("name", "mon_filters_last_force_recalc_sync_time");
syncTimeHashGr.query();
if (!syncTimeHashGr.next())
syncTimeHashGr.setValue("name", "mon_filters_last_force_recalc_sync_time");
syncTimeHashGr.setValue("hash", now);
//this will insert or update
syncTimeHashGr.update();
},
getCmdbCiToAgentMap: function(ignoreStatus) {
var cmdbCiToAgentId = {};
//get only up agents which have CI
var agentsGr = new GlideRecord("sn_agent_ci_extended_info");
agentsGr.addNotNullQuery("cmdb_ci");
if (!ignoreStatus) { // default
agentsGr.addQuery('status', ["0", "1"]);
agentsGr.addQuery('data_collection', '0');
} else {
agentsGr.orderBy('status'); // put higher priority on agents that are active
}
agentsGr.query();
while (agentsGr.next()) {
var cmdbCi = agentsGr.getValue("cmdb_ci");
if (!cmdbCiToAgentId[cmdbCi])
cmdbCiToAgentId[cmdbCi] = agentsGr.getValue("agent_id");
}
return cmdbCiToAgentId;
},
fillPolicyToChecksMap: function(policyToCheckToSpecialComm, proxyPolicyToNonCiCheckIds, specificPolicyId) {
var checkGr = new GlideRecord('sn_agent_check');
//for building client json we need check ids only of proxy policies or in case we have ci refference from a command
checkGr.addEncodedQuery('monitoring_policy.proxy_advanced=true^ORmonitoring_policy.proxy_script_advanced=true^ORmonitoring_policy.single_proxy_agent=true^ORmonitoring_policy.proxy_cluster=true^ORcommandLIKE' + this.CI_PARAM_KEY);
checkGr.addEncodedQuery('monitoring_policy.active=true');
//bring checks only of specifc policy
if (specificPolicyId) {
checkGr.addQuery("monitoring_policy", specificPolicyId);
}
checkGr.query();
while (checkGr.next()) {
var policyId = checkGr.getValue("monitoring_policy");
var checkInstanceId = checkGr.getValue("sys_id");
var command = checkGr.getValue("command");
var ciParamsStart = command.indexOf(this.CI_PARAM_KEY);
if (ciParamsStart > 0) {
var checkToCiParams = policyToCheckToSpecialComm[policyId];
if (!checkToCiParams) {
checkToCiParams = {};
policyToCheckToSpecialComm[policyId] = checkToCiParams;
}
checkToCiParams[checkInstanceId] = this.parseCommandToCiParms(command, ciParamsStart);
} else {
//we get here if this check belongs to proxy but it has no ci in the command
var checkIds = proxyPolicyToNonCiCheckIds[policyId];
if (!checkIds) {
checkIds = [];
proxyPolicyToNonCiCheckIds[policyId] = checkIds;
}
checkIds.push(checkGr.getValue("sys_id"));
}
}
},
updatePolicyClientsJson: function(policyGr, prevHash, checkToSpecialComm, proxyNonCiCheckIds, cmdbCiToAgentId, parentToChildrenMap, childToAgentsListMap, isSingleUpdatePolicy, isManualChange) {
var policySysId = policyGr.getValue("sys_id");
var clientsCis = this.getClientsJson(policyGr, checkToSpecialComm, proxyNonCiCheckIds, cmdbCiToAgentId);
if (this.isChildPolicy(policyGr)) {
// policyGr is a child policy
// Since the update is done according to the children's order, when we update a particular child we know that all its siblings, with a smaller order,
// were updated before and therefore updated in the parentToChildrenMap and childToAgentsListMap.
var siblingsSysIds = parentToChildrenMap[policyGr.getValue("published_parent")];
if (siblingsSysIds && siblingsSysIds != null && siblingsSysIds != "") {
// Subtract all the agents, that associate to siblings with a smaller order, from the current child clients json
clientsCis = this.subtractAgentsListsFromClientsJson(clientsCis, siblingsSysIds, childToAgentsListMap);
}
// Add child to maps
this.fillParentToChildrenMap(policyGr, parentToChildrenMap);
this.fillChildToAgentsListMap(policySysId, childToAgentsListMap, clientsCis);
} else if (this.isParentPolicy(policyGr)) {
// policyGr is a parent policy
// Since the update is done first for the children and then for the parents, when we update a parent we know that all its children were updated in the parentToChildrenMap and childToAgentsListMap.
// Subtract all the agents that associate to children from the parent clients json
clientsCis = this.subtractAgentsListsFromClientsJson(clientsCis, parentToChildrenMap[policySysId], childToAgentsListMap);
}
return this.updatePolicyClientsResults(clientsCis, policyGr, prevHash, isManualChange);
},
fillParentToChildrenMap: function(policyGr, parentToChildrenMap, isChild) {
var parentSysId = policyGr.getValue("published_parent");
if (!parentToChildrenMap[parentSysId]) {
parentToChildrenMap[parentSysId] = [];
}
parentToChildrenMap[parentSysId].push(policyGr.getValue("sys_id"));
},
fillChildToAgentsListMap: function(policySysId, childToAgentsListMap, clientsCis) {
var agents = Object.keys(clientsCis);
childToAgentsListMap[policySysId] = agents;
},
subtractAgentsListsFromClientsJson: function(clientsJson, childrenSysIds, childToAgentsListMap) {
for (var child in childrenSysIds) {
var childSysId = childrenSysIds[child];
if (childToAgentsListMap[childSysId]) {
clientsJson = this.removeClientsFromClientsJson(childToAgentsListMap[childSysId], clientsJson);
}
}
return clientsJson;
},
updatePolicyClientsResults: function(clientsCis, policyGr, prevHash, isManualChange) {
//order agents and cis since I don't want the client result to be sensitive to the order of the agents or the cis in the result
var sortedClientCis = this.sortObject(clientsCis);
var newClientsStr = JSON.stringify(sortedClientCis);
var newHash = this.getStringCheckSum(newClientsStr);
// If nothing had changed: return false;
if (newHash == prevHash) {
return false;
}
var agentsId = Object.keys(clientsCis).join(',');
// Clear some memory.
clientsCis = undefined;
filterResultGr = new GlideRecord('sn_agent_policy_clients');
filterResultGr.addQuery("policy", policyGr.sys_id);
filterResultGr.query();
if (!filterResultGr.next())
filterResultGr.setValue("policy", policyGr.sys_id);
if (isManualChange && GlidePluginManager.isActive('com.itom-monitoring') && gs.getProperty("sn_itmon.enable_auto_alert_closing", "false") == 'true') {
gs.eventQueue("sn_agent.notify_about_filter_changes", policyGr, newClientsStr, filterResultGr.getValue("clients_json")); //send event to notify about changes, only handled if monitoring is installed
}
filterResultGr.setValue("agents_list", agentsId);
filterResultGr.setValue("clients_json", newClientsStr);
filterResultGr.setValue("filter_result_hash", newHash);
//this will insert or update
filterResultGr.update();
// Clear some memory
agentsId = undefined;
newClientsStr = undefined;
newHash = undefined;
//Adding code to update the agent to monitored CI mapping records whenever there is change in the clientjson for proxy agent policies
if (policyGr.single_proxy_agent || policyGr.proxy_advanced || policyGr.proxy_script_advanced || policyGr.proxy_cluster) {
this.populatePolicyProxyAgentsMapping(policyGr, sortedClientCis);
}
return true;
},
populatePolicyProxyAgentsMapping: function(policyGr, sortedClientCis) {
var monitoredCIsGr = new GlideRecord("sn_agent_policy_proxy_agent_mapping");
monitoredCIsGr.addQuery("policy", policyGr.sys_id);
//delete existing records and add new rows again as we the distribution of monitoredCIs to proxy agents would have changed
monitoredCIsGr.deleteMultiple();
//Populating the policy to proxy agent mapping, when agent goes down, this table will be used to see whether force_recalc is required for policy as proxyagent went down.
for (var agentId in sortedClientCis) {
monitoredCIsGr = new GlideRecord("sn_agent_policy_proxy_agent_mapping");
monitoredCIsGr.setValue("policy", policyGr.sys_id);
monitoredCIsGr.setValue("agent", agentId);
//this will insert or update
monitoredCIsGr.update();
}
},
removeClientsFromClientsJson: function(clientsToRemove, clientsToRemoveFromThem) {
for (var client in clientsToRemove) {
delete clientsToRemoveFromThem[clientsToRemove[client]];
}
return clientsToRemoveFromThem;
},
getClientsJson: function(policyGr, checkToSpecialComm, proxyNonCiCheckIds, cmdbCiToAgentId) {
var monitoredCis;
try {
if (policyGr.monitored_ci_type_script) {
var evaluator = new GlideScopedEvaluator();
// We cannot call getMonitoredCIsRelatedToAgentsHosts() method on monitoredCis here because a script may return a complex GlideRecord that
// We cannot reconsturct.
monitoredCis = evaluator.evaluateScript(policyGr, 'monitored_ci_script');
if (!monitoredCis) {
gs.warn(gs.getMessage("PolicyClientsGenerator: The monitored CI type is invalid for policy {0}", policyGr.name));
return {};
}
monitoredCis.query();
} else if (policyGr.monitored_ci_type_group) {
monitoredCis = this.getCisFromGroup(policyGr.monitored_ci_group, policyGr.name, cmdbCiToAgentId);
if (!this.analyzeMonitoredCIsStatus(monitoredCis)) {
gs.warn(gs.getMessage("PolicyClientsGenerator: Failed creating monitored CI query from cmdb group for policy {0}", policyGr.name));
return {};
}
} else {
// Find all the hosts that satisfy the filter condition
monitoredCis = new GlideRecord(policyGr.table);
monitoredCis.addEncodedQuery(policyGr.filter);
monitoredCis = this.getMonitoredCIsRelatedToAgentsHosts(policyGr.name, monitoredCis, Object.keys(cmdbCiToAgentId));
if (!this.analyzeMonitoredCIsStatus(monitoredCis)) {
gs.warn(gs.getMessage("PolicyClientsGenerator: did not get monitored CIs for policy {0}", policyGr.name));
return {};
}
}
} catch (e) {
gs.error(gs.getMessage("PolicyClientsGenerator: Could not calculate monitoring CIs for policy={0} and table={1}. Error={2}", [policyGr.name, policyGr.table, e]));
return {};
}
// Handle the case of proxy agent
if (policyGr.single_proxy_agent || policyGr.proxy_advanced || policyGr.proxy_script_advanced || policyGr.proxy_cluster) {
return this.getProxyClients(policyGr, monitoredCis, checkToSpecialComm, proxyNonCiCheckIds);
} else {
return this.getClientsByCis(monitoredCis, checkToSpecialComm, policyGr.auto_binding, cmdbCiToAgentId, false, policyGr.service_filter, policyGr.is_service_filter);
}
},
getCisFromGroup: function(groupName, policyName, cmdbCiToAgentId) {
var groupRecord = new GlideRecord("cmdb_group");
groupRecord.addQuery("group_name", groupName);
groupRecord.query();
if (groupRecord.next()) {
var groupId = groupRecord.getValue("sys_id");
var results = JSON.parse(sn_cmdbgroup.CMDBGroupAPI.getAllCI(groupId));
if (results && results.idList && results.idList.length > 0) {
var ciRecord = new GlideRecord("cmdb_ci");
ciRecord.get(results.idList[0]);
var className = ciRecord.getValue("sys_class_name");
//find the most generic type that suits this class type
var baseTables = new GlideTableHierarchy(className).getTables();
for (var i = 0; i < baseTables.length; i++) {
var currTable = baseTables[i];
if (currTable == "cmdb_ci_computer" || currTable == "cmdb_ci_docker_container" || currTable == "cmdb_ci_appl" || currTable == "sn_agent_cmdb_ci_agent") {
className = currTable;
break;
}
}
var monitoredCis = new GlideRecord(className);
monitoredCis.addQuery("sys_id", "IN", results.idList);
monitoredCis = this.getMonitoredCIsRelatedToAgentsHosts(policyName, monitoredCis, Object.keys(cmdbCiToAgentId), className, results.idList.length);
return monitoredCis;
}
}
return "";
},
/*
* A proxy client is a client serving other hosts, which do not have a client on their own.
* In this case, we need to provide the IP addresses and additional parameter per monitored entiry
*/
getProxyClients: function(policyGr, hosts, checkToSpecialComm, proxyNonCiCheckIds) {
if (policyGr.proxy_script_advanced) {
return this.getProxyScriptClients(policyGr, hosts, checkToSpecialComm, proxyNonCiCheckIds);
}
var runChecksOnAllAgents = policyGr.run_checks_on_all_proxyagents;
var policyClients = {};
var clients = [];
if (policyGr.proxy_advanced) {
var proxyRecord = new GlideRecord('sn_agent_cmdb_ci_agent');
if (policyGr.proxy_filter) {
proxyRecord.addEncodedQuery(policyGr.proxy_filter);
}
//take only proxy that is up and not silent
proxyRecord.addEncodedQuery("agent_extended_info.statusIN0,1^agent_extended_info.data_collection=0");
proxyRecord.orderBy('name');
proxyRecord.query();
while (proxyRecord.next()) {
clients.push('' + proxyRecord.agent_id);
}
} else if (policyGr.proxy_cluster) {
//get the agents present in that cluster and used them for client json calculation
var agentGR = new GlideRecord('sn_agent_cmdb_ci_agent');
var clusterGR = agentGR.addJoinQuery('sn_agent_in_agentcluster', 'sys_id', 'agent');
agentGR.addEncodedQuery("agent_extended_info.statusIN0,1^agent_extended_info.data_collection=0");
clusterGR.addCondition("cluster", policyGr.agent_cluster_name);
agentGR.orderBy('name');
agentGR.query();
while (agentGR.next()) {
clients.push('' + agentGR.agent_id);
}
} else {
var extendedInfoRecord = policyGr.proxy_agent.agent_extended_info;
//take only proxy that is up and not silent
if (extendedInfoRecord.status != 2 && extendedInfoRecord.data_collection == 0)
clients.push('' + extendedInfoRecord.agent_id);
}
var numOfClients = clients.length;
//If there are no valid proxy agents then return empty {}
if (numOfClients < 1) {
gs.warn(gs.getMessage("In PolicyClientsGenerator: getProxyClients: no live proxy_agents found for policy={0}", [policyGr.name]));
return policyClients;
}
//For non load balancing case each agent has to monitor every monitoredCI, for load balancing case only one agent will monitor monitoredCI
var numOfAgentsTobeUsed;
if (runChecksOnAllAgents) {
numOfAgentsTobeUsed = numOfClients;
} else {
//As load balancing proxy agent feature is enabled, we need to assign the monitoredCI to single proxy agent and not to all agents
//So assigning numOfAgentsUsed=1 so that the below "for" loop executes once
numOfAgentsTobeUsed = 1;
}
//Using this variable to use % operator to assign monitoredCI to agents in roundrobin fashion
var monitoredCisIndex = 0;
while (hosts.next()) {
for (var i = 0; i < numOfAgentsTobeUsed; i++) {
var proxyAgentId;
if (runChecksOnAllAgents) {
proxyAgentId = clients[i];
} else {
//Will assign monitoredCIs on round robin fashion to proxy agents
//Load balance the monitoredCIs among proxy agents, ratherthan running all monitoredCI checks on all agents
proxyAgentId = clients[monitoredCisIndex++ % numOfClients];
}
var currentClientCis = {};
if (policyGr.auto_binding) {
this.addProxyCheckParams(policyGr, currentClientCis, hosts, checkToSpecialComm, proxyNonCiCheckIds, proxyAgentId);
}
if (!policyClients[proxyAgentId])
policyClients[proxyAgentId] = currentClientCis;
else {
// If object already exist, merge the two objects. Should not worry about over-writing as the keys are ids.
for (var key in currentClientCis)
policyClients[proxyAgentId][key] = currentClientCis[key];
}
}
}
return policyClients;
},
isProxyAgentUpAndNotSilent: function(proxyAgentId) {
var proxyRecord = new GlideRecord('sn_agent_ci_extended_info');
proxyRecord.addQuery("agent_id", proxyAgentId);
proxyRecord.addEncodedQuery("statusIN0,1^data_collection=0");
proxyRecord.query();
return proxyRecord.hasNext();
},
getProxyScriptClients: function(gr, hosts, checkToSpecialComm, proxyNonCiCheckIds) {
var policyClients = {};
var clients = [];
var proxyAgentId;
var runChecksOnAllAgents = gr.run_checks_on_all_proxyagents;
//Using this variable to use % operator to assign monitoredCI to agents in roundrobin fashion
var monitoredCisIndex = 0;
while (hosts.next()) {
var evaluator = new GlideScopedEvaluator();
evaluator.putVariable('answer', null);
evaluator.putVariable('currentCiResult', hosts); //pass current host record as param
var proxy_agents = evaluator.evaluateScript(gr, 'proxy_script');
if (!proxy_agents)
proxy_agents = evaluator.getVariable('answer');
// Validate that proxy_agents var is of type string
if (typeof proxy_agents != 'string') {
gs.warn(gs.getMessage("In PolicyClientsGenerator, proxy_agents did not evaluate to type string. proxy_agents type: {0}", [typeof proxy_agents]));
continue;
}
if (proxy_agents) {
var liveProxyAgents = [];
var agentGR = new GlideRecord('sn_agent_ci_extended_info');
agentGR.addQuery('agent_id', 'IN', proxy_agents);
agentGR.addEncodedQuery("statusIN0,1^data_collection=0");
agentGR.orderBy('name');
agentGR.query();
while (agentGR.next()) {
liveProxyAgents.push(agentGR.getValue('agent_id'));
}
var numOfClients = liveProxyAgents.length;
//if there are no Agents that are up and with data_collection on then continue for other monitoredCIs,
//not returning here as script may be written to take different agents for different monitoredCIs and agents may exist for other monitoredCIs
if (numOfClients < 1) {
gs.warn(gs.getMessage("In PolicyClientsGenerator: getProxyScriptClients: no live proxy_agents found for monitoredCI={0} in policy={1}", [hosts.name, gr.name]));
continue;
}
//using numOfAgentsUsed variable willbe same as number of agents for runChecksOnAllAgents usecase
//and for !runChecksOnAllAgents its value will be 1, as for loadbalancing case need to assign monitoredCI for single proxy agent
var numOfAgentsTobeUsed;
if (runChecksOnAllAgents) {
numOfAgentsTobeUsed = numOfClients;
} else {
//As load balancing proxy agent feature is enabled, we need to assign the monitoredCI to single proxy agent and not to all agents
//So assigning numOfAgentsUsed=1 so that the below "for" loop executes once
numOfAgentsTobeUsed = 1;
}
for (var i = 0; i < numOfAgentsTobeUsed; i++) {
if (runChecksOnAllAgents) {
proxyAgentId = liveProxyAgents[i];
} else {
//Load balance the monitoredCIs among proxy agents, ratherthan running all monitoredCI checks on all agents
//Will assign monitoredCIs on round robin fashion to agent to avoid hosts.query() again, create the records clientsjson for that agent.
//If policy is not to run on all agents/load balancing then choose agent in round robin fashion
proxyAgentId = liveProxyAgents[monitoredCisIndex++ % numOfClients];
}
var clientCis = policyClients[proxyAgentId];
if (!clientCis) {
clientCis = {};
policyClients[proxyAgentId] = clientCis;
}
this.addProxyCheckParams(gr, clientCis, hosts, checkToSpecialComm, proxyNonCiCheckIds, proxyAgentId);
}
}
}
return policyClients;
},
addProxyCheckParams: function(gr, clientCis, hostGr, checkToSpecialComm, proxyNonCiCheckIds, proxyAgentId) {
var tableName;
if (gr.monitored_ci_type_script) {
var evaluator = new GlideScopedEvaluator();
var monitoredCis = evaluator.evaluateScript(gr, 'monitored_ci_script');
if (monitoredCis == null) {
gs.error("The monitored CI type is invalid");
} else {
tableName = monitoredCis.getTableName();
}
} else {
tableName = gr.table;
}
var isEntryPointView = tableName == this.ENTRYPOINT_DB_VIEW;
var ciId = hostGr.sys_id;
//if the table is database view - http entrypoint the ciId will be the the sys_id of the entrypoint
if (isEntryPointView) {
ciId = hostGr.endpoint_sys_id;
}
//first, add all ci check params
clientCis[ciId] = this.getCheckToCiParams(checkToSpecialComm, hostGr);
//we want to add ip and service id to all check ids...
var checkIds = [];
if (checkToSpecialComm)
checkIds = Object.keys(checkToSpecialComm);
if (proxyNonCiCheckIds)
checkIds = checkIds.concat(proxyNonCiCheckIds);
var targetIp = hostGr.getValue("ip_address");
for (var i = 0; i < checkIds.length; i++) {
var checkId = checkIds[i];
//in case of entry point view, add service id to all checks
if (isEntryPointView) {
var paramsMap = clientCis[ciId][checkId];
if (!paramsMap) {
paramsMap = {};
clientCis[ciId][checkId] = paramsMap;
}
paramsMap['ep_service_name'] = '' + hostGr.service_name;
var entrypointMetricResourceName = this.getEntrypointMetricsResourceName(proxyAgentId);
// 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))
paramsMap[this.ENTRYPOINT_METRIC_RESOURCE_PARAM] = entrypointMetricResourceName;
}
// Add target IP to all checks, to be used when check returns
if (targetIp && targetIp.length > 0) {
paramsMap = clientCis[ciId][checkId];
if (!paramsMap) {
paramsMap = {};
clientCis[ciId][checkId] = paramsMap;
}
paramsMap['target_ip'] = targetIp;
}
paramsMap = clientCis[ciId][checkId];
if (!paramsMap) {
paramsMap = {};
clientCis[ciId][checkId] = paramsMap;
}
paramsMap[this.IS_PROXY_CHECK] = true;
}
},
getClientsByCis: function(cis, checkToSpecialParams, isAutoBinding, ciToAgentId, isTestCheckRun, serviceFilterQuery, useServiceFilter) {
var policyClients = {};
var tableName = cis.getTableName();
if (tableName == "sn_agent_cmdb_ci_agent") {
while (cis.next()) {
policyClients[cis.getValue("agent_id")] = {};
if (isAutoBinding)
policyClients[cis.getValue("agent_id")][cis.getValue("sys_id")] = {};
}
return policyClients;
}
var hostBasedPolicy = false;
var isDockerContainer = false;
//now check if its in hardware hierarchy OR docker container
var parentTable = this.getParentTable(tableName);
if (parentTable == this.DOCKER_PARENT_TABLE)
isDockerContainer = true;
else if (parentTable == this.HOST_PARENT_TABLE)
hostBasedPolicy = true;
var ciToCheckSpecialParams = {};
while (cis.next()) {
var ciId = cis.getValue("sys_id");
//in case we are in host based policy and we have ciToAgent mapping, check if we have an agent for this CI, if not, continue.
if (hostBasedPolicy && ciToAgentId && !ciToAgentId[ciId]) {
continue;
}
var checkToCiParams = ciToCheckSpecialParams[ciId];
ciToCheckSpecialParams[ciId] = this.getCheckToCiParams(checkToSpecialParams, cis);
}
// We can free up some memory
cis = undefined;
// list of CIs the policy is configured to monitor
var monitoredCisIds = Object.keys(ciToCheckSpecialParams);
if (monitoredCisIds.length == 0)
return {};
if (useServiceFilter) {
monitoredCisIds = this.getCisByServiceQuery(serviceFilterQuery, ciToCheckSpecialParams, monitoredCisIds);
if (monitoredCisIds.length == 0)
return {};
}
var hostToAppsMap = {};
if (hostBasedPolicy) {
var hostIds = monitoredCisIds;
} else if (isDockerContainer) {
hostToAppsMap = this.getHostToDockerContainerMap(monitoredCisIds);
hostIds = Object.keys(hostToAppsMap);
} else {
hostToAppsMap = this.getHostToAppsMap(monitoredCisIds);
hostIds = Object.keys(hostToAppsMap);
}
//in case we are performing this calculation for specific policy (upon policy update or insert), or in case of framework api invocation
if (!ciToAgentId) {
ciToAgentId = this.getHostToAgentMapForSpecificHosts(hostIds, isTestCheckRun);
}
for (i = 0; i < hostIds.length; i++) {
var hostId = hostIds[i];
var agentId = ciToAgentId[hostId];
//if we have don't have an agent for this app, continue
if (!agentId)
continue;
var ciToFullParams = {};
if (isAutoBinding) {
if (hostBasedPolicy) {
ciToFullParams[hostId] = ciToCheckSpecialParams[hostId];
} else {
var apps = hostToAppsMap[hostId];
for (var appIndex in apps) {
var appId = apps[appIndex];
ciToFullParams[appId] = ciToCheckSpecialParams[appId];
}
}
}
policyClients[agentId] = ciToFullParams;
}
// free memory
ciToCheckSpecialParams = undefined;
return policyClients;
},
//handle service filter
getCisByServiceQuery: function(serviceFilterQuery, ciToCheckSpecialParams, monitoredCisIds) {
var serviceFieldsToExtract = {};
//taking the first one, since all CIs has same ci params.
for (var ci in ciToCheckSpecialParams) {
var ciParamsPerCheck = ciToCheckSpecialParams[ci];
//only in case we have actual service info to add
if (ciParamsPerCheck && Object.keys(ciParamsPerCheck).length > 0) {
for (var checkId in ciParamsPerCheck) {
var ciParams = ciParamsPerCheck[checkId]["ci"];
for (var ciParam in ciParams) {
if (ciParam.startsWith(this.SERVICE_INFO_PREFIX)) {
serviceFieldsToExtract[ciParam.substring(this.SERVICE_INFO_PREFIX.length)] = true;
}
}
}
}
//all cis have same ci param, no need to iterate them all.
break;
}
var serviceRecord = new GlideRecord("cmdb_ci_service");
if (serviceFilterQuery)
serviceRecord.addEncodedQuery(serviceFilterQuery);
var svcCiAssocQuery = serviceRecord.addJoinQuery("svc_ci_assoc", "sys_id", "service_id");
svcCiAssocQuery.addCondition("ci_id", "IN", monitoredCisIds.join(",")); // DO NOT REMOVE THE JOIN IN THIS LINE
serviceRecord.query();
var servicesInfo = {};
while (serviceRecord.next()) {
var serviceId = serviceRecord.getValue("sys_id");
var discoverySource = serviceRecord.getValue("discovery_source");
var currentServiceInfo = {};
currentServiceInfo["discovery_source"] = discoverySource;
for (var fieldToExtract in serviceFieldsToExtract) {
currentServiceInfo[this.SERVICE_INFO_PREFIX + fieldToExtract] = serviceRecord.getValue(fieldToExtract);
}
servicesInfo[serviceId] = currentServiceInfo;
}
var serviceIds = Object.keys(servicesInfo);
//in case we didn't find any service to match the condition, return empty clients list
if (serviceIds.length == 0)
return [];
var ciToInsertedServiceInfo = {};
var assocRecord = new GlideRecord("svc_ci_assoc");
assocRecord.addQuery("ci_id", "IN", monitoredCisIds);
assocRecord.addQuery("service_id", "IN", serviceIds);
assocRecord.query();
while (assocRecord.next()) {
var monitoredCiId = assocRecord.getValue("ci_id");
serviceId = assocRecord.getValue("service_id");
discoverySource = servicesInfo[serviceId]["discovery_source"];
//do this processing only if we have service info requested in teh check
if (Object.keys(serviceFieldsToExtract).length > 0) {
var processedServiceInfo = ciToInsertedServiceInfo[monitoredCiId];
if (!processedServiceInfo) {
processedServiceInfo = {};
processedServiceInfo["discovery_source"] = discoverySource;
processedServiceInfo["rel_created_on"] = new GlideDateTime(assocRecord.getValue("sys_created_on"));
ciToInsertedServiceInfo[monitoredCiId] = processedServiceInfo;
} else {
var relCreationTime = new GlideDateTime(assocRecord.getValue("sys_created_on"));
//if we are coming with something newer and prev was not discovered by ACC, ignore
if ((relCreationTime > processedServiceInfo["rel_created_on"] && processedServiceInfo["discovery_source"] != "AgentClientCollector") ||
//if we are coming with something older that is discovered by ACC, ignore
(relCreationTime < processedServiceInfo["rel_created_on"] && discoverySource == "AgentClientCollector"))
continue;
else {
//we will override the processed service info
processedServiceInfo["discovery_source"] = servicesInfo[serviceId]["discovery_source"];
processedServiceInfo["rel_created_on"] = new GlideDateTime(assocRecord.getValue("sys_created_on"));
}
}
ciParamsPerCheck = ciToCheckSpecialParams[monitoredCiId];
for (checkId in ciParamsPerCheck) {
ciParams = ciParamsPerCheck[checkId]["ci"];
for (var param in ciParams) {
if (param.startsWith(this.SERVICE_INFO_PREFIX) && servicesInfo[serviceId][param])
ciParams[param] = servicesInfo[serviceId][param];
}
}
} else {
ciToInsertedServiceInfo[monitoredCiId] = {};
}
}
//our new monitored CIs are those that are related to service that matches the condition on the policy
return Object.keys(ciToInsertedServiceInfo);
},
getHostToAgentMapForSpecificHosts: function(hostIds, isTestCheckRun) {
var ciToAgentId = {};
this.splitIn(hostIds, function(ids) {
var queryStr = "statusIN0,1";
if (!isTestCheckRun) {
queryStr = queryStr + "^data_collection=0";
}
var clients = new GlideRecord("sn_agent_ci_extended_info");
clients.addQuery("cmdb_ci", "IN", ids);
clients.addEncodedQuery(queryStr);
clients.query();
while (clients.next()) {
ciToAgentId[clients.getValue("cmdb_ci")] = clients.getValue("agent_id");
}
});
return ciToAgentId;
},
getCheckToCiParams: function(checkToSpecialParams, cis) {
var checkToCiParams = {};
if (!checkToSpecialParams)
return checkToCiParams;
var checkInstances = Object.keys(checkToSpecialParams);
for (i = 0; i < checkInstances.length; i++) {
var checkId = checkInstances[i];
var ciParams = {};
var specialParams = checkToSpecialParams[checkId];
var specialParamsKeys = Object.keys(specialParams);
for (var j = 0; j < specialParamsKeys.length; j++) {
var param = specialParamsKeys[j];
// serviceInfo will be filled in later step, when using service filter
if (param.startsWith(this.SERVICE_INFO_PREFIX)) {
ciParams[param] = "";
} else {
var paramValue = cis.getValue(param);
if (paramValue) {
if (this.base64CheckParamsUtil.shouldBase64EncodeCheckParam(checkId, this.base64CheckParamsUtil.BASE64_ENC_CHECK_PARAM_TYPES.BASE64_ENC_CHECK_CI_PARAM, param))
paramValue = gs.base64Encode(paramValue);
ciParams[param] = paramValue;
}
}
}
if (Object.keys(ciParams).length > 0) {
var ciParamsWrapper = {};
//we add additional hard coded 'ci' to the map, so the key of the param will match the "params_ci_"
ciParamsWrapper['ci'] = ciParams;
checkToCiParams[checkId] = ciParamsWrapper;
}
}
return checkToCiParams;
},
/*
* scan the command for occurrences of {{.labels.params_ci_xxxxxx}} and add the parameter xxxx to the map
* send to the MID
*/
parseCommandToCiParms: function(command, paramsStart) {
// Look for string like {{.labels.params_ci_xxxxxx}} in the check command
var ciParams = {};
while (paramsStart > 0) {
command = command.substring(paramsStart + 20);
var paramsEnd = command.indexOf('}}');
if (paramsEnd < 0)
break;
var defaultValueStart = command.indexOf('|');
if (defaultValueStart > 0 && defaultValueStart < paramsEnd)
paramsEnd = defaultValueStart;
var paramName = command.substring(0, paramsEnd);
ciParams[paramName] = {};
paramsStart = command.indexOf(this.CI_PARAM_KEY);
}
return ciParams;
},
/*
* return map between hosts and docker container CIs
* Can be used in the future as baseline to expand functionality to
* support any hierarchal ci structure
*/
getHostToDockerContainerMap: function(appIds) {
var findRels = function(parentOrChild, operator, CiId, typeId) {
var relgr = new GlideRecord("cmdb_rel_ci");
relgr.addQuery(parentOrChild, operator, CiId);
relgr.addQuery("type", typeId);
relgr.query();
return relgr;
};
var hostToAppsMap = {};
var engineToContainer = {};
var managedBy = 'bf83653c0ab30150761028c73a4de0f4';
var runsOn = '60bc4e22c0a8010e01f074cbe6bd73c3';
// Going over all containers in chuncks, and creating an Engine -> Containers map.
this.splitIn(appIds, function(ids) {
// Looking for the docker engine the container is manged by
var containerToEngine = findRels('parent', 'IN', ids, managedBy);
while (containerToEngine.next()) {
var engine = '' + containerToEngine.child;
var app = '' + containerToEngine.parent;
if (!engineToContainer[engine]) {
engineToContainer[engine] = [];
}
engineToContainer[engine].push(app);
}
});
var engineIds = Object.keys(engineToContainer);
// Going over all engines in chuncks to find the host they run on
this.splitIn(engineIds, function(ids) {
var engineToHost = findRels('parent', 'IN', ids, runsOn);
while (engineToHost.next()) {
var engine = '' + engineToHost.parent;
var host = '' + engineToHost.child;
// Getting the containers that run on this engine
var containers = engineToContainer[engine];
if (!hostToAppsMap[host]) {
hostToAppsMap[host] = [];
}
// Adding the containers to the agent->container map
// In case that there might be 2 engines running on a single host
// We use concat and not pushing the containers directly
hostToAppsMap[host] = hostToAppsMap[host].concat(containers);
}
});
return hostToAppsMap;
},
/*
* Find all relations to a specific CI(CiId) of a specific type(typeId)
*/
findRelations: function(parentOrChild, operator, CiId, typeId) {
var relgr = new GlideRecord("cmdb_rel_ci");
relgr.addQuery(parentOrChild, operator, CiId);
relgr.addQuery("type", typeId);
relgr.query();
return relgr;
},
/*
* return map between hosts and applications CIs
*/
getHostToAppsMap: function(appIds) {
var hostToAppsMap = {};
this.splitIn(appIds, function(ids) {
var rel = new GlideRecord('cmdb_rel_ci');
rel.addQuery('type', '60bc4e22c0a8010e01f074cbe6bd73c3'); // Runs-On
rel.addQuery('parent', 'IN', ids);
rel.query();
while (rel.next()) {
var app = '' + rel.parent;
var host = '' + rel.child;
if (!hostToAppsMap[host])
hostToAppsMap[host] = [];
hostToAppsMap[host].push(app);
}
});
return hostToAppsMap;
},
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));
}
},
getStringCheckSum: function(my_string) {
var chk = 0x0;
for (var i = 0; i < my_string.length; i++) {
chk += (my_string.charCodeAt(i) * (i + 1));
}
return chk;
},
sortObject: function(object) {
var sortedObj = {},
keys = Object.keys(object);
keys.sort();
for (var index in keys) {
var key = keys[index];
if (typeof object[key] == 'object') {
sortedObj[key] = this.sortObject(object[key]);
} else {
sortedObj[key] = object[key];
}
}
return sortedObj;
},
getAndUpdateReCalcFilterFlag: function(newFlagValue) {
var hashGr = new GlideRecord("sn_agent_flags");
hashGr.addQuery("name", "mon_filters_re_calc");
var currentDomain = new sn_agent.DomainInfo().getCurrentDomain();
hashGr.addQuery("sys_domain", currentDomain);
hashGr.query();
if (hashGr.next()) {
var prevFlag = hashGr.getValue("hash");
//don't update the db if flag wasn't changed
if (prevFlag != newFlagValue) {
hashGr.setValue("hash", newFlagValue);
hashGr.update();
}
return prevFlag;
} else {
hashGr.setValue("name", "mon_filters_re_calc");
hashGr.setValue("hash", newFlagValue);
hashGr.insert();
//if value doesn't exist, we should recalc
return "true";
}
},
updateSinglePolicyClientsJson: function(policyGr, isManualChange) {
var updateResult = false;
var parentToChildrenMap = {};
var childToAgentsListMap = {};
var policySysId = policyGr.getValue("sys_id");
var parentSysId = policyGr.getValue("published_parent");
if (this.isChildPolicy(policyGr)) {
// policyGr is a child policy -> update all its siblings according to their order and its parent
updateResult = this.updateSiblingsAndParent(policySysId, parentSysId, isManualChange);
} else if (this.isParentPolicy(policyGr)) {
// policyGr is a parent policy
parentToChildrenMap = this.fillParentToChildrenMapForCertainParent(policySysId);
childToAgentsListMap = this.fillChildToAgentsListMapForCertainParent(policyGr, parentToChildrenMap[policySysId], childToAgentsListMap);
updateResult = this.updateSinglePolicyContent(policyGr, parentToChildrenMap, childToAgentsListMap, isManualChange);
} else {
updateResult = this.updateSinglePolicyContent(policyGr, parentToChildrenMap, childToAgentsListMap, isManualChange);
}
return updateResult;
},
updateSiblingsAndParent: function(childSysId, parentSysId, isManualChange) {
var parentToChildrenMap = {};
var childToAgentsListMap = {};
var updateResult = false;
var childPolicyGr = new GlideRecord('sn_agent_policy');
// update only for active and published policies
childPolicyGr.addActiveQuery();
childPolicyGr.addEncodedQuery("is_draft=false");
childPolicyGr.addQuery("published_parent", parentSysId);
childPolicyGr.orderBy("order");
childPolicyGr.query();
while (childPolicyGr.next()) {
var result = this.updateSinglePolicyContent(childPolicyGr, parentToChildrenMap, childToAgentsListMap, isManualChange);
if (childPolicyGr.getValue("sys_id") == childSysId) {
updateResult = result;
}
}
this.updateParentPolicyClientsJson(parentSysId, parentToChildrenMap, childToAgentsListMap, isManualChange);
return updateResult;
},
isChildPolicy: function(policyGr) {
return policyGr.getValue("relationship_state") == "2";
},
isParentPolicy: function(policyGr) {
return policyGr.getValue("relationship_state") == "1";
},
getClientsJsonForSinglePolicy: function(policyGr) {
var policyId = policyGr.getValue("sys_id");
var policyToCheckToSpecialComm = {};
var proxyPolicyToNonCiCheckIds = {};
this.fillPolicyToChecksMap(policyToCheckToSpecialComm, proxyPolicyToNonCiCheckIds, policyId);
var cmdbCiToAgentMap = this.getCmdbCiToAgentMap(true);
return this.getClientsJson(policyGr, policyToCheckToSpecialComm[policyId], proxyPolicyToNonCiCheckIds[policyId], cmdbCiToAgentMap);
},
updateSinglePolicyContent: function(policyGr, parentToChildrenMap, childToAgentsListMap, isManualChange) {
var policyId = policyGr.getValue("sys_id");
var prevFilterHash = "";
var filterResultGr = new GlideRecord('sn_agent_policy_clients');
filterResultGr.addQuery("policy", policyId);
filterResultGr.query();
if (filterResultGr.next()) {
prevFilterHash = filterResultGr.getValue("filter_result_hash");
}
var policyToCheckToSpecialComm = {};
var proxyPolicyToNonCiCheckIds = {};
this.fillPolicyToChecksMap(policyToCheckToSpecialComm, proxyPolicyToNonCiCheckIds, policyId);
var cmdbCiToAgentMap = this.getCmdbCiToAgentMap();
if (Object.keys(cmdbCiToAgentMap).length < 1) {
gs.warn(gs.getMessage("PolicyClientsGenerator in updateSinglePolicyContent: No active agents found on the instance. Skipping syncClients()"));
// Write empty values for this policies.
this.updatePolicyClientsResults({}, policyGr, prevFilterHash, isManualChange);
return;
}
try {
return this.updatePolicyClientsJson(policyGr, prevFilterHash, policyToCheckToSpecialComm[policyId], proxyPolicyToNonCiCheckIds[policyId], cmdbCiToAgentMap, parentToChildrenMap, childToAgentsListMap, true, isManualChange);
} catch (e) {
gs.error(gs.getMessage("PolicyClientsGenerator: Could not update Policy Clients for policy={0}. Error={1}", [policyGr.name, e]));
return;
}
},
updateParentPolicyClientsJson: function(parentSysId, parentToChildrenMap, childToAgentsListMap, isManualChange) {
var parentPolicyGr = new GlideRecord('sn_agent_policy');
if (parentPolicyGr.get(parentSysId)) {
this.updateSinglePolicyContent(parentPolicyGr, parentToChildrenMap, childToAgentsListMap, isManualChange);
}
},
// {"parentPolicySysId":["childPolicySysId1","childPolicySysId2"], "parentPolicySysId2": []}
fillParentToChildrenMapForCertainParent: function(parentSysId) {
var parentToChildrenMap = {};
parentToChildrenMap[parentSysId] = [];
var childPolicyGr = new GlideRecord('sn_agent_policy');
childPolicyGr.addEncodedQuery("is_draft=false");
childPolicyGr.addQuery("published_parent", parentSysId);
childPolicyGr.query();
while (childPolicyGr.next()) {
parentToChildrenMap[parentSysId].push(childPolicyGr.getValue("sys_id"));
}
return parentToChildrenMap;
},
// {"childPolicySysId1":["agentId"],"childPolicySysId2":[]}
fillChildToAgentsListMapForCertainParent: function(parentPolicyGr, childrenSysIds, childToAgentsListMap) {
var clientsResult = new GlideRecord('sn_agent_policy_clients');
clientsResult.addQuery("policy", "IN", childrenSysIds);
clientsResult.query();
while (clientsResult.next()) {
var agents = clientsResult.getValue("agents_list");
childToAgentsListMap[clientsResult.getValue("policy")] = agents.split(',');
}
return childToAgentsListMap;
},
getEntrypointMetricsResourceName: function(agentId) {
var agentCiGr = new GlideRecord("sn_agent_cmdb_ci_agent");
agentCiGr.addQuery("agent_id", agentId);
agentCiGr.query();
if (!agentCiGr.next()) {
gs.error(gs.getMessage("sn_agent.PolicyClientsGenerator.getEntrypointMetricsResourceName: ERROR: no agent with agent_id == {0}", agentId));
return null;
}
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("sn_agent.PolicyClientsGenerator.getEntrypointMetricsResourceName: ERROR: agent with agent_id == {0} doesn't have a valid host CI", agentId));
return null;
}
hostname = agentHostGr.getValue("name");
var useHostname = gs.getProperty(this.ENTRY_POINT_METRIC_RESOURCE_USE_HOSTNAME_PROPERTY, "true") === "true";
var useLocation = gs.getProperty(this.ENTRY_POINT_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("sn_agent.PolicyClientsGenerator.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("sn_agent.PolicyClientsGenerator.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 null;
},
getParentTable: function(tablename) {
//now check if its in hardware hierarchy OR docker container
var baseTables = new GlideTableHierarchy(tablename).getTables();
for (var i = 0; i < baseTables.length; i++) {
var currTable = baseTables[i];
if (currTable == this.HOST_PARENT_TABLE || currTable == this.DOCKER_PARENT_TABLE ||
currTable == this.APP_PARENT_TABLE || currTable == this.AGENT_PARENT_TABLE) {
return currTable;
}
}
return tablename;
},
// This is a fast way to get row count. To get row count of 1,183,031 took 0.361 seconds vs. GlideRecord getRowCount() which took 3.8 seconds
getRowCount: function(tableName, tableFilter) {
var ga = new GlideAggregate(tableName);
ga.addEncodedQuery(tableFilter);
ga.addAggregate('COUNT');
ga.query();
if (ga.next())
return ga.getAggregate('COUNT');
return 0;
},
/**
This method decide whether or not to add only the monitored CIs that have agents to the query.
Couple of things to consider:
If the number of monitored CIs is very low - do nothing.
If we have lots of monitored CIs with low number of agents - it is worth adding a query that will results in only monitored CIs that have agents.
If we have a lot of agents but relatively low number of monitored CIs - it is not worth to add to the query the monitored CIs with agents
**/
getMonitoredCIsRelatedToAgentsHosts: function(policyName, monitoredCis, agentsHosts, parentTable, numberOfMonitoredCis) {
if (!monitoredCis || typeof monitoredCis.getTableName !== "function" || typeof monitoredCis.getEncodedQuery !== "function")
return undefined;
var tableName = monitoredCis.getTableName();
var tableFilter = monitoredCis.getEncodedQuery();
if (!gs.tableExists(tableName)) {
gs.warn(gs.getMessage("The table {0} does not exist in the system. Policy {1} will be skipped.", [tableName, policyName]));
return undefined;
}
// This is a fast way to get row count. To get row count of 1,183,031 took 0.361 seconds.
if (!numberOfMonitoredCis)
numberOfMonitoredCis = this.getRowCount(tableName, tableFilter);
if (numberOfMonitoredCis == 0) {
gs.warn(gs.getMessage("PolicyClientsGenerator: We got no monitored CIs for table {0} and policy {1}", [monitoredCis.getTableName(), policyName]));
return this.QUERY_WITH_ZERO_CIS;
}
if (!parentTable)
parentTable = this.getParentTable(tableName);
// For sn_agent_cmdb_ci_agent: add some conditions to the query and return
if (parentTable == this.AGENT_PARENT_TABLE) {
monitoredCis.addNotNullQuery('agent_extended_info.cmdb_ci');
monitoredCis.addQuery('agent_extended_info.status', ["0", "1"]);
monitoredCis.addQuery('agent_extended_info.data_collection', '0');
monitoredCis.addQuery('agent_extended_info.is_duplicate', '0');
var monitoredCisStatus = this.isNoMonitoredCisOrTooMany(monitoredCis, policyName);
if (monitoredCisStatus != this.QUERY_SUCCESSFULLY)
return monitoredCisStatus;
monitoredCis.query();
return monitoredCis;
}
// If we have (relative) small number of monitored CIs - return it for processing.
// Example: we have 200 Tomcat CIs: return the GR with 200 Tomcat CIs.
if (numberOfMonitoredCis < this.LIMIT_TO_DECIDE_SHOULD_ADD_AGENTS) {
monitoredCis.query();
return monitoredCis;
}
// We have more than LIMIT_TO_DECIDE_SHOULD_ADD_AGENTS monitored CIs.
// If the ratio between number of agents to the number of monitored CIs is less than
// MIN_RATIO_TO_ADD_AGENTS_HOSTS_TO_QUERY then add the agents hosts to the monitored CIs query
// Example:
// if MIN_RATIO_TO_ADD_AGENTS_HOSTS_TO_QUERY = 0.6 and number of monitored CIs = 9,999
// This means number of agents needs to be less than 6,000 to enter this condition
// With this condition we have a very good chance to limit the number of monitored CIs
if ((agentsHosts.length / numberOfMonitoredCis) < this.MIN_RATIO_TO_ADD_AGENTS_HOSTS_TO_QUERY) {
// Add another condition if possible (if this is policy for hosts, apps or dockers)
monitoredCis = this.addQueryForOnlyMonitoredCisWithAgents(monitoredCis, agentsHosts, tableName, parentTable);
monitoredCisStatus = this.isNoMonitoredCisOrTooMany(monitoredCis, policyName);
if (monitoredCisStatus != this.QUERY_SUCCESSFULLY)
return monitoredCisStatus;
monitoredCis.query();
return monitoredCis;
}
// At this point we have more than LIMIT_TO_DECIDE_SHOULD_ADD_AGENTS monitored CIs
// Yet the number of agents to monitored CIs ratio is bigger than MIN_RATIO_TO_ADD_AGENTS_HOSTS_TO_QUERY
// It might not be beneficial to add the agents to the query.
// Just add a check we did not reached max monitored CIs per policy before continuing.
monitoredCisStatus = this.isNoMonitoredCisOrTooMany(monitoredCis, policyName);
if (monitoredCisStatus != this.QUERY_SUCCESSFULLY)
return monitoredCisStatus;
monitoredCis.query();
return monitoredCis;
},
isNoMonitoredCisOrTooMany: function(monitoredCis, policyName) {
if (!monitoredCis)
return this.QUERY_FAILED;
var numOfMonitoredCis = this.getRowCount(monitoredCis.getTableName(), monitoredCis.getEncodedQuery());
if (numOfMonitoredCis > this.MAX_NUMBER_OF_MONITORED_CIS_TO_PROCESS) {
gs.error(gs.getMessage("PolicyClientsGenerator: We got {0} monitored CIs, which is more more than the configured max CIs per policy={1}. On table {2} for policy {3}", [numOfMonitoredCis, this.MAX_NUMBER_OF_MONITORED_CIS_TO_PROCESS, monitoredCis.getTableName(), policyName]));
return this.QUERY_WITH_TOO_MANY_MONITORED_CIS;
}
if (numOfMonitoredCis == 0) {
gs.error(gs.getMessage("PolicyClientsGenerator: We got no monitored CIs for table {0} for policy {1}", [monitoredCis.getTableName(), policyName]));
return this.QUERY_WITH_ZERO_CIS;
}
return this.QUERY_SUCCESSFULLY;
},
addQueryForOnlyMonitoredCisWithAgents: function(monitoredCis, hosts, tableName, parentTable) {
if (!hosts || hosts.length < 1)
return undefined;
if (parentTable == this.HOST_PARENT_TABLE && hosts.length < this.MAX_NUMBER_OF_AGENTS_HOSTS_TO_ADD_TO_QUERY) {
monitoredCis.addQuery('sys_id', 'IN', hosts);
return monitoredCis;
}
// Only search in rel ci when we know what we are looking for.
if (parentTable != this.APP_PARENT_TABLE && parentTable != this.DOCKER_PARENT_TABLE)
return monitoredCis;
var monitoredCisWithAgentsSysIds = [];
var relType = '60bc4e22c0a8010e01f074cbe6bd73c3'; // Runs on/Runs;
if (parentTable == this.DOCKER_PARENT_TABLE)
relType = 'bf83653c0ab30150761028c73a4de0f4'; // Managed by/Manage
// Only get the apps/dockers that has a host with an agent connected to it.
var grRel = new GlideRecord('cmdb_rel_ci');
grRel.addQuery('child', 'IN', hosts); // Must have a host with agent
grRel.addQuery('parent.sys_class_name', tableName); // Must have the same app/docker
grRel.addQuery('type', relType);
grRel.query();
while (grRel.next())
monitoredCisWithAgentsSysIds.push(grRel.getValue('parent'));
if (monitoredCisWithAgentsSysIds.length < 1) {
gs.warn(gs.getMessage("PolicyClientsGenerator: Could not find relevant agents for table {0} . Skipping", tableName));
return undefined;
}
monitoredCis.addQuery('sys_id', 'IN', monitoredCisWithAgentsSysIds);
return monitoredCis;
},
analyzeMonitoredCIsStatus: function(monitoredCisStatus, policyGr) {
if (!monitoredCisStatus)
return false;
// We got too many CIs
if (monitoredCisStatus == this.QUERY_WITH_TOO_MANY_MONITORED_CIS) {
policyGr.setValue('active', '0');
var policyDescr = policyGr.getValue('description');
policyGr.setValue(gs.getMessage('Error: Too Many Monitored CIs (per sn_agent.max_number_of_monitored_cis_per_policy; default={0}). Try to narrow the monitored CIs of this policy and reactivate it. Old description: {1}', [this.DEFAULT_MAX_NUMBER_OF_MONITORED_CIS, policyDescr]));
policyGr.update();
gs.warn(gs.getMessage('PolicyClientsGenerator: Policy {0} has more than {1} monitored CIs configured. To prevent Out of Memory issues on the instance, the policy was not processed and was not sent to the agents. The policy was deactivated.', [policyGr.name, this.DEFAULT_MAX_NUMBER_OF_MONITORED_CIS]));
}
// For any other error
if (monitoredCisStatus < 0)
return false;
return true;
},
type: 'PolicyClientsGenerator'
};
Sys ID
4087acd66785c010b7b72dbd2685ef1f