node-deadline
Version:
Module to interface with Deadline Compute Management System by Thinkbox Software
387 lines (318 loc) • 10.3 kB
JavaScript
var enums = require( "../utils/enums" ),
utils = require( "../utils/utils" ),
mongoUtils = require( "../utils/mongo" ),
FrameRange = require( "./FrameRange" ),
Err = require( "../utils/Error" ),
settings = require( "../settings" ),
Job = require( "./Job" );
var jobs = module.exports = {},
p, validParams = {
status: "status",
id: "id",
department: "department",
user: "user",
machine: "machine",
group: "group",
pool: "pool",
secondaryPool:"secondaryPool",
eitherPool: "eitherPool",
priority: "priority",
extraInfo: "extraInfo"
};
jobs.Job = Job;
jobs.get = function( options, callback ) {
jobs.parseQuery( options, function( err, query ) {
if ( err ) {
callback( err, null );
}
//combine with $and if necessary
if ( query.length > 1 ) {
query = mongoUtils.and( query );
}
else if ( query.length == 1 ) {
query = query[ 0 ];
}
else {
query = {};
}
//run the query
jobs.get.find( query, function( err, results ) {
//NOTE can we deal with some errors in here?
callback( err, results );
} );
} );
};
jobs.get.find = function( query, callback ) {
//do the find for the actual jobs
mongoUtils.find( settings.database, "Jobs", query, function( err, res ) {
//get the net config so that we can populate extra info names
mongoUtils.findOne(
settings.database,
"DeadlineSettings",
{ _id: "deadline_network_settings" },
function( err, deadlineNetSet ) {
var i;
if ( err ) {
utils.logError( new Err(
Err.EXTERN, "unable to get custom extra info names", err.message
) );
}
//if there are results, parse them into Job items
if ( res && res.length ) {
for ( i in res ) {
res[ i ] = new Job( res[ i ], deadlineNetSet );
}
}
callback( err, res );
}
);
} );
};
//build jobs.get.by via the valid params
jobs.get.by = {};
function buildByFunction( param ) {
return function( data, callback ) {
var d = {};
d[ param ] = data;
jobs.get( data, callback );
};
}
for ( p in validParams ) {
jobs.get.by[ p ] = buildByFunction( p );
}
jobs.parseQuery = function( options, callback ) {
var option, prop, query = [],
started = 0, completed = 0;
//to log completion and fire on success
function complete() {
completed++;
if ( completed == started ) {
callback( null, query );
}
}
//function to asynchronusly parse an option
function parseOption( opt ) {
started++;
//the asyncronus is forces via a timout of 1ms
setTimeout( function() {
//set the option query to null
var q = null;
switch ( opt ) {
//state is binary set of enums
case "status":
q = [];
if ( options[ opt ] & enums.UNKNOWN ) {
q.push( { Stat: enums.mapJobStateToDeadline( enums.UNKNOWN ) } );
}
if ( options[ opt ] & enums.ACTIVE ) {
q.push( { Stat: enums.mapJobStateToDeadline( enums.ACTIVE ) } );
}
if ( options[ opt ] & enums.SUSPENDED ) {
q.push( { Stat: enums.mapJobStateToDeadline( enums.SUSPENDED ) } );
}
if ( options[ opt ] & enums.COMPLETED ) {
q.push( { Stat: enums.mapJobStateToDeadline( enums.COMPLETED ) } );
}
if ( options[ opt ] & enums.FAILED ) {
q.push( { Stat: enums.mapJobStateToDeadline( enums.FAILED ) } );
}
if ( options[ opt ] & enums.PENDING ) {
q.push( { Stat: enums.mapJobStateToDeadline( enums.PENDING ) } );
}
if ( q.length == 1 ) {
query.push( { Stat: q[ 0 ] } );
break;
}
else if ( q.length === 0 ) {
break;
}
//add the state list to the query
query.push( { $or: q } );
break;
//simple single or array
case "id":
q = utils.parseArrayOption( "_id", options[ opt ] );
query.push( q );
break;
//simple single or array
case "department":
q = utils.parseArrayOption( "Props.Dept", options[ opt ] );
query.push( q );
break;
//simple single or array
case "user":
q = utils.parseArrayOption( "Props.User", options[ opt ] );
query.push( q );
break;
//simple single or array
case "machine":
q = utils.parseArrayOption( "Mach", options[ opt ] );
query.push( q );
break;
//simple single or array
case "group":
q = utils.parseArrayOption( "Props.Grp", options[ opt ] );
query.push( q );
break;
//simple single or array
case "pool":
q = utils.parseArrayOption( "Props.Pool", options[ opt ] );
query.push( q );
break;
//simple single or array
case "secondaryPool":
q = utils.parseArrayOption( "Props.SecPool", options[ opt ] );
query.push( q );
break;
//simple single or array, but need to combine multiple parse results
case "eitherPool":
if ( !Array.isArray( options[ opt ] ) ) {
//simply parse them and combine
q = [
utils.parseArrayOption( "Props.Pool", options[ opt ] ),
utils.parseArrayOption( "Props.SecPool", options[ opt ] )
];
}
else {
//parse them but combine the $or arrays and rebuild
q = utils.combineArrays(
utils.parseArrayOption( "Props.Pool", options[ opt ] ).$or,
utils.parseArrayOption( "Props.SecPool", options[ opt ] ).$or
);
}
q = { $or: q };
query.push( q );
break;
//takes single value, array, or frame range object
case "priority":
//we simply open up a frame range into a flat array
if ( options[ opt ] instanceof FrameRange ) {
options[ opt ] = options[ opt ].toArray();
}
//continue normally
q = utils.parseArrayOption( "Props.Pri", options[ opt ] );
query.push( q );
break;
case "extraInfo":
q = [];
//need to get possible custom values from deadline
mongoUtils.findOne(
settings.database,
"DeadlineSettings",
{ _id: "deadline_network_settings" },
function( err, deadlineNetSet ) {
var final = {},
mapped;
if ( err ) {
callback( new Err(
Err.EXTERN, err.message
), null );
return;
}
//first we convert named properties
for ( prop in options[ opt ] ) {
//ensure its a valid ex value or mapped value
mapped = utils.unmapExtraJobProperty( prop, deadlineNetSet );
if ( null === mapped ) {
callback( new Err(
Err.PARAM,
"Invalid extra property name: " + prop
), null );
}
//append it to the finalized object
//add new name if not there already
if ( "undefined" == typeof final[ mapped ] ) {
final[ mapped ] = options[ opt ][ prop ];
}
else {
//create an array if not one
if ( !Array.isArray( final[ mapped ] ) ) {
final[ mapped ] = [ final[ mapped ] ];
}
//push or concat as necessary
if ( Array.isArray( options[ opt ][ prop ] ) ) {
final[ mapped ] = final[ mapped ].concat(
options[ opt ][ prop ]
);
}
else {
final[ mapped ].push( options[ opt ][ prop ] );
}
}
}
//then we parse the finalized list
for ( prop in final ) {
//each one is a simple single/array query
//combine with previous values
q = utils.combineArrays( q, utils.parseArrayOption( "Props." + prop, final[ prop ] ) );
}
query.push( { $and: q } );
complete();
return;
} );
return;
case "updated":
if ( "undefined" != typeof options[ opt ].before ) {
query.push( { LastWriteTime: { $lt: options[ opt ].before } } );
}
if ( "undefined" != typeof options[ opt ].after ) {
query.push( { LastWriteTime: { $gt: options[ opt ].after } } );
}
break;
default:
callback( new Err(
Err.PARAM,
"Unrecognized property type in query: " + opt
), null );
return;
}
//default compelte call for synchronus code
complete();
}, 1 );
}//parseOption
//build a query from all of the given options
for ( option in options ) {
parseOption( option );
}
//if the query was empty...
if ( started === 0 ) {
callback( null, [] );
}
};
jobs.set = function( jobs, callback ) {
var i,
sent = 0,
completed = 0,
responses = [];
if ( jobs instanceof Job ) {
jobs = [ jobs ];
}
//define the callback
function onComplete( err, res ) {
if ( err ) {
callback( err, null );
return;
}
//all is well, save the response and mark complete
completed++;
responses.push( res );
//call callback if all finished
if ( completed == sent ) {
callback( null, responses );
}
}
for ( i in jobs ) {
try {
jobs[ i ]._calcChanges();
}
catch ( e ) {
callback( e );
}
sent++;
mongoUtils.update(
settings.database, "Jobs",
{ _id: jobs[ i ].id }, { $set: jobs[ i ]._updated },
onComplete
);
}
};