Name
global.SchedulerVNCC
Description
New Scheduler with Versatile Node and Cluster Config logic
Script
/*
* Use SchedulerScriptFactory script include to get SchedulerVNCC object
*
* Scheduler script include used with Versatile Node and Cluster Configuration
*/
gs.include("PrototypeServer");
var SchedulerVNCC = Class.create();
SchedulerVNCC.prototype = {
initialize: function() {
this.local_id = GlideClusterSynchronizer.getSystemID();
this.schedulersFromNodeType = GlideClusterSynchronizer.autoSetNodeTypeByRoleParticipation() ||
GlideProperties.getBoolean("com.snc.cluster.vncc.schedulers_on_node_type");
},
stop: function(node_id) {
this._queueAction(node_id, 'stop');
},
start: function(node_id) {
this._queueAction(node_id, 'start');
},
/*_________________________________________________________________
* Description: Ensures child sys_trigger entries for the specified node
for any run "ALL NODES" scheduled jobs. Handles insertions
and deletions of nodes.
* Parameters:
* Returns:
________________________________________________________________*/
propagateRATriggersForNode: function(node) {
this._logNodeInfo("SchedulerVNCC().propagateRATriggersForNode", node);
var trig = new GlideRecord("sys_trigger");
trig.addQuery("system_id", "IN", "ALL NODES,ACTIVE NODES,PRIMARY NODES");
trig.query();
while (trig.next()) {
this._propagateAllNodes(node, trig);
}
},
/*_________________________________________________________________
* Description: Ensures run "ALL NODES" for a given scheduled job are
propagated to all nodes.
* Parameters:
* Returns:
________________________________________________________________*/
propagateRANodesForTrigger: function(trigger) {
this._logTriggerInfo("SchedulerVNCC().propagateRANodesForTrigger", trigger);
var nd = new GlideRecord("sys_cluster_state");
nd.addQuery("status", "online");
// not querying on schedulers as we want to remove unwanted existing child jobs
nd.query();
while (nd.next()) {
this._propagateAllNodes(nd, trigger);
}
},
_getSchedulersFromNodeType: function() {
return this.schedulersFromNodeType;
},
_log: function(msg) {
gs.print("[SchedulerVNCC] " + msg);
},
_nodeInfo: function(node_gr) {
return "node.system_id=" + node_gr.system_id + ", node.sys_id=" + node_gr.sys_id;
},
_triggerInfo: function(trigger_gr) {
return "trigger.name=" + trigger_gr.name + ", trigger.sys_id=" + trigger_gr.sys_id + ", trigger.system_id=" + trigger_gr.system_id;
},
/**
* a utility method to log a message with node and trigger details
*/
_logInfo: function(msg, node_gr, trigger_gr) {
this._log(msg + "; context: " + this._nodeInfo(node_gr) + "," + this._triggerInfo(trigger_gr));
},
/**
* a utility method to log a message with node details
*/
_logNodeInfo: function(msg, node_gr) {
this._log(msg + "; context: " + this._nodeInfo(node_gr));
},
/**
* a utility method to log a message with trigger details
*/
_logTriggerInfo: function(msg, trigger_gr) {
this._log(msg + "; context: " + this._triggerInfo(trigger_gr));
},
/**
* log reason for not creating child jobs
*/
_logNotCreated: function(reason, node_gr, trgger_gr) {
this._logInfo("Child job not created - " + reason, node_gr, trgger_gr);
},
/*_________________________________________________________________
* Description: Will create or delete sys_trigger entries for run "ALL NODES" jobs.
* Parameters: GlideRecord(SYS_CLUSTER_STATE), GlideRecord(SYS_TRIGGER)
________________________________________________________________*/
_propagateAllNodes: function(node_gr, trigger_gr) {
var mutexName = "propagateAllNodes_" + node_gr.system_id + "_" + trigger_gr.sys_id;
var mutex = new GlideSelfCleaningMutex(mutexName, mutexName);
// 5 secs
var spinWait = 200;
var maxSpins = 25;
// standby nodes can take 30 secs to refresh jobs for a parent
if (this.local_id == node_gr.getValue("system_id") && node_gr.getValue("participation") == "standby") {
// 30 secs
spinWait = 500;
maxSpins = 60;
}
var mutexExpiration = maxSpins * spinWait;
mutex.setSpinWait(spinWait);
mutex.setMaxSpins(maxSpins);
mutex.setMutexExpires(mutexExpiration);
var mutexObtained = mutex.get();
try {
if (!mutexObtained) {
// best attempt to avoid conflict
this._logInfo("Unable to obtain mutex name:" + mutexName + ". Proceeding without Mutex", node_gr, trigger_gr);
// FALL-THROUGH
}
this._propagateNodeAndTriggerInMutex(node_gr, trigger_gr);
} finally {
if (mutexObtained)
mutex.release();
}
},
/*_________________________________________________________________
* Description: Creates and/or deletes child jobs for a given node and trigger
* Parameters: GlideRecord(SYS_CLUSTER_STATE), GlideRecord(SYS_TRIGGER)
________________________________________________________________*/
_propagateNodeAndTriggerInMutex: function(node_gr, trigger_gr) {
this._logInfo("SchedulerVNCC()._propogateAllNodes", node_gr, trigger_gr);
var nodeSystemId = node_gr.getValue("system_id");
var triggerSysId = trigger_gr.getValue("sys_id");
var triggerSystemId = trigger_gr.getValue("system_id");
var nc = new GlideRecord("sys_trigger");
nc.addQuery("sys_id", trigger_gr.sys_id);
nc.query();
//Don't recreate in the case of a delete op (ref cascade handles actual delete)
if (!nc.next())
return;
var ch = GlideRecord("sys_trigger");
ch.addQuery("system_id", node_gr.system_id);
ch.addQuery("parent", trigger_gr.sys_id);
ch.query();
// delete any duplicate child jobs
while (ch.next()) {
this._logInfo("Deleting the existing child job (id- " + ch.sys_id + ")", node_gr, trigger_gr);
ch.deleteRecord();
}
if (nc.system_id != trigger_gr.system_id)
return;
// case when node is deleted
// operation == "delete" is true when sys_cluster_state record is deleted. In that case, this script is called from DELETE Business Rule on sys_cluster_state.
if (node_gr.operation() == "delete" || (!this._isNodeRecordPresent(nodeSystemId))) {
this._logNotCreated("node not present", node_gr, trigger_gr);
return;
}
// parent's job system_id is --None-- => no child job for any node
if (JSUtil.nil(triggerSystemId) || triggerSystemId == "none") {
this._logNotCreated("trigger system_id is " + triggerSystemId, node_gr, trigger_gr);
return;
}
if (node_gr.status == "offline") {
this._logNotCreated("Node " + node_gr.system_id + " status is offline", node_gr, trigger_gr);
return;
}
var schedulers;
if (this._getSchedulersFromNodeType())
schedulers = node_gr.node_type.schedulers;
else
schedulers = node_gr.schedulers;
if (JSUtil.nil(schedulers) || schedulers == "none") {
this._logNotCreated("scheduler mode is none", node_gr, trigger_gr);
return;
}
if (trigger_gr.system_id == "ACTIVE NODES" && schedulers != "any") {
this._logNotCreated("scheduler is not set to ANY (mode is " + schedulers + ")", node_gr, trigger_gr);
return;
}
if (trigger_gr.system_id == "PRIMARY NODES" && node_gr.participation != "primary") {
this._logNotCreated("node participation is not primary (but is " + node_gr.participation + ")", node_gr, trigger_gr);
return;
}
nc.addEncodedQuery(node_gr.node_type.job_criteria);
nc.query();
if (!nc.next())
return;
nc.parent = trigger_gr.sys_id;
nc.system_id = node_gr.system_id;
var nextAction = new GlideDateTime(nc.getValue("next_action"));
var now = new GlideDateTime();
if (now.after(nextAction))
nc.setValue("next_action", now.toString());
var insertSysId = nc.insert();
this._logInfo("Inserted child job " + "(sys_id=" + insertSysId + ")", node_gr, trigger_gr);
},
/**
* returns true if sys_cluster_state record is present with given systemId, otherwise false
*/
_isNodeRecordPresent: function(systemId) {
var ga = new GlideAggregate("sys_cluster_state");
ga.addQuery("system_id", systemId);
return ga.getCount() > 0;
},
_queueAction: function(node_id, action) {
this.node_id = node_id;
var node_list = this._getNodeList();
for (var i = 0; i < node_list.length; i++)
GlideClusterMessage.postDirected('scheduler.action', action, node_list[i]);
},
_getNodeList: function() {
var node_list = new Array();
if (this.node_id == 'ALL') {
var gr = new GlideRecord('sys_cluster_state');
gr.query();
while (gr.next())
node_list.push(gr.system_id + '');
} else
node_list.push(this.node_id);
return node_list;
}
};
Sys ID
2af354275f1200107ef72224de73130a