gaganpoovaiah
ServiceNow Employee
ServiceNow Employee

Writing MID Selector Override script


Overview

What is a MID selector override script

What information is available in the script:

Execute SSH operation: 
Example of MID selector override script filters by CAPI implementation:

List Datacenter operation: 
Example of MID selector override script filters by provider:

List LoadBalancers operation: 
Example of MID selector override script filters by service account:

 

Overview


This document helps you get a head start on writing a MID selector override. The document explains what a MID selector override script is, what information is available in the script along with a few examples to understand it better.

What is a MID selector override script


You can use the override feature to write your own filter condition that overrides the normal MID Server selection criteria for an application. An override has precedence over the default filter and can be defined for a specific selection condition.


What information is available in the script:


candidateMIDList: A list of MID Server objects to be filtered by this function. The candidate MID returns a list of MID servers as an array

midSelectorContext: A JavaScript object created by the caller. The MID selector context is an object that holds information like location name, CAPI implementation name, CAPI method name, service account ID, Provider details. Here are some of the sample output for different operation

Execute SSH operation:

{"location_name":"us-west-1","capi_impl_name":"Node Access API","endpoint_url":null,"correlation_id":null,"capi_method_name":"ExecuteSSHScript","capi_interface_name":"Node Access Interface","targets":["$(Script:CMPVMUtils.getReachableIp[arg=$(Stack.items[].attributes[sys_id])])"],"service_account_id":"562059592944","capi_method_parameters":{"UseSudo":"false","Script":"sleep 600","NodeCredential":"$(capiResolver.NodeCredentialResolver#nodeCredentialId=86d1bbb31b12d490ef280f28cc4bcb49)","NodeAccessOperation":"ExecuteSSHScript","TriggerReaper":"true","NodeAddress":"$(Script:CMPVMUtils.getReachableIp[arg=$(Stack.items[].attributes[sys_id])])","ScriptParameters":"","Provider":"node-access"}}

Example of MID selector override script filters by CAPI implementation:

/**
This is where you define your own mid server selection logic. The filters are applied in the following order: Status Filter, App Filter, Capability Filter, IP Filter.
If the Override Selector is defined, only the Override Selector is applied, all other filters are ignored.

candidateMidList A list of MID Server objects to be filtered by this function.
defaultSelectedMidList A list of MID Server Objects. This is the result after applying the default filter on the candidateMidList.
midSelectorContext A JavaScript object created by the caller.

equestedParameters The MidFilterParameters object requested for mid server selection.

Include application(String), capabilities(MidCapability[]), and Ips(String[]).
MID Server object contains following fields: sysId (String), name (String), applications(String[]), capabilities(MidCapability[])
MidCapability object contains following fields: sysId(String), capability(String), value(String)
After filtering, the result should be an array of MID Server objects. Each MID Server object in the result should contain comprehensive information (sysId, name, applications, capabilities) for the next filter.
*/
(function filter( /* MID Server Array */ candidateMIDList, /* MID Server Array */ defaultSelectedMIDList, /* Native JS object */ midSelectorContext, /* MidFilterParameters */ requestedParameters) {
    gs.info("~~~~~~midSelectorContext is" + midSelectorContext);
    if (JSUtil.notNil(midSelectorContext)) {
        try {
            var invocationContext = JSON.parse(midSelectorContext),
                preferredMidList = [];
            if (!invocationContext.hasOwnProperty('capi_impl_name'))
                return;
            var implName = invocationContext['capi_impl_name'];
            gs.info("@@@@@implName is" + JSON.stringify(implName));
            if (implName && implName == 'Node Access API') {
                candidateMIDList.forEach(function(candidateMid) {
                    if (candidateMid.name == 'MID 1')
                        preferredMidList.push(candidateMid);
                });
            } else {
                candidateMIDList.forEach(function(candidateMid) {
                    if (candidateMid.name == '')
                        preferredMidList.push(candidateMid);
                });
            }
            return preferredMidList;
        } catch (e) {
            e.printStackTrace;
        }
    }
    return candidateMIDList;
})(candidateMIDList, defaultSelectedMIDList, midSelectorContext, requestedParameters);


List Datacenter operation:

{"location_name":null,"capi_impl_name":"AWS Compute API","endpoint_url":null,"correlation_id":null,"capi_method_name":"ListDatacenters","capi_interface_name":"Compute Interface","targets":null,"service_account_id":"562059592944","capi_method_parameters":{"Project":"$(CloudCredential.project_name)","Endpoint":"$(CloudCredential.URL)","Identity":"$(CloudCredential.access_key)","Credentials":"$(CloudCredential.secret_key)","CloudOperation":"ListDatacenters","Provider":"aws-ec2","AccountAliasName":"$(CloudCredential.Alias)"}}

Example of MID selector override script filters by provider:

/**
This is where you define your own mid server selection logic. The filters are applied in the following order: Status Filter, App Filter, Capability Filter, Ip Filter.
If the Override Selector is defined, only the Override Selector is applied, all other filters are ignored.
candidateMidList A list of MID Server objects to be filtered by this function.
defaultSelectedMidList A list of MID Server Objects. This is the result after applying the default filter on the candidateMidList.
midSelectorContext A JavaScript object created by the caller.

equestedParameters The MidFilterParameters object requested for mid server selection.

Include application(String), capabilities(MidCapability[]), and Ips(String[]).
MID Server object contains following fields: sysId (String), name (String), applications(String[]), capabilities(MidCapability[])
MidCapability object contains following fields: sysId(String), capability(String), value(String)
After filtering, the result should be an array of MID Server objects. Each MID Server object in the result should contain comprehensive information (sysId, name, applications, capabilities) for the next filter.
*/
(function filter( /* MID Server Array */ candidateMIDList, /* MID Server Array */ defaultSelectedMIDList, /* Native JS object */ midSelectorContext, /* MidFilterParameters */ requestedParameters) {
    gs.info("~~~~~~midSelectorContext is" + midSelectorContext);
    if (JSUtil.notNil(midSelectorContext)) {
        try {
            var invocationContext = JSON.parse(midSelectorContext),
                preferredMidList = [];
            if (!invocationContext.hasOwnProperty('capi_impl_name'))
                return;
            var provider = invocationContext['capi_impl_name'];
            gs.info("@@@@@provider is" + JSON.stringify(provider));
            if (provider && provider.includes('AWS')) {
                candidateMIDList.forEach(function(candidateMid) {
                    if (candidateMid.name == 'mid1')
                        preferredMidList.push(candidateMid);
                });
            } else if (provider && provider.includes('Azure')) {
                candidateMIDList.forEach(function(candidateMid) {
                    if (candidateMid.name == 'mid2')
                        preferredMidList.push(candidateMid);
                });
            } else {
                candidateMIDList.forEach(function(candidateMid) {
                    if (candidateMid.name == '')
                        preferredMidList.push(candidateMid);
                });
            }
            return preferredMidList;
        } catch (e) {
            e.printStackTrace;
        }
    }
    return candidateMIDList;
})(candidateMIDList, defaultSelectedMIDList, midSelectorContext, requestedParameters);

 

List LoadBalancers operation:

{"location_name":"us-west-1","capi_impl_name":"AWS Load Balancer API","endpoint_url":null,"correlation_id":"06effa421ba61c90ef280f28cc4bcb19","capi_method_name":"ListLoadBalancers","capi_interface_name":"Load Balancer Interface","targets":null,"service_account_id":"562059592944","capi_method_parameters":{"Endpoint":"$(CloudCredential.URL)","ObjectID":"","CheckLoadBalancers":"","Identity":"$(CloudCredential.access_key)","LoadBalancerOperation":"ListLoadBalancers","Credentials":"$(CloudCredential.secret_key)","Provider":"aws-elb","AccountAliasName":"$(CloudCredential.Alias)","Location":"us-west-1"}}


Example of MID selector override script filters by service account

/**
This is where you define your own mid server selection logic. The filters are applied in following order: Status Filter, App Filter, Capability Filter, IP Filter.

If the Override Selector is defined, only the Override Selector will be applied, all other filters will be ignored.
candidateMidList A list of MID Server objects to be filtered by this function.
defaultSelectedMidList A list of MID Server Objects. This is the result after applying the default filter on the candidateMidList.
midSelectorContext A JavaScript object created by the caller.

equestedParameters The MidFilterParameters object requested for mid server selection.
Include application(String), capabilities(MidCapability[]), and Ips(String[]).
MID Server object contains following fields: sysId (String), name (String), applications(String[]), capabilities(MidCapability[])
MidCapability object contains following fields: sysId(String), capability(String), value(String)
After filtering, the result should be an array of MID Server objects. Each MID Server object in the result should contain comprehensive information (sysId, name, applications, capabilities) for the next filter.
*/
(function filter( /* MID Server Array */ candidateMIDList, /* MID Server Array */ defaultSelectedMIDList, /* Native JS object */ midSelectorContext, /* MidFilterParameters */ requestedParameters) {
    gs.info("~~~~~~midSelectorContext is" + midSelectorContext);
    if (JSUtil.notNil(midSelectorContext)) {
        try {
            var invocationContext = JSON.parse(midSelectorContext),
                preferredMidList = [];

            if (!invocationContext.hasOwnProperty('service_account_id'))
                return;
            var serviceAccountID = invocationContext['service_account_id'];
            switch (serviceAccountID) {
                case 'serviceaccountid1':
                    candidateMIDList.forEach(function(candidateMid) {
                        if (candidateMid.name == 'mid1')
                            preferredMidList.push(candidateMid);
                    });
                    break;
                case 'serviceaccountid2':
                    candidateMIDList.forEach(function(candidateMid) {
                        if (candidateMid.name == 'mid2')
                            preferredMidList.push(candidateMid);
                    });
            }
            gs.info("~~~~~~ PREFERRED MID LIST\t:\t" + preferredMidList + "\n@@@@@@@FOR SERVICE ACCOUNT ID\t:\t" + serviceAccountID);

            return preferredMidList;
        } catch (e) {
            e.printStackTrace;
        }
    }
    return candidateMIDList;
})(candidateMIDList, defaultSelectedMIDList, midSelectorContext, requestedParameters);

 

Terraform Environment(open-source) discovery operation:

 

{"location_name":null,"capi_impl_name":"","endpoint_url":"null","correlation_id":"aac2347cb71310103fd41efbce11a9c5","capi_method_name":"ExecuteCommand","capi_interface_name":"Configuration Orchestration Interface","service_account_id":null,"targets":null,"capi_method_parameters":{"ProviderParameters":null,"AdditionalParameters":"{\"ServerType\":\"Linux\"}","ScriptType":"JavaScript","Script":"#!/bin/sh\n\nconstructXMLTag(){\n echo \"<$1>$2</$1>\"\n}\n\ncdataTag(){\n cdata_tag='[CDATA['\"$1\"']]'\n echo '<!'\"$cdata_tag\"'>'\n}\n\nexecuteCommand(){\n\txml_command=\"$1\"\n\txml_output=$(eval $xml_command 2>&1)\n\n\tif [ $? -ne 0 ]; then\n\t\techo $xml_output;\n\t \texit 1;\n\tfi \n\n\techo $xml_output;\n\texit 0;\n}\n\nconstructErrorResponse(){\n op_constr_status_tag=$(constructXMLTag 'status' 'error')\n op_cdata=$(cdataTag \"$1\")\n op_error_tag=$(constructXMLTag 'error' \"$op_cdata\")\n echo \"$op_constr_status_tag$op_error_tag\"\n}\n\nconstructErrorResponseTag(){\n tf_data_tag=$(constructXMLTag 'terraform' \"$2\")\n tf_data_tag=$(constructXMLTag 'output' \"$tf_data_tag\")\n op_status_tag=$(constructErrorResponse \"$1\")\n response_data=\"$op_status_tag$tf_data_tag\"\n echo $(constructXMLTag 'response' \"$response_data\")\n}\n\nconstructResponseTag(){\n tf_response_cdata=$(cdataTag \"$1\")\n tf_cmd_tag=$(constructXMLTag \"$2\" \"$tf_response_cdata\")\n tf_data=\"$3$tf_cmd_tag\"\n echo $tf_data;\n}\n\nevaluate(){\n tf_cmd_response=$(executeCommand \"$1\")\n tf_cmd_exitcode=${?}\n tf_data=$(constructResponseTag \"$tf_cmd_response\" \"$2\" \"$3\")\n if [ $tf_cmd_exitcode -ne 0 ]; then\n echo $(constructErrorResponseTag \"$tf_cmd_response\" \"$tf_data\")\n exit 1;\n fi\n\n echo $tf_data\n exit 0;\n}\nDIR='/home/test/templates';\n\nCHANGE_DIR=$([ -d \"$DIR\" ] && (exit 0;) || { echo \"Parent Directory $DIR not found.\"; exit 1; })\nif [ $? -ne 0 ]; then\n RESPONSE_DATA=$(constructErrorResponse \"$CHANGE_DIR\")\n echo $(constructXMLTag 'response' \"$RESPONSE_DATA\")\n exit 1;\nfi\n\nif ! [ -r \"$DIR\" ];then\n ERROR_MSG=\"No read permission for base directory - $DIR\"\n RESPONSE_DATA=$(constructErrorResponse \"$ERROR_MSG\")\n echo $(constructXMLTag 'response' \"$RESPONSE_DATA\")\n exit 1;\nelif ! [ -x \"$DIR\" ];then\n ERROR_MSG=\"No execute permission for base directory - $DIR\"\n RESPONSE_DATA=$(constructErrorResponse \"$ERROR_MSG\")\n echo $(constructXMLTag 'response' \"$RESPONSE_DATA\")\n exit 1;\nfi\n\nDIRECTORIES=\"\";\ncd \"$DIR\";\nfor FILE in */\ndo\n if [ $FILE = \"*/\" ]; then\n ERROR=\"No sub-directory found in the parent directory.\" \n RESPONSE_DATA=$(constructErrorResponse \"$ERROR\")\n echo $(constructXMLTag 'response' \"$RESPONSE_DATA\")\n exit 1;\n else\n DIR_NAME_CDATA=$(cdataTag \"$FILE\")\n DIR_NAME_TAG=$(constructXMLTag 'name' \"$DIR_NAME_CDATA\") \n DIR_DATA=\"$DIR_NAME_TAG\";\n if ! [ -r \"$FILE\" ];then\n WARNING_MSSG=\"No read permission for directory - $FILE\"\n WARNING_TAG=$(constructXMLTag 'warning' \"$WARNING_MSSG\")\n DIR_DATA=\"$DIR_DATA$WARNING_TAG\";\n elif ! [ -x \"$FILE\" ];then\n WARNING_MSSG=\"No execute permission for directory - $FILE\"\n WARNING_TAG=$(constructXMLTag 'warning' \"$WARNING_MSSG\")\n DIR_DATA=\"$DIR_DATA$WARNING_TAG\";\n else\n cd \"$FILE\";\n TEMPLATES_DATA=\"\";\n for i in $( find . -not -path '*/\\.*' -maxdepth 1 -type f -name '*.tf' -o -name '*.tf.json' -o -name 'metadata.snc');\n do\n TEMPLATE_NAME_CDATA=$(cdataTag \"$i\")\n TEMPLATE_NAME_TAG=$(constructXMLTag 'name' \"$TEMPLATE_NAME_CDATA\")\n TEMPLATE_CONTENT_CMD=$(cat \"$i\")\n TEMPLATE_CONTENT_OP=$(cdataTag \"$TEMPLATE_CONTENT_CMD\")\n TEMPLATE_CONTENT_TAG=$(constructXMLTag 'content' \"$TEMPLATE_CONTENT_OP\")\n TEMPLATE_DATA=\"$TEMPLATE_NAME_TAG$TEMPLATE_CONTENT_TAG\";\n TEMPLATE_TAG=$(constructXMLTag 'template' \"$TEMPLATE_DATA\")\n TEMPLATES_DATA=\"$TEMPLATES_DATA$TEMPLATE_TAG\";\n done\n TEMPLATES_TAG=$(constructXMLTag 'templates' \"$TEMPLATES_DATA\")\n DIR_DATA=\"$DIR_DATA$TEMPLATES_TAG\";\n cd ..\n fi\n DIRECTORY_TAG=$(constructXMLTag 'directory' \"$DIR_DATA\")\n DIRECTORIES=\"$DIRECTORIES$DIRECTORY_TAG\"\n fi\ndone\n\nOP_STATUS_TAG=$(constructXMLTag 'status' 'success')\nDIRECTORIES_DATA_TAG=$(constructXMLTag 'output' \"$DIRECTORIES\")\nRESPONSE_DATA=\"$OP_STATUS_TAG$DIRECTORIES_DATA_TAG\"\necho \"$(constructXMLTag 'response' \"$RESPONSE_DATA\")\"\n exit 0;","TemplateParameters":null,"ScriptExecution":"ExecuteScript","Endpoint":"$(CloudCredential.URL)","ScriptName":"terraform-server-1.0-ExecuteCommand","NodeAddress":"10.44.70.110","ConfigurationParameters":null,"ConfigMgmtProviderInfo":"$(capiResolver.NodeCredentialResolver#nodeCredentialId=f5721137b70310103fd41efbce11a900)","Provider":"terraform-opensource"}}

 

Example of MID selector override script filters by NodeAddress(TF box IP selection)

 

/**
This is where you define your own mid server selection logic. The filters are applied in following order: Status Filter, App Filter, Capability Filter, IP Filter.

If the Override Selector is defined, only the Override Selector will be applied, all other filters will be ignored.
candidateMidList A list of MID Server objects to be filtered by this function.
defaultSelectedMidList A list of MID Server Objects. This is the result after applying the default filter on the candidateMidList.
midSelectorContext A JavaScript object created by the caller.

equestedParameters The MidFilterParameters object requested for mid server selection.
Include application(String), capabilities(MidCapability[]), and Ips(String[]).
MID Server object contains following fields: sysId (String), name (String), applications(String[]), capabilities(MidCapability[])
MidCapability object contains following fields: sysId(String), capability(String), value(String)
After filtering, the result should be an array of MID Server objects. Each MID Server object in the result should contain comprehensive information (sysId, name, applications, capabilities) for the next filter.
*/
(function filter( /* MID Server Array */ candidateMIDList, /* MID Server Array */ defaultSelectedMIDList, /* Native JS object */ midSelectorContext, /* MidFilterParameters */ requestedParameters) {
	gs.info("~~~~~~midSelectorContext is"+midSelectorContext);
    if (JSUtil.notNil(midSelectorContext)) {
        try {
            var invocationContext = JSON.parse(midSelectorContext),
                preferredMidList = [];
			
			var nodeAddress = invocationContext.capi_method_parameters.NodeAddress;

            if (!invocationContext.hasOwnProperty('capi_method_parameters'))
                return;
			
            switch (nodeAddress) {
                case '10.44.70.110':
                    candidateMIDList.forEach(function(candidateMid) {
                        if (candidateMid.name == 'mid1')
                            preferredMidList.push(candidateMid);
                    });
                    break;
                case '10.44.70.112':
                    candidateMIDList.forEach(function(candidateMid) {
                        if (candidateMid.name == 'mid2')
                            preferredMidList.push(candidateMid);
                    });
            }

            return preferredMidList;
        } catch (e) {
            e.printStackTrace;
        }
    }
    return candidateMIDList;
})(candidateMIDList, defaultSelectedMIDList, midSelectorContext, requestedParameters);
Version history
Last update:
‎08-26-2020 10:20 PM
Updated by: