396 views

Description

After setting up reconciliation definitions for a CI type and then running Discovery, the CI updates bypass identification/reconciliation; no records are added to cmdb_datasource_last_update for the updated CIs. This breaks staleness detection for the CMDB Health Dashboard as the dashboard relies on the entries in cmdb_datasource_last_update.

Steps to Reproduce

  1. Create a reconciliation rule:
    • Data Source = Manual Entry
    • Applies to = cmdb_ci_win_server
    • Attribute = ram
  2. Discover a Windows CI.
    Note the RAM value that is added.
  3. Manually edit the RAM field for the CI to a value other than the one Discovery added.
  4. Discover the CI again.
    Note that the manually edited RAM value is replaced with the Discovery value. Because the reconciliation rule was added, Discovery should not be able to overwrite the value after the RAM field was edited manually.

Workaround

The following workaround can be used for cmdb_ci_hardware extended CIs and their base fields (not related lists or extensions of cmdb_ci_appl), so it covers RAM and any other fields that are populated for the hardware CI during the exploration phase.

Inside DiscoveryCMDBUtil add this function:

function updateExistingCi(gr, data) {
    if (JSUtil.nil(gr) || JSUtil.nil(data))
        return null;

    var grClass = gr.sys_class_name+'';
    var payload = 
        { 
            "items": [
                { 
                    "className": grClass,
                    "values": valuesToJson(data),
                }
            ] 
        };

	// Add the sys_id so IE simply updates
	payload.items[0].values.sys_id = gr.getUniqueValue();

    var result = json.decode(cmdbApi.createOrUpdateCI(gs.getProperty('glide.discovery.source_name', "ServiceNow"), json.encode(payload)));
    return getSysIdFromResult(result, grClass);

    /***
     * Make sure that we coerce java strings to javascript and remove nulls
     */
    function valuesToJson(obj) {
        var values = {};
        for (var fieldName in obj) {
            if (JSUtil.notNil(obj[fieldName]))
                values[fieldName] = obj[fieldName] + '';
        }

        return values;
    }

    /***
     * Returns the sys id of the given IE result
     * Assumes that there is only one app per payload
     */
    function getSysIdFromResult(result, grClass) {
        if (JSUtil.nil(result) || JSUtil.nil(result.items) || JSUtil.nil(grClass))
            return null;
    
        var sysId = null;
        for (var i = 0; i < result.items.length; i++) {
            var item = result.items[i];
            if (item.className === grClass) {
                sysId = item.sysId;
                break;
            }
        }

        return sysId;
    }
}

In the same file, add this function declaration as a property of DiscoveryCMDBUtil object (like the other examples):

    /***
     * updateExistingCi(gr, data)
     *
     * Used to call the CMDB ID Engine to update a CI
     * Since the sys_id is passed in, the ID part is skipped and we just update the attributes
     * with the passed in 'data' map
     *
     * @param GlideRecord gr (required) The CI GlideRecord to update
     * @param {} data (required) A map of attributes and their values to update for the CI
     *
     * @return string sys_id of successful insert/update or null if failure
     */
    updateExistingCi: updateExistingCi,

Inside the DiscoverySensor script include, edit the update function as follows:

update: function(data) {
		var gr = this.getCmdbRecord();
		if (gs.nil(gr))
			return;

		var dbom = GlideDBObjectManager.get();
		var tables = dbom.getHierarchy(gr.sys_class_name);

		if (DiscoveryCMDBUtil.useCMDBIdentifiers() && tables.contains("cmdb_ci_hardware")) {
			DiscoveryCMDBUtil.updateExistingCi(gr, data);
		} else {
			for (var fieldName in data) {
				var value = data[fieldName];
				if (JSUtil.notNil(value)) {
					// PRB632583: Use setValue() here instead of gr[fieldName] = value
					// GlideRecord returns times in UTC and setValue() accepts
					// dates in UTC, but assignment uses the current timezone.
					gr.setValue(fieldName, value);
				}
			}
		}

		g_disco_functions.updatedHistory(gr, this.getSensorName(), this.getEccQueueId());
		this.cache[gr.update(this.getSensorName())] = null;	// invalidate the cache after update

		// update the necessary related lists
		if (this._deviceCi === null)
			new DiscoveryReconciler(this.getCmdbRecord(), this.relatedListdataObj).process();
		else
			this._deviceCi.write();
	},

Related Problem: PRB745423

Seen In

Helsinki
Helsinki Patch 6 Hot Fix 1

Fixed In

Jakarta

Associated Community Threads

There is no data to report.

Article Information

Last Updated:2017-05-12 09:20:31
Published:2017-05-10