How to use CMDBTransformUtil in Transform Maps where it requires a Dependency?

Wallace Roets
ServiceNow Employee
ServiceNow Employee

Good day all,

I would like to find out if anyone used the CMDBTransformUtil in transform maps for sub-components?

Apply CI Identification and Reconciliation to Import Sets

NOTE: I've set up the identification rules, precedence etc, accordingly.

This works great for Parent components like Servers etc. However, trying to make use of this for components like IP Address, I get the "MISSING_DEPENDENCY" error

How do I set up the transform map to provide the relationship/"relations"?

I have a transform map where i set all the mappings for 1 table

The payload that gets generated look like this:

{  

    "items":[  

          {  

                "className":"cmdb_ci_ip_address",

                "values":{  

                      "nic":"3f493de24f8c5300c512ecfe0310c72b",

                      "name":"10.52.34.58",

                      "correlation_id":"ab3c16636f8593c4f412e79c6711a95b",

                      "ip_address":"10.52.34.58"

                },

                "lookup":[],

                "related":[]

          }

    ],

    "relations":[]

}

However, it is expecting the payload to contain the dependencies, i.e. something like this (even though the IP is associated with the NIC and not the host directly):

{

    "items":[

          {

                "className":"cmdb_ci_ip_address",

                "lookup":[],

                "values":{

                      "nic":"3f493de24f8c5300c512ecfe0310c72b",

                      "discovery_source":"MySource",

                      "correlation_id":"ab3c16636f8593c4f412e79c6711a95b",

                      "ip_address":"10.52.34.58"

                }

          },

          {

                "className":"cmdb_ci_solaris_server",

                "lookup":[],

                "values":{

                      "correlation_id":"ab3c16636f8593c4f412e79c6711a95b",

                      "discovery_source":"MySource"

                }

          }

    ],

    "relations":[

          {

                "type":"Owns::Owned by",

                "parent":1,

                "child":0

          }

    ]

}

In summary: How does one set up the transform maps to ensure it generates the required "parent", "child" and "relations" values when using CMDBTransformUtil

I've tried to map it in the transform:

find_real_file.png

But that just generates errors like:

Invalid map target parent does not exist in table cmdb_ci_ip_address

Invalid map target child does not exist in table cmdb_ci_ip_address

2 REPLIES 2

Hadyn
Tera Expert

Yeah I recently needed to import some CI's via import sets and decided to use the createOrUpdateCIscript to do this. I notice it is just a wrapper for SNC.IdentificationEngineScriptableApi.createOrUpdateCI().

I had the exact same issue as you as there is no obvious way to include relationships using purely CMDBTransformUtil.

So I updated the CMDBTransformUtil class to be capable of adding relationships.

These are the changes I made:

Add the last line below to the initialize function.

initialize: function() {
	this.json = new JSON();
	this.dataSource = 'ImportSet';
	this.transformEntryTable = 'sys_transform_entry';
	this.cmdbTransformStatsTable = 'cmdb_import_set_run';
	this.CmdbAPI = SNC.IdentificationEngineScriptableApi;
	this.CmdbUtil = SNC.CMDBUtil;
	this.outputPayload = '';
	this.outputRecordSysId = '';
	this.error = '';
	this.transformResult = 'e';
	this.relationships = []; //Add this
},

Add the below function to the class.

/**New function to store some relationships to build later in our payload
*@param {string} relation_class The ci class you are going to relate to
*@param {string} relation_sys_id The sys_id of the ci instance you are going to relate to
*@param {string} relation_type The CI relationship type you want to use to represent the relation
*@param {boolean} im_the_parent Indicates if the CI we are building a payload for is the parent in this relationsip ie. the relationship direction
*/
addRelationship: function(relation_class, relation_sys_id, relation_type, im_the_parent){//ie'cmdb_ci_solaris_server', sys_id, 'Instantiates::Instantiated by', false		
	this.relationships.push({
		relation_class: relation_class,
		relation_sys_id: relation_sys_id.toString(),//just in case we sent a glideelement
		relation_type: relation_type,
		im_the_parent: im_the_parent
	});
},

Change the buildInputPayload function to include building the relationships as below.

// Builds inputPayload for given source record and transform map
buildInputPayload: function(source, map, log) {
	var inputPayload = {};
	inputPayload.items = [];
	var item = {};
	item.className = map.target_table;
	var values = this.getTransformValues(source, map, log);
	var isEmpty = !Object.keys(values).length;
	if (!this.hasError() && !isEmpty) {
		item.values = values;
		inputPayload.items.push(item);
	}
	//Add our code for adding relationships now.
	if(this.relationships.length>0){
		inputPayload.relations = [];
		for(var i in this.relationships){
			var offset = parseInt(i)+1;
			var rel = this.relationships[i];
			//Add the relation to our items array
			inputPayload.items.push({
				"className": rel.relation_class,
				"values": {
					"sys_id": rel.relation_sys_id
				}
			});
			inputPayload.relations.push({
				"parent": rel.im_the_parent ? "0" : offset.toString(),
				"child": !rel.im_the_parent ? "0" : offset.toString(),
				"type": rel.relation_type
			});
		}
	}
	return inputPayload;
},

 

So then when I Import dependant classes I can use the following example code in my OnBefore script in the transform map:

var cmdbUtil = new CMDBTransformUtil();
cmdbUtil.setDataSource('ImportSet');
//Go find if we have a parent we can relate to, since it's mandatory for this class
var parent = new GlideRecord('cmdb_ci_solaris_server');
parent.addQuery('host_name',host);
parent.query();
if(parent.next()) { //If we found a valid parent, continue...
	//Add our relationship which will be added to the payload later in identifyAndReconcile() function call
	cmdbUtil.addRelationship('cmdb_ci_solaris_server', parent.sys_id.toString(), 'Instantiates::Instantiated by', false);
	//Add more than 1 relationship if you want
	cmdbUtil.addRelationship('cmdb_ci_solaris_server', parent.sys_id.toString(), 'Owns::Owned by', false);
	cmdbUtil.identifyAndReconcile(source, map, log);

 

I also had an issue where I actually have more than 1 transform map for my import set, and that does not play nicely with the CMDBTransformUtil class, so I had to make the following adjustment to fix that:

// Get values that need to be transformed for given source record
getTransformValues: function(source, map, log) {
	var values = {};
	var td = GlideTableDescriptor.get(map.target_table);
	var entryGr = new GlideRecord(this.transformEntryTable);
	entryGr.addQuery('source_table', map.source_table);
	entryGr.addQuery('map', map.sys_id); //If you have more than 1 transform map for a table then this will cause issues if this line is not present.
	entryGr.query();

Thanks for posting this, I found very useful.  However, I am having an issue when I try to add more than one relationship.  I get a MULTIPLE_DEPENDENCIES error.  Were you able to add multiple relationships successfully?