How to automate CMDB Discovery Schedules through integrating with DNS/IPAM tools

Camila Godoy
Tera Expert

Authors: @JFro  and @Camila Godoy 

Introduction

In this post, we will describe how we designed and developed an integration to automate our ServiceNow discovery using an IP Management tool.

 

We can often agree that discovery:

  • Can be Complicated to manage
  • Causes large bursts of network traffic and processing overhead
  • Schedules have a lack of change automation
  • No Source of Truth

External links:

Infoblox WAPI documentation link can be obtained from your appliance @ https://dns.company.com/wapidoc/index.html

 

Solution Summary

Our solution relies on Infoblox (IP management tool) to provide subnet data that allows us to automate discovery schedules on CMDB. The following steps represents a summary of actions from teams involved on this project.

  1. The Network Team Created definitions and normalization of their data and an API endpoint.
  2. ServiceNow CMDB Engineering Team developed an integration to pull the data and store it in a table. When inserted or updated, this data will generate discovery schedules to run at the least busy minute. In addition, it will automatically classify if a schedule should run daily, weekly, or not be created based on network type, DMZ type, and on our discovery IP count. These schedules get Activated and Inactivated based on the lifecycle of the network information coming from Infoblox.
  3. The Configuration Management team adopted Discovery Admin to help classify, assign incidents, and solve the errors created by discovery.

 

Post - Simplification of Flowchart infoblox.png

Design

Table format and fields mapping to payload

CamilaGodoy_1-1679428643550.png

 

Full view of the integration

 

Infoblox data Lifecycle

CamilaGodoy_3-1679428690528.png

 

Data logic sample used to classify when a schedule runs

 

Discovery Time Setup

Network Type

DMZ Type

Notes

Cloud

x

y

Exclude – this is already built with an integration.

Daily

a

b

 

Weekly

c

d

 

 

IP count  (Future Enhancement)

When range counts zero IPs for 7 days, regardless of the classification, it will move from daily to weekends. It will move back to daily once the IP counts changes from zero.

See commented code in the end of the discoveryScheduleManager Script include.

 

CamilaGodoy_4-1679428690535.png

 

Step 1 – Network Team

  • Classify the networks:
    • DMZ Type
    • Description
    • Network Type
  • Create an API endpoint integration to your IPAM

 

Step 2 – CMDB Engineering

  • Create outbound REST
    • Construct a message to point the API endpoint on the Infoblox Appliance. You’ll need to configure the credentials to connect to your appliance, here we’re using Basic Auth
image (30).png

 

  • The REST Message contains the HTTP Method. For Infoblox there’s a couple HTTP Query Parameters to configure. ‘_max_results’ when at a negative value the appliance will return an error if the results exceed that value and when positive it just truncates the results. Since we want everything and throw an error if that’s exceeded, we set it to the max negative value =-99999. The ‘_return_fields’ with a value of ‘extattrs’ tells the appliance to also send along the Extensible Attributes fields which is where we’re storing our Network Type, DMZ Type, and Comments for each network.

image (28).png

 

  • Create a Scheduled job to call our script include and run our import function

image (29).png

 

 

  • Create a Staging table for Infoblox Data
 
 

table.PNG

 

 

Infoblox API Fields

ServiceNow Fields

Description

Audit

network

u_name

This field stores network names defined on infoblox.

True

comment

u_description

This field contains general description of the network.

True

network_type

u_network_type

This field contains network type such: iot, data_center, network_infrastructure, and others.

True

Zone

u_dmz_type

This field contains zone type details such: external_dmz, external_transit and others

True

U_cmdb_discovery

u_infoblox_discovery_time

This field stores data defined by Infoblox of what discovery time a combination of zone and network type should have.

True

 

u_discovery_time_setup

This field will indicate if this network will be discovered daily or weekly.

The logic set of this field can be found below on Table 2.

True

 

u_status

This field will update to retired if a network is not updated by the integration for more than 15 days.

True

 

u_network_ip

This field contains the ip network.

True

 

u_network_mask

This field contains the network mask.

It will be populated with a client script  based on the network field.

True

 

u_cmdb_discovery

This field sync with the Active field in the discovery schedule.

True

 

u_discovery_schedule

 This is a field reference to the discovery schedule table.

True

 

u_active_ip_count

 This field will give a count of how many IPS we can interrogate in the ip range.

True

 

u_discovery_ip_zero_count

This counts how many times a Range had 0 ips. If the count is 7 days in a row, this set the range to be discovered weekly.

True

 

u_last_discovered

This field will be updated by the integration.

False

 

  • Create Script Include to parse response and retire records

 

 

 

 

 

var InfobloxImport = Class.create();
InfobloxImport.prototype = {
    initialize: function() {
        var status;
        var requestBody;
        var responseBody;
        var restMessage;
    },
    _fetchdata: function() {
        try {

            var webService = 'Infoblox Networks';
            var command = 'Default Get';
            restMessage = new sn_ws.RESTMessageV2(webService, command);
            var response = restMessage.execute();
            responseBody = response.getBody();
            status = response.getStatusCode();
            return responseBody;
        } catch (ex) {
            responseBody = ex.getMessage();
            status = 500;
            return responseBody;
        } finally {
            requestBody = restMessage ? restMessage.getRequestBody() : null;
        }
    },
    run: function() {
        var payload = this._fetchdata();
        if (status == 200) {
            this._process_results(payload);
        } else {
            var error = "A Infoblox import exception occurred. There was no data returned";
            this._createInc(error);
        }
    },
    _process_results: function(payload) {
        var parsedData = JSON.parse(payload);
        var iputils = new IpUtils;
        var errors_array = [];
        for (var i = 0; i < parsedData.length; i++) {
            var cidr = parsedData[i].network;
            var network = cidr.split("/")[0];
            var subnet = cidr.split("/")[1];
            var networkType = parsedData[i].extattrs.network_type.value;
            var zone = parsedData[i].extattrs.zone.value;
            var schdTime = parsedData[i].extattrs.cmdb_discovery.value;
            var validTimes = gs.getProperty('discovery.infoblox.valid_times');
            var validNetworkTypes = gs.getProperty('discovery.infoblox.valid_network_types');
            var validZones = gs.getProperty('discovery.infoblox.valid_zones');
            var isValidNetworkType = validNetworkTypes.includes(networkType) && networkType != "";
            var isValidZone = validZones.includes(zone) && zone != "";
            var ipValid = iputils.validIP(network);
            var maskValid = iputils.validMask(subnet);
            if(schdTime == "daily" || schdTime == "Daily"){
                infobloxTime = 1;
            } else if (schdTime == "weekly" || schdTime == "Weekly"){
                infobloxTime = 2;
            } else if (schdTime == "cloud" || schdTime == "Cloud") {
                infobloxTime = 3;
            } else if (schdTime == "Internet" || schdTime == "internet") {
                infobloxTime = 4;
            } else if (schdTime == "non-routable") {
                infobloxTime = 5;
            } else {
                infobloxTime = "";
            }
            //check if network and mask are valid
            if (isValidNetworkType && isValidZone && ipValid && maskValid) {
                var grIB = new GlideRecord("u_cmdb_infoblox");
                grIB.addQuery("u_name", cidr);
                grIB.query();
                if (grIB.next()) {
                    grIB.u_description = parsedData[i].comment;
                    grIB.u_infoblox_discovery_time_setup = infobloxTime;
                    grIB.u_dmz_type = zone;
                    grIB.u_network_type = networkType;
                    var rightnow = new GlideDateTime();
                    grIB.u_last_discovered = rightnow;
                    grIB.update();
                } else {
                    //record doesn't exist, insert
                    grIB.initialize();
                    grIB.u_name = cidr;
                    grIB.u_network_ip = network;
                    grIB.u_network_mask = subnet;
                    grIB.u_description = parsedData[i].comment;
                    grIB.u_infoblox_discovery_time_setup = infobloxTime;
                    grIB.u_dmz_type = zone;
                    grIB.u_network_type = networkType;
                    var rightnow = new GlideDateTime();
                    grIB.u_last_discovered = rightnow;
                    grIB.insert();
                }
            } else {
                //subnet or mask from row is invalid
                //create an array to hold anything in error and submit all as one inc
                errors_array.push(parsedData[i]);
            }
        }

        if (errors_array) {
            this._createInc(errors_array);
        }
    },
    _createInc: function(errors_array) {
        grInc = new GlideRecord("incident");
        grInc.initialize();
        grInc.caller = "";
        grInc.assignment_group = "sys_id of assignment group"; //Insert your assignement group here
        grInc.business_service = "sys_id of business service to relate inc to"; // Insert your service if that required
        grInc.u_incident_type = "Data Issue";
        grInc.severity = 3;
        grInc.priority = 5;
        grInc.contact_type = "Self Discovered";
        var errors_to_string = JSON.stringify(errors_array, null, '\t');
        grInc.description = "There was an error with the data import from Infoblox integration. Objects with errors: " + "\n \n" + errors_to_string;
        grInc.short_description = "Infoblox Data Import Error";
        grInc.insert();
        return;
    },
    retire: function(days){
        var ibGr = new GlideRecord("u_cmdb_infoblox");
        var query = "u_last_discoveredRELATIVELT@dayofweek@ago@" + days  + "^u_status=1";
        ibGr.addEncodedQuery(query);
        ibGr.query();
        while(ibGr.next()){
            ibGr.u_status = 4;
            ibGr.update();
        }
    },
    type: 'InfobloxImport'
};

 

 

 

 

 

  • Create Business rules:
    • Manage update or insert
    • Classify network ranges as weekly, daily, or to ignore
    • Call Discovery Schedule Manager Script Include to manage the auto creation of discovery schedules

 

 

 

 

 

 

 

 

(function executeRule(current, previous /*null when async*/ ) {
    var discoScheduleMgmt = new discoveryScheduleManager;

    var dailyNetworks = gs.getProperty('discovery.infoblox.daily_networks');
    var weeklyNetworks = gs.getProperty('discovery.infoblox.weekly_networks');
    var dailyZones = gs.getProperty('discovery.infoblox.daily_zones');
    var weeklyZones = gs.getProperty('discovery.infoblox.weekly_zones');

    var runType = "";
    var networkIp = current.u_network_ip;
    var netmask = current.u_network_mask;
    var networkType = current.u_network_type.toLowerCase();
    var zone = current.u_dmz_type.toLowerCase();

    var isDailyNetwork = (dailyNetworks.indexOf(networkType) > -1);
    var isDailyZone = (dailyZones.indexOf(zone) > -1);
    var isWeeklyNetwork = (weeklyNetworks.indexOf(networkType) > -1);
    var isWeeklyZone = (weeklyZones.indexOf(zone) > -1);
    if (isWeeklyNetwork && isWeeklyZone) {
        runType = 2; //"Weekly"
    } else if (isDailyNetwork && isDailyZone) {
        runType = 1; //"Daily"
    }

    if (current.operation == 'delete') {
        discoScheduleMgmt.removeSchedule(networkIp, netmask, networkType);
    } else if (current.operation() == "update" && current.u_cmdb_discovery.changes() || current.u_status.changes() && current.u_discovery_schedule && !current.u_network_type.changes() && !current.u_dmz_type.changes()) {

        if (current.u_status.changes()) {
            if (current.u_status.changesFrom(1)) {
                current.u_cmdb_discovery = "false";
                //udpate disco schedule record
                var dschd = new GlideRecord("discovery_schedule");
                dschd.get(current.u_discovery_schedule.sys_id);
                dschd.active = "false";
                dschd.update();
            } else if (current.u_status.changesTo(1)) {
                current.u_cmdb_discovery = "true";
                //udpate disco schedule record
                var dschd = new GlideRecord("discovery_schedule");
                dschd.get(current.u_discovery_schedule.sys_id);
                dschd.active = "true";
                dschd.update();
            }
        } else if (current.u_cmdb_discovery.changes()) {
            if (current.u_cmdb_discovery.changesTo("true")) {
                //udpate disco schedule record
                var dschd = new GlideRecord("discovery_schedule");
                dschd.get(current.u_discovery_schedule.sys_id);
                dschd.active = "true";
                dschd.update();
                current.u_status = 1;
            } else if (current.u_cmdb_discovery.changesTo("false")) {
                //udpate disco schedule record
                var dschd = new GlideRecord("discovery_schedule");
                dschd.get(current.u_discovery_schedule.sys_id);
                dschd.active = "false";
                dschd.update();
                //set current status
                current.u_status = 2;
            }
        }
    } else if ((runType == 1 || runType == 2) && (current.operation() == "insert" || current.operation() == "update")) {
        //Create new schedule
        var discoScheduleSysId = discoScheduleMgmt.discoScheduleCreator(networkIp, netmask, runType, networkType, zone);
        current.u_discovery_schedule = discoScheduleSysId;
        //udpate disco schedule record
        var dschd = new GlideRecord("discovery_schedule");
        dschd.get(current.u_discovery_schedule.sys_id);
        dschd.active = "true";
        dschd.update();
        current.u_discovery_time_setup = runType;
        current.u_status = 1;
        current.u_cmdb_discovery = "true";

    } else if ((runType != 1 && runType != 2) && (current.operation() == "insert" || current.operation() == "update")) {
        var dschd = new GlideRecord("discovery_schedule");
        dschd.addQuery("sys_id", current.u_discovery_schedule.sys_id);
        dschd.query();
        if (dschd.next()) {
            dschd.active = "false";
            dschd.update();
        }
        current.u_status = 2; //removed from discovery
        current.u_cmdb_discovery = "false";
        current.u_discovery_time_setup = 3;

    } else if ((runType == 1 || runType == 2) && current.operation() == "update" && (current.u_dmz_type.changes() || current.u_network_type.changes())) {
        //move schedule
        var previousRunTime = previous.u_discovery_time_setup;
        if (previousRunTime != runType) {
            if (previousRunTime == 1 && runType == 2) {
                var discoScheduleSysId = discoScheduleMgmt.moveToWeekend(current.u_discovery_schedule.sys_id);
            } else if (previousRunTime == 2 && runType == 1) {
                var discoScheduleSysId = discoScheduleMgmt.moveToDaily(current.u_discovery_schedule.sys_id);
            }
            //Link Infoblox record to discovery schedule
            if (discoScheduleSysId) {
                current.u_discovery_schedule = discoScheduleSysId;
                current.u_status = 1;
                current.u_cmdb_discovery = "true";
                current.u_discovery_time_setup = runType;
                var rightnow = new GlideDateTime();
                current.u_last_discovered = rightnow;
            }
        }
    } else if (current.operation() == "update" && current.u_active_ip_count.changes() && current.u_network_type == "data_center" || current.u_network_type == "infrastructure_management" || current.u_network_type == "network_infrastructure") { //Move disco schedule between daily or weekly depending on if there was IPs active
        //Future code for swinging schedules between daily and weekly based on discovered ip count
        // if (current.u_active_ip_count.changesTo(0)) {
        //     discoScheduleMgmt.moveToWeekend(current.u_discovery_schedule.sys_id);
        // } else if (current.u_active_ip_count.changesFrom(0)) {
        //     discoScheduleMgmt.moveToDaily(current.u_discovery_schedule.sys_id);
        // }
    }

})(current, previous);

 

 

 

** Commented code is not currently running. 

 

 

  • Create Script Include to manage Discovery Schedules

 

 

 

var discoveryScheduleManager = Class.create();
discoveryScheduleManager.prototype = {
    initialize: function () {
        //Init
    },
    discoScheduleCreator: function (networkIp, netmask, runType, networkType, zone) {
        if (runType == "daily" || runType == "Daily" || runType == 1) {
            var schedulesStartTime = new GlideDateTime("1970-01-01 12:00:00");
            var schedulesEndTime = new GlideDateTime("1970-01-02 05:59:59");
            //Dealing with Glide Time fields in order to pass in a start and end run time made the code hard to follow.
            //Using a simple array that maps the valid run hours
            //1970-01-01 06:00:00  on schedule record = 00:00:00 run time on form
            //1970-01-02 00:00:00 = 18:00:00
            //Run times map, initializing to 0 count for discovery schedules count
            var runTimes = {
                "1970-01-02 00:": 0,
                "1970-01-02 01:": 0,
                "1970-01-02 02:": 0,
                "1970-01-02 03:": 0,
                "1970-01-02 04:": 0,
                "1970-01-02 05:": 0,
                "1970-01-01 06:": 0,
                "1970-01-01 07:": 0,
                "1970-01-01 08:": 0
            };
            runType = 'daily';
            var runAs = 'sys_id of runas account'; //Replace with your run as account
            var runDay = 1;
            var maxRun = '0 01:30:00'; //Schedule Max Run Time
            var midType = 'specific_cluster';
            var InstanceName = "";
            var midCluster = 'sys_id of cluster'; //Replace with your discovery cluster
            var networkName = networkIp + "/" + netmask;
            var schdName = networkType + "/" + zone + " - " + networkName;
        } else if (runType == "weekly" || runType == "Weekly" || runType == 2) {
            var schedulesStartTime = new GlideDateTime("1970-01-01 09:00:00");
            var schedulesEndTime = new GlideDateTime("1970-01-02 00:00:00");
            var runTimes = {
                "1970-01-01 09:": 0,
                "1970-01-01 10:": 0,
                "1970-01-01 11:": 0,
                "1970-01-01 12:": 0,
                "1970-01-01 13:": 0,
                "1970-01-01 14:": 0,
                "1970-01-01 15:": 0,
                "1970-01-01 16:": 0,
                "1970-01-01 17:": 0,
                "1970-01-01 18:": 0,
                "1970-01-01 19:": 0,
                "1970-01-01 20:": 0,
                "1970-01-01 21:": 0,
                "1970-01-01 22:": 0,
                "1970-01-01 23:": 0,
                "1970-01-02 00:": 0
            };
            runType = 'weekly';
            var runDay = "0";
            var runAs = 'sys_id of runas account'; //Replace with sysid of runas account
            var maxRun = '0 01:30:00'; //Schedule Max Run Time
            var midType = 'specific_cluster';
            var midCluster = 'sys_id of cluster'; //Replace with your discovery cluster
            var networkName = networkIp + "/" + netmask;
            var schdName = networkType + "/" + zone + " - " + networkName;
        } else {
            return;
        }
        var grDiscSchedule_gr = this.createNewSchedule(runTimes, networkName, networkIp, netmask, runAs, runDay, midType, midCluster, runType, maxRun, schdName);
        return grDiscSchedule_gr;
    },
    createNewSchedule: function (runTimes, networkName, networkIp, netmask, runAs, runDay, midType, midCluster, runType, maxRun, schdName) {
        var grDiscRangeItemSysId = this.createRangeItem(networkIp, netmask, networkName);
        //Create Range Set
        var grDiscRangeSetSysId = this.createRangeSet(networkName);
        //Set assoc between range and range set
        var grDiscRange = new GlideRecord('discovery_range_item');
        grDiscRange.get(grDiscRangeItemSysId);
        grDiscRange.parent = grDiscRangeSetSysId;
        grDiscRange.setWorkflow('false');
        grDiscRange.update();
        if (runType == "weekly") {
            runDay = this.findLeastBusyWeekendDay();
        }
        var leastBusyHour = this.findLeastBusyHour(runTimes, runType, runDay);
        var leastBusyMin = this.findLeastBusyMin(leastBusyHour, runType, runDay);
        var scheduleRunTime = leastBusyHour + leastBusyMin + ":00";
        var discSched_gr = this.createSchedule(networkName, runDay, runType, scheduleRunTime, runAs, maxRun, midCluster, midType, schdName);

        //Associate Range to Schedule
        var DiscSchedRange = new GlideRecord('discovery_schedule_range');
        DiscSchedRange.addQuery("dscheduler", discSched_gr);
        DiscSchedRange.addQuery("range", grDiscRangeSetSysId);
        DiscSchedRange.query();
        if (DiscSchedRange.next()) {
            //Discovery schedule range to schedule already exists
            return discSched_gr;
        } else {
            DiscSchedRange.init();
            DiscSchedRange.range = grDiscRangeSetSysId;
            DiscSchedRange.dscheduler = discSched_gr;
            DiscSchedRange.insert();
            //Return our disco schedule sys_id to associate back to infoblox table
            return discSched_gr;
        }
    },

    createRangeItem: function (networkIp, netmask, networkName) {
        var grDiscRangeItem = new GlideRecord('discovery_range_item');
        grDiscRangeItem.addQuery('network_ip', networkIp);
        grDiscRangeItem.query();
        if (grDiscRangeItem.next()) {
            var grDiscRangeItemSysId = grDiscRangeItem.sys_id;
            //Discovery Range Item already exists
            return grDiscRangeItemSysId;
        } else {
            grDiscRangeItem.init();
            grDiscRangeItem.name = networkName;
            grDiscRangeItem.summary = networkName;
            grDiscRangeItem.network_ip = networkIp;
            grDiscRangeItem.netmask = netmask;
            grDiscRangeItem.type = 'IP Network';
            var grDiscRangeItemSysId = grDiscRangeItem.insert();
            return grDiscRangeItemSysId;
        }
    },
    createRangeSet: function (networkName) {
        //Create Range Set
        var grDiscRangeSet = new GlideRecord('discovery_range');
        grDiscRangeSet.addQuery('name', networkName);
        grDiscRangeSet.query();
        if (grDiscRangeSet.next()) {
            var grDiscRangeSetSysId = grDiscRangeSet.sys_id;
            //Discovery Range Set already exists
            return grDiscRangeSetSysId;
        } else {
            grDiscRangeSet.init();
            grDiscRangeSet.name = networkName;
            var grDiscRangeSetSysId = grDiscRangeSet.insert();
            return grDiscRangeSetSysId;
        }
    },
    createSchedule: function (networkName, runDay, runType, scheduleRunTime, runAs, maxRun, midCluster, midType, schdName) {
        var grDiscSchedule = new GlideRecord('discovery_schedule');
        grDiscSchedule.addQuery('name', schdName);
        grDiscSchedule.query();
        if (grDiscSchedule.next()) {
            grDiscSchedule.active = 'true';
            grDiscSchedule.update();
            return grDiscSchedule.sys_id;
        } else {
            grDiscSchedule.init();
            grDiscSchedule.name = schdName;
            grDiscSchedule.discover = 'CIs';
            grDiscSchedule.disco_run_type = runType;
            grDiscSchedule.active = 'true';
            grDiscSchedule.run_type = runType;
            grDiscSchedule.run_as = runAs;
            grDiscSchedule.max_run.setDisplayValue(maxRun);
            grDiscSchedule.setValue("run_time", scheduleRunTime);
            grDiscSchedule.mid_cluster = midCluster;
            grDiscSchedule.mid_select_method = midType;
            grDiscSchedule.run_dayofmonth = 1;
            grDiscSchedule.run_dayofweek = runDay;
            grDiscSchedule_gr = grDiscSchedule.insert();
            return grDiscSchedule_gr;
        }
    },

    findLeastBusyWeekendDay: function () {
        var startDay = 6;
        var endDay = 7;
        var runType = "weekly";
        var discoScheduleArray = [{
            "hour": "",
            "count": ""
        }];
        var discoScheduleMap = {};
        //Pad out the array so it contains all days
        for (var i = startDay; i <= endDay; ++i) {
            discoScheduleMap[i] = 0;
        }
        var grDiscoSchedules = new GlideRecord('discovery_schedule');
        grDiscoSchedules.addEncodedQuery("run_dayofweek=6^ORrun_dayofweek=7");
        grDiscoSchedules.addQuery("run_type", runType);
        grDiscoSchedules.query();
        while (grDiscoSchedules.next()) {
            var runday = grDiscoSchedules.run_dayofweek;
            if (runday in discoScheduleMap) {
                discoScheduleMap[runday] = discoScheduleMap[runday] + 1;
            } else {
                discoScheduleMap[runday] = 1;
            }
        }
        //Find the least busy hour in our window by count of schedules in that hour
        var keys = Object.keys(discoScheduleMap);
        // Iterate through all the key values
        var minimumKey = keys[0];
        for (var i = 1; i < keys.length; i++) {
            var minimum = discoScheduleMap[minimumKey];
            var value = discoScheduleMap[keys[i]];
            if (minimum > value) minimumKey = keys[i];
        }
        return minimumKey;
    },
    findLeastBusyHour: function (runTimes, runType, runDay) {
        //Build our query string from our map of run times
        var queryString = "run_type=" + runType + "^run_dayofweek=" + runDay + "^run_timeSTARTSWITH";
        Object.keys(runTimes).forEach(function (key) {
            queryString = queryString + key + "^ORrun_timeSTARTSWITH";
        });
        var grDiscoSchedules = new GlideRecord('discovery_schedule');
        grDiscoSchedules.addEncodedQuery(queryString);
        grDiscoSchedules.query();
        while (grDiscoSchedules.next()) {
            var runhour = grDiscoSchedules.run_time.substring(0, grDiscoSchedules.run_time.getValue().length - 5);
            if (runTimes.hasOwnProperty(runhour)) {
                runTimes[runhour] = runTimes[runhour] + 1;
            } else {
                runTimes[runhour] = 0;
            }
        }
        //Find the least busy hour in our window by count of schedules in that hour
        var keys = Object.keys(runTimes);
        var minimumKey = keys[0];
        for (var i = 1; i < keys.length; i++) {
            var minimum = runTimes[minimumKey];
            var value = runTimes[keys[i]];
            if (minimum > value) minimumKey = keys[i];
        }
        return minimumKey;
    },
    findLeastBusyMin: function (leastBusyHour, runType, runDay) {
        var discoScheduleArray = [{
            "min": "",
            "count": ""
        }];
        var discoScheduleMap = {};
        //Pad out the array so it contains all minutes instead of just what's in the schedules
        for (var i = 0; i <= 59; ++i) {
            minuteIndex = zeroPad(i, 2);
            discoScheduleMap[minuteIndex] = 0;
        }

        function zeroPad(num, places) {
            var zero = places - num.toString().length + 1;
            return Array(+(zero > 0 && zero)).join("0") + num;
        }
        var grDiscoSchedules = new GlideRecord('discovery_schedule');
        grDiscoSchedules.addQuery("run_time", "STARTSWITH", leastBusyHour);
        grDiscoSchedules.addQuery("run_type", runType);
        grDiscoSchedules.addQuery("run_dayofweek", runDay);
        grDiscoSchedules.query();
        while (grDiscoSchedules.next()) {
            var runMin = grDiscoSchedules.run_time.substring(grDiscoSchedules.run_time.getByFormat("yyyy-MM-dd HH:mm:ss").length - 5, grDiscoSchedules.run_time.getByFormat("yyyy-MM-dd HH:mm:ss").length - 3);
            if (runMin in discoScheduleMap) {
                discoScheduleMap[runMin] = discoScheduleMap[runMin] + 1;
            } else {
                discoScheduleMap[runMin] = 1;
            }
        }
        //Find the least busy hour in our window by count of schedules in that hour
        var keys = Object.keys(discoScheduleMap);
        var minimumKey = keys[0];
        for (var i = 1; i < keys.length; i++) {
            var minimum = discoScheduleMap[minimumKey];
            var value = discoScheduleMap[keys[i]];
            if (minimum > value) minimumKey = keys[i];
        }
        return minimumKey;
    },

    moveToWeekend: function (dSchd) {
        //Move discovery schedule to the weekend
        var runTimes = {
            "1970-01-01 09:": 0,
            "1970-01-01 10:": 0,
            "1970-01-01 11:": 0,
            "1970-01-01 12:": 0,
            "1970-01-01 13:": 0,
            "1970-01-01 14:": 0,
            "1970-01-01 15:": 0,
            "1970-01-01 16:": 0,
            "1970-01-01 17:": 0,
            "1970-01-01 18:": 0,
            "1970-01-01 19:": 0,
            "1970-01-01 20:": 0,
            "1970-01-01 21:": 0,
            "1970-01-01 22:": 0,
            "1970-01-01 23:": 0,
            "1970-01-02 00:": 0
        };
        var runType = "weekly";
        var discoSchd = new GlideRecord('discovery_schedule');
        discoSchd.get(dSchd);
        var runDay = this.findLeastBusyWeekendDay();
        var leastBusyHour = this.findLeastBusyHour(runTimes, runType, runDay);
        var leastBusyMin = this.findLeastBusyMin(leastBusyHour, runType, runDay);
        var scheduleRunTime = leastBusyHour + leastBusyMin + ":00";
        discoSchd.setValue("run_time", scheduleRunTime);
        discoSchd.disco_run_type = "weekly";
        discoSchd.run_type = "weekly";
        discoSchd.run_dayofweek = runDay;
        discoSchd.active = 'true';
        discoSchd.update();
        return discoSchd.sys_id;
    },

    moveToDaily: function (dSchd) {
        //Move discovery schedule to daily
        var runTimes = {
            "1970-01-02 00:": 0,
            "1970-01-02 01:": 0,
            "1970-01-02 02:": 0,
            "1970-01-02 03:": 0,
            "1970-01-02 04:": 0,
            "1970-01-02 05:": 0,
            "1970-01-01 06:": 0,
            "1970-01-01 07:": 0,
            "1970-01-01 08:": 0
        };
        var runType = 'daily';
        var runDay = 1;
        var discoSchd = new GlideRecord('discovery_schedule');
        discoSchd.get(dSchd);
        var leastBusyHour = this.findLeastBusyHour(runTimes, runType, runDay);
        var leastBusyMin = this.findLeastBusyMin(leastBusyHour, runType, runDay);
        var scheduleRunTime = leastBusyHour + leastBusyMin + ":00";
        discoSchd.setValue("run_time", scheduleRunTime);
        discoSchd.disco_run_type = runType;
        discoSchd.run_dayofweek = runDay;
        discoSchd.active = 'true';
        discoSchd.update();
        return discoSchd.sys_id;
    },

    removeSchedule: function (networkIp, netmask) {
        var networkName = networkIp + "/" + netmask;
        var grDiscSchedule = new GlideRecord('discovery_schedule');
        grDiscSchedule.addQuery('name', networkName);
        grDiscSchedule.query();
        if (grDiscSchedule.next()) {
            grDiscSchedule.active = 'false';
        }
        return;
    },
    buildAll: function () {
        var infobloxNetworks = new GlideRecord('u_cmdb_infoblox');
        infobloxNetworks.addQuery('u_cmdb_discovery', true);
        infobloxNetworks.query();
        while (infobloxNetworks.next()) {
            var networkType = infobloxNetworks.u_network_type;
            var networkIp = infobloxNetworks.u_network_ip;
            var netmask = infobloxNetworks.u_network_mask;
            var u_discovery_time_setup = infobloxNetworks.u_discovery_time_setup;
            var discoScheduleSysId = this.discoScheduleCreator(networkIp, netmask, networkType, u_discovery_time_setup);
            if (discoScheduleSysId) {
                infobloxNetworks.u_discovery_schedule = discoScheduleSysId;
                infobloxNetworks.u_mapped_to_discovery_schedule = 'true';
                infobloxNetworks.update();
}
        }
        return;
    },
    type: 'discoveryScheduleManager'
};

 

 

 

 

 

Step 3 – Configuration management

 

  • Adopt QuickNexus "Discovery Admin" tooling
    • Configure auto-assignment for incidents related to errors
    • Create KPIs to calculate discovery health
    • Manage errors from Discovery Admin

 

 

3 REPLIES 3

AJ-TechTrek
Giga Sage
Giga Sage

Hi @Camila Godoy 

 

This is very useful information, Thanks for sharing.

 

Thanks

Ajay

Thank you, @AJ-TechTrek ! 

How to configure Discovery process for data received through Service Graph connector for Infoblox