Discovery and creating new CIs

General Add comments
by:

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.

image001

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.

image003

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.

image005

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):

image007

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 “&lt” equals the “<” sign in html –>
for(var i=0;i&lt;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/>
<g:ui_reference name=”u_change_reason” id=”u_change_reason” table=”task” query=”${new CIValidationUtils(current).refQualChangeReason()}”/><br/><br/>

<label for=”available_ci_classes”>${gs.getMessage(‘Please select a ci class to change to.’)}</label><br/>
<select name=”ci_class_chooser” id=”ci_class_chooser”>
<j2:forEach var=”jvar_ci_class” items=”$[jvar_ci_classes]”>
<option value=”$[jvar_ci_class]”>$[jvar_ci_class]</option>
</j2:forEach>
</select><br/>
<g:dialog_buttons_ok_cancel ok=”return validateMandatoryfields()”/>
</g:ui_form>
</j:jelly>

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;
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();
}
graudit.insert();
response.sendRedirect(grci.getLink());
}
}

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.

image009

It’s a lot of code, but it’s also a nice result. Do you like it? Let me know!
.img[at].img

One Response to “Discovery and creating new CIs”

  1. Kelvin Lim Says:

    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());
    }
    }
    }

Leave a Reply