MadhuriGudiseva
Administrator
Administrator

Posted on behalf of Juan Carlos

--------------------------------------------------------

Discovery uses the command ps - with its different variants depending on the flavour of Unix (ps -auxww for Linux, ps -aef for AIX, ...) - in order to retrieve running processes in Unix boxes.

For each of the processes found Discovery will run a Process Classification.

The Process Classification 'DB2 Server' will look at the value of the "command" field of the processes found on a Unix server. If a command contains db2tcpcm then Discovery assumes this process is part of a DB2 instance. Later the ADM probe would associate connections against that process.

db2tcpcm   is the process that listens for incoming connections in legacy DB2 instances. This process used to be reported by the command ps -ef because it was a usual Unix process.

In modern DB2 installations however db2tcpcm runs in a thread that is not reported by ps -ef (not even by ps -efL). It is only reported by the DB2 command db2pd -edus. As there is no db2tcpcm process reported by ps in a modern DB2 installation no   process will   be classified as DB2.

We need to look at db2sysc instead.

db2sysc will have no parameters when the DB2 instance has no partitions .In a partitioned DB2 instance the command has a number indicating the number of partition, e.g.: db2sysc 0, db2sysc 1

We can find more than one DB2 instances running on the same server and these instances can be each of them partitioned or not.

This next list of processes reported by ps represent 3 different DB2 instances. The first one is not partitioned, the second and the third are partitioned:

db2sysc

db2sysc 0

db2sysc 1

db2sysc 2

db2sysc 0

db2sysc 1


db2sysc listens by default on port 5000 for incoming connections.

Discovering modern DB2

1 - Create a Process Classification

Go to Discovery Definition > CI Classification > Process and edit DB2 Server:

  • add a second condition: or Command contains db2sysc
  • add the next script to On classification script:

if (name == "db2tcpcm") {
         current.name = "DB2 Instance@" + g_sensor.deviceGR.name + " (" + values.get('pid') + ")";
}
else {         
         current.name = "DB2 Instance" + " " + values.get('key_parameters') + "@" + g_sensor.deviceGR.name + " (" + values.get('pid') + ")";
}
g_probe_parameters['pid'] = values.get('pid');
g_probe_parameters['port'] = "";

// Look at the TCP half connections (might be stale) to find out the port this process talks on.
var tcpGr = new GlideRecord('cmdb_tcp_half');
tcpGr.addQuery('computer.sys_id', g_sensor.deviceGR.sys_id);
tcpGr.addQuery('pid', values.get('pid'));
tcpGr.addQuery('from_ip', '!=', '127.0.0.1'); 
tcpGr.query();
if (tcpGr.next())
       g_probe_parameters['port'] = tcpGr.from_port + '';

In the end we are classifying both db2tcpcm and db2sysc. These processes will be handled by our new process handler.

For db2tcpcm processes they will directly pass the handler and they will be classified with a name of "DB2 Instance@<computer_name> (<PID of db2tcpcm>)".

For db2sysc processes if the handler didn't "ban" the process we classify it with a name of "DB2 Instance <#_of_instance>@<computer_name> (<PID of db2sysc>)".

We will create a exploration Probe and Sensor later. We are passing "pid" and "port" as parameters to the Probe.

2 - Create a Process Handler

Go to Discovery Definition > CI Classification > Process Handlers and add a new handler with the next values:

  • Name: DB2 Instance
  • Condition: Command contains db2sysc
  • Script:

runHandler();

function runHandler() {
         var _command = current.command.trim();
         var _parameters = current.parameters.trim();
         //gs.print("### CARLOSDBG [DB2 Partitioned - Handler]: command parameters = " + _command + " " + _parameters);

         // Ban anything that does not match exactly db2sysc
         if (!_command.match(/^\s*db2sysc(\s*$|\s+.*$)/)) {
                   current.classify = false;
                   return;
         }

         // In Dublin, "db2sysc X" is not being splitted. It comes as command: "db2sysdbc x", parameters: "". Fixing.
         var idx = _command.indexOf(" ");
         if (idx > 0) {
                   _parameters = _command.substring(idx+1);
                   _command = _command.slice(0,idx);
                   current.name = _command;
                   current.command = _command;
                   current.parameters = _parameters;
         }

         // If there are no parameters then DB2 is not partitioned. Just make it look like single-partition DB2.
         _parameters = (gs.nil(_parameters) || _parameters === "")? 0 : _parameters;

         // Add parameters to the registry "db2sysc_instances".
         // "db2sysc_instances" is an array containing arrays that contain the parameters registered for an instance.
         // For example:
         // [
         //           ["0", "1", "2", "3", "4"], 
         //           ["0", "1", "3"], 
         //           ["0", "1"]
         // ]
         // at that point if "2" comes as parameter it will be added to the 2nd array.
         // If a parameters is added to an existing instance we make the process not to be classified.
         
         if (typeof db2sysc_instances === 'undefined') {
                   db2sysc_instances = [];
         }
         
         var instance = [];
         var _key = "";
         
         if (db2sysc_instances.length == 0) {
                   instance.push(_parameters);
                   db2sysc_instances.push(instance);
                   current.key_parameters = "0";
                   return;
         }
         
         var last_matched = -1;
         for (var i = 0; i < db2sysc_instances.length; i++) {
                   for (var j = 0; j < db2sysc_instances[i].length; j++) {
                             if (db2sysc_instances[i][j] == _parameters) {
                                       last_matched = i;
                             }
                   }
         }
         
         if (last_matched < 0) {
                   /* not found - add to first instance */
                   db2sysc_instances[0].push(_parameters);
                   current.classify = false;
                   _key = 0;
         }
         else {
                   if (last_matched == db2sysc_instances.length - 1) { 
                             /* found in the last instance - add to new instance */
                             instance.push(_parameters);
                             db2sysc_instances.push(instance);
                   }
                   else {
                             /* found - add to next instance */
                             db2sysc_instances[last_matched + 1].push(_parameters);
                             current.classify = false;
                   }
                   _key = last_matched + 1;
         }

         current.key_parameters = "" + _key;
}

We are handling db2sysc processes so that Discovery populates the DB2 Instance table conveniently.

For db2tcpcm we assume that it corresponds to the legacy "non threaded DB2 model" with no partitions and we just let the classifier classify it with no handling.

With this setup the DB2 instances can already be classified and identified.

We are going to explore the DB2 instances in order to retrieve some information (version, db2_home, instance name, ...). In case that we were able to find the instance name we will include it the name of the CI.

3 - Create a DB2 Exploration probe

Go to Discovery Definition > Probes and create a new probe with the next values:

  • Name: DB2 - Instance Details
  • Probe Type: Probe
  • ECC queue topic: SSHCommand
  • ECC queue name: sh ${file:db2_instance.sh} ${pid}
  • Comments: Get the details of a DB2 instance based on the PID of a "db2sysc" process running in the system.
  • Used by Discovery: yes
  • Used by Runbook: no

Save that and add an entry to the "Probe parameters" related list:

  • Name: db2_instance.sh
  • Value:

#!/bin/sh

PID=$1
FAILED=0
ERR=""
PS="ps eww"

get_ps(){
   if [ -x /usr/ucb/ps ] ; then
       PS="/usr/ucb/ps eww" && return
   elif [ "$(which pargs 2>/dev/null)" != "" ] ; then
       PS="$(which pargs) -e" && return
   else
       PS="$(which ps) eww" && return
   fi
   FAILED=1
   ERR="Could not determine how to get environment"
}

get_db2instance(){
   SNC_DB2INSTANCE=`$PS $PID | awk 'NR>1 {for(i=1;i<=NF;i++){printf "%s\n",$i}}' | sed -n 's/^DB2INSTANCE=\(.*\)/\1/p' | head -1 | sed -e 's/^ *//' -e 's/ *$//'`
   [ "${SNC_DB2INSTANCE}" != "" ] && echo "snc_db2instance=${SNC_DB2INSTANCE}" && return
   FAILED=1
   ERR="Could not determine DB2 instance name"
}

get_db2_home(){
   SNC_DB2_HOME=`$PS $PID | awk 'NR>1 {for(i=1;i<=NF;i++){printf "%s\n",$i}}' | sed -n 's/^DB2_HOME=\(.*\)/\1/p' | head -1 | sed -e 's/^ *//' -e 's/ *$//'`
   [ "${SNC_DB2_HOME}" != "" ] && echo "snc_db2_home=${SNC_DB2_HOME}" && return
   FAILED=1
   ERR="Could not determine DB2 home"
}

get_version(){
   SNC_VERSION=`$SNC_DB2_HOME/bin/db2level | sed -n '4s/^.*DB2 v\([0-9\.]*\).*/\1/p'`
   [ "${SNC_VERSION}" != "" ] && echo "snc_version=${SNC_VERSION}" && return
   FAILED=1
   ERR="Could not determine DB2 version"
}

runit(){
   [ ! ${PID} ] && echo "No PID was provided" && exit 1
   get_ps
   get_db2instance
   get_db2_home
   get_version
   return
}

runit
[ "$FAILED" != 0 ] && echo "snc_error=${ERR}"
exit 0

4 - Create a DB2 Sensor

Go to Discovery Definition > Sensors and create a new sensor with the next values:

  • Name: DB2 - Instance Details
  • Reacts to probe: DB2 - Instance Details
  • Sensor type: Javascript
  • Active: yes
  • Description: Get the details of a specific DB2 instance
  • Script:

new DiscoverySensor({
       process: function(result) {
               if (gs.nil(result.output))
                       return;

                   if (this._parseOutput(result.output)) {
                             this._setName();
                   }
       },

       _parseOutput: function(output) {
               var lines = output.split(/\n/);
               for (var i = 0; i < lines.length; i++) {
                       var line = lines[i];

                       var instanceMatch = line.match(/^snc_db2instance=(.+)$/);
                       if (instanceMatch) {
                               current.instance_name = instanceMatch[1];
                               continue;
                       }
                       var homeMatch = line.match(/^snc_db2_home=(.+)$/);
                       if (homeMatch) {
                               current.home = homeMatch[1];
                               continue;
                       }
                       var versionMatch = line.match(/^snc_version=(\d+(?:\.\d+)+)$/);
                       if (versionMatch) {
                               current.version = versionMatch[1];
                               continue;
                       }
                       var errorMatch = line.match(/^snc_error=(.+)$/);
                       if (errorMatch) {
                               this.processError(errorMatch[1]);
                                       return false;
                             }
               }
                   return true;
       },

         _setName: function() {
                   var nameMatch = this.getCmdbRecord().name.match(/^.*@(\S*)(\s*$|\s+.*$)/);
                   if (nameMatch) {
                             current.name = current.instance_name + "@" + nameMatch[1];
                   }
         },
         
       type: "DiscoverySensor"
});

The Sensor is sending the values of "instance_name", "home" and "version" to the CI.

The default DB2 Instance [cmdb_ci_db_db2_instance] table does not contain the fields "instance_name" and "home". We must add them to the table if we want that information to be recorded. The field "version" is out of the box.

The Sensor is also renaming the CI to include the name of the instance and removing the PID of the process used to classify it.

We don't needed anymore because now we can identify the instance by its name.

5 - Set the Sensor to react to the Probe

Go to Discovery Definition > Probes and edit "DB2 - Instance Details":

Add the Sensor to the "Sensors that react to this probe" related list. For that:

  • click Edit in the related list.
  • select "DB2 - Instance Details" in the list bucket (add the Sensor to the right side)
  • click Save.

6 - Set the Probe to trigger when the process is classified

Go to Discovery Definition > CI Classification > Process and edit "DB2 Server"

Add the Probe to the "Triggers probes" related list. For that:

  • click Edit in the related list.
  • select "DB2 - Instance Details" in the list bucket (add the Probe to the right side)
  • click Save.
  • back in the related list, edit the entry and in the "Condition script" field enter the next: type == 'unix'. Also make sure that the entry contains "Phase: Exploration"
  • click Update
Comments
doug_schulze
ServiceNow Employee
ServiceNow Employee

Great stuff.. You should consider putting this on share.servicenow.com!


Sharon Hobart
Mega Guru

Looks like I'll be using this today!   Thanks for all the details.......


mamann
Mega Guru

Thank you, this was extremely helpful!



One thing to note, the table used in the Process Classifer, cmdb_tcp_half, isn't populated anymore and has moved to cmdb_tcp


Gyaneshwar_c01
Tera Contributor

Hi,



I tried this script for DB2 instance discovery. but I'm facing following issues.



1. Duplicate records created for same instance because hostname includes process ID.


The script that is appending process id with the db instance name of the server,


if (name == "db2tcpcm") {


        current.name = "DB2 Instance@" + g_sensor.deviceGR.name + " (" + values.get('pid') + ")";


}


else {        


        current.name = "DB2 Instance" + " " + values.get('key_parameters') + "@" + g_sensor.deviceGR.name + " (" + values.get('pid') + ")";


}



Whenever a record is being added in CMDB, its first checked if the record already exist.
If it exist then the record is updated. Since, in our scenario, we have unique
process ids so a new record is being added for the same instance.



2. The probe that is used to get the details for the DB2 instances is the "DB2 -Instance Details" probe. We then use this probe to trigger the command
"db2_instance.sh" to the target instance,


When I test this probe, we return the following error
as an output: Replaceable simple parameter 'pid' doesn't exist.



3. It would be great, if someone can provide solution to this issue. or OOTB DB2 instance discovery comparison.



Note: I'm working on Fuji Version


robin850
Giga Contributor

Are you setting       g_probe_parameters     so you can pass the PID to your db2_instance.sh   probe?



Regards,



Shaun Brachmann


jimmillet
Mega Guru

We are on Instanbul release running Discovery and it is not detecting DB2. I see all the steps above from 2014 on how to modify SN to detect DB2, does anyone know if these steps are now included in the SN product. Seems like everyone should not have to do all this configuration. I will open a HI ticket if needed, saw this post and wanted to check here first.


Version history
Last update:
‎12-17-2014 01:42 PM
Updated by: