conf-10-check-nrpe/conf/common/usr/local/sbin/check_conf

263 lines
11 KiB
Python
Executable File

#!/usr/bin/env python3
################################################################################
# BSD 3-Clause License
#
# Copyright © 2020-2021 Aurélien Grimal - aurelien.grimal@tech-tips.fr
# All rights reserved.
#
# Please see license file on root of this directory
################################################################################
# !! GIT FILE !!
# https://framagit.org/zorval/scripts/check-nrpe
################################################################################
# Import des modules
import sys
try:
import hashlib, platform, os, shutil, argparse, subprocess as sp, re
except Exception as e:
print("[CRITICAL] Impossible d'importer les modules", e)
sys.exit(2)
# Test si le script est bien lancé en root
if os.geteuid() != 0:
print("[CRITICAL] Il faut être root pour lancer cette commande")
sys.exit(2)
# Variables
config_dir = '/srv/git/conf'
common_config_dir = 'common'
wildcard = '__'
git_monitored_repos_file = '/etc/zorval/git-monitored-repos'
try:
with open('/etc/hostname', 'r') as etc_hostname:
hostname = etc_hostname.read().rstrip()
except Exception:
print('[CRITICAL] Erreur lors de la lecture du fichier /etc/hostname')
# Fonctions
def hash_bytestr_iter(bytesiter, hasher, ashexstr=False):
for block in bytesiter:
hasher.update(block)
return hasher.hexdigest() if ashexstr else hasher.digest()
def file_as_blockiter(afile, blocksize=65536):
with afile:
block = afile.read(blocksize)
while len(block) > 0:
yield block
block = afile.read(blocksize)
def md5sum(file_path):
return hash_bytestr_iter(file_as_blockiter(open(file_path, 'rb')), hashlib.md5())
def walk_git_conf(directory, level=1):
directory = directory.rstrip(os.path.sep)
if not os.path.isdir(directory):
print("[CRITICAL] Le répertoire", directory, "n'existe pas")
sys.exit(2)
num_sep = directory.count(os.path.sep)
# Au moment du tri, on remplace '__' par '###' car dans l'ordre ASCII, le caractère _ est placé après les lettres
# Or pour l'application de la précédence, il doit se trouver avant
for root_abs, dirs, files in sorted(os.walk(directory), key=lambda d: d[0].replace("__", "###")):
root_abs = root_abs.replace("###", "__")
num_sep_this = root_abs.count(os.path.sep)
if num_sep_this >= num_sep + 2 and root_abs.split(os.path.sep)[num_sep + 2] == '.git':
continue
if num_sep_this == 6:
root_rel = os.path.basename(root_abs)
root_rel_ignore = re.sub('.ignore$', '', root_rel)
#print("Analyse de", root_abs, "->", root_rel)
if root_rel_ignore in [common_config_dir, hostname] or re.search('^' + root_rel_ignore.replace(wildcard, '.*') + '$', hostname):
#print(" -> retenu")
yield root_abs
else:
#print(" -> non retenu")
pass
if num_sep + level <= num_sep_this:
del dirs[:]
# Arguments du script
parser = argparse.ArgumentParser(description='Script permettant de surveiller / copier des fichiers entre un dépôt git et la machine locale')
exclusive_grp = parser.add_mutually_exclusive_group()
exclusive_grp.add_argument('--copy-repo-to-local', action='store_true', help='Copie les fichiers manquants / différents depuis le dépôt git ' + config_dir + ' vers la machine locale')
exclusive_grp.add_argument('--copy-local-to-repo', action='store_true', help='Copie les fichiers manquants / différents depuis la machine locale vers le dépôt git ' + config_dir)
parser.add_argument('--force', action='store_true', help='Force la copie, mode non-interactif - /!\ Dangereux /!\\')
args = parser.parse_args()
# Liste des répertoires valides contenant les fichiers à analyser/comparer
# Les répertoires doivent suivre ce modèle :
# - {config_dir}/xxxxxxxx/conf/{common_config_dir}
# - {config_dir}/xxxxxxxx/conf/{hostname}
# - {config_dir}/xxxxxxxx/conf/{part_of_hostname_followed_by_wildcard_pattern}
conf_dirs = []
for directory in walk_git_conf(config_dir, 3):
conf_dirs.append(directory)
# Stockage dans un dictionnaire de chaque fichier à comparer
# Clef : chemin absolu du fichier local
# Valeur : chemin absolu du fichier dans le dépôt git
# Les fichiers déjà trouvés sont remplacés par les éventuels nouveaux, dans l'ordre de parcours (alphanumérique) de 'conf_dirs'
git_conf_file_dict = {}
for directory in conf_dirs:
len_dir = len(directory)
#print("\nRépertoire =", directory)
for item in os.walk(directory):
# Si le fichier contient des fichiers (donc est un répertoire non vide)
if len(item[2]) > 0:
for f in item[2]:
local_f_path = item[0][len_dir:] + '/' + f
git_f_path = item[0] + '/' + f
if directory.endswith('.ignore'):
#print(" -> on oublie", local_f_path)
git_conf_file_dict.pop(local_f_path, None)
else:
#print(" -> on retient", local_f_path)
git_conf_file_dict.update({ local_f_path : git_f_path })
# Génération des listes de fichiers manquants et différents
different_files_dict = {}
missing_files_dict = {}
for f_local, f_repo in git_conf_file_dict.items():
try:
local_md5 = md5sum(f_local)
repo_md5 = md5sum(f_repo)
if local_md5 != repo_md5:
different_files_dict.update({ f_local : f_repo })
except FileNotFoundError:
missing_files_dict.update({ f_local : f_repo })
# Récupération du statut des dépôts git
monitored_git_repos = []
try:
with open(git_monitored_repos_file, 'r') as monitored_repos:
for line in monitored_repos:
git_repo = line.rstrip()
if not os.path.exists(git_repo):
print('[CRITICAL]', git_repo, 'n\'existe pas, merci de vérifier le fichier', git_monitored_repos_file)
sys.exit(2)
if not os.path.isdir(git_repo):
print('[CRITICAL]', git_repo, 'n\'est pas un répertoire, merci de vérifier le fichier', git_monitored_repos_file)
sys.exit(2)
monitored_git_repos.append(git_repo)
except FileNotFoundError as e:
print('[CRITICAL]', e)
sys.exit(2)
git_repos_locally_modified = []
for git_repo in monitored_git_repos:
p = sp.Popen(['git', '--work-tree=' + git_repo, '--git-dir=' + git_repo + '/.git', 'diff', '--name-only'], stdout=sp.PIPE, stderr=sp.PIPE)
stdout, stderr = p.communicate(2)
rc = p.returncode
stdout = stdout.decode('utf-8').replace('\n', ' ')
stderr = stderr.decode('utf-8').replace('\n', ' ')
if rc != 0:
print('[CRITICAL]', git_repo, stderr, 'rc=' + str(rc))
sys.exit(2)
if stdout != '':
git_repos_locally_modified.append(git_repo)
# Output
len_diff = len(different_files_dict)
len_miss = len(missing_files_dict)
if len_diff + len_miss > 0 or len(git_repos_locally_modified) > 0:
# If copy
if args.copy_repo_to_local or args.copy_local_to_repo:
# If there are missing files
if len_miss > 0:
for f_local, f_repo in missing_files_dict.items():
if args.copy_local_to_repo:
print("[CRITICAL] Je ne peux pas copier un fichier qui n'existe pas (" + f_local + " vers " + f_repo + ")")
else:
if not args.force:
print("\nMerci de confirmer l'exécution de cette commande :")
print("cp", f_repo, f_local)
answer = input("Votre réponse ([oui|non]) : ")
if args.force or answer.lower() in ['y', 'yes', 'o', 'oui']:
try:
shutil.copy2(f_repo, f_local)
except FileNotFoundError:
os.makedirs(os.path.dirname(f_local), mode=0o755)
shutil.copy2(f_repo, f_local)
print("-", f_local, ": copie effectuée")
else:
print("-", f_local, ": on ne fait rien")
# If there are different files
if len_diff > 0:
for f_local, f_repo in different_files_dict.items():
if not args.force:
print("\nMerci de confirmer l'exécution de cette commande :")
if args.copy_local_to_repo:
print("cp", f_local, f_repo)
else:
print("cp", f_repo, f_local)
answer = input("Votre réponse ([oui|non]) : ")
if args.force or answer.lower() in ['y', 'yes', 'o', 'oui']:
if args.copy_local_to_repo:
shutil.copy2(f_local, f_repo)
print("-", f_local, ": copie effectuée")
else:
shutil.copy2(f_repo, f_local)
print("-", f_local, ": copie effectuée")
else:
print("-", f_local, ": on ne fait rien")
# Else : just monitoring
else:
print('[WARNING] ', end='')
if len_miss > 0:
if len_miss == 1:
print('- 1 fichier manquant ', end='')
else:
print('-', len_miss, 'fichiers manquants ', end='')
if len_diff > 0:
if len_diff == 1:
print('- 1 fichier différent ', end='')
else:
print('-', len_diff, 'fichiers différents ', end='')
if len(git_repos_locally_modified) > 0:
if len(git_repos_locally_modified) == 1:
print('- 1 dépôt git modifié')
print("\nDépôt git modifié :")
else:
print('-', len(git_repos_locally_modified), 'dépôts git modifiés')
print("\nDépôts git modifiés :")
for git_repo in git_repos_locally_modified:
print('-', git_repo)
if len_miss > 0:
if len_miss == 1:
print("\nFichier manquant :")
else:
print("\nFichiers manquants :")
for f_local, f_repo in missing_files_dict.items():
print("- dépôt :", f_repo)
print(" local :", f_local)
if len_diff > 0:
if len_diff == 1:
print("\nFichier différent :")
else:
print("\nFichiers différents :")
for f_local, f_repo in different_files_dict.items():
print("- dépôt :", f_repo)
print(" local :", f_local)
sys.exit(1)
else:
print("[OK] Cette machine est conforme")
sys.exit(0)