nutools/rruns

460 lines
15 KiB
Plaintext
Raw Permalink Normal View History

2013-08-27 15:14:44 +04:00
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname -- "$0")/lib/ulib/auto" || exit 1
urequire runs runsmod
2013-08-27 15:14:44 +04:00
function display_help() {
uecho "$scriptname: Déploiement distant avec runs
USAGE
$scriptname [-h hosts] [-T tmproot] rscriptname name=value...
$scriptname [-h hosts] [-T tmproot] @recipe name=value...
$scriptname [-h hosts] [-T tmproot] -f rscript name=value...
$scriptname [-h hosts] [-T tmproot] -r recipe name=value...
2013-08-27 15:14:44 +04:00
Lancer ce script sans argument (hors options) est équivalent à le lancer avec
l'argument @default
OPTIONS
-C Ne pas faire le déploiement. Configurer uniquement la connexion par clé
sur les hôtes distants spécifiés pour le user spécifié. Il faut pouvoir
se connecter par mot de passe pour configurer la connexion par clé.
Si l'on veut configurer la connexion par clé pour le user root, mais que
ce n'est pas possible de se connecter par mot de passe avec le user root
sur l'hôte distant, et qu'il existe un user sudoer sur l'hôte distant,
il est possible de faire la configuration avec '--configure root'. La
commande serait alors
$scriptname -h user@host --configure root
-T tmproot
Spécifier le répertoire temporaire sur l'hôte distant, comme par exemple
/var/tmp. Cette option est utile pour les vservers, qui ont par défaut
un /tmp minuscule de 16 Mo.
-S ssh
Spécifier le programme à utiliser pour la connection par ssh.
-h host
-h @hostsfile
Spécifier un ou plusieurs hôtes sur lequels faire le déploiement. Pour
spécifier plusieurs hôtes, il est possible d'utiliser plusieurs fois
l'option -h, ou spécifier en une seule fois plusieurs hôtes en les
séparant par un espace ou le caractère ':', e.g. 'host1 host2' ou
'host1:host2'. Si la spécification contient les caractères { et },
l'expansion est effectuée, e.g
-h 'root@{host1,host2}.univ.run'
2013-08-27 15:14:44 +04:00
Par défaut, la connexion sur l'hôte distant se fait avec l'utilisateur
root. Il est possible de spécifier un autre utilisateur avec la syntaxe
user@host, e.g -h user@host
2013-08-27 15:14:44 +04:00
La forme @hostsfile permet de lire la liste des hôtes depuis le fichier
hostsfile, à raison d'un hôte par ligne.
Si cette option n'est pas spécifiée, et que le répertoire courant est
dans un des répertoires de \$RUNSHOSTSPATH, sélectionner l'hôte
correspondant. Sinon, l'utilisateur doit saisir l'hôte distant de façon
interactive.
-f RSCRIPT
Lancer le script individuel spécifié au lieu de chercher dans les
répertoires \$RUNS{SCRIPTS,HOSTS}PATH
-r RECIPE
Lancer les scripts spécifiés dans le fichier de recettes individuel
spécifié.
-z Forcer la réinstallation des scripts qui se basent sur shouldrun/setdone
-o OUTPUT
Générer l'archive à lancer sur l'hôte distant au lieu de faire le
déploiement. Si plusieurs hôtes sont spécifiés, OUTPUT est considéré
comme un nom de base auquel est ajouté le nom de l'hôte sur lequel
l'archive doit être déployée.
--init
--no-init
Forcer (resp. empêcher) la création des répertoires d'hôte correspondant
aux hôtes spécifiés. Par défaut, la création des répertoires d'hôte est
effectuée uniquement si ce script est lancé sans argument.
--sysinfos
Après un déploiement réussi sur l'hôte distant, inscrire si ce n'est
déjà fait le résultat de la commande usysinfos dans le fichier
sysinfos.conf du répertoire d'hôte.
Cette option est automatiquement activée si ce script est lancé sans
argument (hors options)."
}
runs_before_parse_args
2013-08-27 15:14:44 +04:00
auto_runsmod=1
2013-08-27 15:14:44 +04:00
action=deploy
confuser=
tmproot=
hosts=()
force_make_archive=
rscripts=()
recipes=()
runsreset=
output=
init=auto
init_sysinfos=
sysinfos_data=
runsscriptspath=
runsscriptsdirs=()
2013-08-27 15:14:44 +04:00
runsmodulespath=
runsmodulesdirs=()
2013-08-27 15:14:44 +04:00
runshostspath=
runshostsdirs=()
2013-08-27 15:14:44 +04:00
parse_opts "${PRETTYOPTS[@]}" \
--help '$exit_with display_help' \
--no-auto-runsmod auto_runsmod= \
2013-08-27 15:14:44 +04:00
-C action=configure \
--configure: '$set@ confuser;action=configure' \
-T:,--tmproot: tmproot= \
-S: SSH= \
--force-make-archive force_make_archive=1 \
-h:,-H:,--host: hosts \
-f: rscripts \
-r: recipes \
-z runsreset=1 \
-o: output= \
--init init=1 \
--no-init init= \
--sysinfos init_sysinfos=1 \
--runsscriptspath: runsscriptspath= \
--runsscriptsdir: runsscriptsdirs \
2013-08-27 15:14:44 +04:00
--runsmodulespath: runsmodulespath= \
--runsmodulesdir: runsmodulesdirs \
2013-08-27 15:14:44 +04:00
--runshostspath: runshostspath= \
--runshostsdir: runshostsdirs \
2013-08-27 15:14:44 +04:00
@ args -- "$@" && set -- "${args[@]}" || die "$args"
runs_after_parse_args
2013-08-27 15:14:44 +04:00
__PARSED_HOSTS=()
__PARSED_FILES=()
function parse_hostsfile() {
# Lire chacun des fichiers $* et initialiser __PARSED_HOSTS avec la liste
# des hôtes mentionnés dans les fichiers.
local inputfile basedir inputs input
for inputfile in "$@"; do
inputfile="$(abspath "$inputfile")"
array_contains __PARSED_FILES "$inputfile" && {
ewarn "$(ppath "$inputfile"): inclusion récursive"
continue
}
array_add __PARSED_FILES "$inputfile"
basedir="$(dirname "$inputfile")"
2015-08-20 07:58:17 +04:00
2013-08-27 15:14:44 +04:00
array_from_lines inputs "$(<"$inputfile" filter_conf)" || {
ewarn "$inputfile: fichier ingnoré"
continue
}
for input in "${inputs[@]}"; do
if [ "${input#@}" != "$input" ]; then
# fichier inclus
parse_hostsfile "$(abspath "${input#@}" "$basedir")"
else
array_addu __PARSED_HOSTS "$input"
fi
done
done
}
function __expand_braces() {
if [[ "$1" == *{* ]] && [[ "$1" == *}* ]]; then
eval "echo $1"
else
echo "$1"
fi
}
2013-08-27 15:14:44 +04:00
function __dot_is_localhost() { [ "$1" == "." ] && echo "localhost" || echo "$1"; }
function fix_hosts() {
# Si hosts contient des éléments multiple, comme a:b, séparer ces
# éléments. i.e (a b:c) --> (a b c)
# Supporter la syntaxe @hostsfile qui permet de charger la liste des hôtes
# depuis un fichier.
# Remplacer aussi les '.' par 'localhost'
array_map hosts __expand_braces
array_fix_paths hosts ":"
array_fix_paths hosts " "
2013-08-27 15:14:44 +04:00
local -a _hosts _tmphosts host
for host in "${hosts[@]}"; do
host="${host%/}"
if [ "${host#@}" != "$host" ]; then
__PARSED_HOSTS=()
parse_hostsfile "${host#@}"
array_fix_paths __PARSED_HOSTS
array_extendu _hosts __PARSED_HOSTS
else
array_addu _hosts "$host"
fi
done
array_copy hosts _hosts
array_map hosts __dot_is_localhost
}
function save_initial_paths() {
RUNSSCRIPTSPATH_BACKUP="$RUNSSCRIPTSPATH"
RUNSMODULESPATH_BACKUP="$RUNSMODULESPATH"
RUNSHOSTSPATH_BACKUP="$RUNSHOSTSPATH"
}
function restore_initial_paths() {
RUNSSCRIPTSPATH="$RUNSSCRIPTSPATH_BACKUP"
RUNSMODULESPATH="$RUNSMODULESPATH_BACKUP"
RUNSHOSTSPATH="$RUNSHOSTSPATH_BACKUP"
}
2013-08-27 15:14:44 +04:00
################################################################################
# Configuration de l'accès par clé aux hôtes
if [ "$action" == "configure" ]; then
[ -n "${hosts[*]}" ] || hosts=("$@")
[ -n "${hosts[*]}" ] || die "Vous devez spécifier la liste des hôtes à configurer"
fix_hosts
runs_initdomains
for userhost in "${hosts[@]}"; do
splituserhost "$userhost" user host
[ -n "$user" ] || user=root
if [ "$host" != "localhost" ]; then
splithost "$host" hostname domain
if [ -z "$domain" ]; then
host="$(runs_find_host "$host")"
splithost "$host" hostname domain
fi
[ -n "$domain" ] || host="$(runs_add_domain "$host")"
fi
2015-08-20 07:58:17 +04:00
2013-08-27 15:14:44 +04:00
"$scriptdir/ruinst" -C ${confuser:+--configure "$confuser"} \
${tmproot:+--tmproot "$tmproot"} ${SSH:+-S "$SSH"} \
${force_make_archive:+--force-make-archive} \
-h "$user@$host" || continue
save_initial_paths
[ -n "$auto_runsmod" ] && runsmod_autoconf
2013-08-27 15:14:44 +04:00
etitle "Configuration initiale pour runs" \
"$scriptdir/runs" --no-auto-runsmod \
--runsscriptspath "$RUNSSCRIPTSPATH" \
--runsmodulespath "$RUNSMODULESPATH" \
--runshostspath "$RUNSHOSTSPATH" \
--init -h "$host"
restore_initial_paths
2013-08-27 15:14:44 +04:00
done
exit 0
fi
################################################################################
# Déploiement
SSH="${SSH:-ssh}"
make_archive=
## Hôtes sur lesquels faire le déploiement
if array_isempty hosts; then
save_initial_paths
[ -n "$auto_runsmod" ] && runsmod_autoconf all
2013-08-27 15:14:44 +04:00
array_split hostsdirs "$RUNSHOSTSPATH" :
cwd="$(pwd)"
for hostsdir in "${hostsdirs[@]}"; do
hostsdir="$(abspath "$hostsdir")"
if [ "${cwd#$hostsdir/}" != "$cwd" ]; then
host="${cwd#$hostsdir/}"
host="${host%%/*}"
enote "Sélection automatique de l'hôte $host"
hosts=("$host")
break
fi
done
restore_initial_paths
2013-08-27 15:14:44 +04:00
fi
if array_isempty hosts; then
read_value "Entrez une liste d'hôtes séparés par ':'" hosts "localhost"
fi
fix_hosts
array_isempty hosts && die "Vous devez spécifier l'hôte distant avec l'option -h"
## Déploiement
# si on ne précise pas ce qu'il faut déployer, il faudra utiliser une valeur par
# défaut: @default pour root, @userdefault pour les autres utilisateurs
runs_defaults=
if [ -z "$*" -a -z "${rscripts[*]}" -a -z "${recipes[*]}" ]; then
runs_defaults=1
[ "$init" == auto ] && init=1
init_sysinfos=1
fi
[ "$init" == auto ] && init=
ac_set_tmpdir exportdir
exportdir="$exportdir/runsexport"
runs_initdomains
for userhost in "${hosts[@]}"; do
continue=
etitle "$userhost"
# à cause des 'continue' ci-dessous, il est possible que le nettoyage ne
# soit pas encore fait. Le faire ici
if [ -d "$exportdir" -o -n "$archive" ]; then
etitle "Nettoyage de l'export"
rm -rf "$exportdir"
[ -n "$archive" ] && rm -f "$archive"
archive=
eend
fi
splituserhost "$userhost" user host
prevhost="$host"
if [ "$host" == "localhost" ]; then
[ -n "$user" ] || user="$USER"
else
[ -n "$user" ] || user=root
splithost "$host" hostname domain
if [ -z "$domain" ]; then
host="$(runs_find_host "$host")"
splithost "$host" hostname domain
fi
[ -n "$domain" ] || host="$(runs_add_domain "$host")"
fi
if [ "$host" != "$prevhost" ]; then
enote "Le nom d'hôte utilisé est $host"
fi
save_initial_paths
[ -n "$auto_runsmod" ] && runsmod_autoconf one "$host"
2013-08-27 15:14:44 +04:00
if [ "$host" != "localhost" ]; then
if [ -n "$init" ]; then
action=--init
else
action=--verify
fi
2015-08-20 07:58:17 +04:00
2013-08-27 15:14:44 +04:00
estep "Vérification de la configuration de l'hôte pour runs"
"$scriptdir/runs" --no-auto-runsmod \
--runsscriptspath "$RUNSSCRIPTSPATH" \
--runsmodulespath "$RUNSMODULESPATH" \
--runshostspath "$RUNSHOSTSPATH" \
2013-08-27 15:14:44 +04:00
$action -h "$host" || { eend; continue; }
fi
if [ -n "$runs_defaults" ]; then
# Si on n'a pas précisé ce qu'il faut déployer, calculer une valeur par
# défaut
if [ "$user" == "root" ]; then
set -- @default
else
set -- @userdefault
fi
fi
# Création de l'export
etitle "Préparation de l'export"
args=(
--no-auto-runsmod
--export --runsexportdir "$exportdir"
--runsscriptspath "$RUNSSCRIPTSPATH"
--runsmodulespath "$RUNSMODULESPATH"
--runshostspath "$RUNSHOSTSPATH"
2013-08-27 15:14:44 +04:00
)
[ "$host" != "localhost" ] && args=("${args[@]}" -h "$host")
[ -n "$runsreset" ] && args=("${args[@]}" -z)
for rscript in "${rscripts[@]}"; do
args=("${args[@]}" -f "$rscript")
done
for recipe in "${recipes[@]}"; do
args=("${args[@]}" -r "$recipe")
done
"$scriptdir/runs" "${args[@]}" -- "$@" || { eend; eend; continue; }
eend
# Lancement des scripts locaux
etitle "Lancement des scripts locaux"
(cd "$exportdir"; ./runs-local) || { eend; eend; continue; }
eend
# Création de l'archive
make_archive=
if [ -n "$force_make_archive" ]; then
make_archive=1
elif [ "$host" != "localhost" ]; then
make_archive=1
elif [ -n "$output" ]; then
make_archive=1
fi
archive=
archivename=
if [ -n "$make_archive" ]; then
if [ -n "$output" ]; then
if [ "${#hosts[*]}" -gt 1 ]; then
archive="$(dirname "$output")"
archivename="$(basename "$output")"
splitname "$archivename" basename ext
archive="$archive/$basename-$host${ext:+.$ext}"
else
archive="$output"
fi
else
ac_set_tmpfile archive
fi
archivename="$(basename "$archive")"
2015-08-20 07:58:17 +04:00
2013-08-27 15:14:44 +04:00
[ -z "$output" ] && tmp_archive=1 || tmp_archive=
etitle "Création de l'archive pour le déploiement" \
"$scriptdir/mkusfx" --bare ${tmp_archive:+--tmp-archive} -o "$archive" \
"$exportdir" ./runs-remote || continue
fi
if [ -z "$output" ]; then
# Déploiement
if [ "$host" == "localhost" ]; then
etitle "Déploiement sur l'hôte local"
if [ -n "$force_make_archive" ]; then
"$archive" ${tmproot:+--tmproot "$tmproot"}
else
(cd "$exportdir"; ./runs-remote)
fi
eend
# Réinitialiser sysinfos_data, la valeur de l'hôte courant sera
# utilisé par runs
sysinfos_data=
else
etitle "Déploiement sur $user@$host"
estep "Copie de l'archive"
if scp -S "$SSH" "$archive" "$user@$host:"; then
estep "Lancement du script de déploiement"
"$SSH" -qt "$user@$host" "\
__estack=$(qval "$__estack")
__tlevel=$(qval "$__tlevel")
2013-08-27 15:14:44 +04:00
export __estack __tlevel
${UTOOLS_LANG:+UTOOLS_LANG='$UTOOLS_LANG'; export UTOOLS_LANG
}$(qvals "./$archivename" ${tmproot:+--tmproot "$tmproot"})"
2013-08-27 15:14:44 +04:00
if [ -n "$init_sysinfos" ]; then
estep "Calcul des informations de l'hôte distant"
sysinfos_script='
source /etc/ulib 2>/dev/null && urequire DEFAULTS || exit 1
echo "\
sysname=(${MYSYSNAME[*]})
sysdist=(${MYSYSDIST[*]})
sysver=(${MYSYSVER[*]})
bits=$MYBITS"'
sysinfos_data="$("$SSH" -qt "$user@$host" "$sysinfos_script" | _nl2lf)"
fi
fi
eend
fi
if [ -n "$init_sysinfos" -a -n "$sysinfos_data" ]; then
args=(-y --init --sysinfos "$sysinfos_data")
[ "$host" != "localhost" ] && args=("${args[@]}" -h "$host")
etitle "Configuration de sysinfos.conf" \
"$scriptdir/runs" "${args[@]}"
fi
2015-08-20 07:58:17 +04:00
2013-08-27 15:14:44 +04:00
etitle "Nettoyage de l'export"
rm -rf "$exportdir"
[ -n "$archive" ] && rm -f "$archive"
archive=
eend
fi
restore_initial_paths
2013-08-27 15:14:44 +04:00
eend
done