Name
sn_agent.MainDiscoveryHandler
Description
No description available
Script
var MainDiscoveryHandler = Class.create();
var LOG_ID = 'Agent Client Collector Framework []:';
MainDiscoveryHandler.prototype = {
// Public properties
sourceAgentClientCollector: 'AgentClientCollector',
agentId: '',
ipAddress: '',
ip6Address: '',
ireOutput: '',
initialize: function() {},
/**
* This method is empty for the base object and is design to be overridden by object children.
* This method is called after the CI have been created and we would like to add more data to it.
**/
postIreEnrichDiscovery: function(ciSysId, data) {},
/**
* This method is empty for the base object and is design to be overridden by object children
* This method executed before we send the payload to the IRE
**/
preIreAddDataToPayload: function(data, ciJsonPayload, basicInventoryJson) {},
/**
* This method is empty for the base object and is design to be overridden by object children
* This method is called after IRE to build references between Host CI and referenced CIs or non-CIs
**/
postIreHandleReferences: function(data, ciJsonPayload) {},
/**
* This method is empty for the base object and is design to be overridden by object children
* This method is for attribute that needs further translation (converting form string to sysID or match with a specific String)
* This method is called before the IRE
**/
advanceAttributesToIREParsable: function(columnName, value, valuesJson) {},
// JS does not support "protected" attribute - so we use a getter
getAgentId: function() {
return this.agentId;
},
setLog: function() {},
/**
*
* Entry point for this script include
* input : JSON Object returned by ACC-F discovery
*
**/
handleDiscovery: function(checkResults) {
var startOfProcessing = new GlideDateTime();
var tenMinutesBeforeProcessingTime = new GlideDateTime(startOfProcessing);
tenMinutesBeforeProcessingTime.addSeconds(-600);
for (var index = 0; index < checkResults.length; index++) {
var checkResult = checkResults[index];
var checkStatus = checkResult["check"]["status"];
this.agentId = checkResult["agent_id"];
LOG_ID = LOG_ID.replace(/\[.*\]/, "[" + this.agentId + "]");
this.setLog(); // Sets logs for child as well
// if agent CI record does not exist, ignore the discovery data
var accAPI = new AccAgentsAPI();
if (!accAPI.agentCIExists(this.agentId)) {
gs.warn(LOG_ID + " In MainDiscoveryHandler.handleDiscovery() ignoring data since no agent exists with agent_id=" + this.agentId);
continue;
}
if (checkStatus != "0") {
AgentDiscoverySharedUtils.setAgentsToHostDataCollectionFailedStatus(this.agentId);
gs.error(LOG_ID + " In MainDiscoveryHandler.handleDiscovery() bad status response for discovery check, ignoring: " + JSON.stringify(checkResult));
continue;
}
var checkOutput = checkResult["check"]["output"];
var checkResultOutput = this.extractOutput(checkOutput);
this.addDataIntoCMDB(checkResultOutput, tenMinutesBeforeProcessingTime);
// clear variables
checkResultOutput = 0;
checkOutput = 0;
}
},
// extract output per a single Check Result
extractOutput: function(checkResult) {
var beginIndex = checkResult.indexOf('{');
var endIndex = checkResult.lastIndexOf('}');
if (beginIndex < 0 || endIndex <= 0) {
gs.error("Error parsing discovery output");
return "{}";
}
gs.debug(LOG_ID + " In MainDiscoveryHandler.extractOutput() returning extracted output from check result");
return checkResult.substring(beginIndex, endIndex + 1);
},
// Add data to CMDB per single a single check output result
addDataIntoCMDB: function(result, tenMinutesBeforeProcessingTime) {
LOG_ID = LOG_ID.replace(/\[.*\]/, "[" + this.agentId + "]");
if (!result || result == "{}") {
AgentDiscoverySharedUtils.setAgentsToHostDataCollectionFailedStatus(this.agentId);
gs.error(LOG_ID + " In MainDiscoveryHandler.addDataIntoCMDB() Payload is empty!");
return;
}
var ciInfo;
try {
var data = JSON.parse(result);
if (this.hasFatalErrorInCheck(data)) {
AgentDiscoverySharedUtils.setAgentsToHostDataCollectionFailedStatus(this.agentId);
gs.error(LOG_ID + " In MainDiscoveryHandler.addDataIntoCMDB() fatal error in check, cannot process.");
return;
}
// Check if agent is running in contianer and if property is not defined or set to true
var disableContainerCollection = gs.getProperty('sn_agent.host_data_collection.disable_when_container', 'true');
if (data.basic_inventory.containerized_acc && disableContainerCollection == 'true') {
gs.debug(LOG_ID + " In MainDiscoveryHandler.addDataIntoCMDB() data collection is disabled for containers");
this.setAgentIsContainerized();
AgentDiscoverySharedUtils.setAgentsToHostDataCollectionDisabledStatus(this.agentId);
return;
}
ciInfo = this.addComputerAndRelatedListCisIntoCmdb(data);
AgentDiscoverySharedUtils.setAgentsToHostDataCollectedStatus(this.agentId);
} catch (e) {
gs.error(LOG_ID + " In MainDiscoveryHandler.addDataIntoCMDB() IRE failed. e=" + e.message);
AgentDiscoverySharedUtils.setAgentsToHostDataCollectionFailedStatus(this.agentId);
}
if ((typeof ciInfo === 'object') && ciInfo.didnt_clobber == true) {
gs.debug(LOG_ID + " In MainDiscoveryHandler.addDataIntoCMDB() decision to not clobber existing CI");
return;
}
this.postIreEnrichDiscovery(ciInfo, data);
return ciInfo;
},
/**
*
* Add basic inventory data into CMDB
*
**/
addComputerAndRelatedListCisIntoCmdb: function(data) {
var ciJsonPayload = {
items: []
};
ciJsonPayload.relations = [];
ciJsonPayload.references = [];
// If there is no basic Inventory information return
if (!data.basic_inventory) {
AgentDiscoverySharedUtils.setAgentsToHostDataCollectionFailedStatus(this.agentId);
gs.error(LOG_ID + " In MainDiscoveryHandler.addComputerAndRelatedListCisIntoCmdb() Payload missing basic inventory information to insert CI");
return;
}
// construct a IRE parsable payload for basic inventory and add it to CI payload
var basicInventoryJson = this.formatBasicInventoryJsonToIREParsable(data.basic_inventory);
basicInventoryJson = this.splitAndSetObjectId(basicInventoryJson);
ciJsonPayload.items.push(basicInventoryJson);
// handle cmdb_serial_number table
this.handleCMDBSerialNumberTablePopulation(data, ciJsonPayload);
// handle CI serial number preference
// do this after cmdb_serial_number table so that the data is clean
this.handleCiSerialNumberPreference(ciJsonPayload);
this.preIreAddDataToPayload(data, ciJsonPayload, basicInventoryJson);
// save serial numbers for after to handle deletion
var serialNumbers;
if (ciJsonPayload.items && ciJsonPayload.items[0] && ciJsonPayload.items[0].lookup)
serialNumbers = ciJsonPayload.items[0].lookup;
if (ciJsonPayload.items.length) { //create a relationship only when the current CiJsonpayload is non empty and has basicinfo loaded
//add Agent and Host relationship
ciJsonPayload = this.addDefaultRelationshipForAgentAndHostCI(this.agentId, ciJsonPayload);
} else {
gs.warn(LOG_ID + " In MainDiscoveryHandler.addComputerAndRelatedListCisIntoCmdb() " +
"No basic inventory info hence aborting creating relationship between Host and agent!");
}
//JSON stringify before sending it to IRE
ciJsonPayload = JSON.stringify(ciJsonPayload);
if (!ciJsonPayload) {
gs.error(LOG_ID + " In MainDiscoveryHandler.addComputerAndRelatedListCisIntoCmdb() something went wrong while adding CI into CMDB!");
AgentDiscoverySharedUtils.setAgentsToHostDataCollectionFailedStatus(this.agentId);
return null;
}
var idOutput = sn_cmdb.IdentificationEngine.identifyCI(ciJsonPayload);
var parsedIdOutput = JSON.parse(idOutput);
var host = this.getFirstComputerFromIrePayload(parsedIdOutput);
// Validate that we have both host fields available from the IRE payload
// Not checking sys ID because that is empty for new CIs
if (!host || !host.className) {
gs.error(LOG_ID + " In MainDiscoveryHandler.addComputerAndRelatedListCisIntoCmdb() unable to identify the CI and extract Info");
AgentDiscoverySharedUtils.setAgentsToHostDataCollectionFailedStatus(this.agentId);
return null;
}
var hostCiSysId = host.sysId; // parsedIdOutput.items[0].sysId;
var hostCiSysClassName = host.className; // parsedIdOutput.items[0].className;
var compatibilityUtil = new AgentDiscoveryAgentlessCompatibilityUtil();
// Defer Agent Discovery only if the CI was discovered recently (sn_agent.disco_ci_clobber_of_agentless_disco_threshold_days) by Agentless Discovery (ServiceNow, ServiceWatch)
if (!compatibilityUtil.shouldProcessAgentDiscoveryPayload(hostCiSysId, hostCiSysClassName)) {
this.linkHostCiAndAgent(hostCiSysId, data.basic_inventory.isSystemd);
this.createHostCiAgentCiRelation(hostCiSysId);
return {
didnt_clobber: true
};
}
// Should update CMDB
var output = sn_cmdb.IdentificationEngine.createOrUpdateCI(this.sourceAgentClientCollector, ciJsonPayload);
gs.info(LOG_ID + " In MainDiscoveryHandler.addComputerAndRelatedListCisIntoCmdb() CI insert " +
"output from Identification Engine: " + output);
//We only have all the CIs added. We need to handle references to these CIs
this.postIreHandleReferences(ciJsonPayload, output);
var parsedOutput = JSON.parse(output);
// retrieve agent record that may or may not be linked to agent
var ciSysId = this.connectAgentAndHost(parsedOutput, data.basic_inventory.isSystemd);
// Handle serial number cleanup
this.postIreSerialNumberCleanup(hostCiSysId, serialNumbers);
// If we do not have Discovery plugin - do not attempt to work on TCP ports and processes
if (!GlidePluginManager.isActive('com.snc.discovery.ip_based')) {
gs.warn(LOG_ID + " In MainDiscoveryHandler.addComputerAndRelatedListCisIntoCmdb(): Could not continue to TCP ports and running processes as com.snc.discovery.ip_based plugin is not activated.");
return ciSysId;
}
// Need the CI sysID to add the Application CI into CMDB
// check if tcpConnections and runningProcess exists before accessing any of its child JSON nodes.
var agentGr = this.getAgentRecord(this.agentId);
if ((data.tcp_connections || data.running_processes) && ciSysId) {
if (data.tcp_connections.connections || data.running_processes.processes) {
gs.debug(LOG_ID + " In MainDiscoveryHandler.addDataIntoCMDB() adding data for tcp connections and installed software");
this.insertTcpConnectionsAndRunningProcessInfoIntoCMDB(data.tcp_connections, data.running_processes, basicInventoryJson, ciSysId, agentGr);
}
if (data.tcp_connections.error)
gs.error(LOG_ID + " In MainDiscoveryHandler.addComputerAndRelatedListCisIntoCmdb() tcp_connection error: " + data.tcp_connections.error);
else if (data.running_processes.error)
gs.error(LOG_ID + " In MainDiscoveryHandler.addComputerAndRelatedListCisIntoCmdb() running processes error: " + data.tcp_connections.error);
} else {
gs.debug(LOG_ID + " In MainDiscoveryHandler.addComputerAndRelatedListCisIntoCmdb() missing data for tcp_connection/Running process or ciSysId");
}
this.ireOutput = parsedOutput;
return ciSysId;
},
handleCMDBSerialNumberTablePopulation: function(data, ciJsonPayload) {
if (data.serial_numbers) {
gs.debug(LOG_ID + ' In MainDiscoveryHandler.handleCMDBSerialNumberTablePopulation() processing additional serial numbers');
var serialNumbersJson = this.formatSerialNumbersJsonForIre(data.serial_numbers);
if (serialNumbersJson.length != 0)
ciJsonPayload.items[0].lookup = serialNumbersJson; // CI basic inventory is the first item in the IRE payload
else
gs.debug(LOG_ID + ' In MainDiscoveryHandler.handleCMDBSerialNumberTablePopulation() no additional serial numbers were present');
} else {
gs.debug(LOG_ID + ' In MainDiscoveryHandler.handleCMDBSerialNumberTablePopulation() no additional serial numbers to process');
}
},
// DEF0260576: handle the cmdb_ci.serial_number based on a configurable preference
handleCiSerialNumberPreference: function(ciJsonPayload) {
// use the ciJsonPayload since the serial numbers should be cleaned up
if (!ciJsonPayload || !ciJsonPayload.items[0] || !ciJsonPayload.items[0].lookup)
return;
var serials = ciJsonPayload.items[0].lookup;
// build map of known serial types
var knownSerials = {};
var serialNumber, serialType, serialValue;
for (var s = 0; s < serials.length; s++) {
serialNumber = serials[s].values;
serialType = serialNumber.serial_number_type;
serialValue = serialNumber.serial_number;
knownSerials[serialType] = serialValue;
}
var ciSerialPrefProp = gs.getProperty('sn_agent.ci_serial_number.pref_order', 'bios,system,uuid,baseboard,chassis');
var ciSerialPref = ciSerialPrefProp.split(',');
var prefType, value;
// iterate through the configurable preferences and use the first one found from known serials
for (var i = 0; i < ciSerialPref.length; i++) {
prefType = (ciSerialPref[i] + '').trim().toLowerCase();
if (!knownSerials[prefType])
continue;
value = knownSerials[prefType];
gs.debug(LOG_ID + ' In MainDiscoveryHandler.handleCiSerialNumberPreference() setting to preferred serial number ' + prefType);
ciJsonPayload.items[0].values.serial_number = value;
return;
}
},
postIreSerialNumberCleanup: function(ciSysId, serialNumbers) {
// check for null values
if (!ciSysId || !serialNumbers) {
gs.debug(LOG_ID + ' In MainDiscoveryHandler.postIreSerialNumberCleanup() CI sys_id or serial numbers data not present, abandoning cleanup.');
return;
}
// Get cmdbGr from sys_id
var cmdbGr = new GlideRecord("cmdb_ci_hardware");
cmdbGr.get('sys_id', ciSysId);
var serialNumberArr = [];
// build serial array
for (var s = 0; s < serialNumbers.length; s++) {
serialNumberArr.push(serialNumbers[s].values);
}
// build data object for reconciler
var serialNumberDataObj = {};
serialNumberDataObj['cmdb_serial_number'] = {
data: serialNumberArr,
refName: 'cmdb_ci',
keyName: 'serial_number_type,serial_number,valid',
deleteGr: true
};
new global.DiscoveryReconciler(cmdbGr, serialNumberDataObj).process();
},
getAgentRecord: function(agentId) {
var agentGr = new GlideRecord('sn_agent_ci_extended_info');
agentGr.addQuery('agent_id', agentId);
agentGr.addQuery('is_duplicate', 'false'); // Not a dup
agentGr.query();
return agentGr;
},
getHostSysIdOnAgentRecord: function() {
var agentGr = this.getAgentRecord(this.agentId);
if (agentGr.next()) {
return agentGr.getValue('cmdb_ci');
}
return "";
},
linkHostCiAndAgent: function(hostCiSysId, isSystemd) {
var agentGr = this.getAgentRecord(this.agentId);
if (agentGr.next()) {
agentGr.setValue('cmdb_ci', hostCiSysId);
if (isSystemd)
agentGr.setValue('is_using_systemd', isSystemd);
agentGr.update();
}
},
createHostCiAgentCiRelation: function(hostCiSysId) {
var agentCiGr = new GlideRecord("sn_agent_cmdb_ci_agent");
agentCiGr.get("agent_id", this.getAgentId());
var agentCiSysId = agentCiGr.getValue("sys_id");
var runsOnRelationTypeSysId = '60bc4e22c0a8010e01f074cbe6bd73c3';
var relation = new GlideRecord('cmdb_rel_ci');
relation.addQuery("parent", agentCiSysId);
relation.addQuery("child", hostCiSysId);
relation.addQuery("type", runsOnRelationTypeSysId);
relation.query();
if (!relation.next()) {
relation.initialize();
relation.setValue("parent", agentCiSysId);
relation.setValue("child", hostCiSysId);
relation.setValue("type", runsOnRelationTypeSysId);
relation.insert();
}
},
getFirstComputerFromIrePayload: function(parsedOutput) {
if (parsedOutput.items && parsedOutput.items.length > 0) {
// Changed to cmdb_ci_hardware as part of DEF0364430 to allow for better reclassification of CIs
var hostCiTypes = new GlideTableHierarchy('cmdb_ci_hardware').getAllExtensions();
for (var i = 0; i < parsedOutput.items.length; i++) {
if ('Unknown' != parsedOutput.items[i].sysId && hostCiTypes.indexOf(parsedOutput.items[i].className) >= 0) {
return parsedOutput.items[i];
}
}
}
},
connectAgentAndHost: function(parsedOutput, isSystemd) {
var host = this.getFirstComputerFromIrePayload(parsedOutput);
if (host) {
this.linkHostCiAndAgent(host.sysId, isSystemd);
return host.sysId;
}
},
/**
*
* Sample Input basic inventory Json:
* "basic_inventory": {
* "name": "dhcp-10-11-128-190",
* "manufacturer": "VMware, Inc.",
* "serial_number": "VMware-42 01 35 23 01 2b 50 eb-85 ea 59 d0 81 80 2d de",
* "operating_system_domain": "lab3.service-now.com",
* "operating_system": "CentOS Linux",
* "operating_system_version": "7.3.1611",
* "operating_system_service_pack": "",
* "ram": "1824",
* "cpu_manufacturer": "GenuineIntel",
* "cpu_speed": "1799",
* "cpu_count": "1",
* "cpu_core_count": "1",
* "ip_address": "10.11.128.190",
* "is_virtual": "true",
* "fully_qualified_domain_name": "dhcp-10-11-128-190.lab3.service-now.com",
* "start_date": "1602805664000"
* }
*
* Sample Json Ouput basic inventory Json for Identification Engine to insert the CI:
* {
* "className": "cmdb_ci_linux_server",
* "values": {
* "name": "dhcp-10-11-199-199",
* "host_name": "dhcp-10-11-199-199",
* "manufacturer": "VMware, Inc.",
* "serial_number": "VMware-42 01 35 23 01 2b 50 eb-ki sh or re dd y0 2d ab",
* "dns_domain": "lab3.service-now.com",
* "os": "CentOSLinux",
* "os_version": "7.3.1611",
* "operating_system_service_pack": "",
* "ram": "1824",
* "cpu_manufacturer": "GenuineIntel",
* "cpu_speed": "1999",
* "cpu_count": "9",
* "cpu_core_count": "9",
* "ip_address": "10.11.199.199",
* "virtual": "true",
* "fqdn": "dhcp-10-11-199-199.lab3.service-now.com",
* "start_date": "2020-10-15 16:47:33"
* }
* }
*
*/
formatBasicInventoryJsonToIREParsable: function(basicInventoryJson) {
var accVAtts = ['cpu_manufacturer', 'start_date', 'model_id', 'manufacturer'];
//Ignoring field platform since this is used to only identify platform in this script_include
//and cannot be part of the IRE payload
var ignoreFieldsInBasicInv = ['platform', 'dns_name', 'hostname', 'computername', 'local_hostname'];
//create a Map to list all the payload cols thats need replace to match CMDB cols.
var fieldMap = {};
fieldMap.operating_system_domain = "os_domain";
fieldMap.operating_system = "os";
fieldMap.operating_system_version = "os_version";
fieldMap.fully_qualified_domain_name = "fqdn";
fieldMap.is_virtual = "virtual";
// save the IP Addresses for later
if (basicInventoryJson.ip_address)
this.ipAddress = basicInventoryJson['ip_address'];
if (basicInventoryJson.ip_address_v6)
this.ip6Address = basicInventoryJson['ip_address_v6'];
// handle preference of IPv4 or IPv6 for ip_address and default_gateway fields.
// if IPv6 values exist and should prefer IPv6 OR only have IPv6 value
if ((basicInventoryJson.ip_address_v6 && AgentDiscoverySharedUtils.getPreferredIPVersion() == 6) ||
(basicInventoryJson.ip_address_v6 && global.JSUtil.nil(basicInventoryJson.ip_address))) {
fieldMap.ip_address_v6 = "ip_address";
fieldMap.default_gateway_v6 = "default_gateway";
// San Diego release added support to validate IPv6. For backwards compatibility, if ScopedIpUtils is not avail, don't use it
if (typeof global.ScopedIpUtils == 'function') {
// ensure the values are in canonical form
basicInventoryJson['ip_address_v6'] = global.ScopedIpUtils.canonical(basicInventoryJson['ip_address_v6']);
basicInventoryJson['default_gateway_v6'] = global.ScopedIpUtils.canonical(basicInventoryJson['default_gateway_v6']);
}
delete basicInventoryJson['ip_address'];
delete basicInventoryJson['default_gateway'];
} else {
// ignore the IPv6 fields and use the original IPv4 fields
delete basicInventoryJson['ip_address_v6'];
delete basicInventoryJson['default_gateway_v6'];
}
var basicInventoryJsonData = {};
var valuesJson = {};
gs.info(LOG_ID + " In MainDiscoveryHandler.formatBasicInventoryJsonToIREParsable() iterate through" +
" basic inventory keys and change to IRE compatible structure");
/**
*
* Since the basic invertory payload dosent match the format Identification engine expects
* below we give the payload the right structure.
*
**/
for (key in basicInventoryJson) {
if (!basicInventoryJson.hasOwnProperty(key) || ignoreFieldsInBasicInv.indexOf(key) >= 0)
continue;
var columnName = key;
var value = basicInventoryJson[key];
// don't save serial numbers values that are recognized as invalid.
// required here in case additional serial numbers cannot be collected
if ((key == 'serial_number') && (!this.isValidSerialNumber(value))) {
gs.warn(LOG_ID + " In MainDiscoveryHandler.formatBasicInventoryJsonToIREParsable() ignoring invalid serial number value " + value);
delete basicInventoryJson[key];
continue;
}
//The columns in the payload have no one-to-one matching with the columns in CMDB
//And these columns names have to absolutely match the actual columns in CMDB for a successful insert through IRE
if (fieldMap[key])
columnName = fieldMap[key];
if (accVAtts.indexOf(columnName) > -1) { // This is for ACC-V to handle
this.advanceAttributesToIREParsable(columnName, value, valuesJson);
continue;
}
valuesJson[columnName] = value;
}
if (valuesJson.hasOwnProperty("os") && valuesJson.hasOwnProperty("os_version")) {
var osNameAndVersion = new AgentOSHelper(LOG_ID).getOsNameAndVersion(valuesJson["os"], valuesJson["os_version"]);
valuesJson["os"] = osNameAndVersion[0];
valuesJson["os_version"] = osNameAndVersion[1];
}
// set name, host_name and fqdn just like patterns
if (basicInventoryJson.hasOwnProperty('platform') && basicInventoryJson.platform.toLowerCase() == 'mac')
this.processMacNamePreference(valuesJson, basicInventoryJson);
else
this.processNamesAttributes(valuesJson, basicInventoryJson.platform, basicInventoryJson.fully_qualified_domain_name, basicInventoryJson.dns_name);
//Get the sys_class_name or the table name based on the Operating system
basicInventoryJsonData.className = this.getOsClassName(basicInventoryJson);
basicInventoryJsonData.values = valuesJson;
return basicInventoryJsonData;
},
isValidSerialNumber: function(value) {
// Serial Number is validated by referring to the 'dscy_invalid_serial_list' table and If the serial number turned out
// to be invalid then we do not tamper the existing data in the respective CI's field.
// For more information, refer to -
// https://docs.servicenow.com/bundle/paris-it-operations-management/page/product/discovery/reference/r_SerialNumberTypes.html?cshalt=yes
//
// Please note, the condition "(value.toLowerCase() == 'default string')" is added explicitly because the "Default string"
// isn't added in the Invalid Serial Numbers (dscy_invalid_serial_list) table OOTB until "San Diego" via PRB1497563.
return (new global.SerialNumberManager().isValid(value)) && !(value.toLowerCase() == 'default string');
},
/**
*
* Input:
* {
* "serial_numbers": [
* {
* "serial_number": "VMware-42 1d 5b 5f 32 e7 ba 9b-f2 76 db 63 f1 56 30 8b",
* "serial_number_type": "system"
* },
* {
* "serial_number": "421D5B5F-32E7-BA9B-F276-DB63F156308B",
* "serial_number_type": "uuid"
* }
* ]
* }
*
*
* Output:
* [
* {
* "className": "cmdb_serial_number",
* "values": {
* "valid": "true",
* "serial_number": "VMware-42 1d 5b 5f 32 e7 ba 9b-f2 76 db 63 f1 56 30 8b",
* "serial_number_type": "system"
* }
* },
* {
* "className": "cmdb_serial_number",
* "values": {
* "valid": "true",
* "serial_number": "421D5B5F-32E7-BA9B-F276-DB63F156308B",
* "serial_number_type": "uuid"
* }
* }
* ]
*
**/
formatSerialNumbersJsonForIre: function(originalSerialNumJson) {
var serialNumberJsonArray = [];
var valueJson, serialNumberJson, value;
for (var index = 0; index < originalSerialNumJson.length; index++) {
value = originalSerialNumJson[index].serial_number;
if (gs.nil(value) || !this.isValidSerialNumber(value)) {
gs.debug(LOG_ID + ' In MainDiscoveryHandler.formatSerialNumbersJsonForIre skipping invalid serial number value ' + value);
continue;
}
valueJson = {
valid: "true",
serial_number: value,
serial_number_type: originalSerialNumJson[index].serial_number_type
};
serialNumberJson = {
className: "cmdb_serial_number",
values: valueJson
};
serialNumberJsonArray.push(serialNumberJson);
}
return serialNumberJsonArray;
},
/**
This method handles a host CI 3 naming attributes: name, host_name and fqdn
The main goal is to copy what Patterns are doing.
Input:
valuesJson: the json representing the data to be used in IRE
platofrm: either windows, linux or mac
fqdn:
Windows: hostname + "." + domain (from Registry or Win32_ComputerSystem)
Linux/Mac: from OSquery
dnsName: only valid for windows. dnsName = nslookup result for he host's IP Address
Output:
no output, modifing valuesJson attributes: name, fqdn and host_name if needed
**/
processNamesAttributes: function(valuesJson, platform, fqdn, dnsName) {
if (platform && platform.toLowerCase() == 'windows') {
var isWmiTrusted = (gs.getProperty('glide.discovery.hostname.wmi_trusted', 'false') == 'true');
var isDnsTrusted = (gs.getProperty('glide.discovery.hostname.dns_nbt_trusted', 'false') == 'true');
if (isDnsTrusted && dnsName)
valuesJson.fqdn = dnsName;
if (isWmiTrusted || (!valuesJson.fqdn && fqdn)) // we prefer to trust WMI if it is on
valuesJson.fqdn = fqdn;
} else if (fqdn) {
valuesJson.fqdn = fqdn;
}
// According to the Patterns, we split the data from WMI - not from dns
if (fqdn) {
var arr = fqdn.split('.');
if (arr)
valuesJson.host_name = arr[0]; // always get the hostname without the domain
else
valuesJson.host_name = fqdn;
}
var hostNameFormatter = new global.HostnameJS();
var formattedName = hostNameFormatter.format(valuesJson.fqdn);
if (formattedName)
valuesJson.name = formattedName;
},
processMacNamePreference: function(valuesJson, basicInventoryJson) {
var macNamePrefProp = gs.getProperty('sn_agent.mac_ci_name_pref_order', 'name,hostname,computer_name,local_hostname');
var macNamePref = macNamePrefProp.split(',');
var pref, value;
// iterate through the configurable preferences and use the first one found from known serials
for (var i = 0; i < macNamePref.length; i++) {
pref = (macNamePref[i] + '').trim().toLowerCase();
if (!basicInventoryJson.hasOwnProperty(pref) || basicInventoryJson[pref].trim() == '')
continue;
// format the hostname based on Discovery properties
var hn = new global.HostnameJS();
value = hn.format(basicInventoryJson[pref]);
if (value == null) {
gs.debug(LOG_ID + ' In MainDiscoveryHandler.processMacNamePreference() attempt to format hostname for ' + pref + ' was unsuccessful ');
continue;
}
gs.debug(LOG_ID + ' In MainDiscoveryHandler.processMacNamePreference() setting to preferred CI name to ' + value);
valuesJson.name = value;
return;
}
},
/**
*
* Returns the CI Type based on Discovery Classifications Per platform (windows, unix).
* The default is based on the operating_system
*
* Note: This will be enhanced in a future story
*
**/
getOsClassName: function(inventory) {
try {
var platform = inventory.platform;
var dc = new sn_agent.AgentDiscoveryClassification();
var hostMap = {};
hostMap.version = inventory.operating_system_version;
hostMap.osArchitecture = inventory.operating_system_architecture;
hostMap.ip = inventory.ip_address;
hostMap.osFullName = inventory.operating_system;
hostMap.short_description = inventory.short_description ? inventory.short_description : inventory.operating_system;
if (platform == 'windows') {
dc.setType(platform);
var osName = inventory.operating_system;
// Windows computer must starts with "Windows", for Windows 10 we remove Microsoft as prefix
if (osName && osName.startsWith('Microsoft'))
osName = osName.replaceAll('Microsoft', '').trim();
// The build is the last digit of the version
// 10.0.123 - the build = 123
var build = inventory.operating_system_version;
if (build) {
var arr = build.split('.');
build = arr[arr.length - 1];
}
hostMap.isVIP = 'false'; // Discovery Classification mandatory
hostMap.name = osName;
hostMap.buildNumber = build;
} else if (platform == 'linux') {
dc.setType('unix');
hostMap.name = inventory.short_description;
}
var rtn = dc.findCiType(hostMap);
if (rtn)
return rtn;
} catch (e) {
gs.error(LOG_ID + ". Could not classify device using Discovery Classification, reverting to old behavior. inventory=" + JSON.stringify(inventory) + ". Original excption: " + e);
}
// If could not use Discovery Classifeir - revernt to old behavior
var operatingSystem = inventory.operating_system ? inventory.operating_system.toLowerCase() : '';
var short_description = inventory.short_description ? inventory.short_description.toLowerCase() : '';
if (this._contains(operatingSystem, 'linux') || this._contains(short_description, 'linux')) {
return "cmdb_ci_linux_server";
} else if (this._startsWith(operatingSystem, 'windows')) {
if (this._contains(operatingSystem, 'server')) {
return "cmdb_ci_win_server";
}
return "cmdb_ci_computer"; // Should be cmdb_ci_windows_pc once we have the CMDB store app update with the new CMDB class model for ACC-V
}
return "cmdb_ci_computer"; //Needs to be defaulted to cmdb_ci_pc_hardware
},
/**
*
* Javascript startswith() implementation
*
**/
_startsWith: function(originalString, checkString) {
return originalString.substring(0, checkString.length) === checkString;
},
/**
*
* Util method to check for Substr
*
* Checks if the string is contained in the original/given String.
*
**/
_contains: function(osName, actualOsName) {
if (osName.toLowerCase().includes(actualOsName.toLowerCase()))
return true;
return false;
},
/**
* Inputs:
* - tcp connections [JSON]
* - running processes [JSON]
* - basicInvenoryData [JSON]
* - current CI SYS_ID
* - agent GlideRecord (sn_agent_ci_extended_info)
*
* Adding TCP Connections and Running process infomation into CMDB
* Adds relationship between TCP pid with the appropriate running process in CMDB_TCP table
* Adds Application Shell CIs for any software which matches the discovery classifiers
* Adds a default application shell for the current agent which is already running on the current host
*
**/
insertTcpConnectionsAndRunningProcessInfoIntoCMDB: function(tcpConnections, runningProcesses, basicInventoryJson, ciSysId, agentGr) {
var applicationShellCIPayload = {
items: []
};
applicationShellCIPayload.relations = [];
applicationShellCIPayload.items.push(basicInventoryJson);
// Discovery ADM will replace ip = 0.0.0.0 or * with the CI IP address. In the case of an IPv6 only host, there is no IPv4 to replace
// so we need to remove any tcp listening on IPv4 so that a record would not be created for it.
if (this.ip6Address && !this.ipAddress) {
var cleanTCP = [];
var tcp;
for (var i = 0; i < tcpConnections.connections.length; i++) {
tcp = tcpConnections.connections[i];
if (tcp.type == 'on' && ((tcp.ip == '0.0.0.0') || (tcp.ip == '*')))
continue;
cleanTCP.push(tcp);
}
tcpConnections.connections = cleanTCP;
cleanTCP = []; // free up memory
}
// Add data into tcp connection and running process tables
var admHandler = new AgentDiscoveryHandlerADMHelper(tcpConnections.connections, runningProcesses.processes, ciSysId);
var admResultData = admHandler.addConnectionsAndRelationshipsToCMDB(applicationShellCIPayload.items.length, basicInventoryJson.values.host_name, agentGr);
if (!admResultData) {
gs.info(LOG_ID + " In MainDiscoveryHandler.insertTcpConnectionsAndRunningProcessInfoIntoCMDB, skipping Application Shell CI creation");
return;
}
applicationShellCIPayload.items = applicationShellCIPayload.items.concat(admResultData[0]);
applicationShellCIPayload.relations = applicationShellCIPayload.relations.concat(admResultData[1]);
//Insert data into CMDB
applicationShellCIPayload = JSON.stringify(applicationShellCIPayload);
gs.info(applicationShellCIPayload);
var applicationCIOutput = sn_cmdb.IdentificationEngine.createOrUpdateCI(this.sourceAgentClientCollector, applicationShellCIPayload);
gs.info(LOG_ID + " In MainDiscoveryHandler.insertTcpConnectionsAndRunningProcessInfoIntoCMDB() CI insert " +
"output from Identification Engine: " + applicationCIOutput);
},
/**
*
* Construct default relationship for agent running on the host
*
**/
addDefaultRelationshipForAgentAndHostCI: function(agentId, ciJson) {
if (agentId.startsWith("ASI_"))
return ciJson;
var currentCiPositionInpayload = this.getFirstCiPositionFromCiJsonPayload(ciJson);
if (currentCiPositionInpayload == -1) {
gs.warn(LOG_ID + " In MainDiscoveryHandler.addAgentDefaultRelationshipForAgentWithHostCI() could not find suitable CI to associate the Agent!");
return ciJson;
}
var agentCi = {};
var agentValues = {};
agentCi['className'] = 'sn_agent_cmdb_ci_agent';
agentCi['values'] = agentValues;
agentValues['agent_id'] = agentId;
ciJson.items.push(agentCi);
var currentAgentCiPositionInPayload = ciJson.items.length - 1; //agent CI position after push
var agentRelationShip = {};
agentRelationShip['parent'] = currentAgentCiPositionInPayload;
agentRelationShip['child'] = currentCiPositionInpayload;
agentRelationShip['type'] = 'Runs on::Runs';
ciJson.relations.push(agentRelationShip);
return ciJson;
},
/**
*
* Get CI position from CIJson Payload based on className
* return -1 if not found
*
**/
getFirstCiPositionFromCiJsonPayload: function(ciJson) {
var hostCiTypes = new GlideTableHierarchy('cmdb_ci_computer').getAllExtensions();
for (var i = 0; i < ciJson.items.length; i++) {
if (hostCiTypes.indexOf(ciJson.items[i].className + '') >= 0)
return i;
}
return -1;
},
/**
*
* Take the object_id as it is returned from agent and set to be the last part
* of the string after splitting by '$'. Will need whole string for relationships to cloud resource.
* This function is derived from Patterns post processing script.
* Azure Ex: azure$12345678-1234-1234-1234-123412341234
*
* AWS Ex: amazon$123412341234$us-east2$12345678990
*
**/
splitAndSetObjectId: function(inventory) {
var rawId = inventory.values.object_id;
if (!rawId)
return inventory;
var objectIdParts = rawId.split("$");
if (objectIdParts.length == 4)
inventory.values.object_id = objectIdParts[3];
else if (objectIdParts.length == 2)
inventory.values.object_id = objectIdParts[1];
return inventory;
},
/**
*
* This function will set that the agent is containerized
*
*/
setAgentIsContainerized: function() {
var agentExInfo = this.getAgentRecord(this.agentId);
if (agentExInfo.next()) {
agentExInfo.setValue('is_containerized', 'true');
agentExInfo.update();
}
},
hasFatalErrorInCheck: function(outputJSON) {
var errorKeys = ["error_msg", "error_backtrace", "failed_cmd"];
var result = outputJSON["basic_inventory"] || outputJSON["data_collection"];
// This shouldn't happen, but if it does, it shouldn't be processed
if (!result)
return true;
for (var key in result) {
if (errorKeys.indexOf(key) < 0) {
return false;
}
}
return true;
},
type: 'MainDiscoveryHandler'
};
Sys ID
b26c4d92ff4120108ec45897d53bf116