429 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			429 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
#!/bin/bash
 | 
						|
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
 | 
						|
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
 | 
						|
urequire DEFAULTS runs 
 | 
						|
 | 
						|
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)."
 | 
						|
}
 | 
						|
 | 
						|
set_defaults runs
 | 
						|
 | 
						|
action=deploy
 | 
						|
confuser=
 | 
						|
tmproot=
 | 
						|
hosts=()
 | 
						|
force_make_archive=
 | 
						|
rscripts=()
 | 
						|
recipes=()
 | 
						|
runsreset=
 | 
						|
output=
 | 
						|
init=auto
 | 
						|
init_sysinfos=
 | 
						|
sysinfos_data=
 | 
						|
runsscriptspath=
 | 
						|
runsmodulespath=
 | 
						|
runshostspath=
 | 
						|
parse_opts "${PRETTYOPTS[@]}" \
 | 
						|
    --help '$exit_with display_help' \
 | 
						|
    -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= \
 | 
						|
    --runsmodulespath: runsmodulespath= \
 | 
						|
    --runshostspath: runshostspath= \
 | 
						|
    @ args -- "$@" && set -- "${args[@]}" || die "$args"
 | 
						|
 | 
						|
[ -n "$runsscriptspath" ] && RUNSSCRIPTSPATH="$runsscriptspath"
 | 
						|
[ -n "$runsmodulespath" ] && RUNSMODULESPATH="$runsmodulespath"
 | 
						|
[ -n "$runshostspath" ] && RUNSHOSTSPATH="$runshostspath"
 | 
						|
 | 
						|
__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
 | 
						|
}
 | 
						|
 | 
						|
################################################################################
 | 
						|
# 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
 | 
						|
        etitle "Configuration initiale pour runs" \
 | 
						|
            "$scriptdir/runs" \
 | 
						|
            ${runsscriptspath:+--runsscriptspath "$runsscriptspath"} \
 | 
						|
            ${runsmodulespath:+--runsmodulespath "$runsmodulespath"} \
 | 
						|
            ${runshostspath:+--runshostspath "$runshostspath"} \
 | 
						|
            --init -h "$host"
 | 
						|
    done
 | 
						|
    exit 0
 | 
						|
fi
 | 
						|
 | 
						|
################################################################################
 | 
						|
# Déploiement
 | 
						|
 | 
						|
SSH="${SSH:-ssh}"
 | 
						|
make_archive=
 | 
						|
 | 
						|
## Hôtes sur lesquels faire le déploiement
 | 
						|
if array_isempty hosts; then
 | 
						|
    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
 | 
						|
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
 | 
						|
 | 
						|
    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" \
 | 
						|
            ${runsscriptspath:+--runsscriptspath "$runsscriptspath"} \
 | 
						|
            ${runsmodulespath:+--runsmodulespath "$runsmodulespath"} \
 | 
						|
            ${runshostspath:+--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=(--export --runsexportdir "$exportdir"
 | 
						|
        ${runsscriptspath:+--runsscriptspath "$runsscriptspath"}
 | 
						|
        ${runsmodulespath:+--runsmodulespath "$runsmodulespath"}
 | 
						|
        ${runshostspath:+--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=$(quoted_arg "$__estack")
 | 
						|
__tlevel=$(quoted_arg "$__tlevel")
 | 
						|
export __estack __tlevel
 | 
						|
${UTOOLS_LANG:+UTOOLS_LANG='$UTOOLS_LANG'; export UTOOLS_LANG
 | 
						|
}$(quoted_args "./$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
 | 
						|
 | 
						|
    eend
 | 
						|
done
 |