#!/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 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... 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' 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 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 auto_runsmod=1 action=deploy confuser= tmproot= hosts=() force_make_archive= rscripts=() recipes=() runsreset= output= init=auto init_sysinfos= sysinfos_data= runsscriptspath= runsscriptsdirs=() runsmodulespath= runsmodulesdirs=() runshostspath= runshostsdirs=() parse_opts "${PRETTYOPTS[@]}" \ --help '$exit_with display_help' \ --no-auto-runsmod auto_runsmod= \ -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 \ --runsmodulespath: runsmodulespath= \ --runsmodulesdir: runsmodulesdirs \ --runshostspath: runshostspath= \ --runshostsdir: runshostsdirs \ @ args -- "$@" && set -- "${args[@]}" || die "$args" runs_after_parse_args __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")" 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 } 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 " " 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" } ################################################################################ # 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 "$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 etitle "Configuration initiale pour runs" \ "$scriptdir/runs" --no-auto-runsmod \ --runsscriptspath "$RUNSSCRIPTSPATH" \ --runsmodulespath "$RUNSMODULESPATH" \ --runshostspath "$RUNSHOSTSPATH" \ --init -h "$host" restore_initial_paths 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 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 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" if [ "$host" != "localhost" ]; then if [ -n "$init" ]; then action=--init else action=--verify fi estep "Vérification de la configuration de l'hôte pour runs" "$scriptdir/runs" --no-auto-runsmod \ --runsscriptspath "$RUNSSCRIPTSPATH" \ --runsmodulespath "$RUNSMODULESPATH" \ --runshostspath "$RUNSHOSTSPATH" \ $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" ) [ "$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")" [ -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") export __estack __tlevel ${UTOOLS_LANG:+UTOOLS_LANG='$UTOOLS_LANG'; export UTOOLS_LANG }$(qvals "./$archivename" ${tmproot:+--tmproot "$tmproot"})" 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 etitle "Nettoyage de l'export" rm -rf "$exportdir" [ -n "$archive" ] && rm -f "$archive" archive= eend fi restore_initial_paths eend done