Name
sn_itom_pattern.ContainerizedAppHandler
Description
This script handles the creation of application CIs representing applications running in containers
Script
var ContainerizedAppHandler = Class.create();
ContainerizedAppHandler.prototype = {
initialize: function() {
this.logger = new Logger('ContainerizedAppHandler');
},
// Split the main command in the image into process name and parameters
_parseCommand: function(command) {
var result = {};
if (!command)
return;
var cmdParts = command.split(/\s+/);
if (!cmdParts || cmdParts.length == 0)
return;
var lastSlash = cmdParts[0].lastIndexOf('/');
var lastBackSlash = cmdParts[0].lastIndexOf('\\');
var last = Math.max(lastSlash, lastBackSlash);
var procName = command;
if (last >= 0)
procName = command.substring(last + 1);
if (!procName)
return;
var parameters = command.substring(cmdParts[0].length).trim();
result.command = cmdParts[0];
result.parameters = parameters;
result.procName = procName;
return result;
},
// Find the application class by applying the discovery classifiers to the main command of the image
_getAppClass: function(imageId) {
var imageGr = new GlideRecord('cmdb_ci_oslv_image');
if (!imageGr.get(imageId))
return;
return this.getAppClassByCommand(imageGr.command + '');
},
// Use the discovery classifiers to get the application class that corresponds to given process commmand
getAppClassByCommand: function(command) {
var tableName = '';
var imgCommand = command;
/*
If Image contains both command and entry point,pattern combines both entry point and command sends it as full command like this ‘EntryPoint [enrtypoint] command’
first we will send command to ADM if class not found then we will send entry point to ADM
if image doesn’t start with ‘EntryPoint we will send command as it it.
*/
if (imgCommand.startsWith('Entrypoint')) {
imgCommand = imgCommand.substring(imgCommand.lastIndexOf("]") + 2);
tableName = this._getClassUsingAdm(imgCommand);
if (!tableName) {
imgCommand = '';
imgCommand = command;
imgCommand = imgCommand.substring(imgCommand.indexOf("[") + 1, imgCommand.lastIndexOf("]"));
tableName = this._getClassUsingAdm(imgCommand);
}
} else {
tableName = this._getClassUsingAdm(command);
}
this.logger.debug('Based on the image command "+command+" classifier found "+ tableName+" application class.');
return tableName;
},
_getClassUsingAdm: function(command) {
var parsedCommand = this._parseCommand(command + '');
if (!parsedCommand)
return;
var proc = {
pid: 2,
ppid: 1,
command: parsedCommand.command,
parameters: parsedCommand.parameters,
name: parsedCommand.procName
};
var relatedData = {
running_processes: [proc]
};
var adm = new global.ApplicationDependencyMapping(relatedData);
if (!adm.matched_processes || adm.matched_processes.length == 0)
return;
var tableName = adm.matched_processes[0].classifier.table;
this.logger.debug('classifier found application class ' + tableName + 'for the command ' + command);
return tableName;
},
// Grab the applicationn class from the image status record
_getAppClassFromScanRecord: function(imageId) {
var imageStatGr = new GlideRecord('sn_itom_pattern_container_image_scan_status');
imageStatGr.addQuery('image', imageId);
imageStatGr.query();
if (!imageStatGr.next())
return;
return imageStatGr.ci_class + '';
},
// Calculate the image app class by applying the discovery classifiers and update the image status record
updateApplClass: function(imageId) {
var imageStatGr = new GlideRecord('sn_itom_pattern_container_image_scan_status');
imageStatGr.addQuery('image', imageId);
imageStatGr.query();
if (!imageStatGr.next())
return;
var imageGr = new GlideRecord('cmdb_ci_oslv_image');
if (!imageGr.get(imageId))
return;
tableName = _getClassUsingAdm(imageGr.command + '');
imageStatGr.setValue('ci_class', tableName);
imageStatGr.update();
// We create apps for the first time. We will have to repeat this periodically as more containers using the image are added
this.updateContainerizedAppPerImage(imageGr, tableName);
},
// Create containerized app CIs on all the containers that run given image
updateContainerizedAppPerImage: function(imageGr) {
var tableName = this._getAppClassFromScanRecord(imageGr.getUniqueValue());
if (!tableName)
return;
// Find all containers using the image
var relGr = new GlideRecord('cmdb_rel_ci');
relGr.addQuery('parent', imageGr.getUniqueValue());
relGr.addQuery('type', '1bb40e370a0a0b323d85a1ce84f8feae'); // Instantiates: Instantiated by
relGr.addQuery('child.sys_class_name', 'cmdb_ci_docker_container');
relGr.query();
while (relGr.next()) {
this.createApp(relGr.child + '', imageGr.getUniqueValue(), tableName);
}
},
// Create application on given container based on given image
createApp: function(containerSysId, imageGr, tableName) {
if (!containerSysId)
return;
if (!tableName)
tableName = this._getAppClassFromScanRecord(imageGr.getUniqueValue());
if (!tableName)
return;
var containerGr = new GlideRecord('cmdb_ci_docker_container');
if (!containerGr.get(containerSysId))
return;
var parsedCommand = this._parseCommand(imageGr.command + '');
if (!parsedCommand)
return;
var containerName = containerGr.name + '';
var ire_payload = {
items: [],
relations: []
};
// Place in the payload the container CI
var containerCi = {};
var containerValues = {};
containerValues['sys_id'] = containerSysId;
containerCi['values'] = containerValues;
containerCi['className'] = 'cmdb_ci_docker_container';
ire_payload.items.push(containerCi);
// Place in the payload the application CI
var ci = {};
ci['className'] = tableName;
var ciValues = {};
ci['values'] = ciValues;
ciValues['running_process_command'] = parsedCommand.command;
ciValues['running_process_key_parameters'] = parsedCommand.parameters;
this._runEnrichScript(containerSysId, tableName, ciValues);
var ciGr = new GlideRecord(tableName);
var appName = ciGr.getClassDisplayValue();
appName = appName + '@' + containerName;
ciValues['name'] = appName;
ire_payload.items.push(ci);
// Create the relation
var runsOnRel = {};
runsOnRel['parent'] = 1;
runsOnRel['child'] = 0;
runsOnRel['type'] = 'Runs on::Runs';
ire_payload.relations.push(runsOnRel);
// Call IRE
this.logger.debug('Adding CI ' + appName + ' to container ' + containerSysId);
var payload_str = JSON.stringify(ire_payload);
var output = sn_cmdb.IdentificationEngine.createOrUpdateCI("ServiceNow", payload_str);
var outputObj = JSON.parse(output);
if (outputObj.hasError)
this.logger.debug('IRE output with error: ' + output + '\n\n\nInput payload: ' + payload_str);
// Run SAM post script
this._runSAMPostScript(output);
},
_runSAMPostScript: function(payload) {
if (!GlidePluginManager.isActive('com.snc.sam.core'))
return;
// Find the SAM pre/post script
var prePostGr = new GlideRecord('sa_pattern_prepost_script');
if (!prePostGr.get('ecd06b4953332200d0daae4a16dc3445'))
return;
new GlideScopedEvaluator().evaluateScript(prePostGr, 'script', {
payload: payload,
patternId: 'ContainerImage'
});
},
_runEnrichScript: function(containerSysId, tableName, values) {
var enrichScriptGr = new GlideRecord('sn_itom_pattern_container_enrich_scripts');
enrichScriptGr.addActiveQuery();
enrichScriptGr.addQuery('ci_type', tableName);
enrichScriptGr.orderBy('order');
enrichScriptGr.query();
while (enrichScriptGr.next()) {
var res = new GlideScopedEvaluator().evaluateScript(enrichScriptGr, 'script', {
containerSysId: containerSysId
});
if (res) {
for (var key in res) {
if (res[key])
values[key] = res[key];
}
}
}
},
type: 'ContainerizedAppHandler'
};
Sys ID
8c21bb91f96f5110f8770a8596e24539