Rsync Checker petit script Python sans prétention
Après mon billet Borg Checker, petit script Python sans prétention, voici un autre billet d’un petit outil simple mais effiace, là encore en deux étapes.
Les besoins
Nous avons un Rsync qui se fait dans le sens "Machine distance" en source, "Machine locale" en cible, le tout à travers SSH, lancé avec sudo - pour avoir les droits root et donc aller où on veut et s’affranchir des problèmes de permission.
Nous aimerions valider que les commandes rsync exécutées sont valables /se sont bien déroulées / ne sont pas tombées en erreur. Sachant que nous avons un script shell global qui lance plusieurs scripts shells différents qui eux-même lancent plusieurs commandes Rsync au sein du même script.
SSH
Les connexions SSH se font depuis la machine Backup en tant que client du serveur SSH qui est sur la machine à sauvegarder.
La connexion se fait via une connexion par clef (la clef publique de la machine de sauvegarde, Backup, a été ajoutée sur la machine à sauvegarder.
Rsync avec Sudo à travers SSH
Pour pouvoir copier les fichiers en rsync avec sudo, sans avoir à saisir de mot de passe, il faut faire un sudo visudo ce qui va permettre d’éditer le fichier /etc/sudoers et d’autoriser le lancement de la commande sudo rsync sans avoir à saisir de mot de passe pour l’utilisateur désigné, ici Genma.
sudo visudo
On ajoute en bas de fichier la ligne
genma ALL= NOPASSWD:/usr/bin/rsync
Astuce pour enregister / quitter dans le cas où l’éditeur par défaut est VI :
:wq!
La sauvegarde via Rsync
Exemple de script lançant des commandes Rsync la nuit via une tâche Cron. On écrit des traces / des logs dans un fichier en local. Ce fichier de log permettra de valider l’exécution des commandes (cf section ultérieure dans le fichier).
#!/bin/bash
LOG=/Backup/Machine_distante_A/Sauvegarde_rsync_ssh_<span class="base64" title="PGNvZGUgY2xhc3M9J3NwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lJyBkaXI9J2x0cic+ZGF0ZSArJVktJW0tJWQ8L2NvZGU+"></span>.log
# Fonction qui permet d'écrire des logs dans le fichier de log
# Le texte en paramètre de la fonction sera écrit à la suite de la date et heure
Inlog()
{
echo <span class="base64" title="PGNvZGUgY2xhc3M9J3NwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lJyBkaXI9J2x0cic+ZGF0ZSArJyVZLSVtLSVkICVIOiVNOiVTJzwvY29kZT4="></span>": $1" >> $LOG
}
# La variable "$?" contient le code de retour de la dernière opération.
# Ici c'est le code d'execution de la commande "rsync".
# "0" - la commande "rsync" s'executé correctement,
# Autre valeur en cas échéant, c'est que l'on a donc une erreur
check_rsync()
{
result_rsync=$(echo $?)
# La variable "result_rsync" recupere la valeur de "$?"
# Si "result_rsync" <>"0" alors il avait une erreur lors d'execution de la commande "rsync" et le script s'arrête
if [ "$result_rsync" == "0" ]; then
Inlog "$1 Rsync OK"
else
Inlog "$1 Rsync KO"
fi
}
Inlog "Début Rsync Machine_distante_A /var_www sur Serveur_Sauvegarde"
rsync -avz --rsync-path="sudo rsync" -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" --progress --ignore-existing genma@IP_Machine_Distante:/var/www /Backup/Machine_distante_A/var/www/
# Pour appeler la fonction de check du retour de la commande Rsync
check_rsync "Rsync Machine_distante_A /var_www sur Serveur_Sauvegarde"
Inlog "Fin Rsync Machine_distante_A /var_www sur Serveur_Sauvegarde"
Inlog "Début Rsync Machine_distante_A /data/sql sur Serveur_Sauvegarde"
rsync -avz --rsync-path="sudo rsync" -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" --progress --ignore-existing genma@IP_Machine_Distante:/Backup/mysql/ /Backup/Machine_distante_A/data/sql/
# Pour appeler la fonction de check du retour de la commande Rsync
check_rsync "Rsync Machine_distante_A /data/sql sur Serveur_Sauvegarde"
Inlog "Fin Rsync Machine_distante_A /data/sql sur Serveur_Sauvegarde"
Un peu de Python
Un peu comme on avait plusieurs commandes Borg lancées et qu’on vérifiait que chacune d’elles était correctes (cf Borg Checker, petit script Python sans prétention), on vérifiera que les différents scripts et les différentes commandes Rsync se sont déroulées sans soucis.
Le besoin est donc de savoir quelles sont les Rsync qui ont posés soucis pour ensuite traiter manuellement ces erreurs / cas particuliers.
Pour ce faire, la machine de sauvegarde lance un script Python (via une tâche cron à une heure bien postérieure à celles du déroulement des sauvegardes).
Dans ce script Python, on vérifie la présence du fichier de log à la date du jour : cela valide qu’au moins la commande Cron a bien lancé le script Shell contenant les commandes rsync. Le nom du fichier de log est standardisé (cf script Shell ci-dessus) et se trouve dns un dossier. Le fichier est récupéré via la dernière commande de rsync dans un dossier local du serveur.
Le fichier de log étant présent, on parcourt le fichier de logs à la recherche d’une ligne KO et on affiche la dite ligne si besoin. Cf le script Shell et sa fonction check_rsync() qui teste le retour de la commande rsync.
**Fichier Config.ini** : contient les chemins vers les fichiers de logs de chaque machine sauvegardée.
Un script de sauvegarde Shell contenant des commandes rsync génère un fichier de log par exécution, fichier de log ayant dans son nom la date du jour.
[FichiersLogsRsyncSsh]
Machine_distante_A = /Backup/Machine_distante_A/
Machine_distante_B = /Backup/Machine_distante_B/
**Fichier CheckRsyncSSH.py**
#!/usr/bin/python
# -*-coding:Utf-8 -*
import configparser
import sys
import os.path
import datetime
# Initialisation des chemins
# On a un fichier avec
# * en clef : la sauvegarde à valider
# * en valeur : le chemin dans lequel on vérifie la présence d'un fichier de log
config = configparser.ConfigParser()
config.optionxform = str
config.read('./Config.ini')
configRsync = config['FichiersLogsRsyncSsh']
# Code de la fonction
def fctFichierLogRsynsSSH():
print(" ")
print(--------------------------------------------")
print(" CHECK DES RSYNC QUOTIDIENS")
print(" via un check de présence des fichiers logs")
print("-------------------------------------------")
now = datetime.datetime.now().strftime('%Y-%m-%d')
# Vérification qu'on a bien un fichier de log à la date du jour
for key,value in config.items('FichiersLogsRsyncSsh'):
fichierAtrouver = key + "_" + now + ".log"
found = 0;
for fileName in os.listdir(value):
if (fichierAtrouver == fileName):
found = 1;
break;
if (found == 0):
print(key + ': statut ' + '\x1b[6;31m' + 'KO' + '\x1b[0m' + ' Fichier absent : ' + fichierAtrouver + "!!!")
if(found == 1):
print(key + ': statut ' + '\x1b[6;32m' + 'OK' + '\x1b[0m' ' Fichier trouvé : ' + fichierAtrouver);
# Vérification du contenu du fichier
chemin = value + fichierAtrouver
f = open(chemin,'r')
lignes = f.readlines()
f.close()
ligneKO = 0;
for ligne in lignes:
# est ce que la ligne contient un KO
# KO inscrit car le rsync a renvoyé une erreur (cf script Shell)
# si oui, la ligne est KO
if "KO" in ligne:
# On s'arrête au 1er KO, vu qu'on ira voir le fichier en détail du coup
ligneKO = 1;
break
if (ligneKO == 0):
print(key + ': statut ' + '\x1b[6;32m' + 'OK' + '\x1b[0m' + ' Tous les Rsync sont OK.')
if (ligneKO == 1):
print(key + ': statut ' + '\x1b[6;31m' + 'KO' + '\x1b[0m' ' Au moins un rsync contient un KO !!!')
print("La ligne concernée par un KO est : " +ligne)
ligneKO = 0;
found = 0;
print (" ")
return 0;
# Appel de la fonction principal
fctFichierLogRsynsSSH()
Pour le lancer le script :
python3 CheckRsyncSSH.py
Quel résultat ?
Si on lance le script manuellement, la sortie est donc sur la ligne de commande.
On aura donc un OK en vert ou un KO en rouge qui apparait dans le terminal ce qui permet de facilement distinguer / voir les lignes à analyser. Le Nom du fichier (et par conséquence de la sauvegarde) en échec apparaît. Il faut alors analyser ensuite plus finement en allant relancer le script global ou la commande rsync incriminée, en regardant les logs dans le terminal...