#!/bin/bash # ------- # File: lliurex-cap # Description: LliureX-cap Services manager # Authors: Raul Rodrigo Segura , # Luis Garcia Gisbert # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston MA 02110-1301 USA # -------- # # Resources Path # DATAC2PATH="" STATUSC2PATH="" LOCKC2PATH="" CAPIP="" CONFDIR="lliurex-cap/" INSTALLDIR="" # # Templates Path # DATAC2TEMPLATEPATH="" STATUSC2TEMPLATEPATH="" LOCKC2TEMPLATEPATH="" function cap_global_vars() { CAP_VAR_DIR="/var/lib/lliurex-cap/$CAPNAME" CAP_VAR_CFG_DIR="$CAP_VAR_DIR/config" CAP_STATUS_DIR="$CAP_VAR_DIR/status" CAP_HOSTS="${CAP_VAR_CFG_DIR}/hosts.cap" CAP_ETC_DIR="/etc/lliurex-cap/$CAPNAME" RSYNC_SECRET="$CAP_ETC_DIR/rsync.secrets" CSYNC2_SECRET="$CAP_ETC_DIR/csync2.key" CAP_SECRETS="$RSYNC_SECRET $CSYNC2_SECRET /etc/lliurex-cap-secrets" CAP_CFG_FILE="$CAP_ETC_DIR/lliurex-cap.cfg" CAP_STATUS_FILE="$CAP_STATUS_DIR/${CAP_FQDN}.cap" [ ! -r "$CAP_CFG_FILE" ] || . $CAP_CFG_FILE # FQDN and other variables are required for everything ... [ "$CAP_FQDN" ] || die "Empty CAP_FQDN" [ "$CAP_CLONES_ROOT" ] || die "Empty CAP_CLONES_ROOT" rsync_credentials CAP_MASTER_FQDN="$(get_master)" if [ "$CAP_MASTER_FQDN" ] ; then CAP_MASTER_ID="${CAP_MASTER_FQDN#node-}" CAP_MASTER_ID="${CAP_MASTER_ID%%.*}" fi } function rsync_credentials() { if [ -s $RSYNC_SECRET ] ; then CAP_RSYNCUSER="$(head -1 $RSYNC_SECRET | cut -d ":" -f 1 )" CAP_RSYNCPASS="$(head -1 $RSYNC_SECRET | cut -d ":" -f 2 )" fi } function st_self_fix() { [ "$1" ] || exit 1 LINE="$1" shift [ "$1" ] || exit 1 VALUE="$1" sed -i -e "$LINE s/.*/$VALUE/" $CAP_STATUS_FILE } function rsync_credentials_test() { [ "$CAP_RSYNCUSER" ] || return 1 [ "$CAP_RSYNCPASS" ] || return 1 return 0 } function cap_create() { if capname_test ; then echo "This cap already exist" return 1 fi cap_populate || return $? cap_configure || return $? cap_create_secrets || return $? llxcfg-rsyncd update chg_rank 7 if [ -s $CAP_CFG_FILE ]; then . $CAP_CFG_FILE fi st_new_status st_self_fix 5 1 } function chg_rank() { [ "$1" ] || die "new rank not especified" [ -s $CAP_CFG_FILE ] || die "Config file not exist" sed -i -e "/^CAP_RANK=/d;\$aCAP_RANK=$1" $CAP_CFG_FILE } function cap_populate() { mkdir -p "$CAP_VAR_CFG_DIR" rsync --ignore-existing --exclude=etc $RSYNC_OPTIONS /usr/share/lliurex-cap/ $CAP_VAR_DIR/ touch "$CAP_HOSTS" CAP_HOST_ID=$(cap_host_add 254) cap_host_activate $CAP_HOST_ID } function cap_remove_config() { rm -fr "$CAP_VAR_DIR" "$CAP_ETC_DIR" } function next_cap_id() { # # TODO : review of limits MAX_HOST # local NEXT_HOST MAX_HOST NEXT_HOST=$FIRST_AUTO_HOST_ID MAX_HOST=$(sed -ne "/$CAP_HOST_REGEXP/s%$CAP_HOST_REGEXP%\2%p" "$CAP_HOSTS" | sed -e "/254/d" |sort -u |tail -1) if [ "$MAX_HOST" ] ; then rc=0 echo $MAX_HOST | grep -q "^[[:blank:]]*[0-9]\+[[:blank:]]*$" || rc=1 if [ $rc -ne 1 ]; then [ $MAX_HOST -lt $NEXT_HOST ] || NEXT_HOST=$(($MAX_HOST + 1)) fi fi echo $NEXT_HOST } function cap_host_add() { local NEXT_HOST NEXT_HOST="$1" [ "$NEXT_HOST" ] || NEXT_HOST=$(next_cap_id) TMP_FILE="$(tempfile -m 644)" cat "$CAP_HOSTS" > "$TMP_FILE" echo -e "1\t${NEXT_HOST}\tnode-${1}.${CAPNAME}.cap" >> "$TMP_FILE" mv -f "$TMP_FILE" "$CAP_HOSTS" echo $NEXT_HOST } function cap_host_del() { cap_host_change "$1" 9 } function cap_host_change() { # HOST_ID HOST_STATUS sed -i -e "/^[[:digit:]]\+[[:blank:]]\+${1}[[:blank:]]/s%^[[:digit:]]\+%${2}%" "$CAP_HOSTS" } function cap_host_activate() { cap_host_change $1 0 } function cap_host_available() { CAP_LIST="" i=1 while [ $i -lt $CAP_HOST_MAX_ID ] ; do cap_host_search_id "$i" || CAP_LIST="$CAP_LIST $i" i=$(($i + 1)) done #echo "${CAP_COUNT_MSG}${CAP_LIST}" echo "{CAP_LIST}" } function cap_host_list() { sed -ne "/$CAP_HOST_REGEXP_ACTIVE/{s%^0[[:blank:]]\+$CAP_HOST_ID[[:blank:]]\+%$(hostname)@%;s%^0[[:blank:]]\+\([[:digit:]]\+\)[[:blank:]]\+%caphost\1@%;p}" "$CAP_HOSTS" |tr '\n' ' ' } function cap_host_id_test() { [ "$1" ] || return 1 echo "$1" |grep -q "^[[:digit:]]\+$" || return 1 [ $1 -gt 0 ] || return 1 [ $1 -lt $CAP_HOST_MAX_ID -o $1 -eq 254 ] || return 1 return 0 } function cap_host_search_id() { cap_host_id_test "$1" || return 1 grep -q -e "^[[:digit:]]\+[[:blank:]]\+${1}[[:blank:]]" $CAP_HOSTS || return 1 return 0 } function cap_accept() { # some test to accept new members ??? cap_host_add "$1" echo "${CAP_ACCEPT_MSG}${1}" return 0 } function cap_configure() { cap_create_conf ##Check perm . "$CAP_ETC_DIR/lliurex-cap.cfg" # csync2 gen_c2cfg rsync_configure_base rsync_configure_modules return 0 } function cap_reconfigure() { gen_c2cfg rsync_configure_modules llxcfg-rsyncd update llxcfg-cap-scripts reconfigure "$CAPNAME" "$@" || true } function cap_create_conf() { mkdir -p "$CAP_ETC_DIR" TMP_FILE="$(tempfile -m 644)" sed -e "/^[[:blank:]]*CAP_PRINCIPAL=/d" /etc/lliurex-cap/lliurex-cap.cfg > "$TMP_FILE" [ ! -r "$CAP_CFG_FILE" ] || cat "$CAP_CFG_FILE" > "$TMP_FILE" sed -i -e "/^[[:blank:]]*CAP_HOST_ID=/d" "$TMP_FILE" echo "CAP_HOST_ID=$CAP_HOST_ID" >> "$TMP_FILE" # TODO: add more vars mv -f "$TMP_FILE" "$CAP_CFG_FILE" return 0 } function clones_list() { sed -ne "$CLONES_REGEXP" $CAP_VAR_CFG_DIR/clones.cap } function rsync_delete() { BASE_DIR="rsyncd/modules/lliurex-cap/${CAPNAME}" for t in $(llxcfg-config list "$BASE_DIR/data") ; do llxcfg-config delete "$BASE_DIR/data/$t" done llxcfg-config delete "$BASE_DIR/config/${CAPNAME}_config" } function rsync_configure_modules() { ## ## Modules rsync ## # remove old modules BASE_DIR="rsyncd/modules/lliurex-cap/${CAPNAME}/data" for t in $(llxcfg-config list "$BASE_DIR") ; do llxcfg-config delete "$BASE_DIR/$t" done clones_list |while read CAP_CLONE ; do # clones.cap lines are: JUST SHARE_RELATIVE_PATH export CAP_CLONE CAP_RSYNCUSER llxcfg-config read "$CONFDIR/rsyncd/clone.conf" | llxcfg-template - | llxcfg-config write "$BASE_DIR/${CAPNAME}_data_${CAP_CLONE}" done return 0 } function rsync_configure_base() { # config , lock, status ... in "one shot" (full dir /var/lib/lliurex-cap/CAPNAME) llxcfg-config read "$CONFDIR/rsyncd/config.conf" |llxcfg-template - |llxcfg-config write "rsyncd/modules/lliurex-cap/${CAPNAME}/config/${CAPNAME}_config" return 0 } function cap_create_secrets() { echo "##### Generating rsync pass" llxcfg-passgen $CAP_RSYNCUSER gen || return $? echo "##### Generating password file" echo "$CAP_RSYNCUSER:$(cat /etc/lliurex-secrets/passgen/$CAP_RSYNCUSER.secret)" > $RSYNC_SECRET chmod 600 "$RSYNC_SECRET" rsync_credentials llxcfg-passgen $CAP_RSYNCUSER remove || return $? echo "##### Generating csync secret" csync2-key $CSYNC2_SECRET || return $? echo "##### Generating secret file" echo "rsync_secret:$RSYNC_SECRET" > $CAP_VAR_CFG_DIR/secrets.cap echo "csync_secret:$CSYNC2_SECRET" >> $CAP_VAR_CFG_DIR/secrets.cap echo "Done" return 0 } function cap_export_secrets() { for s in $CAP_SECRETS; do [ -r "$s" ] || return 1 done if tar cvzf "$CAP_TARBALL" $CAP_SECRETS ; then echo "${CAP_EXPORT_MSG}${CAP_TARBALL}" else rm -f "$CAP_TARBALL" return 1 fi return 0 } function cap_join() { local rsync_rc # please, I "wannabe" ... # cap_request cap_import_id "$1" || die "Invalid ID from remote host" cap_confirm 2 || die "Unable to confirm host-level 2" cap_import_secrets || die "Unable to import secrets from remote host" cap_confirm 3 || die "Unable to confirm host-level 3" CAP_RSYNCUSER="$(cat $RSYNC_SECRET | cut -d ':' -f1)" [ -n $CAP_RSYNCUSER ] || return 1 # rsync substitutes "populate" [ -d $CAP_VAR_DIR ] || mkdir -p $CAP_VAR_DIR rsync_credentials_test || die "invalid rsync credentials" RSYNC_PASS_FILE=$(tempfile) echo $CAP_RSYNCPASS > $RSYNC_PASS_FILE rsync $RSYNC_OPTIONS --password-file=$RSYNC_PASS_FILE rsync://$CAP_RSYNCUSER@$CAP_REMOTE_HOST/${CAPNAME}_config $CAP_VAR_DIR/ || die "Unable to rsync cap configuratin from remote host" cap_confirm 4 || die "Unable to confirm host-level 4" # activate self locally to generate correct configuration cap_host_activate $CAP_HOST_ID cap_configure llxcfg-rsyncd update # ok, all the configuration is done, so rsync the shares rsync_rc=0 for CAP_CLONE in $(clones_list) ; do if ! rsync $RSYNC_OPTIONS --password-file=$RSYNC_PASS_FILE rsync://$CAP_RSYNCUSER@$CAP_REMOTE_HOST/${CAPNAME}_data_${CAP_CLONE}/${CAP_CLONE} ${CAP_CLONES_ROOT}/ &> /dev/null ; then rsync_rc=1 break fi done rm -f $RSYNC_PASS_FILE if [ $rsync_rc -ne 0 ] ; then cap_host_change $CAP_HOST_ID 4 die "Unable to do initial rsync of cap shares" fi # request activation from remote host if ! cap_confirm 0 ; then cap_host_change $CAP_HOST_ID 0 die "Unable to confirm remote activation of this host" fi # generate status and start challenge sleep 2 st_new_status return 0 } function cap_import_secrets() { local rc rc=1 ## tiene que tener el rsync.secret, csync2.key REMOTE_SECRETS="$(sshpass -f "$PASS_FILE" ssh $SSHOPTIONS "${CAP_REMOTE_USER}@${CAP_REMOTE_HOST}" cap-export-secrets "$CAPNAME" |sed -ne "/^$CAP_EXPORT_MSG/{s%^$CAP_EXPORT_MSG%%;p}")" [ "$REMOTE_SECRETS" ] || return 1 LOCAL_SECRETS="$(tempfile -s ".tgz")" if sshpass -f "$PASS_FILE" scp $SSHOPTIONS "${CAP_REMOTE_USER}@${CAP_REMOTE_HOST}:${REMOTE_SECRETS}" "$LOCAL_SECRETS" ; then if [ -s "$LOCAL_SECRETS" ] ; then # TODO: replace this code and extract only "well-know" secret files if tar -C "/" -xvzf "$LOCAL_SECRETS"; then rc=0 rsync_credentials fi fi fi rm -f "$LOCAL_SECRETS" # try to rm remote file sshpass -f "$PASS_FILE" ssh $SSHOPTIONS "${CAP_REMOTE_USER}@${CAP_REMOTE_HOST}" rm -f "${REMOTE_SECRETS}" || true return $rc } function cap_import_id() { CAP_HOST_ID="$(sshpass -f "$PASS_FILE" ssh $SSHOPTIONS "${CAP_REMOTE_USER}@${CAP_REMOTE_HOST}" cap-request "${CAPNAME}" $1 |sed -ne "/^${CAP_ACCEPT_MSG}/{s%$CAP_ACCEPT_MSG%%;p}")" [ "$CAP_HOST_ID" ] || return 1 return 0 } function cap_confirm() { if sshpass -f "$PASS_FILE" ssh $SSHOPTIONS "${CAP_REMOTE_USER}@${CAP_REMOTE_HOST}" cap-change-level "${CAPNAME}" "$CAP_HOST_ID" "$1" |grep -q "^${CAP_CONFIRM_MSG}$" ; then return 0 fi return 1 } function cap_revokeme() { if sshpass -f "$PASS_FILE" ssh $SSHOPTIONS "${CAP_REMOTE_USER}@${CAP_REMOTE_HOST}" cap-revoke "${CAPNAME}" "$CAP_HOST_ID" |grep -q "^${CAP_REVOKE_MSG}$" ; then return 0 fi return 1 } function gen_c2cfg() { CAP_LISTSERVERS="$(cap_host_list)" TMP_CLONES="$(tempfile)" clones_list |while read CAP_CLONE ; do echo -e " include $CAP_CLONES_ROOT/$CAP_CLONE;" | sed -e "s%/\+%/%g" >> "$TMP_CLONES" done CAP_CLONES_FILE="$TMP_CLONES" export CAP_LISTSERVERS CAP_CLONES_FILE CAP_RSYNCUSER llxcfg-config listfiles "$CONFDIR/csync2" | while read F ; do f="csync2_${CAPNAME}$(basename "$F")" llxcfg-install --cmd=llxcfg-template "$F" "/etc/$f" done rm -f "$TMP_CLONES" # RUN csync2 -C -xr } function add_rsyncresource() { return 0 } function st_status() { if [ -s "$CAP_STATUS_FILE" ] ; then sed -ne "5p" "$CAP_STATUS_FILE" else echo 0 fi } function st_self_ts() { if [ -s "$CAP_STATUS_FILE" ] ; then SELF_TIMESTAMP="$(sed -ne "${1}p" "$CAP_STATUS_FILE")" fi [ "$SELF_TIMESTAMP" ] || SELF_TIMESTAMP=100000000000000 echo $SELF_TIMESTAMP } function is_zero_terminated() { if echo "$1" |grep -q "0$" ; then return 0 fi return 1 } function st_cap_ts() { MAX_TIMESTAMP="$(find "$CAP_STATUS_DIR" -type f -name "*.cap" -exec sed -ne "${1}p" {} \; |sort -u |tail -1)" [ "$MAX_TIMESTAMP" ] || MAX_TIMESTAMP=100000000000000 echo $MAX_TIMESTAMP } function st_update_status() { MUST_UPDATE="" SELF_STATUS=$(st_status) SELF_NEW_TS=$(st_self_ts 1) CAP_NEW_TS=$(st_cap_ts 1) SELF_OLD_TS=$(st_self_ts 2) CAP_OLD_TS=$(st_cap_ts 2) if [ ${SELF_NEW_TS%?} -eq ${CAP_NEW_TS%?} ] && is_zero_terminated "$SELF_NEW_TS" && [ $SELF_STATUS -ne 0 ] ; then MUST_UPDATE="Y" SELF_NEW_TS=$(($SELF_NEW_TS + 1)) elif [ ${SELF_NEW_TS%?} -lt ${CAP_NEW_TS%?} ] ; then MUST_UPDATE="Y" if [ $SELF_STATUS -ne 0 ] ; then SELF_OLD_TS=$SELF_NEW_TS fi SELF_NEW_TS=$CAP_NEW_TS if is_zero_terminated "$CAP_NEW_TS" ; then SELF_NEW_TS=$(($SELF_NEW_TS + 1)) fi fi if [ $SELF_STATUS -ne 0 ] ; then if [ $SELF_OLD_TS -lt $CAP_OLD_TS ] && ! is_zero_terminated "$CAP_OLD_TS" ; then MUST_UPDATE="Y" SELF_STATUS=0 fi fi if [ "$MUST_UPDATE" ] ; then TMP_FILE="$(tempfile -m 644)" RANDOM=$$ cat <"$TMP_FILE" $SELF_NEW_TS $SELF_OLD_TS $CAP_RANK $(($RANDOM%10)) $SELF_STATUS EOF mv -f "$TMP_FILE" "$CAP_STATUS_FILE" csync2_status fi } function st_new_status() { TMP_FILE="$(tempfile -m 644)" RANDOM=$$ cat <"$TMP_FILE" $(date +%Y%m%d%H%M%S)0 $(st_self_ts 1) $CAP_RANK $(($RANDOM%10)) $(st_status) EOF mv -f "$TMP_FILE" "$CAP_STATUS_FILE" csync2_status } function csync2_status() { csync2 -C "${CAPNAME}status" -xr || true } function csync2_config() { csync2 -C "${CAPNAME}config" -xr || true } function csync2_data() { csync2 -C "${CAPNAME}data" -xr || true } function csync2_all() { for F in $(find /etc -maxdepth 1 -mindepth 1 -xtype f -name "csync2_${CAPNAME}*") ; do f="$(basename "$F")" c=${f#csync2_} csync2 -C "$c" -xr || true done } function usage() { while [ "$1" ] ; do echo -e "$1" >&2 shift done die "Usage: $(basename "$0") {create} CAP_NAME [SECRET_USER]\n\ $(basename "$0") export-secrets CAP_TARBALL [CAP_NAME]\n\ $(basename "$0") request CAP_NAME HOST_ID\n\ $(basename "$0") revoke CAP_NAME HOST_ID\n\ $(basename "$0") change-level CAP_NAME HOST_ID NEW_STATUS\n\ $(basename "$0") reconfigure CAP_NAME [FILES ...]\n\ $(basename "$0") update-status CAP_NAME [FILES ...]\n\ $(basename "$0") set-default VAR_NAME=VAR_VALUE ... \n\ $(basename "$0") hosts-available CAP_NAME\n\ $(basename "$0") add-config CAP_NAME FILES ...\n\ $(basename "$0") remote REMOTE_USER@REMOTE_HOST[:PASSWD_FILE] COMMAND ... \n\ $(basename "$0") join CAP_NAME [HOST_ID] REMOTE_USER@REMOTE_HOST[:PASSWD_FILE]\n\ $(basename "$0") list-servers CAP_NAME \n\ $(basename "$0") sync-by-rsync CAP_NAME \n\ $(basename "$0") sync-with-rconflict CAP_NAME MODULE [PATH_TO_SYNC] \n\ $(basename "$0") disconnect CAP_NAME \n\ $(basename "$0") heartbeat CAP_NAME \n\ $(basename "$0") show-status CAP_NAME \n\ $(basename "$0") show-master CAP_NAME \n\ $(basename "$0") show-timestamps CAP_NAME \n\ $(basename "$0") exec CAP_NAME COMMAND_LINE\n\ $(basename "$0") leave CAP_NAME REMOTE_USER@REMOTE_HOST[:PASSWD_FILE]" >&2 } function die() { echo -e "$@" >&2 [ -z "$PASS_FILE" ] || rm -f "$PASS_FILE" [ -z "$RSYNC_PASS_FILE" ] || rm -f "$RSYNC_PASS_FILE" exit 1 } function create_main_conf() { cp "/usr/share/lliurex-cap/lliurex-cap.cfg" "/etc/lliurex-cap/lliurex-cap.cfg" } function capname_test() { [ "$CAPNAME" ] || return 1 [ -d "$CAP_ETC_DIR" ] || return 1 return 0 } function is_user_at_host() { echo "$1" |grep -q "^.\+@.\+$" || return 1 return 0 } function is_host_colon_file() { echo "$1" |grep -q "^.\+:.\+$" || return 1 return 0 } function get_remote_credentials() { is_user_at_host "$1" || usage CAP_REMOTE_USER="${1%@*}" CAP_REMOTE_HOST="${1#*@}" CAP_REMOTE_PASSFILE="" CAP_REMOTE_PASS="" if is_host_colon_file "$CAP_REMOTE_HOST" ; then CAP_REMOTE_PASSFILE="${CAP_REMOTE_HOST#*:}" CAP_REMOTE_HOST="${CAP_REMOTE_HOST%:*}" fi if [ -z "$CAP_REMOTE_PASSFILE" ] ; then read -p "Enter password for user $CAP_REMOTE_USER:" -s CAP_REMOTE_PASS else [ -r "$CAP_REMOTE_PASSFILE" ] || usage CAP_REMOTE_PASS="$(head -1 "$CAP_REMOTE_PASSFILE")" fi [ "$CAP_REMOTE_PASS" ] || die "Invalid Password" PASS_FILE="$(tempfile)" echo "$CAP_REMOTE_PASS" > "$PASS_FILE" } function get_rsync_sync() { rsync_credentials rsync_credentials_test || exit 1 RSYNC_PASS_FILE=$(tempfile -m 600) echo $CAP_RSYNCPASS > $RSYNC_PASS_FILE rsync $RSYNC_OPTIONS --password-file=$RSYNC_PASS_FILE rsync://$CAP_RSYNCUSER@$CAP_REMOTE_HOST/${CAPNAME}_config $CAP_VAR_DIR/ || die "Unable to rsync cap configuratin from remote host" rsync_rc=0 for CAP_CLONE in $(clones_list) ; do if ! rsync $RSYNC_OPTIONS --password-file=$RSYNC_PASS_FILE rsync://$CAP_RSYNCUSER@$CAP_REMOTE_HOST/${CAPNAME}_data_${CAP_CLONE}/${CAP_CLONE} ${CAP_CLONES_ROOT}/ &> /dev/null ; then rsync_rc=1 break fi done rm -f $RSYNC_PASS_FILE } function get_master() { { find $CAP_STATUS_DIR/ -type f -name "*.cap" | while read f; do cat "$f" | tr -d "\n"; echo ":$(basename "$f" ".cap")" ; done ; }|sort -r -t : -k 1 | head -1 | cut -d : -f 2 } function do_heartbeat() { # max time to wait for a challenge response (in secs) CAP_TIME_TOLERANCE=180 # max interval between challenges (in secs) CAP_LIMIT_CHALLENGE=360 STATUS="$(st_self_ts 5)" NOW=$(date +%Y%m%d%H%M%S) NEW_TS=$(st_self_ts 1 ) DIFF=$(( $NOW - ${NEW_TS%?} )) if [ "$STATUS" = "0" ] ; then # # SERVER IS WAITING. # if [ $DIFF -le $CAP_TIME_TOLERANCE ] ; then return 0 fi # # Search Leader to sync # # Join on one line all content of all server file status. By this line get update status for server. Next print this line and name file. Order by line to get server more updated. First server on this list is leader. Get status file leader, that the name is FQDN of LEADER. # if [ -n "$CAP_MASTER_FQDN" -a "$CAP_FQDN" != "$CAP_MASTER_FQDN" ] ; then CAP_REMOTE_HOST="$CAP_MASTER_FQDN" get_rsync_sync fi # # If I'm the leader or aren't, i do nothing and change my status to standar # else i sync with leader and chage my status to standar. # By to two way server is sync ( I'm leader or i'm updated ) # st_self_fix 5 1 else if [ $DIFF -le $CAP_LIMIT_CHALLENGE ] ; then return 0 fi # # Should be launch challenge # RANK=$(st_self_ts 3) LUCK=$(st_self_ts 4) RL="${RANK}${LUCK}" # RL interval with current servers is 30-79 # TTS=$(( 9 - RANK + LUCK )) TTS=$(( (100 - $RL)/10 )) # TTS for masters is between 2-3 and for slaves 6-7 sleep $TTS NOW=$(date +%Y%m%d%H%M%S) NEW_TS=$(st_self_ts 1) DIFF=$(( $NOW - ${NEW_TS%?} )) if [ $DIFF -le $CAP_LIMIT_CHALLENGE ] ; then return 0 fi st_self_fix 1 "${NOW}0" st_self_fix 2 "${NEW_TS}" fi csync2 -C "${CAPNAME}status" -xr || true return 0 } # main # cap-'action' wrapper CMD0="$(basename $0)" if [ "$CMD0" != "lliurex-cap" ] ; then if echo "$CMD0" |grep -q "^cap\-.*$" ; then ARG0="${CMD0#cap-}" exec pe --no-interactive lliurex-cap $ARG0 "$@" fi fi ACTION="$1" # some tests [ "$ACTION" ] || usage shift # global vars CAP_EXPORT_MSG="I am your father:" CAP_ACCEPT_MSG="Join me, and I will complete your training:" CAP_CONFIRM_MSG="You have done well, my apprentice:" CAP_COUNT_MSG="I need a midichlorian count:" CAP_REVOKE_MSG="You cannot hide forever, Luke" CAPNAME="" CAP_SECRETS="" RSYNC_SECRET="" CSYNC2_SECRET="" CAP_ETC_DIR="" CAP_VAR_DIR="" CAP_STATUS_DIR="" CAP_VAR_CFG_DIR="" CAP_HOSTS="" CAP_TARBALL="" CAP_HOST_ID="" CAP_HOST_REGEXP="^\([[:digit:]]\+\)[[:blank:]]\+\([[:digit:]]\+\)[[:blank:]]\+\([^[:blank:]]\+\).*$" CAP_HOST_REGEXP_ACTIVE="^\(0\)[[:blank:]]\+\([^[:blank:]]\+\).*$" CAP_CFG_FILE="/etc/lliurex-cap/$CAP_PRINCIPAL/lliurex-cap.cfg" CAP_FQDN="" CAP_RANK="" CAP_STATUS_FILE="" CAP_RSYNCUSER="C3PO" CAP_RSYNCPASS="" CAP_CLONES_ROOT="/net/" SSHOPTIONS="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" RSYNC_OPTIONS="-axA" FIRST_AUTO_HOST_ID=1 CLONES_REGEXP="s%/%%g;/^[[:blank:]]*$/d;/^[[:blank:]]*#/d;p" RSYNC_PASS_FILE="" CAP_HOST_MAX_ID=220 CAP_REMOTE_USER="" CAP_REMOTE_HOST="" PASS_FILE="" CAP_MASTER_FQDN="" CAP_MASTER_ID="" # read configuration [ -r "/etc/lliurex-cap/lliurex-cap.cfg" ] || create_main_conf . "/etc/lliurex-cap/lliurex-cap.cfg" [ -z "$CAP_PRINCIPAL" ] || CAPNAME="$CAP_PRINCIPAL" # [ ! -r "/etc/lliurex-cap/$CAP_PRINCIPAL/lliurex-cap.cfg" ] || . "/etc/lliurex-cap/$CAP_PRINCIPAL/lliurex-cap.cfg" # export some vars. The following vars will be available in templates and scripts export CAPNAME CAP_CLONES_ROOT CAP_ETC_DIR CAP_VAR_DIR CAP_STATUS_DIR CAP_VAR_CFG_DIR CAP_HOST_ID CAP_CFG_FILE CAP_FQDN CAP_RANK CAP_STATUS_FILE CAP_MASTER_FQDN CAP_MASTER_ID rc=0 case "$ACTION" in create) CAPNAME="$1" [ "$CAPNAME" ] || usage [ ! "${2}" ] || CAP_RSYNCUSER="${2}" cap_global_vars cap_create || rc=$? if [ $rc -ne 0 ]; then die "### ERROR ON CREATE CAP ###" fi llxcfg-cap-scripts --stop-on-error create "$CAPNAME" || rc=$? ;; export-secrets) [ -n "$1" ] || usage CAP_TARBALL="$1" shift [ -n "$1" ] || usage CAPNAME="$1" cap_global_vars capname_test || die "Invalid CAPNAME" cap_export_secrets || die "Error exporting secrets" ;; join) [ "$1" ] || usage CAPNAME="$1" ! capname_test || die "CAPNAME already joined" shift [ "$1" ] || usage CAP_NEWHOST_ID="" if ! is_user_at_host "$1" ; then CAP_NEWHOST_ID="$1" shift fi get_remote_credentials "$1" cap_global_vars cap_join "$CAP_NEWHOST_ID" || rc=$? llxcfg-cap-scripts join "$CAPNAME" || rc=$? csync2_status ;; leave) [ "$1" ] || usage CAPNAME="$1" cap_global_vars capname_test || die "Invalid CAPNAME" shift [ "$1" ] || usage get_remote_credentials "$1" # propagate pending changes csync2_all if cap_revokeme ; then llxcfg-cap-scripts leave "$CAPNAME" || true cap_remove_config else die "Error leaving $CAPNMAE" fi ;; request) [ "$1" ] || usage CAPNAME="$1" shift CAP_NEWHOST_ID="$1" cap_global_vars [ "$CAP_NEWHOST_ID" ] || CAP_NEWHOST_ID=$(next_cap_id) ! cap_host_search_id $CAP_NEWHOST_ID || die "Invalid HOST_ID" capname_test || die "Invalid CAPNAME" cap_accept "$CAP_NEWHOST_ID" || die "$CAP_NEW_HOST NOT accepted in $CAPNAME" llxcfg-cap-scripts request "$CAPNAME" || true ;; revoke) [ "$1" ] || usage CAPNAME="$1" shift [ "$1" ] || usage CAP_NEWHOST_ID="$1" cap_global_vars cap_host_search_id $CAP_NEWHOST_ID || die "Invalid HOST_ID" # remove host cap_host_del "$CAP_NEWHOST_ID" # reconfigure cap_reconfigure # propagate config csync2_config sleep 2 # start a challenge st_new_status csync2_status # send reply to host echo "$CAP_REVOKE_MSG" ;; change-level) [ "$1" ] || usage "You need CAPNAME\n" CAPNAME="$1" shift [ "$1" ] || usage "You need host id (second param on hosts.cap file)\n" CAP_NEWHOST_ID="$1" shift [ "$1" ] || usage "You need host-level\n" CAP_NEW_STATUS="$1" cap_global_vars capname_test || die "Invalid CAPNAME" cap_host_search_id $CAP_NEWHOST_ID || die "Invalid HOST_ID" cap_host_change $CAP_NEWHOST_ID $CAP_NEW_STATUS if [ $CAP_NEW_STATUS -eq 0 ] ; then # reconfigure cap_reconfigure # propagate config csync2_config # status will be propagated by the new member llxcfg-cap-scripts joinedserver "$CAPNAME" "$@" fi echo "$CAP_CONFIRM_MSG" ;; reconfigure) [ "$1" ] || usage "You need CAPNAME\n" CAPNAME="$1" shift cap_global_vars CFG_REQUIRED="" for f in hosts.cap clones.cap ; do if echo "$*" |grep -q "$f" ; then CFG_REQUIRED="Y" break fi done if [ "$CFG_REQUIRED" ] ; then cap_reconfigure else # only run reconfigure scripts llxcfg-cap-scripts reconfigure "$CAPNAME" "$@" || true fi ;; update-status) [ "$1" ] || usage "You need CAPNAME\n" CAPNAME="$1" shift [ -n "$1" ] || CAP_CHALLENGE_LAUNCHER="$1" cap_global_vars st_update_status ;; set-default) while [ "$1" ] ; do echo "$1" |grep -q "^.\+=.\+$" || usage VAR_NAME="${1%%=*}" sed -i -e "/^[[:blank:]]*${VAR_NAME}=/s/.*/${1}/" /etc/lliurex-cap/lliurex-cap.cfg shift done ;; hosts-available) [ "$1" ] || usage "You need CAPNAME\n" CAPNAME="$1" shift cap_global_vars cap_hosts_available ;; remote) [ "$1" ] || usage "You need REMOTE_USER@RMOTE_HOST\n" get_remote_credentials "$1" shift exec sshpass -f "$PASS_FILE" ssh $SSHOPTIONS "${CAP_REMOTE_USER}@${CAP_REMOTE_HOST}" lliurex-cap "$@" ;; add-config) [ "$1" ] || usage "You need CAPNAME\n" CAPNAME="$1" shift [ "$1" ] || usage "You need some files" cap_global_vars while [ "$1" ] ; do [ -f "$1" ] || usage "$1 is not a valid file" cp "$1" "$CAP_VAR_CFG_DIR" shift done csync2_config ;; list-servers) ## Get all servers without me [ "$1" ] || usage "You need CAPNAME\n" CAPNAME="$1" cap_global_vars [ -r "/etc/lliurex-cap/$CAPNAME/lliurex-cap.cfg" ] || die "This cap no have config" . "/etc/lliurex-cap/$CAPNAME/lliurex-cap.cfg" cap_host_list ;; sync-by-rsync) [ "$1" ] || usage "You need CAPNAME\n" CAPNAME="$1" CAP_REMOTE_HOST="$2" cap_global_vars get_rsync_sync ;; sync-with-rconflict) [ "$1" ] || usage "You need CAPNAME\n" CAPNAME="$1" shift [ "$1" ] || usage "You need MODULE\n" MODULE="$1" shift PATH_TO_SYNC="" if [ "$1" != "" ] ; then PATH_TO_SYNC="$1" fi shift SERVERS="" if [ "$1" != "" ] ; then SERVERS="$(cat $1)" SERVERS="-P $SERVERS" fi csync2 $SERVERS -C ${CAPNAME}${MODULE} -xr $PATH_TO_SYNC ;; disconnect) [ "$1" ] || usage "You need CAPNAME\n" CAPNAME="$1" cap_global_vars capname_test || die "CAPNAME not exist" llxcfg-cap-scripts pre-disconnect "$CAPNAME" || rc=$? #TODO #disconnect eval "$(llxcfg-showvars LDAP_SRV_MODE)" if [ $CAP_HOST_ID -eq 254 ]; then NUMHOST="$(sed -e "/$CAP_FQDN/d" "$CAP_VAR_DIR/config/hosts.cap" | wc -l)" [ $NUMHOST -eq 0 ] || die "You are MASTER of LDAP and there are slaves" fi sed -i -e "/$CAP_FQDN/d" "$CAP_VAR_DIR/config/hosts.cap" rm "$CAP_VAR_DIR/status/${CAP_FQDN}" csync2_all for EXTENSION in "config" "data" "lock" "status"; do rm "/etc/csync2_${CAPNAME}${EXTENSION}.cfg" done rm -r "$CAP_ETC_DIR" rm -r "$CAP_VAR_DIR" rsync_delete llxcfg-rsync update llxcfg-cap-scripts post-disconnect "$CAPNAME" || rc=$? ;; heartbeat) [ "$1" ] || usage "You need CAPNAME\n" CAPNAME="$1" cap_global_vars capname_test || die "CAPNAME not exist" do_heartbeat ;; show-status) [ "$1" ] || usage "You need CAPNAME\n" CAPNAME="$1" cap_global_vars capname_test || die "CAPNAME not exist" st_self_ts 5 ;; show-master) [ "$1" ] || usage "You need CAPNAME\n" CAPNAME="$1" cap_global_vars capname_test || die "CAPNAME not exist" get_master ;; show-timestamps) [ "$1" ] || usage "You need CAPNAME\n" CAPNAME="$1" cap_global_vars capname_test || die "CAPNAME not exist" echo " CAP NEW TS: $(st_cap_ts 1)" echo "SELF NEW TS: $(st_self_ts 1)" echo " CAP OLD TS: $(st_cap_ts 2)" echo "SELF OLD TS: $(st_self_ts 2)" ;; exec) [ "$1" ] || usage "You need CAPNAME\n" CAPNAME="$1" shift [ "$1" ] || usage cap_global_vars capname_test || die "CAPNAME not exist" exec "$@" ;; *) usage ;; esac exit $rc