Join the #BuildWithBuildAgent Challenge! Get recognized, earn exclusive swag, and inspire the ServiceNow Community with what you can build using Build Agent.  Join the Challenge.

SlightlyLoony
Tera Contributor

find_real_file.pngOn occasion in this blog, I've mentioned Script Includes (navigate to System Definition → Script Includes) and talked about classes using the prototype.js library. On the Service-now instance, JavaScript classes are generally defined within a Script Include, so on our platform the two notions are related. In this post, I'm going to start a series of posts on scripting within the Service-now platform. All of these posts will result in code that you could actually use, at least theoretically. Even if you can't use them directly, they will serve as practical examples. If you've got ideas for things you'd like to see me discuss, by all means let me know about them.

In today's post, I'm going to start with this problem: I want a reusable bit of code that will let me find the CI that has a given network MAC address. Such a piece of code might be useful in a UI action (perhaps to let a service technician find out what device a particular MAC address belongs to), or it might be useful in a business rule associated with a change process. The real point is this: it's a bit of code that I'd like to write once, then be able to use it in multiple places without having to write it all over again.

In this particular case, I'm envisioning being able to write code like this:


var mac = new MACResolver();
var gr = mac.resolve('00:50:56:ae:33:d9');
gs.log(gr.sys_class_name + ': ' + gr.name);


How would I do this?

First I have to know how the CMDB schema needed works. There are two possible ways that the MAC address could be stored. The simplest way is that it could be in the mac_address field of the CI I want. If you're not using Discovery, you may have your desktop computers in your CMDB this way. The slightly more complex way is that the MAC address could be in the mac_address field of a cmdb_ci_network_adapter record, and that record would have a reference in the cmdb_ci field to the CI it's a part of (and that's what I really need).

find_real_file.pngSo with that information, here's how my script include/class might look. I start by creating the new Script Include. At right is a screenshot of what it looked like after I clicked "New" and filled in the name and description. The stuff in the script field is the default, automagically put in as a guide for you when creating a new JavaScript class. Then I modified that script to what you see below. Note in particular how I modified the default script to put in the name of my class, which matches the name of the Script Include (and it must!). Most of what is happening in the script below you should be able to follow without any trouble, but there are a couple of points that may not be obvious:
  1. The _get_real_gr function is needed because our original GlideRecord is on the cmdb_ci table, and in two places we need the GlideRecord for the actual class of that CI. For example, in one place the script detects that a CI is a network adapter and uses this function to return a GlideRecord for the network adapter. This is necessary because the original GlideRecord (for cmdb_ci) doesn't have access to the fields that are specific to cmdb_ci_network_adapter — and we need one of them (cmdb_ci).
  2. The script skips right over records in dscy_swtch_fwd_rule (Switch Forwarding Rules). Unless you know our CMDB well, this won't make any sense at all. Here's why we skip over it: in the case of this table, the MAC address field doesn't represent the MAC address of that particular device, but rather a configuration of that device (a network switch). Because we know this can't be the device we really want, we're just skipping it.


var MACResolver = Class.create();

MACResolver.prototype = {
initialize: function() {
// naught to do here...
},

/*
* If there is a CI in the CMDB with the given MAC address, a GlideRecord instance
* for that CI is returned. Otherwise a null is returned.
*/
resolve: function(mac) {
// find a CI with this MAC...
var gr = new GlideRecord('cmdb_ci');
gr.addQuery('mac_address', mac);
gr.query();
while (gr.next()) {
// if it's not installed, try another...
if (gr.install_status == 100)
continue;

var klass = '' + gr.sys_class_name;

// if we have a switch forwarding rule, just move along...
if (klass == 'dscy_swtch_fwd_rule')
continue;

// if we have a NIC here, get the real CI...
if (klass == 'cmdb_ci_network_adapter') {
gr = this._get_real_gr(gr);
if (!gr)
continue;

var ci = '' + gr.cmdb_ci;
gr = new GlideRecord('cmdb_ci');
if (!gr.get(ci))
continue;
}

// right now we have a GlideRecord on cmdb_ci
// get the correct table
gr = this._get_real_gr(gr);
if (gr)
return gr;
}
// if we get here, then we failed to find a CI...
return null;
},

_get_real_gr: function(gr) {
var sys_id = gr.getUniqueValue();
var table = '' + gr.sys_class_name;
var real_gr = new GlideRecord(table);
return real_gr.get(sys_id) ? real_gr : null;
}
}

5 Comments