How do I edit the AssetAndCISynchronizer to include a newly created field?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
05-25-2016 03:22 PM
I created a new field in both the CMDB and the Asset tables called u_firmware_version. I would like to add it to the AssetAndCISynchronizer script include so that it updates just like all the other fields do. Our AssetAndCISynchronizer script is below. I tried just adding "changes += this._syncField('u_firmware_version', source, destination);" under the tag " // synchronize data for fields that have the same name in both tables" but that didn't work. How do I do this? Thanks!
var AssetAndCISynchronizer = Class.create();
AssetAndCISynchronizer.prototype = {
initialize : function() {
},
/*
* Synchronize data between related asset and CI records Record linked to
* source record in the destination table will get updated
*/
syncRecords : function(source, destinationBaseTable) {
var destination = new GlideRecord(destinationBaseTable);
var foreignKey = (destinationBaseTable == 'alm_asset') ? 'asset' : 'ci';
var destinationId = source[foreignKey].toString();
destination = null;
if ('cmdb_ci' == destinationBaseTable) {
// when synchronizing to a CI, we have to check first for hardware
// because
// some status fields exist only on hardware table
destination = new GlideRecord('cmdb_ci_hardware');
// fast forward glide record to proper destination ci,
// reset to null if no hardware ci found and proceed with base ci
destination.query("sys_id", destinationId);
if (!destination.next())
destination = null;
}
if (destination == null) {
destination = new GlideRecord(destinationBaseTable);
// fast forward glide record to proper destination record
// abandon processing if no such record found
destination.query("sys_id", destinationId);
if (!destination.next())
return;
}
var changes = this.syncRecordsWithoutUpdate(source, destination,
destinationBaseTable, false);
if (changes > 0) {
destination.skip_sync = true;
destination.update();
}
},
/*
* Update the fields of destination record with information from source
* record but do not trigger an update of the destination record itself
* if asynchUpdate, destination will be modified even if source hasn't changed
*/
syncRecordsWithoutUpdate : function(source, destination,
destinationBaseTable, asyncUpdate) {
var changes = 0;
// Sanity protection
var me = source.getValue('sys_id') + '';
if ('cmdb_ci' == destinationBaseTable) {
// Check that we are pointing at ourselves
var back = destination.getValue('asset') + '';
if ( back != me) {
gs.log('Aborting Asset to CI sync due to pointer mismatch');
return 0;
}
}
if ('alm_asset' == destinationBaseTable) {
// Check that we are pointing back at ourselves
var back = destination.getValue('ci') + '';
if ( back != me) {
gs.log('Aborting CI to Asset sync due to pointer mismatch');
return 0;
}
}
// synchronize data for fields that have the same name in both tables
changes += this._syncField('location', source, destination);
changes += this._syncField('asset_tag', source, destination);
changes += this._syncField('serial_number', source, destination);
changes += this._syncField('assigned_to', source, destination);
changes += this._syncField('assigned', source, destination);
changes += this._syncField('checked_in', source, destination);
changes += this._syncField('checked_out', source, destination);
changes += this._syncField('company', source, destination);
changes += this._syncField('cost_center', source, destination);
changes += this._syncField('delivery_date', source, destination);
changes += this._syncField('department', source, destination);
changes += this._syncField('due_in', source, destination);
changes += this._syncField('due', source, destination);
changes += this._syncField('gl_account', source, destination);
changes += this._syncField('invoice_number', source, destination);
changes += this._syncField('justification', source, destination);
changes += this._syncField('lease_id', source, destination);
changes += this._syncField('managed_by', source, destination);
changes += this._syncField('order_date', source, destination);
changes += this._syncField('owned_by', source, destination);
changes += this._syncField('support_group', source, destination);
changes += this._syncField('supported_by', source, destination);
changes += this._syncField('vendor', source, destination);
changes += this._syncField('warranty_expiration', source, destination);
changes += this._syncField('po_number', source, destination);
changes += this._syncField('purchase_date', source, destination);
changes += this._syncField('install_date', source, destination);
changes += this._syncField('sys_domain', source, destination);
// fields that differ slightly
changes += this._syncModel(destinationBaseTable, source, destination);
changes += this._syncCost(destinationBaseTable, source, destination, asyncUpdate);
// fields that differ wildly
changes += this._syncState(destinationBaseTable, source, destination, asyncUpdate);
return changes;
},
/*
* Simple synchronization: fields have the same name in both tables and
* value simply need to be carried over
*/
_syncField : function(fieldName, source, destination) {
if (source.getElement(fieldName) != destination.getElement(fieldName)) {
if (!source.getElement(fieldName).hasValue())
destination.setValue(fieldName, '');
else
destination.setValue(fieldName, source.getValue(fieldName));
return 1;
}
return 0;
},
/*
* Model synchronization: model fields are named differently but should hold
* the same value
*/
_syncModel : function(destinationBaseTable, source, destination) {
if ('alm_asset' == destinationBaseTable) {
if (source.model_id != destination.model) {
destination.model = source.model_id;
return 1;
}
return 0;
}
if ('cmdb_ci' == destinationBaseTable) {
if (source.model != destination.model_id) {
destination.model_id = source.model;
return 1;
}
return 0;
}
return 0;
},
/*
* Cost synchronization: cost fields are named differently and have
* different data format
*/
_syncCost : function(destinationBaseTable, source, destination, asyncUpdate) {
if ('alm_asset' == destinationBaseTable) {
if (source.cost.changes() || source.cost_cc.changes() || asyncUpdate) {
//cost fields are informational only on ci side and cannot be modified
return 0;
}
return 0;
}
if ('cmdb_ci' == destinationBaseTable) {
if (source.cost.changes() || asyncUpdate) {
var field = source.getElement('cost');
destination.cost = field.getCurrencyValue();
destination.cost_cc = field.getCurrencyCode();
return 2;
}
return 0;
}
return 0;
},
/*
* State fields are a different in name, number and data format. Careful
* mapping needs to be done.
*/
_syncState : function(destinationBaseTable, source, destination, asyncUpdate) {
if (destinationBaseTable == 'alm_asset')
return this._inferAssetStatuses(source, destination, asyncUpdate);
else if (destinationBaseTable == 'cmdb_ci')
return this._inferCIStatuses(source, destination, asyncUpdate);
return 0;
},
/*
* Infer asset state fields from ci state fields and update as appropriate
*/
_inferAssetStatuses : function(ci, asset, asyncUpdate) {
// HW statuses take precedence over CI statuses
if (this._isHardwareCI(ci)) {
if (!ci.hardware_status.changes() && !ci.hardware_substatus.changes() && !asyncUpdate)
return 0;
return this._inferAssetStatusesHardware(ci, asset);
} else {
if (!ci.install_status.changes() && !asyncUpdate)
return 0;
return this._inferAssetStatusesBase(ci, asset);
}
},
/*
* Infer asset ci fields from asset state fields and update as appropriate
*/
_inferCIStatuses : function(asset, ci, asyncUpdate) {
if (!asset.install_status.changes() && !asset.substatus.changes() && !asyncUpdate)
return 0;
var changes = this._inferCIStatusesBase(asset, ci);
if (this._isHardwareCI(ci))
changes += this._inferCIStatusesHardware(asset, ci);
return changes;
},
/*
* Look for clues that the ci is a hardware ci
*/
_isHardwareCI : function(ci) {
return (ci.hardware_status != undefined);
},
/*
* Figure out what CI install status should be based on asset's state and
* substate
*/
_inferCIStatusesBase : function(asset, ci) {
var status = asset.install_status.toString();
var substatus = asset.substatus.toString();
switch (status) {
case '6':
ci.install_status = this
._inferCIStatusForInStockOrInTransitAsset(substatus);
return 1;
case '8':
ci.install_status = this._inferCIStatusForMissingAsset(substatus);
return 1;
case '9':
ci.install_status = this
._inferCIStatusForInStockOrInTransitAsset(substatus);
return 1;
default:
ci.install_status = status;
return 1;
}
},
/*
* Figure out what hardware CI status and substatus should be based on
* asset's state and substate
*/
_inferCIStatusesHardware : function(asset, ci) {
var status = asset.install_status.toString();
var substatus = asset.substatus.toString();
switch (status) {
case '1':
ci.hardware_status = 'installed';
ci.hardware_substatus = 'in_use';
return 2;
case '2':
ci.hardware_status = 'on_order';
ci.hardware_substatus = '';
return 2;
case '3':
ci.hardware_status = 'in_maintenance';
ci.hardware_substatus = '';
return 2;
case '6':
ci.hardware_status = this
._inferHardwareCIStatusForInStockAsset(substatus);
ci.hardware_substatus = this
._inferHardwareCISubtatusForInStockAsset(substatus);
return 2;
case '7':
ci.hardware_status = 'retired';
ci.hardware_substatus = this
._inferHardwareCISubtatusForRetiredAsset(substatus);
return 2;
case '8':
if ('lost' == substatus) {
ci.hardware_status = 'retired';
ci.hardware_substatus = 'lost';
} else {
ci.hardware_status = 'stolen';
ci.hardware_substatus = '';
}
return 2;
case '9':
ci.hardware_status = 'in_transit';
ci.hardware_substatus = this
._inferHardwareCISubtatusForInTransitAsset(substatus);
return 2;
default:
return 0;
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferHardwareCIStatusForInStockAsset : function(assetSubstatus) {
switch (assetSubstatus) {
case 'available':
case 'reserved':
return 'in_stock';
case 'pending_disposal':
return 'in_disposition';
default:
return assetSubstatus;
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferHardwareCISubtatusForInStockAsset : function(assetSubstatus) {
switch (assetSubstatus) {
case 'available':
case 'reserved':
return assetSubstatus;
default:
return '';
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferHardwareCISubtatusForRetiredAsset : function(assetSubstatus) {
switch (assetSubstatus) {
case 'disposed':
return 'scrapped';
case 'vendor_credit':
return 'credit';
default:
return assetSubstatus;
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferHardwareCISubtatusForInTransitAsset : function(assetSubstatus) {
switch (assetSubstatus) {
case 'defective':
return 'repairable';
case 'pending_disposal':
return 'disposable';
case 'pending_install':
return 'reserved';
default:
return assetSubstatus;
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferCIStatusForInStockOrInTransitAsset : function(assetSubstatus) {
switch (assetSubstatus) {
case 'pending_install':
return '4';
case 'pending_repair':
case 'defective':
return '5';
default:
return '6';
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferCIStatusForMissingAsset : function(assetSubstatus) {
switch (assetSubstatus) {
case 'stolen':
return '8';
default:
return '100'; // lost
}
},
/*
* Figure out what asset state and substate should be based on ci's install
* status
*/
_inferAssetStatusesBase : function(ci, asset) {
var status = '';
var substatus = '';
status = ci.install_status.toString();
switch (status) {
case '4':
asset.install_status = '6';
asset.substatus = 'pending_install';
return 2;
case '5':
asset.install_status = '6';
asset.substatus = 'pending_repair';
return 2;
case '6':
asset.install_status = '6';
asset.substatus = 'available';
return 2;
case '8':
asset.install_status = '8';
asset.substatus = 'stolen';
return 2;
case '100':
asset.install_status = '8';
asset.substatus = 'lost';
return 2;
default:
asset.install_status = ci.install_status;
asset.substatus = '';
return 2;
}
},
/*
* Figure out what asset state and substate should be based on hardware ci's
* state and substate
*/
_inferAssetStatusesHardware : function(ci, asset) {
var status = '';
var substatus = '';
var hwStatus = ci.hardware_status.toString();
var hwSubstatus = ci.hardware_substatus.toString();
switch (hwStatus) {
case 'installed':
asset.install_status = '1';
asset.substatus = '';
return 2;
case 'in_maintenance':
asset.install_status = '3';
asset.substatus = '';
return 2;
case 'in_stock':
asset.install_status = '6';
asset.substatus = this._inferAssetSubstatusFromHWStatusFields(
hwStatus, hwSubstatus);
return 2;
case 'in_transit':
asset.install_status = '9';
asset.substatus = this._inferAssetSubstatusFromHWStatusFields(
hwStatus, hwSubstatus);
return 2;
case 'defective':
asset.install_status = '6';
asset.substatus = 'defective';
return 2;
case 'in_disposition':
asset.install_status = '6';
asset.substatus = 'pending_disposal';
return 2;
case 'retired':
// duplicate error is irrelevant for asset -> no change
if ('duplicate_error' == hwSubstatus)
return 0;
// valid retirement reasons
asset.install_status = this
._inferAssetStatusFromHWSubstatusForRetiredCI(hwSubstatus);
asset.substatus = this._inferAssetSubstatusFromHWStatusFields(
hwStatus, hwSubstatus);
return 2;
case 'on_order':
asset.install_status = '2';
asset.substatus = '';
return 2;
case 'pending_transfer':
asset.install_status = '6';
asset.substatus = 'pending_transfer';
return 2;
case 'stolen':
asset.install_status = '8';
asset.substatus = 'stolen';
return 2;
case 'pending_install':
asset.install_status = '6';
asset.substatus = 'pending_install';
return 2;
case 'pending_repair':
asset.install_status = '6';
asset.substatus = 'pending_repair';
return 2;
case 'out_of_stock':
// out of stock is irrelevant for asset -> no change
return 0;
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferAssetStatusFromHWSubstatusForRetiredCI : function(hwSubstatus) {
switch (hwSubstatus) {
case 'lost':
case 'stolen':
return '8';
default:
return '7';
}
},
/*
* Helper to reduce the depth of switch statement
*/
_inferAssetSubstatusFromHWStatusFields : function(hwStatus, hwSubstatus) {
switch (hwSubstatus) {
case 'in_use':
case 'received':
return 'available';
case 'repairable':
if ('in_transit' == hwStatus)
return 'defective';
return 'pending_repair';
case 'disposable':
return 'pending_disposal';
case 'credit':
return 'vendor_credit';
case 'scrapped':
return 'disposed';
case 'donated':
return 'donated';
case 'reserved':
case 'lost':
case 'sold':
case 'available':
case 'stolen':
return hwSubstatus;
default:
return '';
}
},
type : 'AssetAndCISynchronizer'
};
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
08-08-2016 07:14 AM
Serial number is a built-in field that exists as a field in both the CI and Asset tables and is set up to synchronize.
State (install_status) is similar: two fields configured to coordinate between the two tables, but unlike serial number, it is not always a direct match up.
These fields need to live in both places. Serial number because it is important information often used to help uniquely identify an item in the environment. If you could not input the serial number on the asset record, you would need to find some other way to uniquely identify the CI record to update it with discovered information and vice versa, if a discovery source could not discover a serial number an asset source could use to coalesce, then it could make it very difficult to manage these records. This is information that it makes sense to be able to manage from both locations. Just like the Assigned to value.
State (asset) and Status (CI) are synchronized in a way, but one is used more to represent the current State of the asset in the asset lifecycle while the other represents its operational status. These are going to be related to one another, and so they synchronize.
When you create new fields, though, as a best practice you should determine where the information most appropriately belongs and put it there. Then display it on the other field if it is information you need there, as you have done. This is just to display information, though, not to manage the information in both locations.
If you want to manage this information in both locations, you need to create the field in both places.
To get them to synchronize pre-Helsinki, you need to modify the AssetAndCISynchronizer script, which I highly discourage as it will affect upgrades and it pretty complex.
In Helsinki, we introduced the ability to define synchronization between asset and CI fields, so this process is significantly easier. That being said, it should not be used as a replacement for good data design. It is better to determine where the information should live and only store it in one location if possible. Only set up multiple fields and synchronization if you have a very good reason to have the information live in multiple locations.
I hope this helps.
Ben
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
08-08-2016 08:09 AM
Briliant stuff Ben, really helped out.
We are having to add both fields ( they are both on Asset and CI) because due to security we need to lock it down in some cases and open it up in others. So we do not want some teams to edit CIs but we do want the fields to be editable from either form