Name
global.CloudApplicationDiscovery
Description
This script is called when Cloud Application Discovery schedule is started. It launches horizontal disocovery probes that discover cloud based applications such as AWS RDS, AWS ELB, Azure Web Server etc.
Script
var CloudApplicationDiscovery = Class.create();
CloudApplicationDiscovery.prototype = {
initialize: function() {
this.probes_sent = false;
this.patternAppService = new SNC.PatternLibraryAppService();
this.cloudDiscoveryUtil = new CloudResourceDiscoveryUtil();
// Exclude Azure OOTB Top Down patterns (with datacenter os family) from horizontal discovery
// Excluded patterns: Azure WebSite TD, Azure Functions TD, Azure DataBase TD
this.azureTopDownPatternsExcludeSysIds = ['3ccb90d993183200859c5999357ffbad', '0a48c4251b69a51005e5777d8b4bcbb6', 'a62cbf4193183200859c5999357ffb2e'];
},
_printEventError: function (errorMsg, configObj) {
gs.error(errorMsg + " event configObj- "+JSON.stringify(configObj , null, 2));
},
/**
run discovery of cloud applications on all logical data center hosted by the service account
*/
discoverServiceAccount: function (serviceAccount, status) {
var relCiGr = new GlideRecord("cmdb_rel_ci");
relCiGr.addQuery("child", serviceAccount);
relCiGr.addQuery('type', '5f985e0ec0a8010e00a9714f2a172815'); // Hosted-on relation
relCiGr.query();
while (relCiGr.next()) {
gs.log("Discovering LDC " + relCiGr.parent);
this.discoverLDC(serviceAccount, relCiGr.parent, status, []);
}
return this.probes_sent;
},
deleteCiById: function(ci_sys_id) {
var ciGr = new GlideRecord("cmdb_ci");
ciGr.addQuery("sys_id", ci_sys_id );
ciGr.query();
if (ciGr.next()) {
ciGr.deleteRecord();
return true;
} else {
this._printEventError("CloudApplicationDiscovery-> deleteCiById -> Can't find Ci's sys_id " , ci_sys_id);
return false;
}
},
/**
* Run discovery of a specific cloud resource applications via pattern invocation
* configObj ={
* serviceAccountSysId - sys_id of the service account hosting the logicial data center
* ldcSysId - sys_id of the logical data center
* discoveryStatusId - sys_id of the created DiscoveryStatus for that
* ldcsMap - A Map between ldc-name to its sys_id of the current serviceAccount
* patternId - sys_id of the pattern to be invoked
* }
*/
discoverSpecificResourceByPattern: function(configObj) {
// Check if the LDC exists
var ldcGr = new GlideRecord("cmdb_ci_logical_datacenter");
ldcGr.get(configObj.ldcSysId);
if (!ldcGr.isValid()) {
this._printEventError("discoverSpecificResourceByPattern-> No logical datacenter found with the given ldc sys_id", configObj);
return false;
}
// pick up the service account ID.. this is needed if the customer has specified a MID script override which needs the service account id
var serviceAccountGr = new GlideRecord("cmdb_ci_cloud_service_account");
serviceAccountGr.get(configObj.serviceAccountSysId);
var service_account_id = serviceAccountGr.getValue("account_id");
//Check if Specific mid is defined or not
var discoveryStatusGR = new GlideRecord('discovery_status');
discoveryStatusGR.get(configObj.discoveryStatusId);
var midServerName = this.cloudDiscoveryUtil.getSelectedMidFromSchedule(discoveryStatusGR.dscheduler.sys_id);
// Find MID based on LDC sys_class_name
var mid = midServerName ? midServerName : this.findMid(configObj.ldcSysId, service_account_id);
if (!mid) {
this._printEventError("discoverSpecificResourceByPattern-> No MID found per required capabilities", configObj );
return false;
}
var ldcName = ldcGr.name;
// Iterate over patterns and launch HorizontalDiscoveryProbe
var patternGr = new GlideRecord('sa_pattern');
patternGr.get(configObj.patternId);
if (!patternGr.isValid()) {
this._printEventError("discoverSpecificResourceByPattern-> No pattern found having sys_id " , configObj);
return false;
}
gs.log("Discovers specific resource by pattern - " + patternGr.name);
this.launchPatternSingleMode(mid, configObj) ;
return this.probes_sent;
},
/**
run discovery using specific pattern on given service account
*/
discoverServiceAccountWithPattern: function (serviceAccount, status, discovery_schedule, pattern_id, agent) {
// Checking for Specified MID
var midServerName = this.cloudDiscoveryUtil.getSelectedMidFromSchedule(discovery_schedule);
agent = midServerName ? midServerName : agent;
var relCiGr = new GlideRecord("cmdb_rel_ci");
relCiGr.addQuery("child", serviceAccount);
relCiGr.addQuery('type', '5f985e0ec0a8010e00a9714f2a172815'); // Hosted-on relation
relCiGr.query();
while (relCiGr.next()) {
gs.log("Discovering LDC " + relCiGr.parent);
this.discoverLDCwithPattern(serviceAccount, relCiGr.parent, status, discovery_schedule, pattern_id, agent);
}
// If no probes were sent, close the discovery status
if (!this.probes_sent) {
var discoStatusGr = new GlideRecord('discovery_status');
discoStatusGr.get(status);
if (discoStatusGr.isValid()) {
discoStatusGr.setValue('state', 'Completed');
discoStatusGr.update();
}
}
},
/**
run discovery of cloud applications on given logical data center
serviceAccount - sys_id of the service account hosting the logicial data center
ldc - sys_id of the logical data center
status - DiscoveryStatus object
ciTypes - class names to CIs to be discovered. if null or empty, we discover all
*/
discoverLDC: function(serviceAccount, ldc, status, ciTypes) {
// Check if the LDC exists
var ldcGr = new GlideRecord("cmdb_ci_logical_datacenter");
ldcGr.get(ldc);
if (!ldcGr.isValid()) {
gs.log("No logical datacenter found having sys_id " + ldc);
return false;
}
// pick up the service account ID.. this is needed if the customer has specified a MID script override which needs the service account id
var serviceAccountGr = new GlideRecord("cmdb_ci_cloud_service_account");
serviceAccountGr.get(serviceAccount);
var service_account_id = serviceAccountGr.getValue("account_id");
// Change domain do that we pick the correct MID
var dh = new SNC.DomainHelper();
try {
dh.adjustDomainByCiSysId(ldc);
// Checking for the Specific MID
var discoveryStatusGR = new GlideRecord('discovery_status');
discoveryStatusGR.get(status);
var midServerName = this.cloudDiscoveryUtil.getSelectedMidFromSchedule(discoveryStatusGR.dscheduler.sys_id);
// If MID is defined select same else Find MID based on LDC sys_class_name
var mid = midServerName ? midServerName : this.findMid(ldc, service_account_id);
if (!mid) {
gs.log("No MID found per required capabilities" );
return false;
}
var ldcName = ldcGr.name;
// Iterate over patterns and launch HorizontalDiscoveryProbe
this.launchPatterns(ldcGr.sys_class_name, ldc, ldcName, serviceAccount, mid, status, ciTypes);
} finally {
dh.restoreCurrentSessionParams();
}
return this.probes_sent;
},
/**
run discovery using specific pattern on a given LDC
serviceAccount - sys_id of the service account hosting the logicial data center
ldc - sys_id of the logical data center
status - DiscoveryStatus object
discovery_schedule - the discovery schedule assocateated with the status
pattern_id - the pattern to execute
agent - MID server
*/
discoverLDCwithPattern: function(serviceAccount, ldc, status, discovery_schedule, pattern_id, agent) {
// Check if the LDC exists
var ldcGr = new GlideRecord("cmdb_ci_logical_datacenter");
ldcGr.get(ldc);
if (!ldcGr.isValid()) {
gs.log("No logical datacenter found having sys_id " + ldc);
return false;
}
var ldcName = ldcGr.name;
// Launch the pattern
this.launchPattern(serviceAccount, ldc, ldcName, status, discovery_schedule, pattern_id, agent);
},
launchPatternCore: function(ldc, ldcName, status, discovery_schedule, pattern_id, midAgent , probeParams, ppe ) {
if (arguments.length !== 8) {
gs.error("launchPatternCore -> Unexpected number of input arguments -" + JSON.stringify(arguments , null, 2));
return null;
}
var probeValues = {};
probeValues['computer'] = ldc;
// Add dummy entry point. MID pattern execution engine needs that
var entryPoint = {};
entryPoint['sys_class_name'] = 'cmdb_ci_endpoint_tcp';
probeParams['pattern_id'] = pattern_id;
probeParams['endpointAttributes'] = new JSON().encode(entryPoint);
probeParams['logical_datacenter_name'] = ldcName;
var sourceName = ldcName; //PRB1294636: Since we don't have source in cloud and we wantl to avoid empty cells
var patternLauncher = new SNC.PatternLauncherManager();
patternLauncher.launchPattern(status, discovery_schedule, midAgent, sourceName, pattern_id, probeParams, probeValues, ppe);
this.probes_sent = true;
},
/**
* launch discovery pattern ONLY on a specific resource of a specific LDC (logical data center)
*/
launchPatternSingleMode: function(midAgent, configObj) {
//Adding the necessary parmaeters to the patern context infavor the pattern could parse it
var ppe = this.createPreExecutionData( configObj.serviceAccountSysId, configObj.ldcSysId);
ppe.addString('account_id', configObj.accountId);
ppe.addString('ldc_id', configObj.ldcSysId);
ppe.addString('input_object_id', configObj.inputObjectId);
ppe.addString('cloudId', configObj.cloudId);
//For Azure we need to send all aviliable LDCs in order the MID could chose the right one from them later on
if (configObj.eventType === "AZURE"){
ppe.addTableEntry('ldcs_map' , configObj.ldcsMap);
}
//Add the parameters also on the prob for future manipulation on the 'HorizontalDiscoveryResultHandler -> runDeleteHandler'
var probeParams = {
"account_id": configObj.accountId,
"ldc_id" : configObj.ldcSysId,
"event_id": configObj.eventId,
"input_object_id": configObj.inputObjectId,
"class_type" : configObj.classType,
"send_event_on_completion" : true,
"correlation_id" : configObj.correlation_id
};
//No need for discovery_schedule(sending empty discoveryStatusId ) - We want to do a quick discovery and avoid multi schedules
this.launchPatternCore(configObj.ldcSysId, configObj.ldc , configObj.discoveryStatusId, '', configObj.patternId, midAgent, probeParams, ppe);
},
/**
* launch discovery of specific pattern on specific LDC (logical data center)
*/
launchPattern: function(serviceAccount, ldc, ldcName, status, discovery_schedule, pattern_id, agent) {
var ppe = this.createPreExecutionData(serviceAccount, ldc);
var probeParams = {};
this.launchPatternCore(ldc, ldcName, status, discovery_schedule, pattern_id, agent, probeParams, ppe);
},
/*
Find patterns associated with the class name (having os_familty that contains this name)
*/
launchPatterns: function (ldcClassName, ldc, ldcName, serviceAccount, mid, status, ciTypes) {
var patternList = this.patternAppService.getPatternsByFilter(['1'], ciTypes, [ldcClassName], true);
var patternGr = new GlideRecord('sa_pattern');
patternGr.addQuery('sys_id',patternList);
patternGr.addQuery('sys_id', 'NOT IN', this.azureTopDownPatternsExcludeSysIds);
patternGr.query();
while (patternGr.next()) {
// Omit patterns of included items
if (this.patternAppService.isIncludedPattern(patternGr.getUniqueValue()))
continue;
gs.log("Sending probe on pattern " + patternGr.name);
this.launchProbe(ldc, ldcName, serviceAccount, patternGr, mid, status);
}
},
/*
Launch horizontal discovery probe per given pattern and LDC
*/
launchProbe: function(ldc, ldcName, serviceAccount, patternGr, mid, status) {
var probeGr = new GlideRecord('discovery_probes');
probeGr.addQuery('name', 'Horizontal Pattern');
probeGr.query();
if (probeGr.next()) {
var values = {};
values['computer'] = ldc;
var probe = new SncProbe(probeGr, values);
probe.addParameter('pattern', patternGr.name);
probe.addParameter('patternId', patternGr.sys_id);
probe.addParameter('pattern_type', patternGr.ci_type);
probe.addParameter('logical_datacenter_name', ldcName);
if (JSUtil.notNil(status))
probe.setCorrelator(status);
this.addParamsToContext(probe, serviceAccount, ldc);
probe.create(mid);
this.probes_sent = true;
}
} ,
/*
* Create the pre-execution-data structure. It contains the service account, ldc, and relation type
*/
createPreExecutionData:function(serviceAccount, ldc) {
var ppe = new SNC.PrePatternExecutionData();
// Add service account
var serviceAccountGr = new GlideRecord('cmdb_ci_cloud_service_account');
serviceAccountGr.get(serviceAccount);
if (serviceAccountGr.isValid())
ppe.addTableEntry('service_account', this.glideRecordToMap(serviceAccountGr));
/*
* Add LDC
* When LDC is null, it still manages to pass ldcGr.isValid()
* This causes a 'null' key to be created in addition to a 'null' value
* in 'ldc' key
*/
if (ldc != null) {
var ldcGr = new GlideRecord('cmdb_ci_logical_datacenter');
ldcGr.get(ldc);
if (ldcGr.isValid()){
var className = ldcGr.getValue('sys_class_name');
ppe.addTableEntry(className, this.glideRecordToMap(ldcGr));
ppe.addTableEntry('ldc', this.glideRecordToMap(ldcGr));
ppe.addString('build_relation_auto','true');
}
}
// Add relation type
ppe.addString('hosted_relation_type', 'Hosted on::Hosts');
ppe.addString('hosted_relation_id', '5f985e0ec0a8010e00a9714f2a172815');
return ppe;
},
/*
Add to probe the service account,LDC details, and hosting relation type
*/
addParamsToContext: function(probe, serviceAccount, ldc) {
var ppe = this.createPreExecutionData(serviceAccount, ldc);
// Convert to JSON and add to probe
var json = ppe.getJSON();
probe.addParameter('prePatternExecutionData', json);
// Add dummy entry point. MID pattern execution engine needs that
var entryPoint = {};
entryPoint['sys_class_name'] = 'cmdb_ci_endpoint_tcp';
probe.addParameter('endpointAttributes', new JSON().encode(entryPoint));
},
/*
* convert glide record to javascript map
*/
glideRecordToMap: function (gr) {
var map = {};
var iterator = gr.getFields().iterator();
while (iterator.hasNext()) {
var field = iterator.next();
if (JSUtil.notNil(gr.getValue(field.getName()))) {
var fieldName = field.getName() + '';
if (fieldName.indexOf('sys_') != 0 || fieldName == 'sys_class_name')
map[field.getName()] = gr.getValue(field.getName());
}
}
if (map) {
map['sys_id'] = gr.getValue('sys_id');
}
return map;
},
/*
Find MID based on the class of the LDC
*/
findMid: function(ldc, service_account_id) {
var ldcGr = new GlideRecord('cmdb_ci_logical_datacenter');
if (!ldcGr.get(ldc)) {
gs.log('Logical datacenter ' + ldc + ' not found in the CMDB');
return null;
}
var invocationContext = {};
invocationContext['service_account_id'] = service_account_id;
var cmdMidSelector = new global.CloudMidSelectionApi();
var mid_id = cmdMidSelector.selectMid(null, null, ldcGr.region, JSON.stringify(invocationContext));
if (!mid_id)
return mid_id;
// Find the mid name
var midGr = new GlideRecord('ecc_agent');
if (!midGr.get(mid_id)) {
gs.log('MID ' + mid_id + ' not found in table ecc_agent');
return null;
}
return midGr.name;
},
type: 'CloudApplicationDiscovery'
};
Sys ID
f82bdda4c35322003e76741e81d3ae91