If discovery tools are used within ServiceNow it can happen that based on the information provided it is not possible to determine the specific CI Class in ServiceNow. Therefore it could be useful to change the class after creation. This is possible as described in the following article by a colleague of mine: Change the Class of a CI. This article enables the option for the end user (without admin access) to change the ServiceNow CI class.
In this article a custom class Middleware CIs is used to explain the functionality.
The class structure looks like this:
Base Class | Extended Class |
Middleware Cis (u_cmdb_ci_middleware) | (none) |
Application Servers (u_cmdb_ci_application_server) | u_cmdb_ci_middleware |
Database Instances (u_cmdb_ci_database_instance) | u_cmdb_ci_middleware |
Interfaces (u_cmdb_ci_interface) | u_cmdb_ci_middleware |
MFX (u_cmdb_ci_mfx) | u_cmdb_ci_middleware |
MQ Cis (u_cmdb_ci_mq) | u_cmdb_ci_middleware |
Below an example of a Middleware record in ServiceNow which is created by the interface as a ‘Discovered’ CI.
On the form in the “Related Links” section a link is available called ‘Change class’ to change the class of a CI. When clicking on this link the following Dialog Window is visible.
The Dialog Window is initiated by using a UI Action and created with the following details below:
Field | Value |
Name | Change class |
Table | Any CI table on which you want to enable this functionality (in this example it is the u_cmdb_ci_middleware table) |
Onclick | changeClass(); |
Condition | new TableUtils(”+current.getTableName()).hasExtensions() && !current.isNewRecord() && current.operational_status == 0 && current.canWrite() |
Script | function changeClass() { var gdw = new GlideDialogWindow(‘cmdb_change_class’); gdw.setTitle(‘Change CI class’); gdw.setPreference(‘sysparm_sys_id’, g_form.getUniqueValue()); gdw.setPreference(‘sysparm_sys_class_name’, g_form.getTableName()); gdw.render(); } |
The condition can be changed as needed, in this example it is shown when the class of the CI has extended tables, if it is a existing record, the status of the CI is “Discovered” and the user can update (write) the record.
One of the options on how to make the UI Action visible can be chosen below (in this article the it is shown as a link):
The Dialog window is a UI Page (cmdb_change_class), is created with the following details:
Field | Value |
Name | cmdb_change_class |
Category | General |
HTML | <?xml version=”1.0″ encoding=”utf-8″ ?> <j:jelly trim=”false” xmlns:j=”jelly:core” xmlns:g=”glide” xmlns:j2=”null” xmlns:g2=”null”> <g:ui_form> <input type=”hidden” name=”sysparm_sys_id” id=”sysparm_sys_id” value=”${RP.getParameterValue(‘sysparm_sys_id’)}”/> <input type=”hidden” name=”sysparm_sys_class_name” id=”sysparm_sys_class_name” value=”${RP.getParameterValue(‘sysparm_sys_class_name’)}”/> <g2:evaluate> var ciclass = []; var tbu = new TableUtils(‘${RP.getParameterValue(“sysparm_sys_class_name”)}’).getTableExtensions(); <!– tag “<” equals the “<” sign in html –> for(var i=0;i<tbu.size();i++) { ciclass.push(tbu.get(i)); } ciclass = ciclass.join(‘,’); ciclass; </g2:evaluate><g2:tokenize var=”jvar_ci_classes” delim=”,”> $[ciclass] </g2:tokenize> <label for=”available_select”>${gs.getMessage(‘Change reason’)}</label><br/> <label for=”available_ci_classes”>${gs.getMessage(‘Please select a ci class to change to.’)}</label><br/> |
Client script | function validateMandatoryfields() { if(gel(‘u_change_reason’).value == ”) { alert(‘Please provide a change reason.’); return false; } else if(gel(‘ci_class_chooser’).value == ”) { alert(‘Please provide a ci class to convert’); } } |
Processing script | moveCIClass();function moveCIClass() { var grci = new GlideRecord(‘cmdb_ci’); if(grci.get(sysparm_sys_id)) { source = {}; source.x_need_change_reason = false; grci.sys_class_name = ci_class_chooser; var graudit = new GlideRecord(‘sys_audit’); |
I found a some helpful examples on the community website of ServiceNow (https://community.servicenow.com/thread/164789) which helped me to create the script below. It gets all the extended tables as a comma separated string and uses the g2:tokenize tag to split it is a array function does.
var ciclass = [];
var tbu = new TableUtils('${RP.getParameterValue("sysparm_sys_class_name")}').getTableExtensions();
for(var i=0;i<tbu.size();i++) {
ciclass.push(tbu.get(i));
}
ciclass = ciclass.join(',');
ciclass;
$[ciclass]
Then it was possible to loop through the delimited string using the forEach tab below.
In this article the extended tables were made available, however it is also possible to select parent tables or all the classes available within the class tree. The available options are listed below. (http://wiki.servicenow.com/index.php?title=TableUtils).
Function Purpose
getHierarchy() Returns all the classes within the tree
getTableExtensions() Retuns all the extended classes in the tree
getTables() Returns all the parent classes in the tree
After a function is chosed in the table above, the following changes have to be done in the scripts:
UI Action
Change the condition of the UI Action, at least remove the “new TableUtils(”+current.getTableName()).hasExtensions()” part.
UI Page
Update the part “getTableExtensions” below with one of the functions available above.
var ciclass = [];
var tbu = new TableUtils('${RP.getParameterValue("sysparm_sys_class_name")}').getTableExtensions();
for(var i=0;i<tbu.size();i++) {
ciclass.push(tbu.get(i));
}
ciclass = ciclass.join(',');
ciclass;
In case the class (sys_class_name) is updated in ServiceNow it is not audited, therefore a ‘custom’ audit record is created so the change of the class is visible in the History List and Calendar.
It’s a lot of code, but it’s also a nice result. Do you like it? Let me know!
[at]
January 7th, 2015 at 14:24
I found out there is an error in the processing script. It is blanking all the fields which are not defined on the cmdb_ci table, therefore an extra query is needed. Please find below the updated version of the processing script.
moveCIClass();
function moveCIClass() {
var grci = new GlideRecord(‘cmdb_ci’);
if(grci.get(sysparm_sys_id)) {
grci = new GlideRecord(”+grci.sys_class_name);
if(grci.get(sysparm_sys_id)) {
source = {};
source.x_need_change_reason = false;
grci.sys_class_name = ci_class_chooser;
grci.u_change_reason = u_change_reason;
grci.update();
var graudit = new GlideRecord(‘sys_audit’);
graudit.initialize();
graudit.documentkey = sysparm_sys_id;
graudit.fieldname = ‘sys_class_name’;
graudit.tablename = sysparm_sys_class_name;
var grchg = new GlideRecord(‘task’);
if(grchg.get(u_change_reason)) {
graudit.reason = grchg.getDisplayValue();
}
response.sendRedirect(grci.getLink());
}
}
}