Monday, 30 May 2016

Discovery and creating new CIs

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 ClassExtended 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:
FieldValue
NameChange class
TableAny CI table on which you want to enable this functionality (in this example it is the u_cmdb_ci_middleware table)
OnclickchangeClass();
Conditionnew TableUtils(”+current.getTableName()).hasExtensions() && !current.isNewRecord() && current.operational_status == 0 && current.canWrite()
Scriptfunction 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:
FieldValue
Namecmdb_change_class
CategoryGeneral
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 scriptfunction 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 scriptmoveCIClass();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.
    <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>
Then it was possible to loop through the delimited string using the forEach tab below.
      <j2:forEach var="jvar_ci_class" items="$[jvar_ci_classes]">
        <option value="$[jvar_ci_class]">$[jvar_ci_class]</option>
      </j2:forEach>
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.
  <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>
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!
abhi.jain39@outlook.com
-AJNow

5 comments:


  1. Iam very impressive your site gives the best and the most interesting information. This is just the kind of information that i had been looking for, i'm already your rss reader now and i would regularly watch out for the new posts, once again hats off to you

    servicenow online training hyderabad

    ReplyDelete
  2. Great Article. its is very very helpful for all of us and I never get bored while reading your article because, they are becomes a more and more interesting from the starting lines until the end.

    servicenow online training

    ReplyDelete
  3. It is Great Article it is very useful information for us . i am searching for same information to save my time please check it once.Hadoop Online Traning Hyderabad

    ReplyDelete
  4. Great Article. its is very very helpful for all of us and I never get bored while reading your article because, they are
    becomes a more and more interesting from the starting lines until the end.
    AWS Online Course

    ReplyDelete