// submitjob.cpp
//
// Drew Weitz
// November 4, 2002
//
// This program takes a command from the user, and 
// using Bproc C library calls, determines the least
// loaded compute node, and moves the process to that
// node and executes the user command.

// compile line: g++ -O -o submitjob.out submitjob.cpp -lbproc

#include <iostream.h>
#include <sys/bproc.h> 
#include <string>
#include <stdlib.h>
#include <time.h>

//  This struct will hold the useful information
//  about processes running on each node, and
//  each nodes status 
struct node_summary {
	int processes;	// Number of processes running on a node
	int condition;  // Condition 4 is "up" ... it is the only
			// one we care about.
};

int main (int argc, char *argv[]) {

	int max_nodes = -1;		// variable used for loop control
	int status = -1;		// used to catch return values
	int index_of_min = 0;		// used to mark least loaded processor
	int current_min = 10000;	// used to track how many processes are 
					// running on candidate compute nodes
	int qualified_max = -1;         // used to index qualified node array
	                                // declared after total num of nodes
	                                // is determined.
	int random_index = -1;          // the randomly generated index to pick
	                                // from equally qualified nodes.

	char command_line[256] = "";	// stores the user command
	int total_args = argc;		// total number of arguments
	char *env[1] = {(char *)0 };    // used as the environmental variables
	                                // in the bproc_execmove function call
	
	char *user_cmd[argc-1];         // used to store the user command as an array
	char *user_env[] = {(char *)0}; // array to store environmental variables for
	                                // the user supplied command 

	//  This is a hack... Since I don't need to know 
	//  all the information about each process
	//  running on the compute nodes, just the number
	//  in existance, I create this list and pointers
	//  to it, but ingore it in the end.  This will be
	//  cleaned up in the next iteration of the program.
	bproc_proc_info_t list;
	bproc_proc_info_t *processes = &list;
	bproc_proc_info_t **hack = &processes;

	// Initialize random number generator
	// used to select random node from equally
	// qualified potential nodes.
	srand (time(NULL));

	//  Parsing the command line
	for (int i = 1; i < argc; i++)
	  user_cmd[i-1] = argv[i];

	user_cmd[argc-1] = (char *)0;
	
	// Determine the number of nodes in the cluster
	// (not hard coded to provide scalability).
	max_nodes = bproc_numnodes();

	// I create an array of cluster nodes now, since I did
	// not know the max number of nodes until now.	
	node_summary cluster_nodes[max_nodes];

	// Create an array that will list all equally qualified
	// nodes for execution later
	int qualified_nodes[max_nodes];

	// Loops through all possible nodes (except node 0,
	// which is not actually a node in the cluster) and
	// stores process and status information in the
	// cluster_node struct array.
	for (int i = 0; i < max_nodes; i++) {
		cluster_nodes[i].processes = bproc_proclist(i,hack);
		cluster_nodes[i].condition = bproc_nodestatus(i);
	}
	
	// This loops scans through the compute nodes and checks
	// for the fewest number of processes running.  The node
	// in condition 4 with the fewest number of processes
	// running will be selected.
	for (int i = 0; i < max_nodes; i++) {
		if ( (cluster_nodes[i].condition == 4) && 
		     (cluster_nodes[i].processes < current_min) ) {

			index_of_min = i;
			current_min = cluster_nodes[i].processes;
			qualified_nodes[0] = i;
			qualified_max = 1;
			
		}

		else if ( (cluster_nodes[i].condition == 4) &&
			  (cluster_nodes[i].processes == current_min) ) {
		  
		  qualified_nodes[qualified_max] = i;
		  qualified_max++;
		}
	}

	// If the index_of_min is still -1 (the initialized value)
	// then there is a problem.
	if (index_of_min == -1 || qualified_max == -1) {
		cout << "An error has occured." << endl;
		cout << "Please try again." << endl;

		exit(1);
	}

	// Otherwise, generate a random key to select a qualified
	// node, and execute the command on that node.
	else {
	        random_index = rand()%qualified_max;
		cout << "Executing on node " << qualified_nodes[random_index] << endl;
		bproc_execmove(qualified_nodes[random_index], user_cmd[0], 
			       user_cmd, user_env); 
		
	}
		
	
	return 0;
	
}

