#!/bin/bash #----------------------------------------------------------- # $Id: distexe.sh 138 2010-05-12 17:34:01Z rosiere $ #----------------------------------------------------------- #-----[ global variable ]----------------------------------- declare VERSION="1.3" declare -i SLEEP=2; declare -i TRAP=0; # Need : test, echo, cd, dirname, basename, ssh, ps aux, ls, wc, sed # lock.sh, unlock.sh, date.sh, nb_cpu.sh, distexe_execute_n.sh, distexe_execute.sh #-----[ distexe_usage ]------------------------------------- function distexe_usage () { echo "Usage : ${0} file [ dir ]"; echo "Arguments : "; echo " * file : list of command"; echo " * dir : work directory (default is current directory)."; # echo " * nb_process : number of process (default (and maximum) is the number of processor)"; echo ""; echo "Note : "; echo " * File content list of command. Each line is execute by an host."; echo " A line can content many shell command."; echo " Don't forgot the final end of line (else the last command is not executed)"; echo " * For each command, a directory (in work directory) is created : Task_X (X is the number of line in command file)."; echo " The command is execute in this directory. (Warning, set your environment ! but we source .bashrc)."; echo " Three file is generate :"; echo " - \"distexe.info\" content information as host, pid ..."; echo " - \"distexe.output\" is the output (standard and error) of the execution"; echo " - \"distexe.command\" is the command launch."; # echo " * A command empty (no command on a line of file) is a synchronisation with all process" echo " * The environment variable DISTEXE_HOSTS list hosts to execute command"; echo " After each host, a number is to the number of process. It's optionnal, the default value is the number of processor."; echo " * All hosts must have network file systems (per example nfs or samba)"; echo " Work directory must be in this network file systems !"; echo ""; echo "Example : "; echo " # Create command file"; echo ' echo "export PATH=${PATH}:${HOME}/my_appli/bin; my_appli 13" > command_file.txt'; echo ' echo "export PATH=${PATH}:${HOME}/my_appli/bin; my_appli 21" >> command_file.txt'; echo ' echo "export PATH=${PATH}:${HOME}/my_appli/bin; my_appli 7" >> command_file.txt'; echo ' echo "" >> command_file.txt'; echo " # Set hosts list variable"; echo ' export DISTEXE_HOSTS="localhost/1 nod/4 gdi/2"'; echo " # Execute distexe"; echo " ${0} command_file.txt;"; exit; } #-----[ distexe_test_usage ]-------------------------------- function distexe_test_usage () { if test ${#} -ne 1 -a ${#} -ne 2; then distexe_usage; fi; if test -z "${MORPHEO_SCRIPT}"; then echo "Environment variable MORPHEO_SCRIPT is not set"; distexe_usage; fi; if test ! -f ${1}; then echo "File \"${1}\" is invalid"; distexe_usage; fi; if test ! -s ${1}; then echo "File \"${1}\" is empty."; distexe_usage; fi; if test ${#} -eq 2; then if test ! -d ${2}; then echo "Directory \"${2}\" is invalid."; distexe_usage; fi; fi; } #-----[ header ]-------------------------------------------- function header () { echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> =========================================="; echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> distexe ${VERSION}"; echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> =========================================="; } #-----[ distexe_wait_end ]---------------------------------- function distexe_wait_end() { while true; do nb_file=$(ls -1 ${PREFIX_FILE_HOST}* 2>/dev/null| wc -l); if test ${nb_file} -eq 0; then break; fi; # wait (to not have 100% cpu) sleep ${SLEEP}; done; rm -f ${FILE_CPT}; }; #-----[ distexe_no_trap ]----------------------------------- function distexe_no_trap() { TRAP=1; } #-----[ distexe_trap ]-------------------------------------- function distexe_trap() { echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> Trap signal detected"; # echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> !!!!!!!!!!!!!!!! Trap signal detected !!!!!!!!!!!!!!!!"; local -a files_host=($(ls ${PREFIX_FILE_HOST}* 2>/dev/null)); local -i nb_files_host=${#files_host[*]}; if test ${nb_files_host} -ne 0; then for file_host in ${files_host[*]}; do local host=$(basename ${file_host} |sed s/$(basename ${PREFIX_FILE_HOST})//); # ssh can not set yet pid in file_host local -i nb_word=0; while test ${nb_word} -eq 0; do nb_word=$(echo ${file_host} |wc -m); done; ${MORPHEO_SCRIPT}/lock.sh ${LOCK_HOST}; if test -f ${file_host}; then local cmd="kill -s SIGINT $(cat ${file_host})"; ssh ${host} ${cmd}; fi; ${MORPHEO_SCRIPT}/unlock.sh ${LOCK_HOST}; done; fi; # echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> >>>>>>>>>>>>>>>> Trap signal detected >>>>>>>>>>>>>>>>"; distexe_wait_end; # echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> ################ Trap signal detected ################"; echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> an error occure"; exit 1; } #-----[ distexe ]------------------------------------------- function distexe () { trap distexe_no_trap INT TERM; distexe_test_usage ${*}; # construct file command with absolute path cd $(dirname ${1}); local FILE_CMD=${PWD}/$(basename ${1}); cd -; # Absolute path of work directory local PATH_EXE; if test ${#} -eq 2; then cd ${2} &> /dev/null; PATH_EXE=${PWD}; cd - &> /dev/null; else PATH_EXE=${PWD}; fi; local FILE_CPT="${PATH_EXE}/distexe-control-"$(basename ${FILE_CMD}); local LOCK_HOST="${PATH_EXE}/distexe-host-lock"; local PREFIX_FILE_HOST="${PATH_EXE}/distexe-host-"; local PREFIX_FILE_PROCESS="${PATH_EXE}/distexe-process-"; local hosts="${DISTEXE_HOSTS}"; header; echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> file : ${FILE_CMD}"; echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> path : ${PATH_EXE}"; echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> sleep : ${SLEEP}"; echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> hosts : ${hosts}"; for line in ${hosts}; do local host=$(echo ${line} | cut -d/ -f1); local -i nb_process=$(echo ${line} | cut -d/ -f2); # lunch service local FILE_HOST=${PREFIX_FILE_HOST}'${HOSTNAME}'; local valid=$(ssh ${host} "if test -f ${FILE_HOST}; then echo \"ko\"; else touch ${FILE_HOST}; echo \"ok\"; fi"); if test ${valid} = "ok"; then echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> station : ${host} (${nb_process} process(es))"; local cmd="export MORPHEO_SCRIPT=${MORPHEO_SCRIPT};. ${MORPHEO_SCRIPT}/distexe_execute_n.sh ${PATH_EXE} ${FILE_CMD} ${FILE_CPT} ${FILE_HOST} ${LOCK_HOST} ${PREFIX_FILE_PROCESS} ${SLEEP} ${nb_process};"; ssh ${host} ${cmd} & else echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> station \"${host}\" is already used"; fi; done; echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> all hosts working"; trap distexe_trap INT TERM; if test ${TRAP} -eq 1; then distexe_trap; fi; # Wait end ok all Task distexe_wait_end; echo " * {"$(${MORPHEO_SCRIPT}/date.sh)"} <${HOSTNAME}> all hosts is done"; } #-----[ Corps ]--------------------------------------------- distexe ${*};