#!/bin/sh # # Manage LeoFS servers. # #------------------------------------------------------------------------------- # GLOBALS #------------------------------------------------------------------------------- LEOFS_SERVERS=" gateway manager manager_slave storage " LEOFS_COMMANDS= LEOFS_COMMANDS_MINIHELP= PROGNAME=$(basename $0) PROGPATH=$(realpath $0) #------------------------------------------------------------------------------- # Functions #------------------------------------------------------------------------------- in_list() { local needle=$1 local i shift for i do test "$i" = "$needle" && return 0 done return 1 } #------------------------------------------------------------------------------- usage() { local servers servers=$(echo $LEOFS_SERVERS | sed -e 's/ /|/g') echo "usage: ${PROGNAME} [help | -h | --help]" echo " ${PROGNAME} ${servers} []" echo echo "Commands:" echo "$LEOFS_COMMANDS_MINIHELP" echo echo "See '${PROGNAME} help ' for more information on a specific command." } #------------------------------------------------------------------------------- # cmd utils #------------------------------------------------------------------------------- register_cmd() { local cmd=$1 LEOFS_COMMANDS="${LEOFS_COMMANDS} ${cmd}" LEOFS_COMMANDS_MINIHELP=$(printf "%s\n %-16s%s" \ "${LEOFS_COMMANDS_MINIHELP}" "${cmd}" "$(cmd_${cmd} help_summary)") } #------------------------------------------------------------------------------- check_cmd() { local cmd=$1 if ! in_list "$cmd" $LEOFS_COMMANDS then echo "unknown command: $1" >&2 echo >&2 usage >&2 exit 1 fi } #------------------------------------------------------------------------------- run_cmd() { local cmd=$1 check_cmd "${cmd}" shift "cmd_${cmd}" "$@" } #------------------------------------------------------------------------------- print_cmd_help() { local cmd=$1 local desc check_cmd "${cmd}" echo "NAME" echo " ${cmd} - $(cmd_${cmd} help_summary)" echo echo "SYNOPSIS" echo " $(cmd_${cmd} help_synopsis)" desc="$(cmd_${cmd} help_desc)" if [ -n "${desc}" ] then echo echo "DESCRIPTION" echo -n "${desc}" | sed -e 's/^/ /' fi opts="$(cmd_${cmd} help_opts)" if [ -n "${opts}" ] then echo echo "OPTIONS" echo -n "${opts}" | sed -e 's/^/ /' fi echo } #------------------------------------------------------------------------------- # make ENV #------------------------------------------------------------------------------- check_app() { local app=$1 if ! in_list "${app}" $LEOFS_SERVERS then echo "unknown server: $1" >&2 echo >&2 usage >&2 exit 1 fi } #------------------------------------------------------------------------------- setusercontext() { local cmd : ${LEOFS_USER=%%LEOFS_USER%%} user=`whoami` if [ ${user} = ${LEOFS_USER} ] then return fi if [ ${user} != root ] then echo "Must be run as ${LEOFS_USER} user or root (or set LEOFS_USER)" >&2 exit 1 fi # Restart as LEOFS_USER cmd="${PROGPATH} $@" exec /usr/bin/su -m ${LEOFS_USER} -c "${cmd}" } #------------------------------------------------------------------------------- gen_config() { local args cfg_dir app_config vm_args res snmp_cfg dir cfg_dir=${LEOFS_DBDIR}/etc mkdir -p ${cfg_dir} if [ ${LEOFS_ETCDIR}/${LEOFS_SERVER}.conf -ot ${cfg_dir}/app.config ] then return fi rm -f ${cfg_dir}/app.*.config ${cfg_dir}/vm.*.args args=`PATH=${ERTS_PATH}:${PATH} \ ${LEOFS_BASEDIR}/bin/cuttlefish \ -i ${LEOFS_BASEDIR}/etc/${LEOFS_SERVER}.schema \ -c ${LEOFS_ETCDIR}/${LEOFS_SERVER}.conf \ -d ${cfg_dir}` app_config=`echo ${args} | sed -nEe 's/^.*(app\.[0-9.]*\.config).*$/\1/p'` vm_args=`echo ${args} | sed -nEe 's/^.*(vm\.[0-9.]*\.args).*$/\1/p'` if [ -z "${app_config}" -o -z "${vm_args}" ] then echo "Failed to parse ${LEOFS_SERVER}.conf" >&2 exit 1 fi # Sanity check the app.config file res=`${ERTS_PATH}/escript \ ${ERTS_PATH}/nodetool chkconfig ${cfg_dir}/${app_config}` if [ "${res}" != "ok" ] then echo "Error reading ${app_config}: ${res}" >&2 exit 1 fi mv ${cfg_dir}/$app_config ${cfg_dir}/app.config mv ${cfg_dir}/$vm_args ${cfg_dir}/vm.args } #------------------------------------------------------------------------------- make_env() { local app=${1#leo_} # Remove 'leo_' prefix in server name if present. local user check_app "${app}" LEOFS_SERVER=leo_${app} LEOFS_ETCDIR=%%LEOFS_ETCDIR%% LEOFS_BASEDIR=%%LEOFS_BASEDIR%% LEOFS_DBDIR=%%LEOFS_DBDIR%%/${LEOFS_SERVER} LEOFS_RUNDIR=%%LEOFS_RUNDIR%% LEOFS_PIPE=${LEOFS_RUNDIR}/${LEOFS_SERVER}.pipe LEOFS_LOGDIR=%%LEOFS_LOGDIR%%/${LEOFS_SERVER} START_ERL=`cat ${LEOFS_BASEDIR}/releases/${LEOFS_SERVER%_slave}/start_erl.data` ERTS_VSN=${START_ERL% *} APP_VSN=${START_ERL#* } ERTS_PATH=${LEOFS_BASEDIR}/erts-${ERTS_VSN}/bin : ${ERLEXEC_MODE=embedded} LEOFS_BOOT=${LEOFS_BASEDIR}/releases/${LEOFS_SERVER%_slave}/${APP_VSN}/${LEOFS_SERVER%_slave} HOME=%%LEOFS_DBDIR%% cd ${HOME} setusercontext "$@" gen_config snmp_cfg=`sed -nEe 's/^-config *(.*)$/\1.config/p' ${LEOFS_DBDIR}/etc/vm.args` sed -nEe 's:^.*dir, *"((%%LEOFS_DBDIR%%|%%LEOFS_LOGDIR%%)[^"]*)".*$:\1:p' \ ${LEOFS_DBDIR}/etc/app.config ${snmp_cfg} | sort -u | while read dir do mkdir -p "${dir}" done } #------------------------------------------------------------------------------- # Erlang tools #------------------------------------------------------------------------------- nodetool() { local cmd=$1 ; shift local vmargs_path name_arg cookie_arg vmargs_path=${LEOFS_DBDIR}/etc/vm.args name_arg=`egrep '^-s?name' ${vmargs_path}` if [ -z "${name_arg}" ] then echo "vm.args needs either -name or -sname parameter" >&2 exit 1 fi cookie_arg=`grep '^-setcookie' ${vmargs_path}` if [ -z "${cookie_arg}" ] then echo "vm.args needs a -setcookie parameter" 2>&2 exit 1 fi ${ERTS_PATH}/escript ${ERTS_PATH}/nodetool ${name_arg} ${cookie_arg} ${cmd} } #------------------------------------------------------------------------------- check_node() { local expected_state=$1 local status nodetool ping >/dev/null 2>&1 status=$? if [ "${expected_state}" = DOWN ] then if [ ${status} -eq 0 ] then echo "Node is already running" >&2 exit 1 fi else if [ ${status} -ne 0 ] then echo "Node is not running" >&2 exit 1 fi fi } #------------------------------------------------------------------------------- remsh() { local vmargs_path name_arg cookie_arg local remsh_type remsh_name remsh_name_arg remsh_remsh_arg vmargs_path=${LEOFS_DBDIR}/etc/vm.args name_arg=`egrep '^-s?name' ${vmargs_path}` if [ -z "${name_arg}" ] then echo "vm.args needs either -name or -sname parameter" >&2 exit 1 fi cookie_arg=`grep '^-setcookie' ${vmargs_path}` if [ -z "${cookie_arg}" ] then echo "vm.args needs a -setcookie parameter" 2>&2 exit 1 fi # Extract the name type and name from the name_arg for remsh remsh_type=${name_arg% *} remsh_name=${name_arg#* } # `date +%s` is used to allow multiple remsh to the same node transparently remsh_name_arg="${remsh_type} remsh$(date +%s)@${remsh_name#*@}" remsh_remsh_arg="-remsh ${remsh_name}" ${ERTS_PATH}/erl ${remsh_name_arg} ${remsh_remsh_arg} ${cookie_arg} } #------------------------------------------------------------------------------- # Commands #------------------------------------------------------------------------------- cmd_help() { case "$1" in help_summary) echo "Display help information about ${PROGNAME}" exit ;; help_synopsis) echo "${PROGNAME} help []" exit ;; help_desc) echo "With no command given, the synopsis of the ${PROGNAME} command" echo "and a list of possible ${PROGNAME} commands are printed on the" echo "standard output." echo echo "If a ${PROGNAME} command is named, a mini help for that command is" echo "brought up." exit ;; help_*) exit ;; esac if [ -n "$1" ] then print_cmd_help "$1" else usage fi } register_cmd help #------------------------------------------------------------------------------- cmd_attach() { case "$1" in help_summary) echo "Attach to the running node" exit ;; help_synopsis) echo "${PROGNAME} attach" exit ;; help_*) exit ;; esac check_node UP ${ERTS_PATH}/to_erl ${LEOFS_PIPE} } register_cmd attach #------------------------------------------------------------------------------- cmd_console() { local config_path vmargs_path cmd case "$1" in help_summary) echo "Start the server in console" exit ;; help_synopsis) echo "${PROGNAME} console" exit ;; help_*) exit ;; esac export ROOTDIR=${LEOFS_BASEDIR} export BINDIR=${ERTS_PATH} export EMU=beam export PROGNAME=${PROGNAME} config_path=${LEOFS_DBDIR}/etc/app.config vmargs_path=${LEOFS_DBDIR}/etc/vm.args cmd="${BINDIR}/erlexec -boot ${LEOFS_BOOT} -mode ${ERLEXEC_MODE} \ -config ${config_path} -args_file ${vmargs_path} -- console $@" # Dump environment info for logging purposes echo Exec: ${cmd} echo Root: ${ROOTDIR} ${cmd} } register_cmd console #------------------------------------------------------------------------------- cmd_console_clean() { case "$1" in help_summary) echo "Start the VM in console using start_clean.boot" exit ;; help_synopsis) echo "${PROGNAME} cosole_clean" exit ;; help_*) exit ;; esac LEOFS_BOOT=$(dirname ${LEOFS_BOOT})/start_clean cmd_console } register_cmd console_clean #------------------------------------------------------------------------------- cmd_ping() { case "$1" in help_summary) echo "See if the VM is alive" exit ;; help_synopsis) echo "${PROGNAME} ping" exit ;; help_*) exit ;; esac nodetool ping } register_cmd ping #------------------------------------------------------------------------------- cmd_reboot() { case "$1" in help_summary) echo "Restart the VM completely (uses heart to restart it)" exit ;; help_synopsis) echo "${PROGNAME} reboot" exit ;; help_*) exit ;; esac nodetool reboot } register_cmd reboot #------------------------------------------------------------------------------- cmd_remote_console() { case "$1" in help_summary) echo "Run remote shell command to control node" exit ;; help_synopsis) echo "${PROGNAME} remote_console" exit ;; help_*) exit ;; esac check_node UP remsh "$@" } register_cmd remote_console #------------------------------------------------------------------------------- cmd_restart() { case "$1" in help_summary) echo "Restart the VM without exiting the process" exit ;; help_synopsis) echo "${PROGNAME} restart" exit ;; help_*) exit ;; esac nodetool restart } register_cmd restart #------------------------------------------------------------------------------- cmd_start() { local i res case "$1" in help_summary) echo "Launch the application" exit ;; help_synopsis) echo "${PROGNAME} start" exit ;; help_*) exit ;; esac check_node DOWN export HEART_COMMAND="${PROGPATH} ${LEOFS_SERVER} start" ${ERTS_PATH}/run_erl -daemon ${LEOFS_PIPE} ${LEOFS_LOGDIR} \ "exec ${PROGPATH} ${LEOFS_SERVER} console $@" 2>&1 res=$? if [ "${res}" -ne 0 ] then exit ${res} fi # Wait up to 1 minute for the node to start responding on ping. for i in `jot 60` do nodetool ping >/dev/null 2>&1 || exit 0 sleep 1 done exit 1 } register_cmd start #------------------------------------------------------------------------------- cmd_stop() { local pid res i case "$1" in help_summary) echo "Stop the application" exit ;; help_synopsis) echo "${PROGNAME} stop" exit ;; help_*) exit ;; esac pid=`ps xww -o pid= -o command= | grep "${LEOFS_BASEDIR}.*/[b]eam.*${LEOFS_SERVER}" | awk '{print $1}'` nodetool stop > /dev/null res=$? if [ "${res}" -ne 0 ] then if [ -z "${pid}" ] then echo "${LEOFS_SERVER} is not running" else echo "Failed to stop ${LEOFS_SERVER}" fi exit ${res} fi # Wait up to 1 minute for the process to terminate. for i in `jot 60` do kill -0 ${pid} 2>/dev/null || exit 0 sleep 1 done echo "Failed to terminate the ${LEOFS_SERVER} process (pid ${pid})" exit 1 } register_cmd stop #------------------------------------------------------------------------------- # Main #------------------------------------------------------------------------------- if in_list "$1" "" "help" "-h" "--help" || test -z "$2" then cmd=help ; shift else make_env "$@" app="$1" ; shift cmd="$1" ; shift fi run_cmd "${cmd}" "$@"