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

Offical Documentation

Official Docs: