Name

global.DiscoveryFunctions

Description

DiscoveryFunctions is a set of misc functions used in discovery

Script

var DiscoveryFunctions = Class.create();

(function() {

var relTypes = { };

DiscoveryFunctions.prototype = {
  initialize: function() {
  },
  
  getDiscoverySource: function() {
  	var gr = new GlideRecord("sys_properties");
  	if(gr.get("name", "glide.discovery.source_name"))
  		return gr.getValue('value');
  	return "";
  },

  getScheduleRecord: function() {
      var statusGR = this.getStatusRecord();
      if (!statusGR)
          return;

      var gr = new GlideRecord('discovery_schedule');
      if (statusGR.dscheduler)
          gr.get('sys_id', statusGR.dscheduler);
      return gr;
  },

  getStatusRecord: function() {
      var statusSysId = g_probe.getCorrelator();
      if (gs.nil(statusSysId)) {
          if (typeof agent_correlator != 'string' && agent_correlator)
              statusSysId = agent_correlator;
          else if (current) {
              if (current.agent_correlator)
                  statusSysId = current.agent_correlator;
              else if (current.getTableName() == "discovery_status")
                  return current;
          }
      }

      if (!statusSysId)
          return null;

      var gr = new GlideRecord('discovery_status');
      if (statusSysId)
          gr.get('sys_id', statusSysId);
      return gr;
  },

  getDiscoveryType: function(statusID) {
      var statusGR = new GlideRecord('discovery_status');
      statusGR.get(statusID);
      var schedGR = new GlideRecord('discovery_schedule');
      schedGR.get(statusGR.dscheduler);
      return schedGR.discover;
  },

  getService: function(port) {
      var gr = new GlideRecord("cmdb_ip_service");
      if (!gr.get("port", port)) {
          gr.initialize();
          gr.setValue("port", port);
          gr.setValue("name", "unknown - port(" + port + ")");
          gr.insert();
      }

      return gr;
  },

  isInRange: function(ip) {
      var gr = this.getStatusRecord();
      if (gr != null) {
          var hostList = gr.scratchpad.host_list;

          if (hostList) {
              var hosts = StringUtil.split(hostList, ",");
              if (hosts.contains(ip))
                  return true;
          }

          return SncDiscoveryRangesDB.inRanges(ip, gr.dscheduler);
      }

      return false;
  },

  getXMLMemoryTable: function(tableName, doc, path) {
      var result = null;
      try {
          result = GlidesoftXMLMemoryTable.getFromDocAndXPath(tableName, doc, path);
      } catch (iae) {
          // do nothing; we'll return a null...
      }
      return result;
  },

  getFieldsThatChanged: function(gr) {
      var changes = [];

      if (gr == null || !gr.isValid())
          return changes;

      var gru = GlideScriptRecordUtil.get(gr);
      var ignoreables = new Packages.java.util.ArrayList();
      ignoreables.add("last_discovered");

      var whatChanged = gru.getChangedFields(ignoreables);
      for (var i = 0; i < whatChanged.size(); i++) {
          var f = whatChanged.get(i);
          changes.push(f);
      }

      return changes;
  },

  updatedHistory: function(gr, sensor, eccID) {
      var fieldsChanged = this.getFieldsThatChanged(gr);
      if (fieldsChanged.length > 0)
          g_device.log("Updated device fields " + fieldsChanged.join(", "), sensor, eccID);
  },

  /*
   *  Return the sys_id of the relationship created. Otherwise return null or undefined.
   */
  createRelationshipFromSysIds: function(parentId, childId, pDescription, cDescription) {
      if (JSUtil.nil(parentId) || JSUtil.nil(childId))
          return;
      if (!this.validCISysId(parentId) || !this.validCISysId(childId)) {
  		return;
  	}

      if (!cDescription && pDescription.indexOf("::") > -1) {
          var parts = pDescription.split("::");
          pDescription = parts[0];
          cDescription = parts[1];
      }

  	var ECMDBUtil = SncECMDBUtil;
      return ECMDBUtil.createCIRelationship('cmdb_rel_ci', parentId, childId, pDescription, cDescription);
  },

  // Validate sysid
  validCISysId: function(sysID) {
  	// Case 1: Check for null sys id
  	if (gs.nil(sysID)) {
  		gs.log('Invalid Rel CI Record :: Null SysID');
  		return false;
  	}

  	//extracting only alphanumeric and comparing 32 chars
  	if (sysID.match(/[0-9a-f]{32}/) == sysID) {
  		return true;
  	} else {
  		// Case 3:SysID is not null
  		// but it is not a valid sysID by its strig format [Alphanumeric and exactly 32 chars]
  		gs.log('Invalid Rel CI Record :: Invalid SysID(Regex only) :SysID ' + sysID);
  		return false;
  	}
  },

  //
  // Args:    parent <pDescription> child   /    child <cDescription> parent
  // Example: vmware   Runs on      sanops  /    sanops    Runs       vmware
  // parent, child input can either be GlideRecord or sys_id
  //
  createRelationship: function(parent, child, pDescription, cDescription) {
      if (JSUtil.nil(parent) || JSUtil.nil(child))
          return;

      var parentId = parent;
      var childId  = child;

      //If parent or child is a GlideRecord object, we need to extract the sys_id
      if (parent instanceof GlideRecord)
          parentId = parent.sys_id;
      if (child instanceof GlideRecord)
          childId  = child.sys_id;

  	return this.createRelationshipFromSysIds(parentId, childId, pDescription, cDescription);
  },

  /*
   *  Return the sys_id of the relationship if found or created. Otherwise return null or undefined.
   */
  createRelationshipIfNotExists: function(parent, child, descriptor, ensureUnique) {
      var result = this.relationshipExists(parent, child, descriptor);
      if (JSUtil.notNil(result))
          return result;

      var createdRelationship =  this.createRelationship(parent, child, descriptor);

  //checking unique for the newly created relationship
  if (ensureUnique)
  	this.deleteRelationshipIfMultipleExists(parent, child, descriptor);

  return createdRelationship;
  },

  /*
   *  Checks to see if a relationship already exists
   */
  relationshipExists: function(parent, child, descriptor) {
      if (JSUtil.nil(parent) || JSUtil.nil(child) || JSUtil.nil(descriptor))
          return;

      var parentId = parent;
      var childId  = child;

      //If parent or child is a GlideRecord object, we need to extract the sys_id
      if (parent instanceof GlideRecord)
          parentId = parent.sys_id;
      if (child instanceof GlideRecord)
          childId  = child.sys_id;

      var relationType = this.findCIRelationshipType("cmdb_rel_type", descriptor);

      var gr = new GlideRecord("cmdb_rel_ci");
      gr.addQuery("parent", parentId);
      gr.addQuery("child", childId);
      gr.addQuery("type", relationType);
      gr.query();
      if (gr.next())
          return gr;

      return;
  },

  /*
   *  Deleted duplicate relationship from cmdb_rel_ci
   */
  deleteRelationshipIfMultipleExists: function  (parent, child, relTypeName) {
  	var gr = new GlideRecord('cmdb_rel_ci');
  	var relTypeId = this.findCIRelationshipType('cmdb_rel_type', relTypeName);

  	gr.addQuery('parent', parent);
  	gr.addQuery('child', child);
  	gr.addQuery('type', relTypeId);
  	gr.orderBy('sys_id');
  	gr.query();

  	if (gr.getRowCount() > 1) {
  		gs.info('Duplicate relationship %% ' + relTypeName + ' exists between the parent  ' + parent + ' and child ' + child);

  		// Don't allow exact duplicates.  We'll keep the one with the lowest sys_id
  		// to avoid a race condition where two threads insert a record at the same
  		// time and both decide to delete it.
  		gr.next();

  		// Delete duplicate record
  		while (gr.next()) {
  			gr.deleteRecord();
  			gs.info('Deleted Duplicate relationship %% ' + relTypeName + ' between the parent  ' + parent + ' and child ' + child);
  		}
  	}
  },

  /**
    * get a child of this parent of type matched by the descriptor
    *
    * Descriptor is of form <Parent Relationship>::<Child Relationship>
    * so, for example calling this method on a web server with descriptor "RunsOn::Runs"
    * returns the computer it runs on.  Thus, web server is the parent and computer is the child
    */

  getChildCI: function(parent, descriptor) {
  	var gr = new GlideRecord("cmdb_rel_ci");
  	gr.addQuery("parent", parent.sys_id);
  	if (descriptor) {
  		var relationType = this.findCIRelationshipType("cmdb_rel_type", descriptor);
  		if (relationType)
  			gr.addQuery("type", relationType);
  	}
  	gr.query();

  	if (gr.next()) {
  		var cigr = new GlideRecord("cmdb_ci");
  		if (gr.child && cigr.get('sys_id', gr.child)) {
  			return cigr;
  		}
  	}
  	return null;
  },

  reclassify: function(gr, newClassName, reason) {
      // we are already that class, just return
      if (gr.sys_class_name == newClassName)
          return gr;

      var rci = SncReclassifyCI.createFromGlideRecord(gr, newClassName);
      rci.setApplyDefaults(false);
      rci.setReason(reason);
      return rci.reclassify();
  },

  deleteCIBySysId: function(sysId, workflow) {
      var gr = new GlideRecord("cmdb_ci");
      if (!sysId || !gr.get('sys_id', sysId))
          return;

      this.deleteCI(gr, workflow);
  },

  deleteCI: function(gr, workflow) {
      var al = [];
      al.push(gr.getValue("sys_id"));

      //Check to see if it matches these two criteria
      if (!(gr.instanceOf("cmdb_ci_computer") || gr.instanceOf("cmdb_ci_appl"))) {
          gr.deleteRecord();
          return;
      }

      if (workflow && workflow != "false") {
          this.deleteCIAndRelationshipsRecursive(al);
          return;
      }

      try {
          var oldWorkflow = session.getWorkflow();
          this.deleteCIAndRelationshipsRecursive(al);
      } finally {
          session.setWorkflow(oldWorkflow);
      }

  },

  /*
  * This is where the heavy lifting of traversing through the relationship tree is done
  * We only traverse through the relatinoships that have RUNS_ON and HOSTED_ON relationships at this point
  *
  * We basically look for all the parent CIs that runs on or is hosted on the child CI(s) and delete them.
  */

   deleteCIAndRelationshipsRecursive: function(appList) {
   if (appList.length == 0)
          return appList;

   var appListNew = [];
   var tempSysId;
   var sysID;
       for (var i = 0; i < appList.length; i++) {
           sysID = appList[i];
       var gr = new GlideRecord("cmdb_rel_ci");
       gr.addQuery("child", sysID);
       var qc = gr.addQuery("type", this.findCIRelationshipType("cmdb_rel_type", "Runs on::Runs"));
       qc.addOrCondition("type", this.findCIRelationshipType("cmdb_rel_type", "Hosted on::Hosts"));
       gr.query();
       while (gr.next()) {
       tempSysId = gr.getValue("parent");

       //Check to see if same CI could runs-on or be hosted on same child (maybe possible?)
       if (!contains(appListNew, tempSysId))
          appListNew.push(tempSysId);

               gr.deleteRecord(); //delete the relationship entry
       }

       var cigr = new GlideRecord("cmdb_ci");
       if (sysID && cigr.get('sys_id', sysID))
      cigr.deleteRecord();
        }

    return this.deleteCIAndRelationshipsRecursive(appListNew);

        function contains(arr, value) {
           for (var i=0; i<arr.length; i++)
             if (arr[i] == value)
                return true;
           return false;
        }

     },

  findOrCreateRelationshipType: function(refTable, descriptor) {
  	var result = this.findCIRelationshipType(refTable, descriptor);
  	if (JSUtil.notNil(result))
  		return result;

  	var al = descriptor.split("::");
      var gr = new GlideRecord(refTable);
      gr.parent_descriptor = al[0];
      gr.child_descriptor = al[1];
  	return gr.insert();
  },

  findCIRelationshipType: function(refTable, descriptor) {

  	// refTable should be an optional parameter (because we always pass the
  	// same value).  I can't change the function signature, so this is the
  	// only way to make it optional.
  	if (!descriptor)
  		return this.findCIRelationshipTypeByDesc('cmdb_rel_type', refTable);

  	return this.findCIRelationshipTypeByDesc(refTable, descriptor);
  },

  findCIRelationshipTypeByDesc: function(refTable, pDescriptor, cDescriptor) {

  	var descriptor = pDescriptor;
  	if (cDescriptor)
  		descriptor += '::' + cDescriptor;

  	relTypes[descriptor] = relTypes[descriptor] || getRelType();

  	return relTypes[descriptor];

  	function getRelType() {

  		if (pDescriptor && !cDescriptor) {
  			pDescriptor = pDescriptor.split('::');
  			cDescriptor = pDescriptor[1];
  			pDescriptor = pDescriptor[0];
  		}

  		var gr = new GlideRecord(refTable);
  		gr.addQuery("parent_descriptor", pDescriptor);
  		gr.addQuery("child_descriptor", cDescriptor);
  		gr.query();
  		if (gr.next())
  			return '' + gr.getValue("sys_id");

  		return "";
  	}
  },


  /* Manage the PIDs creation and update */
  insertPIDs: function(pids, app_sys_id, ci_sys_id) {
      if (StringUtil.nil(pids))
          return;

      var pid_array = pids.match(/\d+/g);
      var gr = new GlideRecord("cmdb_pid");
      gr.initialize();
      for (var i=0; i<pid_array.length; i++) {
         gr.pid = pid_array[i];
         gr.application = app_sys_id;
         gr.cmdb_ci = ci_sys_id;
         gr.insert();
      }
  },

  updatePIDs: function(pids, app_sys_id, ci_sys_id) {
      if (StringUtil.nil(pids))
          return;

      var gr = new GlideRecord("cmdb_pid");
      gr.addQuery("application", app_sys_id);
      gr.query();
      gr.deleteMultiple();

      this.insertPIDs(pids, app_sys_id, ci_sys_id);
  },

  /**********************************************
   * Manage the Windows installed software xml
   *
   * Example payload:
   *   <results probe_time="6313">
   *     <result>
   *       <Registry>
   *         <entry key="HKEY_LOCAL_MACHINE">
   *           <entry key="Software">
   *             <entry key="Microsoft">
   *               <entry key="Windows">
   *                 <entry key="Name">
   *                   <value>Just a name</value>
   *                 </entry>
   *               </entry>
   *             </entry>
   *           </entry>
   *         </entry>
   *       </Registry>
   *     </entry>
   *   </results>
   *
   *   // To find the value of the key called "name", here's how these methods can be used.
   *   // registry is a variable representing the payload
   *   var node = findRegistryNode(registry, "HKEY_LOCAL_MACHINE.Software.Microsoft");
   *   var name = findNodeValueWithAttribute(node, "Name");
   *********************************************/
  findRegistryNode: function(currNode, regName){
  var node = currNode;
  var names = regName.split(".");

  for (var i=0; i<names.length; i++) {
      node = this.findNodeWithAttribute(node, names[i]);
          if (!node)
              return null;
      }

  return node;
  },

  findNodeWithAttribute: function(currNode, attrName) {
      if (JSUtil.nil(currNode))
          return "";

      var nodeArray = g_array_util.ensureArray(currNode.entry);
  for (var i=0; i<nodeArray.length; i++)
      if (nodeArray[i]['@key'] == attrName)
          return nodeArray[i];

  return null;
  },

  findNodeValueWithAttribute: function(currNode, attrName) {
      if (JSUtil.nil(currNode))
          return "";

      var nodeArray = g_array_util.ensureArray(currNode.entry);
  for (var i=0; i<nodeArray.length; i++)
      if (nodeArray[i]['@key'] == attrName)
          return nodeArray[i].value;

      return "";
  },

  type: "DiscoveryFunctions"
};

}());

Sys ID

4bcc49be0a0a0b1c00f5801137a5d422

Offical Documentation

Official Docs: