1
0
Fork 0

Commit initial: récupération et tri rapide

This commit is contained in:
Chl 2019-07-23 22:28:09 +02:00
commit a52829f96c
104 changed files with 11892 additions and 0 deletions

View file

@ -0,0 +1,123 @@
# Ici se trouvent réunies les fonctions et la configuration utiles au script
# de gestion des mailing-lists
# Config
LDAP_HOST="ldap.example.net"
LDAP_ROOT="dc=example,dc=net"
CONFIG_IDENTIFIANTS_SMBLDAP="/etc/smbldap-tools/smbldap_bind.conf"
. $CONFIG_IDENTIFIANTS_SMBLDAP
LDAP_PASSWD="$masterPw"
LDAP_BIND_DN="$masterDN"
# Liste, sur stdout, les mailing-lists dont fait déjà parti l'adresse
# email fournie en argument.
# args: email
#
# note : effectué en connexion anonyme. A priori, les droits sont suffisants.
afficher_abonnements_en_cours() {
ldapsearch -x -h "$LDAP_HOST" -b "ou=mailing-lists,$LDAP_ROOT" -LLL "(rfc822MailMember=$1)" cn | sed -n 's/^cn: \(.*\)/\1/p' | sort
}
# Affiche la liste des mailing-lists
#
# note : effectué en connexion anonyme. A priori, les droits sont suffisants.
afficher_listing_mailinglists() {
ldapsearch -x -h "$LDAP_HOST" -b "ou=mailing-lists,$LDAP_ROOT" -LLL cn | sed -n 's/^cn: \(.*\)/\1/p' | sort
}
# Affiche la liste des abonnés d'une mailing-list
# args: mailing-list
#
# note : effectué en connexion anonyme. A priori, les droits sont suffisants.
afficher_abonnes_mailinglist() {
ldapsearch -x -h "$LDAP_HOST" -b "cn=$1,ou=mailing-lists,$LDAP_ROOT" -LLL rfc822MailMember | sed -n 's/^rfc822MailMember: //p'
}
# Supprime l'email d'une mailing-list
# args: email, mailing-list
supprimer_abonnement() {
# Ecriture du mot de passe
LDAP_PASSWDFILE=$( mktemp )
printf "%s" "$LDAP_PASSWD" >$LDAP_PASSWDFILE
# Requete
cat <<EOF | ldapmodify -x -h $LDAP_HOST -D "$LDAP_BIND_DN" -y "$LDAP_PASSWDFILE"
dn: cn=$2,ou=mailing-lists,$LDAP_ROOT
changetype: modify
delete: rfc822MailMember
rfc822MailMember: $1
EOF
RETOUR="$?"
# Effacement du mot de passe
rm -f "$LDAP_PASSWDFILE"
return $RETOUR
}
# Ajoute l'email à une mailing-list
# args: email, mailing-list
creer_abonnement() {
# Ecriture du mot de passe
LDAP_PASSWDFILE=$( mktemp )
printf "%s" "$LDAP_PASSWD" >$LDAP_PASSWDFILE
# Requête
cat <<EOF | ldapmodify -x -h $LDAP_HOST -D "$LDAP_BIND_DN" -y "$LDAP_PASSWDFILE"
dn: cn=$2,ou=mailing-lists,$LDAP_ROOT
changetype: modify
add: rfc822MailMember
rfc822MailMember: $1
EOF
RETOUR="$?"
# Effacement du mot de passe
rm -f "$LDAP_PASSWDFILE"
return $RETOUR
}
# Détruit une mailing-list
# args: mailing-list
supprimer_mailinglist() {
# Ecriture du mot de passe
LDAP_PASSWDFILE=$( mktemp )
printf "%s" "$LDAP_PASSWD" >$LDAP_PASSWDFILE
# Requete
cat <<EOF | ldapmodify -x -h $LDAP_HOST -D "$LDAP_BIND_DN" -y "$LDAP_PASSWDFILE"
dn: cn=$1,ou=mailing-lists,$LDAP_ROOT
changetype: delete
EOF
RETOUR="$?"
# Effacement du mot de passe
rm -f "$LDAP_PASSWDFILE"
return $RETOUR
}
# Crée une mailing-list
# args: mailing-list
creer_mailinglist() {
# Ecriture du mot de passe
LDAP_PASSWDFILE=$( mktemp )
printf "%s" "$LDAP_PASSWD" >$LDAP_PASSWDFILE
# Requete
cat <<EOF | ldapmodify -x -h $LDAP_HOST -D "$LDAP_BIND_DN" -y "$LDAP_PASSWDFILE"
dn: cn=$1,ou=mailing-lists,$LDAP_ROOT
changetype: add
cn: $1
objectClass: nisMailAlias
objectClass: top
EOF
RETOUR="$?"
# Effacement du mot de passe
rm -f "$LDAP_PASSWDFILE"
return $RETOUR
}

114
oldies/sauvegarde_pfsense.sh Executable file
View file

@ -0,0 +1,114 @@
#!/bin/sh
# Ce script sert à la sauvegarde de notre firewall pfSense
# Très inspiré du script http://www.scribd.com/doc/92371725/pfsense
# Configuration
BASE_DIR="$HOME/backups"
PREFIXE="sauv_pfsense_"
SUFFIXE=$( date +%Y%m%d-%H%M )
DUREE_DE_VIE=0
USERNAME="backuppc"
PASSWORD="toto123"
COOKIES_FILE=$( mktemp )
CSRF_MAGIC=""
# Arrêt à la première erreur non-catchée
set -e
# Fonctions
# affiche le message d'aide
usage() {
cat <<EOF
$0 [-b base_dir] [-p prefixe] [-s suffixe] [-d duree_de_vie] hostname/IP
$0 -h
-b base_dir : répertoire de sauvegarde (defaut: $BASE_DIR),
-p prefixe : prefixe des fichiers de sauvegarde (defaut: $PREFIXE),
-s suffixe : suffixe des fichiers de sauvegarde (default basé sur la date, ex: $SUFFIXE),
-d duree_de_vie : age en nb de jours a partir duquel les anciennes archives sont supprimees. Si 0, pas de suppression. (defaut: $DUREE_DE_VIE),
hostname/IP : nom d'hôte ou IP du firewall pfsense à sauvegarder.
EOF
}
clean_up_and_exit() {
test -f "$COOKIES_FILE" && rm -f "$COOKIES_FILE"
exit $1
}
# Début du code
# gestion des options de lancement
while getopts b:p:s:d:h o; do
case $o in
'b')
BASE_DIR="$OPTARG"
;;
'p')
PREFIXE="$OPTARG"
;;
's')
SUFFIXE="$OPTARG"
;;
'd')
DUREE_DE_VIE="$OPTARG"
;;
'h')
usage
exit 0
;;
\?)
usage >&2
exit 1
;;
esac
done
shift $( expr $OPTIND - 1 )
HOSTNAME_PFSENSE="$1"
BASE_NAME=$( printf "%s" $( basename "$REPDRUPAL" ) | tr -c "a-zA-Z" "-" )
if [ -z "$HOSTNAME_PFSENSE" ]; then
echo "ERREUR: veuillez indiquer un hostname (ou une IP)." >&2
clean_up_and_exit 1
fi
if [ ! -d "$BASE_DIR" ]; then
echo "ERREUR: repertoire de destination '$BASE_DIR' inexistant." >&2
clean_up_and_exit 1
fi
# Récupération du CSRF
CSRF_MAGIC="$( wget -q -O - --keep-session-cookies --save-cookies "$COOKIES_FILE" https://$HOSTNAME_PFSENSE/index.php | sed -n "s/.*<input type='hidden' name='__csrf_magic' value=\"\([^\"]\+\)\".*/\1/p" || echo "" )"
if [ -z "$CSRF_MAGIC" ]; then
echo "ERREUR: erreur lors de la récupération du __csrf_magic." >&2
clean_up_and_exit 1
fi
# Authentification
if ! wget -q -O /dev/null --keep-session-cookies --save-cookies "$COOKIES_FILE" --load-cookies "$COOKIES_FILE" --post-data "__csrf_magic=$CSRF_MAGIC&usernamefld=$USERNAME&passwordfld=$PASSWORD&login=Login" https://$HOSTNAME_PFSENSE/index.php; then
echo "ERREUR: echec a la premiere connexion." >&2
clean_up_and_exit 1
fi
# On vérifie que l'on est bien connecté
if [ $( wget -q -O - --keep-session-cookies --save-cookies "$COOKIES_FILE" --load-cookies "$COOKIES_FILE" https://$HOSTNAME_PFSENSE/diag_backup.php | grep -c "Diagnostics: Backup/restore" ) -lt 1 ]; then
echo "ERREUR: impossible d'accéder à la page de backup." >&2
clean_up_and_exit 1
fi
#Config. seule
wget -q -O "$BASE_DIR/$PREFIXE$BASE_NAME$HOSTNAME_PFSENSE-conf_$SUFFIXE.xml" --keep-session-cookies --save-cookies "$COOKIES_FILE" --load-cookies "$COOKIES_FILE" --post-data "Submit=Download%20configuration&donotbackuprrd=on" https://$HOSTNAME_PFSENSE/diag_backup.php
#Config + données RRD
wget -q -O "$BASE_DIR/$PREFIXE$BASE_NAME$HOSTNAME_PFSENSE-confrrd_$SUFFIXE.xml" --keep-session-cookies --save-cookies "$COOKIES_FILE" --load-cookies "$COOKIES_FILE" --post-data "Submit=Download%20configuration" https://$HOSTNAME_PFSENSE/diag_backup.php
# Suppression des vieux backups
if [ "$DUREE_DE_VIE" -ne "0" ]; then
find $BASE_DIR -name "$PREFIXE$BASE_NAME$HOSTNAME_PFSENSE-conf_*.xml" -mtime "+$DUREE_DE_VIE" -print0 | xargs -n 200 -r -0 rm -f
find $BASE_DIR -name "$PREFIXE$BASE_NAME$HOSTNAME_PFSENSE-confrrd_*.xml" -mtime "+$DUREE_DE_VIE" -print0 | xargs -n 200 -r -0 rm -f
fi
clean_up_and_exit 0

25
oldies/sauvegarde_samba.sh Executable file
View file

@ -0,0 +1,25 @@
#!/bin/sh
# Configuration
# Le fichier sera nommé BASE_DIR/PREFIXEbasenameSUFFIXE
BASE_DIR=/var/backups/bdd-samba
PREFIXE=sauv_samba_
SUFFIXE=_$( date +%Y%m%d-%H%M ).tar.gz
DUREE_DE_VIE=100
# Vérifications initiales
if [ ! -d "$BASE_DIR" ]; then
echo "ERREUR : répertoire de sauvegarde inexistant : $BASE_DIR ." >&2
exit 1
fi
# Sauvegarde
cd /var/lib
tar -czf "$BASE_DIR/$PREFIXE"example.net"$SUFFIXE" samba
# Suppression des anciennes sauvegardes
if [ "$1" = "--delete-olds" ]; then
find $BASE_DIR -name "$PREFIXE*" -mtime +$DUREE_DE_VIE -print0 | xargs -n 200 -r -0 rm -f
fi

View file

@ -0,0 +1,242 @@
#!/bin/sh
# Ce script aide à la création d'un compte LDAP
# Il s'occupe :
# - création LDAP/SMB
# - mot de passe
# - création de la boîte Cyrus
# - ajout aux listes d'emails (salaries@example.net, ...)
# Ce script nécessite :
# - des identifiants admin sur les listes emails (utilisation des identifiants smbldap pour l'instant. Pas glop.)
# - des identifiants admin sur Cyrus. Ces identifiants seront contenus dans le fichier $CONFIG_IDENTIFIANTS_CYRUS, au format :
# user=LOGIN
# password=PASS
# Config
LDAP_HOST="ldap.example.net"
LDAP_ROOT="dc=example,dc=net"
CYRUS_HOST="messagerie.example.net"
CONFIG_EMAIL_DOMAIN="example.net"
CONFIG_DEFAULT_GID=513
CONFIG_DEFAULT_QUOTA=1048576
CONFIG_IDENTIFIANTS_SMBLDAP="/etc/smbldap-tools/smbldap_bind.conf"
CONFIG_IDENTIFIANTS_CYRUS="/root/.cyrus_auth"
# Petite mise en bouche
cat <<EOF
Script de création d'un compte utilisateur dans l'annuaire LDAP et
dans Cyrus (IMAP/POP3), puis d'inscription aux mailing-lists.
Dernière modification : 2010-09-30 (chl)
(interruption possible via Ctrl-C)
EOF
# Fonctions (désolé pour la lisibilité, 'faut les définir au-dessus des appels)
check_string() {
# Vérifie que la chaîne ne contient pas "trop" de caractères
# spéciaux (accepte accents, espaces et tirets)
# TODO
test "$1" != ""
}
check_email() {
# Vérifie la syntaxe et l'unicité de l'adresse e-mail
# Recherche dans LDAP d'une adresse identique
RACINE_EMAIL=$( echo "$1" | sed 's/@.*//g' )
if [ $( ldapsearch -x -h "$LDAP_HOST" -b "ou=users,$LDAP_ROOT" -LLL "mailLocalAddress=$RACINE_EMAIL" | grep -c "^dn: " ) -gt 0 ]; then
# Listing du(es) login(s) utilisant cette adresse
TMP=$( ldapsearch -x -h "$LDAP_HOST" -b "ou=users,$LDAP_ROOT" -LLL "mailLocalAddress=$RACINE_EMAIL" uid | sed -n 's/^uid: //p' )
echo "ERREUR: adresse email déjà utilisée par :" $TMP "." >&2
return 1
fi
#TODO : vérif syntaxe
test "$1" != ""
}
check_login() {
# n'accepte que les minuscules et les chiffres (doit commencer par une lettre)
# locale "C" pour que [a-z] ne contienne pas à, é, ...
OLDLANG="$LANG"
LANG="C"
RETURN=0
# Passage à la regexp de la mort
if [ $( printf "%s" "$1" | egrep -c "^[a-z][a-z0-9]{2,7}$" ) -ne 1 ]; then
RETURN=1
# Si le login est vide, on reste clément et rien n'est affiché
test -n "$1" && echo "ERREUR: login non valide (regexp: ^[a-z][a-z0-9]{2,7}$ )" >&2
fi
LANG="$OLDLANG"
test "$RETURN" -eq 0 || return 1
# Vérification que le login (=uid LDAP) n'est pas déjà utilisé
if [ $( ldapsearch -x -h "$LDAP_HOST" -b "ou=users,$LDAP_ROOT" -LLL "uid=$1" | grep -c "^dn: " ) -gt 0 ]; then
echo "ERREUR: login déjà existant." >&2
return 1
fi
}
check_unix_homedir() {
# Rien à faire (?)
test "$1" != "" # renvoie false si $1 vide
}
generate_default_email() {
# Initiale du prénom + nom
RETURN=$( printf "%.01s%s@%s" "$1" "$2" "$CONFIG_EMAIL_DOMAIN" | tr "A-Z" "a-z" )
# Suppression des caractères spéciaux et sortie de la fonction
LANG=C echo "$RETURN" | tr -dc "a-z0-9-.@"
}
generate_default_login() {
# on se base lâchement sur generate_default_email :)
# (TODO : bugs divers si prénom + nom < 3 caractères...)
printf "%.03s" $( generate_default_email "$1" "$2" )
}
generate_default_unix_homedir() {
# On renvoie simplement /home/LOGIN
echo "/home/$1"
}
# Enfin : le code...
# 1ère étape : on demande interactivement toutes les infos
# et on crée le compte + mot de passe
#Prénom
while ! check_string "$PRENOM"; do
printf "Prénom : "; read PRENOM
done
#Nom
while ! check_string "$NOM"; do
printf "Nom : "; read NOM
done
#login
DEFAULT_LOGIN=$( generate_default_login "$PRENOM" "$NOM" )
while ! check_login "$LOGIN"; do
printf "Login [%s] : " "$DEFAULT_LOGIN"; read LOGIN
test "$LOGIN" = "" && LOGIN="$DEFAULT_LOGIN"
done
#email
DEFAULT_EMAIL=$( generate_default_email "$PRENOM" "$NOM" )
while ! check_email "$EMAIL"; do
# Prompt avec proposition
printf "Email [%s] : " "$DEFAULT_EMAIL"; read EMAIL
# Si l'utilisateur laisse le champ vide : utilisation de la proposition
test "$EMAIL" = "" && EMAIL="$DEFAULT_EMAIL"
done
#Home directory (UNIX)
DEFAULT_UNIX_HOMEDIR=$( generate_default_unix_homedir "$LOGIN" )
while ! check_unix_homedir "$UNIX_HOMEDIR"; do
printf "Homedir [%s] : " "$DEFAULT_UNIX_HOMEDIR"; read UNIX_HOMEDIR
test "$UNIX_HOMEDIR" = "" && UNIX_HOMEDIR="$DEFAULT_UNIX_HOMEDIR"
done
# Création du compte avec smbldap-useradd + demande du mot de passe
echo "Création du compte dans LDAP... (éviter le Ctrl-C à partir de maintenant)"
if ! smbldap-useradd -a -g $CONFIG_DEFAULT_GID -M "$EMAIL" -N "$PRENOM" -S "$NOM" -d "$UNIX_HOMEDIR" "$LOGIN"; then
echo "ERREUR: smbldap-useradd a renvoyé une erreur. Arrêt du script $0." >&2
return 1
fi
echo "Compte LDAP créé."
echo
# mot de passe
echo "Veuillez renseigner le mot de passe..."
TMP=""
while test "$TMP" = "" && ! smbldap-passwd "$LOGIN"; do
echo "WARNING: problème sur le mot de passe." >&2
printf "Voulez-vous réessayer ? (O/n)"
read TMP
if [ $( echo "$TMP" | egrep -ic "^o$" ) -eq 1 ]; then
TMP=""
fi
done
echo
# 2ème étape : création de la boîte mail dans Cyrus
#
# Tout se fait via un script Perl parce que les outils d'admin
# de Cyrus sont pas franchement faciles à scripter...
printf "Créer la boîte mail dans Cyrus ? (O/n)"
read TMP
if [ -n "$TMP" ] && [ $( echo "$TMP" | egrep -ic "^o$" ) -eq 0 ]; then
echo "Ah bon ?! Ben à la prochaine alors." >&2
return 0
fi
RACINE_EMAIL=$( echo "$EMAIL" | sed 's/@.*//' )
cat <<EOF | perl -MCyrus::IMAP::Admin
# Connexion
my \$conn = Cyrus::IMAP::Admin->new('$CYRUS_HOST') or die "Echec de connexion.";
# Lecture du fichier de configuration
open CONFIG, '$CONFIG_IDENTIFIANTS_CYRUS';
while (<CONFIG>) {
chomp; # no newline
s/^#.*//; # no comments
s/^\s+//; # no leading white
s/\s+$//; # no trailing white
next unless length; # anything left?
my (\$var, \$value) = split(/\s*=\s*/, \$_, 2);
\$cyrus_config{\$var} = \$value;
}
# Authentification
\$conn->authenticate(
"-user" => \$cyrus_config{'user'},
"-password" => \$cyrus_config{'password'},
"-mechanism" => 'LOGIN',
) or die "Echec à l'authentification.";
# Création de la boîte Cyrus
\$conn->create('user.$RACINE_EMAIL') or die "Echec à la création de la boîte ('va falloir aller voir à la main si qqchose a été fait...)";
# Spécification du quota
\$conn->setquota('user.$RACINE_EMAIL', 'STORAGE', '$CONFIG_DEFAULT_QUOTA') or die "Echec lors de la mise en place du quota";
EOF
if [ "$?" -eq 0 ]; then
echo "Boîte créée."
else
echo "ERREUR: erreur à la création de la boîte. Le compte LDAP existe néamoins. Voir avec Cyrus. Abandon." >&2
return 1
fi
echo
# 3ème étape : les listes mails
echo "Ajout aux mailing-lists..."
if ! ./script_ml-gestion-abonne.sh -a salaries -i "$EMAIL"; then
cat <<EOF >&2
NOTICE: apparemment, il y a eu un pépin à la gestion des mailing-lists.
Le compte est néanmoins créé, on vous laisse vous débrouiller avec
./script_ml-gestion-abonne.sh ou PHPLDAPAdmin.
Cordialement.
EOF
return 1
fi
# 4ème étape : envoi du mail de bienvenue
# TODO
echo "Fin du script."

View file

@ -0,0 +1,136 @@
#!/bin/sh
RACINE_MULTIINSTANCES="/var/lib/tomcat7-multiinstances"
RACINE_PATCHS="$HOME/patchs"
WAR_FILE=""
WEBAPP_NAME="ROOT"
RELANCE_AUTO_TOMCAT=1
JUST_TEST_THE_PATCHS=0
set -e
# Fonctions
# fonction d'aide
usage() {
cat <<EOF
$0 [options] -w package.war instance1 instance2 ...
$0 -h
Ce script déploie un war dans les instances Tomcat indiquées
puis les patchs en utilisant le fichier homonyme suffixé en .diff
Par ex:
$0 -w myapp-1.1.1.war tomcat7_003_titi tomcat7_008_toto
va déployer 'myapp-1.1.1.war' dans la webapp ROOT des 2 instances, puis
va appliquer les patchs tomcat7_003_titi.diff et tomcat7_008_toto.diff
qui se trouvent dans '$RACINE_PATCHS'.
Ce script commence par tester chacun des patchs sur un dézippage temporaire
du war, puis crée une copie par instance, la patche, éteint Tomcat, remplace
l'ancienne webapp par la nouvelle copie patchée et reboote tomcat.
Options :
-w : war à déployer
-d : répertoire racine du multiinstances (par défaut : '$RACINE_MULTIINSTANCES')
-D : répertoire contenant les patchs (par défaut : '$RACINE_PATCHS')
-n : nom de la webapp à écraser (par défaut : '$WEBAPP_NAME')
-T : juste tester les patchs sur le war, ne pas faire le déploiement
-h : this help screen
EOF
}
# Début du code
# gestion des options de lancement
while getopts d:D:n:hTw: o; do
case $o in
'd')
RACINE_MULTIINSTANCES="$OPTARG"
;;
'D')
RACINE_PATCHS="$OPTARG"
;;
'n')
WEBAPP_NAME="$OPTARG"
;;
'T')
JUST_TEST_THE_PATCHS=1
;;
'w')
# Utilisation de readlink pour avoir un chemin absolu,
# vu qu'on bouge pas mal entre les répertoires :-/
WAR_FILE="$( readlink -f "$OPTARG" )"
;;
'h')
usage
exit 0
;;
\?)
usage >&2
exit 1
;;
esac
done
shift $( expr $OPTIND - 1 )
#REPDRUPAL="$1"
# Petites vérifs
if [ ! -f "$WAR_FILE" ]; then
echo "ERREUR: fichier '$WAR_FILE' introuvable." >&2
exit 1
fi
# Vérification des patchs
echo "INFO: vérification des patchs..."
# on dézippe le war dans un dossier temporaire
TEMP_REP=$( mktemp -d )
cd "$TEMP_REP"
unzip -q "$WAR_FILE"
# ... et on boucle sur chacun de patchs demandés
for INSTANCE in $@; do
echo "$INSTANCE"
if [ ! -f $RACINE_PATCHS/$INSTANCE.diff ]; then
echo "ERREUR: patch inexistant pour l'instance '$INSTANCE'." >&2
exit 1
fi
if ! patch --dry-run -p1 < $RACINE_PATCHS/$INSTANCE.diff; then
echo "ERREUR: patch non applicable sur instance $INSTANCE." >&2
exit 1
fi
done
cd /
rm -rf "$TEMP_REP"
# Si on se contentait de tester les patchs, on s'arrête là
[ "$JUST_TEST_THE_PATCHS" -eq 1 ] && exit 0
# Déploiement
echo "INFO: déploiement..."
for INSTANCE in $@; do
echo "$INSTANCE"
# Dézippage du war dans un dossier temporaire
cd "$RACINE_MULTIINSTANCES/$INSTANCE"
mkdir ROOT_new
cd ROOT_new
unzip -q "$WAR_FILE"
# Patch de la copie dézippée
patch -p1 < $RACINE_PATCHS/$INSTANCE.diff
# Relance de tomcat
if [ "$RELANCE_AUTO_TOMCAT" -ne 0 ]; then
service $INSTANCE stop
sleep 3
rm -rf "../webapps/$WEBAPP_NAME"
mv -i ../ROOT_new "../webapps/$WEBAPP_NAME"
service $INSTANCE start
else
# TODO : affichage des commandes ?
true
fi
done

128
oldies/script_inotify-rsync.sh Executable file
View file

@ -0,0 +1,128 @@
#!/bin/sh
# Configuration
DIR_SOURCE=""
DIR_DESTINATION=""
MAX_WAIT=60
RSYNC_OPTIONS="-q -aH --delete"
INOTIFYWAIT_OPTIONS="-qq -r -e close_write -e attrib -e move -e create -e delete -e unmount"
#RSYNC_OPTIONS="-v -aH --delete"
#INOTIFYWAIT_OPTIONS="-r -e close_write -e attrib -e move -e create -e delete -e unmount"
# On essaie de diminuer la priorité de la chose
# Aucune importance si les utilitaires ne sont pas dispos
renice 15 -p $$ >/dev/null 2>&1
ionice -c 3 -p $$ >/dev/null 2>&1
# Arrêt à la moindre erreur non-catchée
set -e
# Fonctions
# Little helper to centralize syslog calls
loglog() {
LEVEL="$1"
shift
logger -s -t inotify-rsync -p user.$LEVEL $@
}
# affiche le message d'aide
usage() {
cat <<EOF
$0 [-i inotifywait_options] [-r rsync_options] [-t max_wait] -s source_directory -d rsync_destination
$0 -h
-s : specify the rsync source (needs to be a local directory)
-d : specify the rsync destination (can be anythin rsync accepts)
-i : inotifywait options (minus the -t option)
(default : "$INOTIFYWAIT_OPTIONS")
-r : rsync options
(default : "$RSYNC_OPTIONS")
-t : Even without event (or in case of missed event), rsync will be launched every "max_wait" seconds
(0 = wait forever) (default: $MAX_WAIT)
-h : this help screen
EOF
}
# Début du code
# gestion des options de lancement
while getopts s:d:r:w:h o; do
case $o in
's')
DIR_SOURCE="$OPTARG"
;;
'd')
DIR_DESTINATION="$OPTARG"
;;
'i')
INOTIFYWAIT_OPTIONS="$OPTARG"
;;
'r')
RSYNC_OPTIONS="$OPTARG"
;;
'w')
MAX_WAIT="$OPTARG"
;;
'h')
usage
exit 0
;;
\?)
usage >&2
exit 1
;;
esac
done
#shift $( expr $OPTIND - 1 )
#REPDRUPAL="$1"
# Some checks
if [ -z "$DIR_SOURCE" ] || [ ! -d "$DIR_SOURCE" ]; then
loglog err "ERROR: no source specified or directory inexistent." >&2
exit 1
fi
if [ -z "$DIR_DESTINATION" ]; then
loglog err "ERROR: no destination specified." >&2
exit 1
fi
while true; do
# rsync can send error code != 0, we'll have to check
# what this code means before failing
set +e
rsync $RSYNC_OPTIONS "$DIR_SOURCE" "$DIR_DESTINATION"
RSYNC_RETURN_CODE="$?"
set -e
while [ "$RSYNC_RETURN_CODE" -ne "0" ]; do
for i in 24; do
if [ "$RSYNC_RETURN_CODE" -eq "$i" ]; then
# If the return code is "acceptable", we go on
loglog debug "DEBUG: rsync returned code '$RSYNC_RETURN_CODE'"
break 2
fi
done
# The return code wasn't in the "acceptable" list => log + exit
loglog err "ERROR: rsync returned unexpected error code : $RSYNC_RETURN_CODE. Script stopped."
exit $RSYNC_RETURN_CODE
done
# Wait for another change
# inotifywait can send error code != 0, we'll have to check
# what this code means before failing
set +e
inotifywait $INOTIFYWAIT_OPTIONS -t "$MAX_WAIT" "$DIR_SOURCE"
INOTIFYWAIT_RETURN_CODE="$?"
set -e
if [ "$INOTIFYWAIT_RETURN_CODE" -ne "0" ]; then
if [ "$INOTIFYWAIT_RETURN_CODE" -ne "2" ]; then
loglog err "ERROR: inotifywait returned unexpected error code : $INOTIFYWAIT_RETURN_CODE. Script stopped."
exit $INOTIFYWAIT_RETURN_CODE
fi
fi
done

View file

@ -0,0 +1,138 @@
#! /bin/sh
### BEGIN INIT INFO
# Provides: rsync-inotify
# Required-Start: $syslog $local_fs $network
# Required-Stop: $syslog $local_fs $network
# Should-Start: $named
# Should-Stop: $named
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Rsync-inotify launcher
# Description: This script launch the rsync-inotify script on the upload
# of the XXX instances project.
### END INIT INFO
# Do NOT "set -e"
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Rsync-Inotify for XXX instances"
NAME=inotify-rsync
DAEMON=/root/script_inotify-rsync.sh
# J'arrive pas à le faire passer correctement à start-stop-daemon :-(
#DAEMON_ARGS='-r "-q -aH --delete --password-file=/root/.inotify-rsync-password" -s /srv/XXX/ -d "rsync://XXX-storage-master@srv2.example.net/XXX-storage/"'
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/inotify-rsync.sh
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
if [ -n "$( pgrep -f "$DAEMON" )" ]; then
return 1
fi
start-stop-daemon --start -b --make-pidfile --iosched idle --nicelevel 10 --exec "$DAEMON" --pidfile "$PIDFILE" --test > /dev/null \
|| return 1
start-stop-daemon --start -b --make-pidfile --iosched idle --nicelevel 10 --exec "$DAEMON" --pidfile "$PIDFILE" -- \
-r "-q -aH --delete --password-file=/root/.inotify-rsync-password" -s /srv/XXX/ -d "rsync://storage-master@srv2.example.net/XXX-storage/" \
|| return 2
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
# Petite vérification supplémentaire :
# comme on ne semble pas pouvoir utiliser l'option --exec avec le --stop
# de start-stop-daemon, on vérifie que le PID dans PID_FILE correspond bien
# notre process
PID_CHECK="$( pgrep -f "$DAEMON" )"
if [ -z "$PID_CHECK" ] || [ "$PID_CHECK" != "$( cat "$PIDFILE" 2>/dev/null )" ]; then
return 2
fi
# start-stop-daemon ne semble pas arrêter proprement le inotifywait qui reste
# zombifié jusqu'à son timeout. Du coup, on commence par pkill
pkill --signal INT -P "$PID_CHECK" || return 2
if [ -n "$( pgrep -f "$DAEMON" )" ]; then
start-stop-daemon --stop --quiet --retry=INT/5/KILL/5 --pidfile "$PIDFILE"
fi
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
restart|force-reload)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
exit 3
;;
esac
:

View file

@ -0,0 +1,54 @@
#!/bin/sh
BASE_REP="/backuppc/backuppc/pc"
MAX_NBLIGNES_INCR=30
LISTING_FULL=$(printf "%s\n\t%s\t%s\n" "Volumes des dernières sauvegardes complètes :" "Nb fichiers" "Taille" )
LISTING_INCR=$(printf "%s\n\t%s\t%s\n" "Volumes des $MAX_NBLIGNES_INCR dernières sauvegardes incrémentales :" "Nb fichiers" "Taille" )
somme_ligne() {
TOTAL_FILES=0
TOTAL_SIZE=0
# note : c'est un peu sale.
# Explications : on fait une boucle basée sur un pipe, les variables dans cette boucle
# deviennent donc locales à cette boucle.
# 'faut que je retrouve comment en sortir proprement
cat - | while read line; do
echo $(( $TOTAL_FILES + $(printf "%s" "$line" | awk 'BEGIN { FS = "\t" } ; { print $5 }' ) ))
TOTAL_FILES=$(( $TOTAL_FILES + $( printf "%s" "$line" | awk 'BEGIN { FS = "\t" } ; { print $5 }' ) ))
TOTAL_SIZE=$(( $TOTAL_SIZE + $( printf "%s" "$line" | awk 'BEGIN { FS = "\t" } ; { print $6 }' ) ))
printf "%d\t%d\n" "$TOTAL_FILES" "$TOTAL_SIZE"
done | tail -n 1
}
cd "$BASE_REP"
for REP in *; do
# On ne prend que les dossiers qui contiennent un fichier non-vide "backups"
if [ ! -d "$REP" ]; then
continue
fi
if [ ! -s "$REP/backups" ]; then
continue
fi
# Listing full
LISTING_FULL="$( printf "%s\n%s\t%s" "$LISTING_FULL" "$REP" "$( cat "$REP/backups" | egrep "^[0-9]+[[:space:]]+full" | tail -n 1 | somme_ligne )" )"
# Listing incrémental
#décompte nb sauv.
NBLIGNES_INCR="$( cat "$REP/backups" | egrep "^[0-9]+[[:space:]]+incr" | wc -l )"
if [ "$NBLIGNES_INCR" -gt $MAX_NBLIGNES_INCR ]; then
NBLIGNES_INCR=$MAX_NBLIGNES_INCR
fi
LISTING_INCR="$( printf "%s\n%s (%dj)\t%s" "$LISTING_INCR" "$REP" "$NBLIGNES_INCR" "$( cat "$REP/backups" | egrep "^[0-9]+[[:space:]]+incr" | tail -n $MAX_NBLIGNES_INCR | somme_ligne )" )"
done
echo "$LISTING_FULL"
echo
echo "$LISTING_INCR"

View file

@ -0,0 +1,177 @@
#!/bin/sh
# Ce script sert à gérer les abonnements d'une adresse email
#
# exemple d'utilisation :
# - supprimer une adresse de toutes les mailings-list (suppression d'un compte)
# - ajouter interactivement une adresse à certaines ML avec un choix par
# défaut (salaries, ...)
#
# note: dans l'autre sens (ie. pour gérer une mailing-list donnée), il vaut
# mieux passer par phpldapadmin ou similaire, c'est plus pratique.
#
# Known bug: ne pas utiliser avec les mailings-lists ou adresses emails
# dont le nom contient un espace.
# Inclusion de la conf. + fonctions de base
. ./lib_gestion-mailinglists.sh
# Initialisation des variables "globales"
MODE_INTERACTIF="0"
MODE_LISTING="0"
AJOUTS=""
SUPPRESSIONS=""
LOGFILE=$( mktemp )
LISTING_MAILING_LISTS=$( afficher_listing_mailinglists )
# Fonctions
# affiche le message d'aide
usage() {
cat <<EOF
$0 [-i] [-r liste] [-r ALL] [-a liste] [-a ...] mail
$0 -h
-i : mode interactif.
-a liste : ajoute le mail à la liste. En mode interactif, propose par défaut cette liste au prompt.
-r liste : supprime le mail de la liste. ALL = suppression de toutes les listes. En mode interactif, supprime les listes de la proposition.
-l : liste les mailings-lists dans lesquelles le mail apparaît.
Note: les ajouts prennent le pas sur les suppressions.
EOF
}
terminaison() {
if [ "$1" = "" ] || [ "$1" -eq 0 ]; then
rm -f "$LOGFILE"
RETOUR=0
else
if [ -s "$LOGFILE" ]; then
printf "NOTICE: log dispo : %s\n" "$LOGFILE" >&2
else
rm -f "$LOGFILE"
fi
RETOUR="$1"
fi
exit "$RETOUR"
}
# Début du code
# gestion des options de lancement
while getopts a:r:ihl f; do
case $f in
'a')
AJOUTS=$( echo $AJOUTS $OPTARG )
;;
'r')
SUPPRESSIONS=$( echo $SUPPRESSIONS $OPTARG )
;;
'i')
MODE_INTERACTIF=1
;;
'l')
MODE_LISTING=1
;;
'h')
usage
terminaison 0
;;
\?)
usage >&2
terminaison 1
;;
esac
done
shift $( expr $OPTIND - 1 )
MAIL="$1"
# Petite vérif.
if [ "$MAIL" = "" ]; then
echo "ERREUR: aucune adresse email fournie." >&2
usage
terminaison 1
fi
# Mode listing
if [ "$MODE_LISTING" -gt 0 ]; then
afficher_abonnements_en_cours "$MAIL"
terminaison 0
fi
# Les mailings-lists demandées existent-elles ?
TMP=0
for ML in $AJOUTS $SUPPRESSIONS; do
if [ $( printf "%s\nALL\n" "$LISTING_MAILING_LISTS" | egrep -c "^$ML$" ) -ne 1 ]; then
echo "ERREUR: la mailing-list ne semble pas exister : $ML" >&2
TMP=1
fi
done
test "$TMP" -eq 0 || terminaison 1
# Construction de la liste finale des abonnements
if [ $( echo "$SUPPRESSIONS" | grep -wc "ALL" ) -gt 0 ]; then
# Suppression de tous les abonnements (paramètre "-r ALL")
SUPPRESSIONS=$( afficher_abonnements_en_cours "$MAIL" )
fi
LISTE_FINALE=$( (afficher_abonnements_en_cours "$MAIL" | grep -wFv "$( echo "$SUPPRESSIONS" | sed 's/ \+/\n/g' )" ; echo "$AJOUTS" | sed 's/ \+/\n/g') | grep -v "^$" | sort -u )
# Entrée dans le "mode interactif"
BOUCLE_INTERACTIVE=$MODE_INTERACTIF # simple copie cosmétique, histoire de garder le paramètre jusqu'au bout (sait-on jamais :)
while [ "$BOUCLE_INTERACTIVE" -eq 1 ]; do
# Listing des mailing-lists disponibles
echo "Listing des mailing-lists : "
printf "%s\n" "$LISTING_MAILING_LISTS" | sed 's/^/ /'
# Prompt pour la modification du listing
LISTE_FINALE=$( echo $LISTE_FINALE ) # ça permet de convertir facilement en une liste mono-ligne :)
printf "Indiquer les listes auxquelles l'email %s doit être abonné, ligne vide pour terminer\n(. pour ignorer la proposition et annuler les entrées précédentes)\n[%s]\n" "$MAIL" "$LISTE_FINALE"
LISTE_USER=$( while read LINE; do if [ "$LINE" = "" ]; then exit 0; else echo "$LINE"; fi; done )
# écrasement de LISTE_FINALE si l'utilisateur a entré qqchose
if [ "$LISTE_USER" != "" ]; then
LISTE_FINALE=""
fi
# On vérifie chaque entrée (un peu redondant avec la boucle de vérif. précédente)
BOUCLE_INTERACTIVE=0
for ML in $LISTE_USER; do
# Si c'est un "." qui a été rentré, on efface la liste finale
if [ "$ML" = "." ]; then
LISTE_FINALE=""
# sinon, on vérifie simplement que la mailing-list existe
elif [ $( printf "%s\n" "$LISTING_MAILING_LISTS" | egrep -c "^$ML$" ) -ne 1 ]; then
# Si elle n'existe pas: nouveau tour gratuit
echo "ERREUR: la mailing-list ne semble pas exister : $ML" >&2
BOUCLE_INTERACTIVE=1
else
# On concatène (astuce avec echo pour enlever les espaces inutiles)
LISTE_FINALE=$( echo $LISTE_FINALE $ML )
fi
done
done
# - les suppressions
for ML in $( afficher_abonnements_en_cours "$MAIL" | grep -wFv "$( echo "$LISTE_FINALE" | sed 's/ \+/\n/g' )" ); do
printf "Suppression de %s de la mailing-list %s.\n" "$MAIL" "$ML"
supprimer_abonnement "$MAIL" "$ML" >"$LOGFILE" 2>&1 || terminaison 1
done
# - les créations d'abonnement
for ML in $( echo "$LISTE_FINALE" | sed 's/ \+/\n/g' | grep -wFv "$( afficher_abonnements_en_cours "$MAIL" )" ); do
printf "Ajout de %s à la mailing-list %s.\n" "$MAIL" "$ML"
creer_abonnement "$MAIL" "$ML" >"$LOGFILE" 2>&1 || terminaison 1
done
# Fin du script - nettoyage
terminaison 0

View file

@ -0,0 +1,214 @@
#!/bin/sh
# Ce script sert à créer/supprimer une mailing-list
#
# exemple d'utilisation :
# - créer une mailing-list et lui ajouter des abonnés,
# - retirer des abonnés d'une mailing-list,
# - supprimer une mailing-list
#
# note: dans l'autre sens (ie. gérer les abonnements d'une adresse donnée),
# il existe le script_ml-gestion-abonne.sh
#
# Known bug: ne pas utiliser avec les mailings-lists ou adresses emails
# dont le nom contient un espace.
# Inclusion de la conf. + fonctions de base
. ./lib_gestion-mailinglists.sh
# Initialisation des variables "globales"
MODE_INTERACTIF="0"
MODE_LISTING_ABONNES="0"
CREATION_SUPPRESSION="0" # 0: neutre, -1: suppression, 1: creation
AJOUTS=""
SUPPRESSIONS=""
LOGFILE=$( mktemp )
# Fonctions
# affiche le message d'aide
usage() {
cat <<EOF
$0 [-i] [ -c | -d ] [-r email] [-r ALL] [-a email] [-a ...] mail
$0 -L
$0 -h
-i : mode interactif.
-c : crée la mailing-list.
-d : supprime la mailing-list.
-a mail : ajoute le mail à la liste. En mode interactif, propose par défaut cet
email au prompt.
-r mail : supprime le mail de la liste. ALL = suppression de tous les abonnés.
En mode interactif, supprime les listes de la proposition.
-l : liste les abonnés de la mailing-list.
-L : liste les mailing-list
Note: les ajouts prennent le pas sur les suppressions.
EOF
}
terminaison() {
if [ "$1" = "" ] || [ "$1" -eq 0 ]; then
rm -f "$LOGFILE"
RETOUR=0
else
if [ -s "$LOGFILE" ]; then
printf "NOTICE: log dispo : %s\n" "$LOGFILE" >&2
else
rm -f "$LOGFILE"
fi
RETOUR="$1"
fi
exit "$RETOUR"
}
# Début du code
# gestion des options de lancement
while getopts cda:r:ihlL f; do
case $f in
'c')
CREATION_SUPPRESSION=1
;;
'd')
CREATION_SUPPRESSION=-1
;;
'a')
AJOUTS=$( echo $AJOUTS $OPTARG )
;;
'r')
SUPPRESSIONS=$( echo $SUPPRESSIONS $OPTARG )
;;
'i')
MODE_INTERACTIF=1
;;
'l')
MODE_LISTING_ABONNES=1
;;
'L')
afficher_listing_mailinglists
terminaison 0
;;
'h')
usage
terminaison 0
;;
\?)
usage >&2
terminaison 1
;;
esac
done
shift $( expr $OPTIND - 1 )
MAILINGLIST="$1"
# Petite vérif.
if [ "$MAILINGLIST" = "" ]; then
echo "ERREUR: aucune mailing-list n'a été indiquée." >&2
usage
terminaison 1
fi
# Mode listing des abonnes d'une mailing-linst
if [ "$MODE_LISTING_ABONNES" -gt 0 ]; then
# On vérifie quand même que la mailing-list existe...
if [ $( afficher_listing_mailinglists | grep -wc "^$MAILINGLIST$" ) -ne 1 ]; then
echo "ERREUR: mailing-list inexistante." >&2
terminaison 1
fi
# Listing des abonnés de la mailing-list
afficher_abonnes_mailinglist "$MAILINGLIST"
terminaison 0
fi
# La mailing-list demandée existe-elle ?
if [ $( printf "%s\n" "$( afficher_listing_mailinglists )" | egrep -c "^$MAILINGLIST$" ) -ne 1 ]; then
# Si elle n'existe pas, doit-on la créer ?
if [ $CREATION_SUPPRESSION -lt 1 ]; then
echo "ERREUR: la mailing-list ne semble pas exister : $MAILINGLIST" >&2
terminaison 1
else
# On crée la mailing-list
printf "Création de la mailing-list..."
if creer_mailinglist "$MAILINGLIST" >"$LOGFILE" 2>&1; then
printf " [OK]\n"
else
printf "\nProblèmes à la création de la mailing-list. Cf log.\n" >&2
terminaison 1
fi
fi
else
# Si elle existe, doit-on la supprimer ?
if [ "$CREATION_SUPPRESSION" -lt 0 ]; then
# On la supprime et on arrête là le script
printf "Suppression de la mailing-list..."
if supprimer_mailinglist "$MAILINGLIST" >"$LOGFILE" 2>&1; then
printf "[OK]\n";
terminaison 0
else
printf "\nProblèmes à la suppression de la mailing-list. Cf log.\n" >&2
terminaison 1
fi
fi
fi
# Construction de la liste finale des abonnements
if [ $( echo "$SUPPRESSIONS" | grep -wc "ALL" ) -gt 0 ]; then
# Suppression de tous les abonnés (paramètre "-r ALL")
SUPPRESSIONS=$( afficher_abonnes_mailinglist "$MAILINGLIST" )
fi
LISTE_FINALE=$( (afficher_abonnes_mailinglist "$MAILINGLIST" | grep -wFv "$( echo "$SUPPRESSIONS" | sed 's/ \+/\n/g' )" ; echo "$AJOUTS" | sed 's/ \+/\n/g') | grep -v "^$" | sort -u )
# Entrée dans le "mode interactif"
BOUCLE_INTERACTIVE=$MODE_INTERACTIF # simple copie cosmétique, histoire de garder le paramètre jusqu'au bout (sait-on jamais :)
while [ "$BOUCLE_INTERACTIVE" -eq 1 ]; do
# Prompt pour la modification du listing
LISTE_FINALE=$( echo $LISTE_FINALE ) # ça permet de convertir facilement en une liste mono-ligne :)
printf "Indiquer les emails qui seront abonnés à la liste '%s', ligne vide pour terminer\n(. pour ignorer la proposition et annuler les entrées précédentes)\n[%s]\n" "$MAILINGLIST" "$LISTE_FINALE"
LISTE_USER=$( while read LINE; do if [ "$LINE" = "" ]; then exit 0; else echo "$LINE"; fi; done )
# écrasement de LISTE_FINALE si l'utilisateur a entré qqchose
if [ "$LISTE_USER" != "" ]; then
LISTE_FINALE=""
fi
# On vérifie chaque entrée (un peu redondant avec la boucle de vérif. précédente)
BOUCLE_INTERACTIVE=0
for MAIL in $LISTE_USER; do
# Si c'est un "." qui a été rentré, on efface la liste finale
if [ "$MAIL" = "." ]; then
LISTE_FINALE=""
else
# sinon, on concatène (astuce avec echo pour enlever les espaces inutiles)
LISTE_FINALE=$( echo $LISTE_FINALE $MAIL )
fi
done
done
# - les suppressions
for MAIL in $( afficher_abonnes_mailinglist "$MAILINGLIST" | grep -wFv "$( echo "$LISTE_FINALE" | sed 's/ \+/\n/g' )" ); do
printf "Suppression de %s de la mailing-list %s.\n" "$MAIL" "$MAILINGLIST"
supprimer_abonnement "$MAIL" "$MAILINGLIST" >"$LOGFILE" 2>&1 || terminaison 1
done
# - les créations d'abonnement
for MAIL in $( echo "$LISTE_FINALE" | sed 's/ \+/\n/g' | grep -wFv "$( afficher_abonnes_mailinglist "$MAILINGLIST" )" ); do
printf "Ajout de %s à la mailing-list %s.\n" "$MAIL" "$MAILINGLIST"
creer_abonnement "$MAIL" "$MAILINGLIST" >"$LOGFILE" 2>&1 || terminaison 1
done
# Fin du script - nettoyage
terminaison 0

View file

@ -0,0 +1,591 @@
#!/bin/sh
# Ce script sert à créer ou détruire :
# - une instance PostgreSQL (PGDATA).
# Il peut également gérer un serveur esclave en définissant la
# variable $ESCLAVE . Dans ce cas là, mieux vaut avoir une authentification
# par clef (les opérations se font par ssh)
#
# Attention, ce script affiche les identifiants à l'écran.
# Prérequis :
# - PostgreSQL
# Si utilisation d'un esclave :
# - une authentification pour l'utilisateur root local vers root@$ESCLAVE par clef,
# - une authentification pour l'utilisateur postgres local vers postgres@$ESCLAVE par clef.
# Trucs à nettoyer (si quelque chose part en vrille, ou simplement
# pour désinstaller ):
# - TODO
# Version de PostgreSQL : 9.1 seule testée
VERSION_POSTGRESQL="9.1"
# Mettre ci-dessous le nom d'hôte ou l'IP du serveur esclave
# (utilisé par la config PG + les connexions SSH pour l'exécution
# des commandes distantes de ce script)
ESCLAVE=""
# idem pour le maître (vu depuis l'esclave en cas de réseau privé)
# (utilisé pour la config PG)
MAITRE=""
# Initialisation des variables globales
MODE_CREATION=0
MODE_DESTRUCTION=0
POSTGRESQL_REPLICATION_USER_NAME="repliquser"
POSTGRESQL_LOCALE="fr_FR.utf8"
POSTGRESQL_PORT=""
# Par défaut, on arrête le script à la première erreur non "catchée"
set -e
# On s'exécute dans un dossier accessible à l'utilisateur postgres
cd /tmp
# Fonctions
# affiche le message d'aide
usage() {
cat <<EOF
$0 -c cluster-name [ -e <hostname de l'esclave> -m <hostname du maître> ] [ -p <numéro de port> ]
$0 -d cluster-name [ -e <hostname de l'esclave> -m <hostname du maître> ]
$0 -h
-c xxx : création d'une instance/cluster nommée 'xxx'
-d xxx : destruction de l'instance nommée 'xxx'
-p nnn : numéro de port à affecter au nouveau cluster (par défaut, 'auto')
# Ce script peut générer la configuration nécessaire au streaming replication
-e esclave : nom d'hôte ou IP de l'esclave
-m maitre : nom d'hôte ou IP du serveur maître
-h : ce message d'aide
EOF
}
# Arguments
# $1 : IPv4, IPv6 ou nom d'hôte
# Cette fonction suffixe d'un /32 ou /128
# la chaîne si celle-ci est une adresse IP
add_mask_ip() {
test -n "$1" || exit 1
if [ $( echo "$1" | egrep -c "^([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}$" ) -gt 0 ]; then
printf "$1/32"
elif [ $( echo "$1" | egrep -c "^[[:xdigit:]:]*$" ) -gt 0 ]; then
printf "$1/128"
else
printf "$1"
fi
}
# Arguments
# $1 : IP esclave
# $2 : version PostgreSQL
# $3 : nom de l'instance
generate_patch_postgresqlconf_master() {
test -n "$1" && test -n "$2" && test -n "$3" || exit 1
cat <<EOF
@@ -56,6 +56,8 @@
# - Connection Settings -
+# Script MI PostgreSQL : écoute sur toutes les interfaces nécessaire pour la streaming replication
+listen_addresses = '*'
#listen_addresses = 'localhost' # what IP address(es) to listen on;
# comma-separated list of addresses;
# defaults to 'localhost', '*' = all
@@ -150,6 +152,8 @@ shared_buffers = 24MB # min 128kB
# - Settings -
+# Script MI PostgreSQL : serveur maître
+wal_level = hot_standby
#wal_level = minimal # minimal, archive, or hot_standby
# (change requires restart)
#fsync = on # turns forced synchronization on or off
@@ -178,9 +182,10 @@ shared_buffers = 24MB # min 128kB
# - Archiving -
-#archive_mode = off # allows archiving to be done
+# Script MI PostgreSQL : Serveur maître: activation de l'archivage et de l'envoi vers l'esclave
+archive_mode = on # allows archiving to be done
# (change requires restart)
-#archive_command = '' # command to use to archive a logfile segment
+archive_command = 'scp -q -B -C %p postgres@$1:/var/lib/postgresql/$2/$3/archives_from_master/%f' # command to use to archive a logfile segment
#archive_timeout = 0 # force a logfile segment switch after this
# number of seconds; 0 disables
@@ -193,10 +198,12 @@
# These settings are ignored on a standby server
+max_wal_senders = 3
#max_wal_senders = 0 # max number of walsender processes
# (change requires restart)
#wal_sender_delay = 1s # walsender cycle time, 1-10000 milliseconds
-#wal_keep_segments = 0 # in logfile segments, 16MB each; 0 disables
+# Script MI PostgreSQL : serveur maître: conservation des WAL
+wal_keep_segments = 32 # in logfile segments, 16MB each; 0 disables
#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
#replication_timeout = 60s # in milliseconds; 0 disables
#synchronous_standby_names = '' # standby servers that provide sync rep
@@ -207,6 +213,9 @@ shared_buffers = 24MB # min 128kB
# These settings are ignored on a master server
+# Script MI PostgreSQL : mis en place sur le maître comme sur l'esclave, pour homogénéiser
+# comme il est ignoré sur le maître, ça ne pose pas de souci :)
+hot_standby = on
#hot_standby = off # "on" allows queries during recovery
# (change requires restart)
#max_standby_archive_delay = 30s # max delay before canceling queries
EOF
}
# Arguments
# $1 : nom de l'instance
generate_patch_postgresql_pghbaconf() {
test -n "$1" || exit 1
cat <<EOF
@@ -87,11 +87,11 @@ local all postgres peer
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
-local all all peer
+local $1 $1 md5
# IPv4 local connections:
-host all all 127.0.0.1/32 md5
+host $1 $1 127.0.0.1/32 md5
# IPv6 local connections:
-host all all ::1/128 md5
+host $1 $1 ::1/128 md5
# Allow replication connections from localhost, by a user with the
# replication privilege.
#local replication postgres peer
EOF
}
# Pas d'arguments
generate_patch_postgresqlconf_slave() {
cat <<EOF
@@ -56,6 +56,8 @@
# - Connection Settings -
+# Script MI PostgreSQL : écoute sur toutes les interfaces nécessaire pour la streaming replication
+listen_addresses = '*'
#listen_addresses = 'localhost' # what IP address(es) to listen on;
# comma-separated list of addresses;
# defaults to 'localhost', '*' = all
@@ -150,6 +152,8 @@ shared_buffers = 24MB # min 128kB
# - Settings -
+# Script MI PostgreSQL : serveur esclave
+wal_level = hot_standby
#wal_level = minimal # minimal, archive, or hot_standby
# (change requires restart)
#fsync = on # turns forced synchronization on or off
@@ -193,10 +198,12 @@
# These settings are ignored on a standby server
+max_wal_senders = 3
#max_wal_senders = 0 # max number of walsender processes
# (change requires restart)
#wal_sender_delay = 1s # walsender cycle time, 1-10000 milliseconds
-#wal_keep_segments = 0 # in logfile segments, 16MB each; 0 disables
+# Script MI PostgreSQL : serveur maître: conservation des WAL
+wal_keep_segments = 32 # in logfile segments, 16MB each; 0 disables
#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
#replication_timeout = 60s # in milliseconds; 0 disables
#synchronous_standby_names = '' # standby servers that provide sync rep
@@ -207,6 +213,9 @@ shared_buffers = 24MB # min 128kB
# These settings are ignored on a master server
+# Script MI PostgreSQL : mis en place sur le maître comme sur l'esclave, pour homogénéiser
+# comme il est ignoré sur le maître, ça ne pose pas de souci :)
+hot_standby = on
#hot_standby = off # "on" allows queries during recovery
# (change requires restart)
#max_standby_archive_delay = 30s # max delay before canceling queries
EOF
}
# Arguments:
# $1 : IP maître
# $2 : port maître (idem esclave, en fait)
# $3 : utilisateur dédié à la réplication
# $4 : mot de passe dudit utilisateur
# $5 : version de PostgreSQL
# $6 : nom de l'instance
# $7 : chemin complet de pg_archivecleanup (si vide, désactivé)
generate_file_postgresqlrecoveryconf_slave() {
local ARCHIVE_CLEANUP_COMMAND
test -n "$1" && test -n "$2" && test -n "$3" && test -n "$4" && test -n "$5" && test -n "$6" || exit 1
if ! test -n "$7"; then
ARCHIVE_CLEANUP_COMMAND="#archive_cleanup_command = 'pg_archivecleanup /var/lib/postgresql/$5/$6/archives_from_master %r'"
else
ARCHIVE_CLEANUP_COMMAND="archive_cleanup_command = '$7 /var/lib/postgresql/$5/$6/archives_from_master %r'"
fi
cat <<EOF
# Note that recovery.conf must be in \$PGDATA directory.
# Specifies whether to start the server as a standby. In streaming replication,
# this parameter must be set to on.
standby_mode = 'on'
# Specifies a connection string which is used for the standby server to connect
# with the primary.
primary_conninfo = 'host=$1 port=$2 user=$3 password=$4'
# Specifies a trigger file whose presence should cause streaming replication to
# end (i.e., failover).
# Cf pg_ctlcluster promote pour une alternative
trigger_file = '/var/lib/postgresql/trigger-postgresql'
# Specifies a command to load archive segments from the WAL archive. If
# wal_keep_segments is a high enough number to retain the WAL segments
# required for the standby server, this may not be necessary. But
# a large workload can cause segments to be recycled before the standby
# is fully synchronized, requiring you to start again from a new base backup.
restore_command = 'cp /var/lib/postgresql/$5/$6/archives_from_master/%f "%p"'
# Specifies a command to clean up obsolete archive logs
$ARCHIVE_CLEANUP_COMMAND
EOF
}
# Arguments:
# $1 : Nom de la bdd / de l'utilisateur
# $2 : mot de passe de l'utilisateur
generate_script_postgresql_creation_db_et_user() {
local NOM_INSTANCE POSTGRESQL_PASSWORD
test -n "$1" && test -n "$2" || exit 1
NOM_INSTANCE="$1"
POSTGRESQL_PASSWORD="$2"
cat <<EOF
-- Création de l'utilisateur principal
CREATE USER "$NOM_INSTANCE" WITH PASSWORD '$POSTGRESQL_PASSWORD' CONNECTION LIMIT 50;
-- Restriction des privilèges par défaut
-- Cette partie du script vient en grande partie de :
-- http://wiki.postgresql.org/wiki/Shared_Database_Hosting
-- Connexion à template1
\c template1
-- Révocation sur la base template1
REVOKE ALL ON DATABASE template1 FROM public;
REVOKE ALL ON SCHEMA public FROM public;
GRANT ALL ON SCHEMA public TO postgres;
-- Ce passage-là est plus parano, mais à étudier avant suppression
REVOKE ALL ON pg_user FROM public;
REVOKE ALL ON pg_roles FROM public;
REVOKE ALL ON pg_group FROM public;
REVOKE ALL ON pg_authid FROM public;
REVOKE ALL ON pg_auth_members FROM public;
REVOKE ALL ON pg_database FROM public;
REVOKE ALL ON pg_tablespace FROM public;
REVOKE ALL ON pg_settings FROM public;
-- Création de la nouvelle base de données
CREATE DATABASE "$NOM_INSTANCE" WITH OWNER = "$NOM_INSTANCE" ENCODING = 'UTF8';
REVOKE ALL ON DATABASE "$NOM_INSTANCE" FROM public;
\c $NOM_INSTANCE
REVOKE ALL ON SCHEMA public FROM public;
GRANT ALL ON SCHEMA public TO "$NOM_INSTANCE";
EOF
}
# Arguments:
# $1 : nom de l'utilisateur dédié à la réplication
# $2 : mot de passe dudit utilisateur
generate_script_postgresql_creation_replication_user() {
test -n "$1" && test -n "$2" || exit 1
POSTGRESQL_REPLICATION_USER_NAME="$1"
POSTGRESQL_REPLICATION_PASSWORD="$2"
cat <<EOF
-- Création de l'utilisateur utilisé par l'esclave pour la streaming replication
CREATE USER $POSTGRESQL_REPLICATION_USER_NAME WITH PASSWORD '$POSTGRESQL_REPLICATION_PASSWORD' REPLICATION;
EOF
}
# Arguments
# none
log_alias() {
# Cette fonction est utilisée pour "capturer" les sorties des différentes
# commandes (hors messages générés par le script lui-même)
# Note : elle peut être modifiée pour envoyer les messages vers un fichier
# de log temporaire. Ça serait plus propre.
sed 's/^/ /'
}
fancylog() {
BEGINNING="$( printf "%s" "$1" | head -n 1 | sed -n "s/^\([A-Z]\+\):.*/\1/p" )"
if [ -n "$BEGINNING" ] && [ -t 1 ] && tput setaf 1 >/dev/null 2>&1; then
case "$BEGINNING" in
"ERREUR"|"ERROR")
tput setaf 1;;
"WARN"|"WARNING")
tput setaf 3;;
"INFO"|"DEBUG")
tput setaf 2;;
esac
printf "%s" "$BEGINNING"
tput op
printf "%s\n" "$( printf "%s" "$1" | sed '1 s/^[A-Z]\+:/:/' )"
else
printf "%s\n" "$1"
fi
}
# Début du code
# gestion des options de lancement
while getopts c:d:e:m:p:h f; do
case $f in
'c')
MODE_CREATION=1
NOM_INSTANCE="$OPTARG"
;;
'd')
MODE_DESTRUCTION=1
NOM_INSTANCE="$OPTARG"
;;
'e')
ESCLAVE="$OPTARG"
;;
'm')
MAITRE="$OPTARG"
;;
'p')
if [ "$OPTARG" = "auto" ]; then
unset POSTGRESQL_PORT
else
POSTGRESQL_PORT="$( printf "%d" "$OPTARG" )"
fi
;;
'h')
usage
exit 0
;;
\?)
usage >&2
exit 1
;;
esac
done
#(code inutile, mais que je garde parce qu'on ne sait jamais)
#shift $( expr $OPTIND - 1 )
#DATA="$1"
if [ -n "$MAITRE" ] && [ -n "$ESCLAVE" ]; then
MAITRE_WITH_MASK=$( add_mask_ip "$MAITRE" )
ESCLAVE_WITH_MASK=$( add_mask_ip "$ESCLAVE" )
fi
# Petite vérif.
case $(( $MODE_CREATION + $MODE_DESTRUCTION )) in
'0')
fancylog "ERREUR: veuillez choisir entre création et destruction." >&2
usage >&2
exit 1
;;
'2')
fancylog "ERREUR: tu veux créer et détruire en même temps, petit malin ?" >&2
exit 1
;;
esac
# Petites vérifications préliminaires :
fancylog "INFO: petites vérifications préliminaires..."
# - vérifications sur le nom du clustername fourni
OLD_LC_ALL="$LC_ALL"
export LC_ALL=C
export LANG=C
if [ $( printf "$NOM_INSTANCE" | egrep -c "^[a-z][a-z0-9_]{0,14}$" ) -ne 1 ]; then
fancylog "ERREUR: clustername vide ou contenant des caractères interdits : regexp : [a-z][a-z0-9_]{0,14}" >&2
exit 1
fi
export LC_ALL="$OLD_LC_ALL"
export LANG="$OLD_LC_ALL"
# - est-ce que l'auth. SSH par clef fonctionne pour root et postgres ?
if [ -n "$ESCLAVE" ]; then
# - vérification de l'auth. par clefs SSH
if ! ssh -o BatchMode=yes root@$ESCLAVE /bin/true; then
fancylog "ERREUR: auth. par clef SSH non configurée pour l'utilisateur 'root'."
exit 1
fi
if ! su -c "ssh -o BatchMode=yes postgres@$ESCLAVE /bin/true" postgres; then
fancylog "ERREUR: auth. par clef SSH non configurée pour l'utilisateur 'postgres'."
exit 1
fi
# - vérification de la présence de $MAITRE
if [ ! -n "$MAITRE" ]; then
fancylog "ERREUR: serveur esclave indiqué, mais pas de serveur maître (option -m)." >&2
exit 1
fi
fi
# - est-ce qu'on est bien sur une Debian 6 ou 7
# (c'est surtout pour éviter les mauvaises surprises, ce script n'ayant pas été testé ailleurs)
if [ ! -f "/etc/debian_version" ] || [ $( egrep -c "^(6.|7.)" /etc/debian_version ) -ne 1 ]; then
fancylog "ERREUR: mauvaise version de Debian détectée. Arrêt par prudence. Vérifiez le code de ce script avant de forcer." >&2
exit 1
fi
if [ -n "$ESCLAVE" ] && [ "$( ssh root@$ESCLAVE 'egrep -c "^(6.|7.)" /etc/debian_version' )" -ne 1 ]; then
fancylog "ERREUR: ESCLAVE: mauvaise version de Debian détectée. Arrêt par prudence. Vérifiez le code de ce script avant de forcer." >&2
exit 1
fi
# - est-ce que la version attendue de PostgreSQL est bien installée ?
if ! dpkg -s "postgresql-$VERSION_POSTGRESQL" >/dev/null 2>&1; then
fancylog "ERREUR: PostgreSQL $VERSION_POSTGRESQL ne semble pas installée." >&2
exit 1
fi
if [ -n "$ESCLAVE" ] && ! ssh root@$ESCLAVE dpkg -s "postgresql-$VERSION_POSTGRESQL" >/dev/null 2>&1; then
fancylog "ERREUR: PostgreSQL $VERSION_POSTGRESQL ne semble pas installée." >&2
exit 1
fi
# Fin des vérifications
fancylog "INFO: fin des vérifications. Lancement des opérations."
if [ "$MODE_CREATION" -eq 1 ]; then
POSTGRESQL_PASSWORD=$( dd if=/dev/random 2>/dev/null bs=1 count=10 status=noxfer | base64 | sed 's#[/=]##g' )
POSTGRESQL_REPLICATION_PASSWORD=$( dd if=/dev/random 2>/dev/null bs=1 count=20 status=noxfer | base64 | sed 's#[/=]##g' )
# Création de l'instance dédiée
fancylog "INFO: création du cluster PG primaire..."
if [ -z "$POSTGRESQL_PORT" ]; then
pg_createcluster --locale=$POSTGRESQL_LOCALE --start-conf=auto "$VERSION_POSTGRESQL" "$NOM_INSTANCE"
# Détermination du port généré
# TODO: récupérer ça via pg_lsclusters serait plus "propre"
POSTGRESQL_PORT=$( grep "^port" /etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/postgresql.conf | sed 's/^port[[:space:]]*=[[:space:]]*\([0-9]\+\).*$/\1/' )
else
pg_createcluster --locale=$POSTGRESQL_LOCALE --start-conf=auto --port "$POSTGRESQL_PORT" "$VERSION_POSTGRESQL" "$NOM_INSTANCE"
fi
fancylog "INFO: modification du pg_hba.conf..."
if ! generate_patch_postgresql_pghbaconf "$NOM_INSTANCE" | patch -s --dry-run /etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/pg_hba.conf; then
fancylog "ERREUR: modification du fichier pg_hba.conf en échec. Arrêt."
exit 1
fi
generate_patch_postgresql_pghbaconf "$NOM_INSTANCE" | patch -s /etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/pg_hba.conf
fancylog "INFO: démarrage de l'instance PostgreSQL..."
pg_ctlcluster "$VERSION_POSTGRESQL" "$NOM_INSTANCE" start
# Lancement du script fourre-tout
fancylog "INFO: création de l'utilisateur et de la base de données, et affinage des droits d'accès..."
generate_script_postgresql_creation_db_et_user "$NOM_INSTANCE" "$POSTGRESQL_PASSWORD" "$POSTGRESQL_REPLICATION_USER_NAME" "$POSTGRESQL_REPLICATION_PASSWORD" | su -c "psql -p $POSTGRESQL_PORT -v ON_ERROR_STOP=1" postgres
if [ -n "$ESCLAVE" ]; then
fancylog "INFO: création de l'utilisateur de réplication..."
generate_script_postgresql_creation_replication_user "$POSTGRESQL_REPLICATION_USER_NAME" "$POSTGRESQL_REPLICATION_PASSWORD" | su -c "psql -p $POSTGRESQL_PORT -v ON_ERROR_STOP=1" postgres
# configuration en tant que maître
fancylog "INFO: modification de la configuration BDD de l'instance primaire..."
if ! generate_patch_postgresqlconf_master "$ESCLAVE" "$VERSION_POSTGRESQL" "$NOM_INSTANCE" | patch -s --dry-run /etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/postgresql.conf; then
fancylog "ERREUR: modification du fichier postgresql.conf en échec. Arrêt."
exit 1
fi
generate_patch_postgresqlconf_master "$ESCLAVE" "$VERSION_POSTGRESQL" "$NOM_INSTANCE" | patch -s /etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/postgresql.conf
# On rajoute les lignes pour la connexion streaming replication dans le pg_hba.conf
echo "host replication $POSTGRESQL_REPLICATION_USER_NAME $ESCLAVE_WITH_MASK md5" >>/etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/pg_hba.conf
echo "host replication $POSTGRESQL_REPLICATION_USER_NAME $MAITRE_WITH_MASK md5" >>/etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/pg_hba.conf
# Création sur l'esclave
# Note: on commence par créer l'arborescence de l'esclave pour
# avoir le répertoire de destination des archives logs
# le plus tôt possible (en tout cas, avant le pg_stop_backup()
# qui envoie forcément un WAL)
fancylog "INFO: création du cluster PG secondaire..."
ssh "root@$ESCLAVE" "cd /tmp; pg_createcluster --locale='$POSTGRESQL_LOCALE' -p '$POSTGRESQL_PORT' --start-conf=auto '$VERSION_POSTGRESQL' '$NOM_INSTANCE'"
fancylog "INFO: création du dossier de réception des archives logs et arret du backup..."
su -c "mkdir '/var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/archives_from_master'" postgres
su -c "ssh postgres@$ESCLAVE mkdir '/var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/archives_from_master'" postgres
fancylog "INFO: redémarrage du serveur primaire..."
pg_ctlcluster "$VERSION_POSTGRESQL" "$NOM_INSTANCE" restart
# rsync du PGDATA vers l'esclave
fancylog "INFO: écrasement du PGDATA secondaire par celui de l'instance primaire..."
echo "SELECT pg_start_backup('script_mi_pg', true);" | su -c "psql -p $POSTGRESQL_PORT -v ON_ERROR_STOP=1" postgres
# on attend un peu, il y a parfois des fichiers WAL qui "vanished" pendant le rsync
sleep 1
# On exclut :
# - postmaster.pid : raison évidente :)
# - postmaster.opts : a priori, le contenu est identique mais par prudence...
# - server.key/server.crt : rsync génère une erreur sur la date de modification des symlinks :-/
# - recovery.* : le recovery.done du maitre référence l'esclave, et vice versa pour le recovery.conf de l'esclave
# - archives_from_master : cela écraserait tout éventuel WAL déjà envoyé
# - lost+found : au cas où la partition existe déjà (pg_createcluster se bloque mais ça permet de récupérer cette ligne de commande ailleurs :)
# A étudier : pas de --delete ?
# test -c car problème de synchro bizarre
su -c "rsync -aHc --exclude=/postmaster.pid --exclude=/postmaster.opts --exclude=/server.key --exclude=/server.crt --exclude=/recovery.conf --exclude=/recovery.done --exclude=/archives_from_master --exclude=lost+found /var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/ postgres@$ESCLAVE:/var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/" postgres
echo "SELECT pg_stop_backup();" | su -c "psql -p $POSTGRESQL_PORT -v ON_ERROR_STOP=1" postgres
# configuration de l'esclave
fancylog "INFO: configuration de l'instance PG secondaire..."
if ! ssh root@$ESCLAVE dpkg -s "postgresql-contrib-$VERSION_POSTGRESQL" >/dev/null 2>&1; then
fancylog "WARNING: Le paquet postgresql-contrib-$VERSION_POSTGRESQL ne semble pas installé sur le serveur esclave, archive_cleanup_command sera désactivé." >&2
fancylog "NOTICE: cf fichier /var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/recovery.conf en cas d'installation après-coup." >&2
ARCHIVE_CLEANUP_COMMAND=""
else
# TODO: à rendre plus portable ?
ARCHIVE_CLEANUP_COMMAND="/usr/lib/postgresql/$VERSION_POSTGRESQL/bin/pg_archivecleanup"
fi
generate_file_postgresqlrecoveryconf_slave "$MAITRE" "$POSTGRESQL_PORT" "$POSTGRESQL_REPLICATION_USER_NAME" "$POSTGRESQL_REPLICATION_PASSWORD" "$VERSION_POSTGRESQL" "$NOM_INSTANCE" "$ARCHIVE_CLEANUP_COMMAND" | su -c "ssh postgres@$ESCLAVE 'cat - >/var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/recovery.conf'" postgres
generate_file_postgresqlrecoveryconf_slave "$ESCLAVE" "$POSTGRESQL_PORT" "$POSTGRESQL_REPLICATION_USER_NAME" "$POSTGRESQL_REPLICATION_PASSWORD" "$VERSION_POSTGRESQL" "$NOM_INSTANCE" "$ARCHIVE_CLEANUP_COMMAND" | su -c "cat - >/var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/recovery.done" postgres
generate_patch_postgresqlconf_slave | ssh "root@$ESCLAVE" patch -s /etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/postgresql.conf
# (note: même fichier pg_hba.conf pour tout le monde, homogénéisation avec peu de risques il me semble)
scp -q /etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/pg_hba.conf root@$ESCLAVE:/etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/pg_hba.conf
# démarrage des instances maître et esclave
fancylog "INFO: démarrage de l'instance PostgreSQL esclave..."
ssh root@$ESCLAVE pg_ctlcluster "$VERSION_POSTGRESQL" "$NOM_INSTANCE" start
# Vérification de la réplication
# TODO (via numéro de transaction (requête) ? processus/connexion ?)
fi
# Fin
fancylog "INFO: Création terminée. Instance PostgreSQL allumée."
fancylog "INFO: pour mémo :"
fancylog "INFO: BDD: $NOM_INSTANCE, port: $POSTGRESQL_PORT, login: $NOM_INSTANCE, mdp: $POSTGRESQL_PASSWORD"
TMP="$MAITRE"
if [ -z "$MAITRE" ]; then
TMP="localhost"
fi
fancylog "INFO: pgpass: $TMP:$POSTGRESQL_PORT:$NOM_INSTANCE:$NOM_INSTANCE:$POSTGRESQL_PASSWORD"
fi
if [ "$MODE_DESTRUCTION" -eq 1 ]; then
fancylog "INFO: suppression de l'instance locale..."
# Note : en guise de garde-fou, on ne met pas l'option --stop
pg_dropcluster "$VERSION_POSTGRESQL" "$NOM_INSTANCE"
if [ -n "$ESCLAVE" ]; then
fancylog "INFO: suppression de l'instance esclave..."
ssh root@$ESCLAVE pg_dropcluster "$VERSION_POSTGRESQL" "$NOM_INSTANCE"
fi
fi

View file

@ -0,0 +1,453 @@
#!/bin/sh
# Ce script sert à créer ou détruire une instance Tomcat
# en créant un CATALINA_BASE indépendant.
# L'usage habituel se fait sur une Debian 7 "Wheezy" avec
# tomcat, libapache2-mod-jk et apache2 installé.
# Prérequis :
# - Debian Wheezy (Squeeze non testée)
# - tomcat7 (6 non testée)
# - libapache2-mod-jk avec le workers.properties du module
# Trucs à nettoyer (si quelque chose part en vrille, ou simplement
# pour désinstaller ):
# - supprimer tout le répertoire REP_TOMCAT_MULTIINSTANCE
# - supprimer /etc/default/tomcatN_XXX
# - supprimer /etc/init.d/tomcatN_XXX
# - supprimer les utilisateurs tomcatN_YYYY
# - nettoyer les clusters PostgreSQL
# Version de Tomcat : 6 ou 7
# TODO : voir si on peut le passer en paramètre pour gérer
# Tomcat 6 & 7 sur la même machine
VERSION_TOMCAT="7"
# Quelques constantes internes
MAX_INSTANCES=999
START_RANGE_TOMCATUID=8000
START_RANGE_TOMCAT_PORT_SYS=15000
START_RANGE_TOMCAT_PORT_HTTP=18000
START_RANGE_TOMCAT_PORT_AJP=19000
# Initialisation des variables globales
MODE_CREATION=0
MODE_DESTRUCTION=0
TOMCAT_ENABLE_SHUTDOWN_PORT=""
OPTION_WORKERS_PROPERTIES=""
OPTION_VHOST_APACHE_SERVERNAME=""
NB_CHIFFRES_DANS_NUM_INSTANCE=$( printf "%d" "$MAX_INSTANCES" | wc -c )
REP_TOMCAT_MULTIINSTANCE="/var/lib/tomcat$VERSION_TOMCAT-multiinstances"
# On récupère également le nom de l'utilisateur et du groupe de Tomcat
if [ ! -f /etc/default/tomcat$VERSION_TOMCAT ]; then
echo "ERREUR: /etc/default/tomcat$VERSION_TOMCAT inaccessible." >&2
exit 1
fi
. /etc/default/tomcat$VERSION_TOMCAT
# Par défaut, on arrête le script à la première erreur non "catchée"
set -e
# Fonctions
# Arguments
# $1 : Version de Tomcat
# $2 : nom du worker
# $3 : port AJP du worker
ajout_worker_properties() {
test -n "$1" && test -n "$2" && test -n "$3" || exit 1
# On se fiche un peu de la version de Tomcat, en fait :)
case "$1" in
6|7)
cat <<EOF
# Script MI Tomcat : ajout du worker $2
worker.$2.port = $3
worker.$2.host = localhost
worker.$2.type = ajp13
EOF
;;
*)
echo "ERREUR: version de Tomcat non-gérée." >&2
exit 1
;;
esac
}
# Arguments
# $1 : Version de Tomcat
# $2 : port système (d'habitude 8005)
# $3 : port connecteur HTTP (8080, d'habitude)
# $4 : port connecteur AJP (8009, d'habitude)
generate_patch_tomcat_server_xml() {
test -n "$1" && test -n "$2" && test -n "$3" && test -n "$4" && test -n "$5" || exit 1
case "$1" in
6|7)
generate_patch_tomcat_server_xml_6 "$2" "$3" "$4" "$5"
;;
*)
echo "ERREUR: version de Tomcat non-gérée." >&2
exit 1
;;
esac
}
# Arguments
# $1 : port système (d'habitude 8005)
# $2 : port connecteur HTTP (8080, d'habitude)
# $3 : port connecteur AJP (8009, d'habitude)
generate_patch_tomcat_server_xml_6() {
local NOM_WORKER TOMCAT_PORT_SYS TOMCAT_PORT_HTTP TOMCAT_PORT_AJP
test -n "$1" && test -n "$2" && test -n "$3" && test -n "$4" || exit 1
NOM_WORKER="$1"
TOMCAT_PORT_SYS="$2"
TOMCAT_PORT_HTTP="$3"
TOMCAT_PORT_AJP="$4"
cat <<EOF
--- server.xml
+++ server.xml
@@ -19,7 +19,7 @@
define subcomponents such as "Valves" at this level.
Documentation at /docs/config/server.html
-->
-<Server port="8005" shutdown="SHUTDOWN">
+<Server port="$TOMCAT_PORT_SYS" shutdown="SHUTDOWN">
<!-- Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" />
-->
@@ -69,10 +69,12 @@
APR (HTTP/AJP) Connector: /docs/apr.html
Define a non-SSL HTTP/1.1 Connector on port 8080
-->
- <Connector port="8080" protocol="HTTP/1.1"
+ <!--
+ <Connector port="$TOMCAT_PORT_HTTP" protocol="HTTP/1.1"
connectionTimeout="20000"
URIEncoding="UTF-8"
redirectPort="8443" />
+ -->
<!-- A "Connector" using the shared thread pool-->
<!--
<Connector executor="tomcatThreadPool"
@@ -91,9 +93,7 @@
-->
<!-- Define an AJP 1.3 Connector on port 8009 -->
- <!--
- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
- -->
+ <Connector port="$TOMCAT_PORT_AJP" protocol="AJP/1.3" redirectPort="8443" address="127.0.0.1" />
<!-- An Engine represents the entry point (within Catalina) that processes
@@ -105,7 +105,7 @@
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
-->
- <Engine name="Catalina" defaultHost="localhost">
+ <Engine name="Catalina" defaultHost="localhost" jvmRoute="$NOM_WORKER">
<!--For clustering, please take a look at documentation at:
/docs/cluster-howto.html (simple how to)
EOF
}
# Petites fonctions pour éviter de faire des concatenations
# hasardeuses un peu partout
get_tomcat_user() {
case $VERSION_TOMCAT in
'6')
printf "%s" "$TOMCAT6_USER"
;;
'7')
printf "%s" "$TOMCAT7_USER"
;;
esac
}
get_tomcat_group() {
# TODO : une idée à creuser serait de créer un groupe tomcat incluant tomcat6 et tomcat7
case $VERSION_TOMCAT in
'6')
printf "%s" "$TOMCAT6_GROUP"
;;
'7')
printf "%s" "$TOMCAT7_GROUP"
;;
esac
}
# affiche le message d'aide
usage() {
cat <<EOF
$0 -c suffixe
$0 -d numero_instance
$0 -h
-c xxxx : création d'une instance avec le suffixe 'xxxx' (15 caract. max)
-d NNN : destruction de l'instance numéro NNN
-S : activation du port d'extinction de Tomcat
(déconseillé en production, ne gêne pas le gestionnaire de démon de Debian :
http://tomcat.apache.org/tomcat-7.0-doc/config/server.html#Common_Attributes
http://wiki.apache.org/tomcat/FAQ/Security#Q2 )
Ce script peut également modifier le fichier workers.properties
pour y ajouter les infos du worker créé.
-w /etc/.../workers.properties : Modification du workers.properties
-h : ce message d'aide
EOF
}
# Début du code
# gestion des options de lancement
while getopts c:d:Sw:h f; do
case $f in
'c')
MODE_CREATION=1
SUFFIX="$OPTARG"
OLD_LC_ALL="$LC_ALL"
export LC_ALL=C
export LANG=C
if [ $( printf "$SUFFIX" | egrep -c "^[a-z][a-z0-9_]{0,14}$" ) -ne 1 ]; then
echo "ERREUR: suffixe vide ou contenant des caractères interdits : regexp : [a-z][a-z0-9_]{0,14}" >&2
exit 1
fi
export LC_ALL="$OLD_LC_ALL"
export LANG="$OLD_LC_ALL"
;;
'd')
MODE_DESTRUCTION=1
NUM_INSTANCE=$( printf "%d" "$OPTARG" )
;;
'h')
usage
exit 0
;;
'S')
TOMCAT_ENABLE_SHUTDOWN_PORT="1"
;;
'w')
OPTION_WORKERS_PROPERTIES="$OPTARG"
if [ ! -f "$OPTION_WORKERS_PROPERTIES" ]; then
echo "ERREUR: fichier workers.properties inexistant : $OPTION_WORKERS_PROPERTIES" >&2
exit 1
fi
;;
\?)
usage >&2
exit 1
;;
esac
done
#(code inutile pour ce script, mais que je garde parce qu'on ne sait jamais)
#shift $( expr $OPTIND - 1 )
#DATA="$1"
# Petite vérif.
case $(( $MODE_CREATION + $MODE_DESTRUCTION )) in
'0')
echo "ERREUR: veuillez choisir entre création et destruction." >&2
usage >&2
exit 1
;;
'2')
echo "ERREUR: tu veux créer et détruire en même temps, petit malin ?" >&2
exit 1
;;
esac
# - est-ce qu'on est bien sur une Debian 6 ou 7
# (c'est surtout pour éviter les mauvaises surprises, ce script n'ayant pas été testé ailleurs)
if [ ! -f "/etc/debian_version" ] || [ $( egrep -c "^(6.|7.)" /etc/debian_version ) -ne 1 ]; then
echo "ERREUR: mauvaise version de Debian détectée. Arrêt par prudence. Vérifiez le code de ce script avant de forcer." >&2
exit 1
fi
# - est-ce que Tomcat est bien installé ?
if ! dpkg -l tomcat$VERSION_TOMCAT >/dev/null 2>&1; then
echo "ERREUR: tomcat$VERSION_TOMCAT ne semble pas avoir été installé." >&2
exit 1
fi
# - on vérifie que le /etc/default/tomcatN contient bien les infos qui nous intéresse
if [ ! -n $( get_tomcat_user ) ] || [ ! -n $( get_tomcat_group) ]; then
echo "ERREUR: TOMCAT_USER ou TOMCAT_GROUP indeterminé." >&2
exit 1
fi
# Fin des vérifications
if [ "$MODE_CREATION" -eq 1 ]; then
# Détermination du numéro d'instance :
# On cherche un numéro inutilisé ni par une instance Tomcat, ni par une instance Postgres
for NUM_INSTANCE in $( seq 1 $MAX_INSTANCES ); do
# Finalement, on base tout sur le nom de l'instance : ça
# nous permet de retrouver facilement le répertoire de l'instance
# lors de la suppression (le HOME de tomcat7 est sur CATALINA_HOME,
# pas sur CATALINA_BASE :'(
NOM_INSTANCE=$( printf "%s_%0$NB_CHIFFRES_DANS_NUM_INSTANCE""d_%s" "$( get_tomcat_user )" "$NUM_INSTANCE" "$SUFFIX" )
USERID_TOMCAT=$(( $START_RANGE_TOMCATUID + $NUM_INSTANCE ))
USERNAME_TOMCAT="$NOM_INSTANCE"
SCRIPTNAME_TOMCAT="$NOM_INSTANCE"
WORKERNAME_TOMCAT="worker_$NOM_INSTANCE"
if [ ! -d "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE" ] && ! getent passwd "$USERID_TOMCAT" >/dev/null 2>&1 && ! getent passwd "$USERNAME_TOMCAT" >/dev/null 2>&1; then
echo "INFO: Numéro d'instance libre trouvé : $NUM_INSTANCE"
break
fi
done
if [ "$NUM_INSTANCE" -eq "$MAX_INSTANCES" ]; then
echo "ERREUR: plus de numéro d'instance disponible." >&2
exit 1
fi
# Vérifier que le REP_TOMCAT_MULTIINSTANCE pré-existe.
# TODO review : finalement pas une bonne idée de le faire créer par le script ?
if [ ! -d "$REP_TOMCAT_MULTIINSTANCE" ]; then
# (finalement, on le crée nous même...)
echo "WARNING: le répertoire '$REP_TOMCAT_MULTIINSTANCE' n'existe pas (normal si première exécution). Création..."
mkdir -p "$REP_TOMCAT_MULTIINSTANCE"
chown -R "root:$( get_tomcat_group )" "$REP_TOMCAT_MULTIINSTANCE"
chmod 750 "$REP_TOMCAT_MULTIINSTANCE"
fi
# Copie du script d'init et du /etc/default de Tomcat
# Note : pour les scripts d'init, j'aurais aimé faire de simples liens symboliques vers
# un script générique. Malheureusement, ça pose de gros soucis pour LSB (insserv
# semble avoir les liens symboliques en horreur) et on aurait dû tout faire à la
# main, d'où plus d'inconvénients que d'avantages.
echo "INFO: création de l'utilisateur système dédié..."
useradd -u "$USERID_TOMCAT" -s /bin/false -g $( get_tomcat_group ) -d $( getent passwd $( get_tomcat_user ) | cut -d : -f 6 ) "$USERNAME_TOMCAT"
echo "INFO: création du script de lancement..."
cat /etc/init.d/tomcat$VERSION_TOMCAT | \
sed "s/^\(#[[:space:]]*Provides[[:space:]]*:[[:space:]]*\)tomcat[0-9]\+[[:space:]]*$/\1$SCRIPTNAME_TOMCAT/" | \
sed 's/^NAME=tomcat[0-9]\+$/NAME=$( basename "$0" | sed "s\/^[SK][0-9]\+\/\/" )/' \
>/etc/init.d/$SCRIPTNAME_TOMCAT
chmod +x /etc/init.d/$SCRIPTNAME_TOMCAT
echo "INFO: création du fichier de paramétrage dans /etc/default..."
cat /etc/default/tomcat$VERSION_TOMCAT | sed "s/^\(TOMCAT[0-9]\+_USER\)=tomcat[0-9]\+$/\1=$USERNAME_TOMCAT/" >/etc/default/$SCRIPTNAME_TOMCAT
echo "# Script MI Tomcat : Spécification des CATALINA_HOME et CATALINA_BASE" >>/etc/default/$SCRIPTNAME_TOMCAT
echo "CATALINA_HOME=/usr/share/tomcat$VERSION_TOMCAT" >>/etc/default/$SCRIPTNAME_TOMCAT
echo "CATALINA_BASE=$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE" >>/etc/default/$SCRIPTNAME_TOMCAT
# Activation du script d'init
echo "INFO: activation du script d'init..."
update-rc.d $SCRIPTNAME_TOMCAT defaults
# Instanciation du CATALINA_BASE
# Copie du /var/lib/tomcatN (notre template, en gros)
echo "INFO: génération du CATALINA_BASE ( $REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE )..."
mkdir "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE"
cp -a /var/lib/tomcat$VERSION_TOMCAT/* "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/"
rm "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/conf"
rm "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/logs"
rm "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/work"
mkdir "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/conf"
mkdir "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/logs"
mkdir "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/work"
cp -r /var/lib/tomcat$VERSION_TOMCAT/conf/* "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/conf/"
# Parfois, le work est vide. Solution temporaire : on émet un warning
if [ "$( ls /var/lib/tomcat$VERSION_TOMCAT/work/* 2>/dev/null | wc -l )" -gt 0 ]; then
cp -r /var/lib/tomcat$VERSION_TOMCAT/work/* "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/work/"
else
echo "INFO: le répertoire /work/ du template est vide. A priori, c'est normal."
fi
chown -R $USERNAME_TOMCAT:$( get_tomcat_group ) "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE"
# Réglage des ports TCP/IP
generate_patch_tomcat_server_xml "$VERSION_TOMCAT" \
"$WORKERNAME_TOMCAT" \
$( test -n "$TOMCAT_ENABLE_SHUTDOWN_PORT" && echo $(( $START_RANGE_TOMCAT_PORT_SYS + $NUM_INSTANCE )) || echo "-1" ) \
$(( $START_RANGE_TOMCAT_PORT_HTTP + $NUM_INSTANCE )) \
$(( $START_RANGE_TOMCAT_PORT_AJP + $NUM_INSTANCE )) | \
patch $REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/conf/server.xml
# Génération du fichier de config de logrotate
echo "INFO: génération du logrotate..."
cat /etc/logrotate.d/tomcat$VERSION_TOMCAT | \
sed "s#^/var/log/tomcat[0-9]\+/catalina.out #$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/logs/catalina.out #" | \
sed "s/create 640 tomcat[0-9]\+ adm/create 640 $USERNAME_TOMCAT adm/" \
>/etc/logrotate.d/$SCRIPTNAME_TOMCAT
# partie annexe n°1 : workers.properties
if [ -f "$OPTION_WORKERS_PROPERTIES" ]; then
echo "INFO: Modification du workers.properties..."
# Ajout du worker à la worker.list
TMP_SUFFIX=$( date +%Y%m%d-%H%M%S )
sed -i.$TMP_SUFFIX -e "s/^worker.list=\(.\+\)$/worker.list=\1,$WORKERNAME_TOMCAT/g" -e "s/^worker.list=$/worker.list=$WORKERNAME_TOMCAT/g" "$OPTION_WORKERS_PROPERTIES"
echo "NOTICE: une sauvegarde $OPTION_WORKERS_PROPERTIES.$TMP_SUFFIX a été créée, n'hésitez pas à la supprimer."
# Ajouts de la définition du worker
ajout_worker_properties "$VERSION_TOMCAT" "$WORKERNAME_TOMCAT" $(( $START_RANGE_TOMCAT_PORT_AJP + $NUM_INSTANCE )) >> "$OPTION_WORKERS_PROPERTIES"
fi
# Fin
echo "INFO: Création terminée. Serveur Tomcat éteint."
echo "INFO: Pour connecter mod-jk au worker, voici la ligne à ajouter au vhost : JkMount /* $WORKERNAME_TOMCAT"
echo "INFO: Pour allumer l'instance Tomcat : service $SCRIPTNAME_TOMCAT start"
fi
if [ "$MODE_DESTRUCTION" -eq 1 ]; then
USERID_TOMCAT=$(( $START_RANGE_TOMCATUID + $NUM_INSTANCE ))
#echo "NOTICE: le script est en mode -e. Il s'arrête donc à la première erreur. Le cas échéant, il faudra terminer le nettoyage à la main."
# On recherche le nom de l'instance
USERNAME_TOMCAT=$( getent passwd $USERID_TOMCAT | cut -d : -f 1 )
NOM_INSTANCE="$USERNAME_TOMCAT"
SCRIPTNAME_TOMCAT="$NOM_INSTANCE"
WORKERNAME_TOMCAT="worker_$NOM_INSTANCE"
if [ ! -n "$USERNAME_TOMCAT" ]; then
echo "ERREUR: l'utilisateur correspondant à l'uid $USERID_TOMCAT est introuvable." >&2
exit 1
fi
if service "$SCRIPTNAME_TOMCAT" status >/dev/null 2>&1; then
echo "ERREUR: l'instance tomcat tourne toujours. Ne pas oublier le service $SCRIPTNAME_TOMCAT stop" >&2
exit 1
fi
# Suppression utilisateur et répertoire dédié
echo "INFO: suppression de l'utilisateur..."
userdel $USERNAME_TOMCAT
echo "INFO: suppression du répertoire dédié..."
rm -rf "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE"
echo "INFO: suppresion fichier default..."
rm -f "/etc/default/$SCRIPTNAME_TOMCAT"
# Suppression du script d'init
echo "INFO: désactivation et suppression du script d'init..."
rm -f /etc/init.d/$SCRIPTNAME_TOMCAT
update-rc.d "$SCRIPTNAME_TOMCAT" remove
# Suppression logrotate
echo "INFO: suppression logrotate..."
rm -f /etc/logrotate.d/$SCRIPTNAME_TOMCAT
# partie annexe n°1 : workers.properties
if [ -f "$OPTION_WORKERS_PROPERTIES" ]; then
echo "INFO: Modification du workers.properties..."
# Soustraction de la définition du worker
TMP_SUFFIX=$( date +%Y%m%d-%H%M%S )
sed -i.$TMP_SUFFIX -e "/^worker.list=/s/$WORKERNAME_TOMCAT[,]\?//g" \
-e '/^worker.list=.*,$/s/,$//' \
-e "/^# Script MI Tomcat : ajout du worker $WORKERNAME_TOMCAT$/d" \
-e "/^worker.$WORKERNAME_TOMCAT/d" \
"$OPTION_WORKERS_PROPERTIES"
echo "NOTICE: une sauvegarde $OPTION_WORKERS_PROPERTIES.$TMP_SUFFIX a été créée, n'hésitez pas à la supprimer."
fi
# Fin
echo "INFO: suppression terminée."
fi

View file

@ -0,0 +1,23 @@
#!/bin/sh
ESCLAVE="a.b.c.d"
for i in $@; do
# On récupère le numéro de port
POSTGRESQL_PORT="$( pg_lsclusters | sed 's/[[:space:]]\+/ /g' | cut -f 2,3 -d ' ' | grep -w "$i" | cut -f 2 -d ' ' )"
if [ -z "$POSTGRESQL_PORT" ]; then
echo "ERREUR: pas de port trouvé pour '$i'." >&2
continue
fi
# dump1 : local
DUMP1=$( mktemp local-XXXX )
DUMP2=$( mktemp distant-XXXXX )
su -l -c "pg_dumpall -p $POSTGRESQL_PORT" postgres > $DUMP1
su -l -c "ssh postgres@$ESCLAVE pg_dumpall -p $POSTGRESQL_PORT" postgres > $DUMP2
echo "$i..."
diff -u "$DUMP1" "$DUMP2"
rm -f "$DUMP1" "$DUMP2"
done

View file

@ -0,0 +1,59 @@
#!/bin/sh
# Ce script génère juste du SQL permettant, à partir
# du résultat du script multi-pg, de renommer l'utilisateur
# de l'instance en instance_admin et de lui donner les
# droits nécessaires au bon fonctionnement de phppgadmin,
# et de créer un utilisateur instance avec des droits
# encore plus limités (pas de modif de la structure, seulement
# select,insert,update,delete et gestion des séquences)
#
# TODO: message d'aide pour les arguments:
# script.sh nom_instance ancien_mot_de_passe
# TODO 2: essayer d'éviter le mot de passe dans la ligne de commande :-(
set -e
# Arguments:
# $1 : Nom de la bdd / de l'utilisateur
# $2 : mot de passe de l'utilisateur
# $3 : nom de l'utilisateur dédié à la réplication
# $4 : mot de passe dudit utilisateur
generate_script_postgresql_creation_db_et_user() {
local NOM_INSTANCE POSTGRESQL_PASSWORD ADMIN_PASSWORD
test -n "$1" && test -n "$2" && test -n "$3" || exit 1
NOM_INSTANCE="$1"
POSTGRESQL_PASSWORD="$2"
ADMIN_PASSWORD="$3"
ADMIN_USERNAME="$NOM_INSTANCE""_admin"
cat <<EOF
ALTER USER $NOM_INSTANCE RENAME TO $ADMIN_USERNAME;
ALTER USER $ADMIN_USERNAME WITH PASSWORD '$ADMIN_PASSWORD';
-- Re-création de l'utilisateur normal
CREATE USER $NOM_INSTANCE WITH PASSWORD '$POSTGRESQL_PASSWORD' CONNECTION LIMIT 50;
GRANT CONNECT ON DATABASE $NOM_INSTANCE TO $NOM_INSTANCE;
\c $NOM_INSTANCE
GRANT USAGE ON SCHEMA public TO $NOM_INSTANCE;
GRANT SELECT,INSERT,UPDATE,DELETE ON ALL TABLES IN SCHEMA public TO $NOM_INSTANCE;
ALTER DEFAULT PRIVILEGES FOR ROLE $ADMIN_USERNAME IN SCHEMA public GRANT select,insert,update,delete ON TABLES TO $NOM_INSTANCE;
ALTER DEFAULT PRIVILEGES FOR ROLE $ADMIN_USERNAME IN SCHEMA public GRANT USAGE,UPDATE ON SEQUENCES TO $NOM_INSTANCE;
-- Ce passage-là est plus parano, mais à étudier avant suppression
GRANT SELECT ON pg_user TO $ADMIN_USERNAME;
GRANT SELECT ON pg_roles TO $ADMIN_USERNAME;
GRANT SELECT ON pg_group TO $ADMIN_USERNAME;
GRANT SELECT ON pg_authid TO $ADMIN_USERNAME;
GRANT SELECT ON pg_auth_members TO $ADMIN_USERNAME;
GRANT SELECT ON pg_database TO $ADMIN_USERNAME;
GRANT SELECT ON pg_tablespace TO $ADMIN_USERNAME;
GRANT SELECT ON pg_settings TO $ADMIN_USERNAME;
EOF
}
NOM_INSTANCE="$1"
POSTGRESQL_PASSWORD="$2"
ADMIN_PASSWORD=$( dd if=/dev/random 2>/dev/null bs=1 count=10 status=noxfer | base64 | sed 's#[/=]##g' )
generate_script_postgresql_creation_db_et_user "$NOM_INSTANCE" "$POSTGRESQL_PASSWORD" "$ADMIN_PASSWORD"

86
oldies/script_multi-pg-reset.sh Executable file
View file

@ -0,0 +1,86 @@
#!/bin/sh
generate_dump() {
test -n "$1" || exit 1
cat <<EOF
--
-- PostgreSQL database cluster dump
--
\connect postgres
--
-- Database creation
--
CREATE DATABASE $1 WITH TEMPLATE = template0 OWNER = $1_admin;
REVOKE ALL ON DATABASE $1 FROM PUBLIC;
REVOKE ALL ON DATABASE $1 FROM $1_admin;
GRANT ALL ON DATABASE $1 TO $1_admin;
GRANT CONNECT ON DATABASE $1 TO $1;
REVOKE ALL ON DATABASE template1 FROM PUBLIC;
REVOKE ALL ON DATABASE template1 FROM postgres;
GRANT ALL ON DATABASE template1 TO postgres;
\connect $1
--
-- PostgreSQL database dump
--
SET statement_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;
--
-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner:
--
CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
--
-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner:
--
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
--
-- Name: public; Type: ACL; Schema: -; Owner: postgres
--
REVOKE ALL ON SCHEMA public FROM PUBLIC;
REVOKE ALL ON SCHEMA public FROM postgres;
GRANT ALL ON SCHEMA public TO postgres;
GRANT ALL ON SCHEMA public TO $1_admin;
GRANT USAGE ON SCHEMA public TO $1;
SET search_path = public, pg_catalog;
--
-- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: $1_admin
--
ALTER DEFAULT PRIVILEGES FOR ROLE $1_admin IN SCHEMA public REVOKE ALL ON SEQUENCES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES FOR ROLE $1_admin IN SCHEMA public REVOKE ALL ON SEQUENCES FROM $1_admin;
ALTER DEFAULT PRIVILEGES FOR ROLE $1_admin IN SCHEMA public GRANT USAGE,UPDATE ON SEQUENCES TO $1;
--
-- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: public; Owner: $1_admin
--
ALTER DEFAULT PRIVILEGES FOR ROLE $1_admin IN SCHEMA public REVOKE ALL ON TABLES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES FOR ROLE $1_admin IN SCHEMA public REVOKE ALL ON TABLES FROM $1_admin;
ALTER DEFAULT PRIVILEGES FOR ROLE $1_admin IN SCHEMA public GRANT SELECT,INSERT,DELETE,UPDATE ON TABLES TO $1;
EOF
}
generate_dump "$1"

View file

@ -0,0 +1,66 @@
#!/bin/sh
# Script en version alpha-brouillon
# À n'utiliser qu'en connaissance de cause et avec
# une parfaite maîtrise du fonctionnement de la SR
# config sur MAITRE inutile ici
MAITRE=""
ESCLAVE="a.b.c.d"
set -e
# on ne peut pas faire le | while read avec du ssh dans la boucle ??
#for NOM_INSTANCE in $( pg_lsclusters -h | sed 's/[[:space:]]\+/ /g' | cut -d ' ' -f 2 ); do
for NOM_INSTANCE in $@; do
# Récupération de la ligne 'pg_lsclusters' complète
LINE="$( pg_lsclusters -h | sed 's/[[:space:]]\+/ /g' | egrep "^[0-9.]+ $NOM_INSTANCE " )"
if [ -z "$LINE" ]; then
echo "ERREUR: instance '$NOM_INSTANCE' inexistante." >&2
exit 1
fi
# Découpage des éléments de ladite ligne
VERSION_POSTGRESQL="$( echo "$LINE" | cut -d ' ' -f 1 )"
POSTGRESQL_PORT="$( echo "$LINE" | cut -d ' ' -f 3 )"
STATUT="$( echo "$LINE" | cut -d ' ' -f 4 )"
POSTGRESQL_PGDATA="$( echo "$LINE" | cut -d ' ' -f 6 )"
# On ne s'occupe que des instances "online" pures
if [ "$( echo "$LINE" | cut -d ' ' -f 4 )" != "online" ]; then
echo "NOTICE: on ignore l'instance '$NOM_INSTANCE' dont le statut ('$STATUT') n'est pas 'online'."
continue
fi
# On vérifie que l'instance en face est bien éteinte
if [ "$( ssh root@$ESCLAVE pg_lsclusters -h | grep -w "$NOM_INSTANCE" | grep online | wc -l )" -ne 0 ]; then
echo "NOTICE: l'instance '$NOM_INSTANCE' semble toujours allumée sur le serveur distant. On l'ignore."
continue
fi
# ...et on vérifie que le fichier recovery.done est dispo
if ! su -c "ssh postgres@$ESCLAVE test -f $POSTGRESQL_PGDATA/recovery.done" postgres; then
echo "NOTICE: le fichier recovery.done ne semble pas dispo pour l'instance distante '$NOM_INSTANCE'."
continue
fi
echo "INFO: resynchro de l'instance '$NOM_INSTANCE'..."
# Marquage
echo "INFO: rsync..."
echo "SELECT pg_start_backup('resynchro_maitre', true);" | su -c "psql -p $POSTGRESQL_PORT -v ON_ERROR_STOP=1" postgres
# Resynchro des fichiers
sleep 1
su -c "rsync -aHc --exclude=/postmaster.pid --exclude=/postmaster.opts --exclude=/server.key --exclude=/server.crt --exclude=/recovery.conf --exclude=/recovery.done --exclude=/archives_from_master --exclude=lost+found /var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/ postgres@$ESCLAVE:/var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/" postgres
# Vidange du dossier 'archives_from_master' distant, par prudence
su -c "ssh postgres@$ESCLAVE rm -rvf $POSTGRESQL_PGDATA/archives_from_master/*" postgres
sleep 1
echo "SELECT pg_stop_backup();" | su -c "psql -p $POSTGRESQL_PORT -v ON_ERROR_STOP=1" postgres
# Renommage du fichier recovery.conf distant pour activation
echo "INFO: renommage recovery.done distant en .conf..."
su -c "ssh postgres@$ESCLAVE mv -v $POSTGRESQL_PGDATA/recovery.done $POSTGRESQL_PGDATA/recovery.conf" postgres
# Démarrage instance distante
ssh root@$ESCLAVE pg_ctlcluster "$VERSION_POSTGRESQL" "$NOM_INSTANCE" start
done
echo "The end."

View file

@ -0,0 +1,409 @@
#!/bin/sh
# Ce petit script customise une VZ Debian 7
# pour préparer l'arrivée d'un Drupal :
# - install Apache/PHP
# - install mysql ou postgresql
# - install éventuelle de PHPMyAdmin/PHPPgAdmin
TYPE_BDD=""
INSTALL_PHPBDDADMIN=""
INSTALL_DRUSH=""
BDD_CONNEXIONS_OPEN_BAR=""
NOM_UTILISATEUR="deploy"
# Arrêt à la première erreur non-catchée
set -e
usage() {
cat <<EOF
$0 [{ -m | -p } [ -l ] [ -a ]] nom_site
$0 -h
-m : site avec MySQL
-p : site avec PostgreSQL
-a : ajout de PHPMyAdmin / PHPPgAdmin
-l : ouverture des connexions à la BDD depuis le LAN
-d : install drush via php-pear
nom_site : pseudo du site à créer (nom du répertoire et du fichier du vhost)
-h : ce message d'aide
EOF
}
patch_apache_default_vhost() {
cat <<EOF
diff --git a/apache2/sites-available/default b/apache2/sites-available/default
index b0703f5..3efc3dd 100644
--- a/apache2/sites-available/default
+++ b/apache2/sites-available/default
@@ -1,26 +1,18 @@
<VirtualHost *:80>
ServerAdmin webmaster@localhost
- DocumentRoot /var/www
+ DocumentRoot /var/www/default
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
- <Directory /var/www/>
+ <Directory /var/www/default/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
- ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
- <Directory "/usr/lib/cgi-bin">
- AllowOverride None
- Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
- Order allow,deny
- Allow from all
- </Directory>
-
ErrorLog \${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
diff --git a/apache2/sites-available/default-ssl b/apache2/sites-available/default-ssl
index ea454b8..cca8e63 100644
--- a/apache2/sites-available/default-ssl
+++ b/apache2/sites-available/default-ssl
@@ -2,26 +2,18 @@
<VirtualHost _default_:443>
ServerAdmin webmaster@localhost
- DocumentRoot /var/www
+ DocumentRoot /var/www/default
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
- <Directory /var/www/>
+ <Directory /var/www/default/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
- ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
- <Directory "/usr/lib/cgi-bin">
- AllowOverride None
- Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
- Order allow,deny
- Allow from all
- </Directory>
-
ErrorLog \${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
EOF
}
patch_mysql_access_from_lan() {
cat <<EOF
diff --git a/mysql/my.cnf b/mysql/my.cnf
index 1f96915..94ea010 100644
--- a/mysql/my.cnf
+++ b/mysql/my.cnf
@@ -44,7 +44,7 @@ skip-external-locking
#
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
-bind-address = 127.0.0.1
+#bind-address = 127.0.0.1
#
# * Fine Tuning
#
EOF
}
patch_phpmyadmin_access_from_lan() {
cat <<EOF
diff --git a/phpmyadmin/apache.conf b/phpmyadmin/apache.conf
index 415acb9..7f274a3 100644
--- a/phpmyadmin/apache.conf
+++ b/phpmyadmin/apache.conf
@@ -1,11 +1,14 @@
# phpMyAdmin default Apache configuration
-Alias /phpmyadmin /usr/share/phpmyadmin
+#Alias /phpmyadmin /usr/share/phpmyadmin
<Directory /usr/share/phpmyadmin>
Options FollowSymLinks
DirectoryIndex index.php
+ Order allow,deny
+ Allow from 172.20.0.0/16
+
<IfModule mod_php5.c>
AddType application/x-httpd-php .php
diff --git a/apache2/sites-available/phpmyadmin b/apache2/sites-available/phpmyadmin
new file mode 100644
index 0000000..8d81d37
--- /dev/null
+++ b/apache2/sites-available/phpmyadmin
@@ -0,0 +1,23 @@
+<VirtualHost *:80>
+ ServerAdmin webmaster@localhost
+
+ DocumentRoot /usr/share/phpmyadmin
+
+ Include /etc/apache2/conf.d/phpmyadmin.conf
+
+ ServerName phpmyadmin
+ ServerAlias phpmyadmin.*
+
+ <Directory />
+ Options FollowSymLinks
+ AllowOverride None
+ </Directory>
+
+ ErrorLog \${APACHE_LOG_DIR}/phpmyadmin_error.log
+
+ # Possible values include: debug, info, notice, warn, error, crit,
+ # alert, emerg.
+ LogLevel warn
+
+ CustomLog \${APACHE_LOG_DIR}/phpmyadmin_access.log combined
+</VirtualHost>
EOF
}
# Début du code
# gestion des options de lancement
while getopts almpdh f; do
case $f in
'a')
INSTALL_PHPBDDADMIN="true"
;;
'l')
BDD_CONNEXIONS_OPEN_BAR="yes"
;;
'm')
TYPE_BDD="mysql"
;;
'p')
TYPE_BDD="postgresql"
;;
'd')
INSTALL_DRUSH="true"
;;
'h')
usage
exit 0
;;
\?)
usage >&2
exit 1
;;
esac
done
#(code inutile, mais que je garde parce qu'on ne sait jamais)
shift $( expr $OPTIND - 1 )
NOM_SITE="$1"
# On part dans le répertoire de travail
cd /etc
# Petites vérifications
if [ -z "$NOM_SITE" ]; then
echo "ERREUR: veuillez donner un identifiant au site." >&2
exit 1
fi
if [ ! -z "$INSTALL_PHPBDDADMIN" ] && [ -z "$TYPE_BDD" ]; then
echo "ERREUR: veuillez choisir PostgreSQL/MySQL." >&2
exit 1
fi
if ! git status >/dev/null 2>&1 || [ $( git status --porcelain | wc -l ) -gt 0 ]; then
echo "ERREUR: travaux non-commités dans /etc." >&2
exit 1
fi
# Installation des paquets communs
# (note: php5-mcrypt est une dépendance de phpmyadmin. On le met par défaut pour éviter
# des disparités dans les install dev/prod)
echo "INFO: aptitude update..."
aptitude update
echo "INFO: aptitude install..."
aptitude install libapache2-mod-php5 php5-gd php-apc php5-mcrypt
git add .
git commit -a -m "Installation Apache/PHP"
# Petite config. optimisée de php-apc
if [ -f "/etc/php5/mods-available/apc.ini" ]; then
echo "apc.rfc1867 = 1" >>/etc/php5/mods-available/apc.ini
echo "apc.shm_size = 128M" >>/etc/php5/mods-available/apc.ini
git commit -a -m "PHP/APC: configuration pour Drupal"
fi
echo "INFO: modification du default vhost..."
mkdir /var/www/default
mv /var/www/index.html /var/www/default/
patch_apache_default_vhost | git apply -
git commit -a -m "Apache: default vhost dans sous-rep."
case "$TYPE_BDD" in
'mysql')
echo "INFO: aptitude install php5-mysql mysql-server ..."
echo "NOTICE: laisser le mot de passe vide."
aptitude install php5-mysql mysql-server
git add .
git commit -a -m "Install MySQL"
if [ -n "$BDD_CONNEXIONS_OPEN_BAR" ]; then
patch_mysql_access_from_lan | git apply -
git commit -a -m "MySQL: accès depuis le réseau"
fi
# Génération d'un mot de passe aléatoire
MYSQL_ADMIN_PASSWORD=$( dd if=/dev/random 2>/dev/null bs=1 count=10 status=noxfer | base64 | sed 's#[/=]##g' )
echo "UPDATE mysql.user SET Password = PASSWORD('$MYSQL_ADMIN_PASSWORD') where User = 'root'" | mysql
echo "FLUSH PRIVILEGES" | mysql
# mise en place de la sauvegarde
printf "[client]\npassword=\"%s\"\n" "$MYSQL_ADMIN_PASSWORD" >~/.my_sauvegarde.cnf
RANDOM_NUMBER=$( hexdump -n 2 -e '/2 "%u"' /dev/urandom )
echo "$(( $RANDOM_NUMBER % 60 )) 10,16 * * 1-5 root test -x /root/sauvegarde_mysql.sh && /root/sauvegarde_mysql.sh --delete-olds" >>/etc/crontab
git commit -a -m "cron: sauvegarde MySQL"
;;
'postgresql')
echo "INFO: aptitude install php5-pgsql postgresql-9.1 ..."
aptitude install php5-pgsql postgresql-9.1
git add .
git commit -a -m "Install PostgreSQL"
# mise en place de la sauvegarde
RANDOM_NUMBER=$( hexdump -n 2 -e '/2 "%u"' /dev/urandom )
echo "$(( $RANDOM_NUMBER % 60 )) 11,17 * * 1-5 root test -x /root/sauvegarde_postgresql.sh && /root/sauvegarde_postgresql.sh --delete-olds" >>/etc/crontab
git commit -a -m "cron: sauvegarde PostgreSQL"
;;
esac
if [ ! -z "$INSTALL_PHPBDDADMIN" ]; then
case "$TYPE_BDD" in
'mysql')
echo "mdp root MySQL à copier : $MYSQL_ADMIN_PASSWORD"
echo "appuyer sur entree pour continuer"
read TOTO
aptitude install phpmyadmin
git add .
git commit -a -m "Install phpmyadmin"
patch_phpmyadmin_access_from_lan | git apply -
a2ensite phpmyadmin
git add .
git commit -a -m "PHPMyAdmin: vhost dédié"
;;
'postgresql')
aptitude install phppgadmin
git add .
git commit -a -m "Install phppgadmin"
# TODO vhost
;;
esac
fi
echo "INFO: a2enmod rewrite..."
a2enmod rewrite
git add .
git commit -a -m "apache: activation mod-rewrite"
# création vhost site Drupal
if [ ! -f "/etc/apache2/sites-available/$NOM_SITE" ]; then
echo "INFO: Création du vhost..."
cat <<EOF >/etc/apache2/sites-available/$NOM_SITE
<VirtualHost *:80>
ServerAdmin webmaster@localhost
ServerName $NOM_SITE
#ServerAlias www
#ServerAlias www.*
DocumentRoot /var/www/$NOM_SITE
<Directory />
Options None
AllowOverride None
deny from all
</Directory>
<Directory /var/www/$NOM_SITE/>
Options FollowSymLinks
AllowOverride All
Order allow,deny
allow from all
</Directory>
ErrorLog \${APACHE_LOG_DIR}/${NOM_SITE}_error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog \${APACHE_LOG_DIR}/${NOM_SITE}_access.log combined
</VirtualHost>
EOF
mkdir /var/www/$NOM_SITE
a2ensite $NOM_SITE
apache2ctl configtest
git add .
git commit -a -m "apache: création vhost $NOM_SITE"
else
echo "WARNING: pas de création du vhost." >&2
fi
if ! id "$NOM_UTILISATEUR" >/dev/null 2>&1; then
echo "INFO: Création compte utilisateur..."
adduser "$NOM_UTILISATEUR"
usermod -a -G www-data "$NOM_UTILISATEUR"
git commit -a -m "création utilisateur dédié pour déploiement/maintenance"
else
echo "WARNING: pas de création de l'utilisateur." >&2
fi
if [ -d "/home/$NOM_UTILISATEUR" ] && [ -d "/var/www/$NOM_SITE" ] && id "$NOM_UTILISATEUR" >/dev/null 2>&1; then
echo "INFO: on en profite pour mettre les droits spéciaux :)"
chown $NOM_UTILISATEUR:www-data /var/www "/var/www/$NOM_SITE"
chmod 2750 /var/www "/var/www/$NOM_SITE"
echo "INFO: création lien symbolique..."
su -c "ln -s '/var/www/$NOM_SITE' '/home/$NOM_UTILISATEUR/www-$NOM_SITE'" "$NOM_UTILISATEUR"
else
echo "WARNING: pas de création du lien symbolique dans le home de l'utilisateur." >&2
fi
# Installation de Drush pour l'utilisateur dédié, si demandée
if [ -n "$INSTALL_DRUSH" ]; then
aptitude install php-pear
git add .
git commit -a -m "Installation de php-pear (pour Drush)"
su -c 'pear config-create $HOME $HOME/.pearrc' "$NOM_UTILISATEUR"
su -c "pear channel-discover pear.drush.org" "$NOM_UTILISATEUR"
su -c "pear install drush/drush" "$NOM_UTILISATEUR"
printf '\n# modification PATH pour drush\nPATH=$PATH:$HOME/pear\n' >>"$( getent passwd $NOM_UTILISATEUR | cut -d : -f 6)/.bashrc"
fi
# Pré-remplissage du fichier /etc/exports
cat <<EOF
Ligne à ajouter à /etc/exports :
/var/www/$NOM_SITE/sites/default/files 172.20.0.0/255.255.0.0(rw,insecure,all_squash,anonuid=33,anongid=33)
EOF
cat <<EOF
Ne pas oublier :
- le serveur de mail,
- la taille des uploads (post_max_size & upload_max_filesize)
EOF
echo "The end."

View file

@ -0,0 +1,200 @@
#!/bin/sh
# Ce script aide à la suppression d'un compte LDAP
# Il s'occupe :
# - suppression LDAP/SMB
# - désabonnement aux listes d'emails (salaries@example.net, ...)
# - destruction de la boîte Cyrus
#
# Ce script nécessite :
# - des identifiants admin sur les listes emails (utilisation des identifiants smbldap pour l'instant. Pas glop.)
# - des identifiants admin sur Cyrus. Ces identifiants seront contenus dans le fichier $CONFIG_IDENTIFIANTS_CYRUS, au format :
# user=LOGIN
# password=PASS
#
# TODO: meilleure détection quand l'email n'existe pas.
# Config
LDAP_HOST="ldap.example.net"
LDAP_ROOT="dc=example,dc=net"
CYRUS_HOST="messagerie.example.net"
CONFIG_EMAIL_DOMAIN="example.net"
CONFIG_IDENTIFIANTS_SMBLDAP="/etc/smbldap-tools/smbldap_bind.conf"
CONFIG_IDENTIFIANTS_CYRUS="/root/.cyrus_auth"
# Initialisation des variables "globales"
MODE_INTERACTIF="1"
LOGFILE=$( mktemp )
# Fonctions
# affiche le message d'aide
usage() {
cat <<EOF
$0 [-f] login
$0 -h
-f : mode bourrin, ne pose pas de questions.
EOF
}
terminaison() {
if [ "$1" = "" ] || [ "$1" -eq 0 ]; then
rm -f "$LOGFILE"
RETOUR=0
else
if [ -s "$LOGFILE" ]; then
printf "NOTICE: log dispo : %s\n" "$LOGFILE" >&2
else
rm -f "$LOGFILE"
fi
RETOUR="$1"
fi
exit "$RETOUR"
}
check_login() {
# Vérifie que le login existe
# Si aucun argument fourni, exit
test -n "$1" || return 1
# Vérification que le login (=uid LDAP) existe
if [ $( ldapsearch -x -h "$LDAP_HOST" -b "ou=users,$LDAP_ROOT" -LLL "uid=$1" | grep -c "^dn: " ) -eq 0 ]; then
echo "ERREUR: login inexistant." >&2
return 1
fi
}
# Enfin : le code...
# gestion des options de lancement
while getopts fh f; do
case $f in
'f')
MODE_INTERACTIF=0
;;
'h')
usage
terminaison 0
;;
\?)
usage >&2
terminaison 1
;;
esac
done
shift $( expr $OPTIND - 1 )
LOGIN="$1"
# Petite vérif.
while ! check_login "$LOGIN"; do
# En mode "bourrin", on gicle direct.
if [ "$LOGIN" != "" ] && [ "$MODE_INTERACTIF" -eq 0 ]; then
terminaison 1
fi
# Sinon, on demande poliment
printf "Login : "; read LOGIN
done
# Récupération de l'email
RACINE_EMAIL=$( ldapsearch -x -h "$LDAP_HOST" -b "ou=users,$LDAP_ROOT" -LLL "(uid=$LOGIN)" mailLocalAddress | sed -n 's/^mailLocalAddress: //p' )
if [ "$RACINE_EMAIL" = "" ]; then
echo "ERREUR: impossible de retrouver l'adresse e-mail." >&2
terminaison 1
fi
EMAIL="$RACINE_EMAIL@$CONFIG_EMAIL_DOMAIN"
# 1ere étape : Suppression du compte avec smbldap-userdel
echo "Suppression du compte dans LDAP... (éviter le Ctrl-C à partir de maintenant)"
if ! smbldap-userdel "$LOGIN"; then
echo "ERREUR: smbldap-userdel a renvoyé une erreur. Arrêt du script $0." >&2
return 1
fi
echo "Compte LDAP supprimé."
echo
# 2ème étape : Suppression de l'adresse e-mail des mailing-lists
printf "Suppression de l'e-mail %s de toutes les mailing-lists...\n" "$EMAIL"
if ! ./script_ml-gestion-abonne.sh -r ALL "$EMAIL"; then
cat <<EOF >&2
NOTICE: apparemment, il y a eu un pépin à la gestion des mailing-lists.
Le compte est supprimé, donc l'utilisateur ne peut plus consulter ses mails mais
sa boîte peut néanmoins en recevoir.
On on vous laisse vous débrouiller avec
./script_ml-gestion-abonne.sh ou PHPLDAPAdmin.
EOF
fi
echo
# 3ème étape : suppression de la boîte mail dans Cyrus
#
# Tout se fait via un script Perl parce que les outils d'admin
# de Cyrus sont pas franchement faciles à scripter...
#
# En mode non-bourrin, on demande une confirmation de la destruction.
if [ "$MODE_INTERACTIF" -eq 1 ]; then
printf "Supprimer la boîte mail dans Cyrus ? (O/n)"
read TMP
if [ -n "$TMP" ] && [ $( echo "$TMP" | egrep -ic "^o$" ) -eq 0 ]; then
echo "Ah bon ?! Ben à la prochaine alors." >&2
return 0
fi
fi
printf "Suppression de la boîte user.%s dans Cyrus.\n" "$RACINE_EMAIL"
cat <<EOF | perl -MCyrus::IMAP::Admin
# Connexion
my \$conn = Cyrus::IMAP::Admin->new('$CYRUS_HOST') or die "Echec de connexion.";
# Lecture du fichier de configuration
open CONFIG, '$CONFIG_IDENTIFIANTS_CYRUS';
while (<CONFIG>) {
chomp; # no newline
s/^#.*//; # no comments
s/^\s+//; # no leading white
s/\s+$//; # no trailing white
next unless length; # anything left?
my (\$var, \$value) = split(/\s*=\s*/, \$_, 2);
\$cyrus_config{\$var} = \$value;
}
# Authentification
\$conn->authenticate(
"-user" => \$cyrus_config{'user'},
"-password" => \$cyrus_config{'password'},
"-mechanism" => 'LOGIN',
) or die "Echec à l'authentification.";
# On se donne les droits pour supprimer la boîte mail
\$conn->setacl('user.$RACINE_EMAIL',\$cyrus_config{'user'},"c") or die "Erreur dans la gestion des ACLs.";
if ( \$conn->error ) {
die "Erreur dans la gestion des ACLS.";
}
# Destruction de la boîte
\$conn->delete('user.$RACINE_EMAIL') or die "Erreur à la destruction de la boîte mail.";
if ( \$conn->error ) {
die "Erreur à la suppression de la boîte.";
}
EOF
if [ "$?" -eq 0 ]; then
echo "Boîte supprimée."
else
echo "ERREUR: erreur à la destruction de la boîte. C'était la dernière étape, voir avec Cyrus. Abandon." >&2
return 1
fi
echo
echo "Fin du script."

View file

@ -0,0 +1,94 @@
#!/bin/sh
# Ce script liste les derniers passages des disques
# d'externalisation.
# Selon les options fournies, il peut aussi mettre à
# jour le listing.
# Config
EXTERNAL_DISK_UUID="394165d5-b1dc-42a1-aa2e-006516f8e33f"
DATABASE="/root/disques-externalisation_timestamp.lst"
CORRESPONDANCE="/root/disques-externalisation_correspondance.lst"
# Initialisation
MISE_A_JOUR=0
SILENCIEUX=0
# affiche le message d'aide
usage() {
cat <<EOF
$0 [ -u ] [ -U UUID ] [ -q ]
$0 -h
-u : met à jour la BDD
-U UUID : utilise cet UUID pour identifier la partition utilisée
-q : silencieux, n'affiche pas le listing final
EOF
}
# Début du code
# gestion des options de lancement
while getopts uU:h f; do
case $f in
'u')
MISE_A_JOUR=1
;;
'U')
$EXTERNAL_DISK_UUID="$OPTARG"
;;
'h')
usage
exit 0
;;
\?)
usage >&2
exit 1
;;
esac
done
shift $( expr $OPTIND - 1 )
if [ "$MISE_A_JOUR" -gt 0 ]; then
# On récupère l'id du disque
# TODO : alerte si plusieurs devices correspondants ?
if [ ! -e "/dev/disk/by-uuid/$EXTERNAL_DISK_UUID" ]; then
exit 1
fi
DISQUE_ID=$( find /dev/disk/by-id -lname "$( readlink /dev/disk/by-uuid/$EXTERNAL_DISK_UUID )" | head -n 1 | xargs basename )
# ...et on l'ajoute au listing
TMPFILE=$( mktemp )
cat $DATABASE | grep -v "$DISQUE_ID" | grep -v "^$" >$TMPFILE
printf "%d\t%s\n" "$( date +%s )" "$DISQUE_ID" >>$TMPFILE
cat "$TMPFILE" | sort -n >$DATABASE
rm -f "$TMPFILE"
fi
if [ "$SILENCIEUX" -eq 0 ]; then
TS_NOW=$( date +%s )
# Listing avec nb de jours depuis dernière mise à jour
printf " Age Label\n"
cat "$DATABASE" | while read LINE; do
TS=$( echo $LINE | awk '{ printf $1 }' )
ID=$( echo $LINE | awk '{ printf $2 }' )
LABEL=$( test -f "$CORRESPONDANCE" && cat "$CORRESPONDANCE" | sed -n "s/^$ID[[:space:]]\+\(.*\)$/\1/p" )
# Affichage du nombre de jours
printf "%3dj " "$(( ( $TS_NOW - $TS ) / 86400 ))"
# Affichage du label de la table de correspondance (ou, si absent, de l'Id)
if [ "$LABEL" == "" ]; then
LABEL="$ID"
fi
printf "%s\n" "$LABEL"
done
fi

View file

@ -0,0 +1,66 @@
#!/bin/sh
LOGFILE=/var/log/externalisation.log
DIR_SCRIPTS="/root/scripts-admin"
ADRESSES_MAIL="supervision-externalisation@example.net"
$DIR_SCRIPTS/script_synchro-externalisation-from-serveur-sauvegarde.sh >$LOGFILE 2>&1
# Vérification de l'état de sortie
case "$?" in
0)
( cat - <<EOF ; $DIR_SCRIPTS/script_synchro-externalisation-stats-dates-disques.sh -u ) | mail -s "[externalisation] Ok" -a "Content-Type: text/plain; charset=UTF-8" $ADRESSES_MAIL
La synchro du disque d'externalisation s'est déroulée avec succès.
Le disque est à présent disponible.
-
Âge des dernières synchro par disque :
EOF
;;
1)
( cat - <<EOF ; $DIR_SCRIPTS/script_synchro-externalisation-stats-dates-disques.sh ; printf "\n -\n"; tail "$LOGFILE" ) | mail -s "[externalisation] Erreur" -a "Content-Type: text/plain; charset=UTF-8" $ADRESSES_MAIL
Erreur de synchronisation : le disque n'est pas disponible, intervention manuelle requise.
La synchronisation ne s'est pas déroulée normalement et le disque n'est pas disponible.
10 dernières lignes du log ci-dessous pour plus d'information.
Log dispo dans $LOGFILE
-
Âge des dernières synchro par disque :
EOF
;;
2)
( cat - <<EOF ; $DIR_SCRIPTS/script_synchro-externalisation-stats-dates-disques.sh ; printf "\n -\n"; tail "$LOGFILE" ) | mail -s "[externalisation] Pas de disque" -a "Content-Type: text/plain; charset=UTF-8" $ADRESSES_MAIL
10 dernières lignes du log ci-dessous pour plus d'information.
Log dispo dans $LOGFILE
-
Âge des dernières synchro par disque :
EOF
;;
3)
( cat - <<EOF ; $DIR_SCRIPTS/script_synchro-externalisation-stats-dates-disques.sh -u ; printf "\n -\n"; tail "$LOGFILE" ) | mail -s "[externalisation] Erreur post-synchro" -a "Content-Type: text/plain; charset=UTF-8" $ADRESSES_MAIL
Le coeur de la synchro a bien été effectuée, mais le disque n'est néanmoins pas disponible.
Intervention manuelle requise.
10 dernières lignes du log ci-dessous pour plus d'information.
Log dispo dans $LOGFILE
-
Âge des dernières synchro par disque :
EOF
;;
esac
# Rotation des logs
savelog -c 7 "$LOGFILE" >/dev/null