
Quelques modifications du secours.py : * il faut modifier les fichiers de conf de squid3 et non pas squid sur sable * lors de la perte de connexion, sable perd des resolutions DNS, il faut donc compter le nombre de 'is unreachable' et de 'address not found' pour comparer au nombre d'hotes. darcs-hash:20090616195359-f33a2-9a36b7df941e72033d7a5223726b10bd100e85dc.gz
283 lines
8.6 KiB
Python
283 lines
8.6 KiB
Python
#! /usr/bin/env python
|
|
# -*- encoding: utf-8 -*-
|
|
"""Reconfiguration des services pour la connexion de secours
|
|
|
|
Arguments :
|
|
- test : en mode automatique, teste la connexion et passe en secours
|
|
si besoin -> configure l'état maître
|
|
- normal : force la connexion normale
|
|
- secours : force la connexion de secours
|
|
- auto : libère la modification automatique de l'état -> teste la
|
|
connexion, et passe en secours si besoin
|
|
|
|
Sans argument, configure les services selon l'état maître
|
|
|
|
Auteurs :
|
|
- Frédéric Pauget, Août 2005 (Implémentation initiale)
|
|
- Nicolas Dandrimont, Avril 2009 (Réécriture pour nettoyages)
|
|
"""
|
|
|
|
import commands
|
|
import os
|
|
from socket import gethostname
|
|
import sys
|
|
|
|
HOSTNAME = gethostname().split(".")[0]
|
|
SECOURS_PATH = "/usr/scripts/var/secours"
|
|
|
|
ETAT_HOTE = os.path.join(SECOURS_PATH, "etat_%s" % HOSTNAME)
|
|
ETAT_MAITRE = os.path.join(SECOURS_PATH, "etat_maitre")
|
|
|
|
### Fichiers à modifier, chaine indiquant un commentaire dans ceux-ci
|
|
### et commandes à excécuter après édition
|
|
FICHIERS = {
|
|
'rouge': {
|
|
'/etc/bind/named.conf.options': '//',
|
|
'/etc/postfix/main.cf': '#',
|
|
},
|
|
'sable': {
|
|
'/etc/bind/named.conf.options': '//' ,
|
|
'/etc/squid3/squid.conf': '#',
|
|
},
|
|
'sila': {
|
|
'/etc/bind/named.conf.options': '//' ,
|
|
},
|
|
'zamok': {
|
|
'/etc/postfix/main.cf': '#',
|
|
},
|
|
}.get(HOSTNAME, {})
|
|
|
|
COMMANDES = {
|
|
'rouge': [
|
|
'/etc/init.d/postfix restart',
|
|
'/etc/init.d/bind9 reload',
|
|
],
|
|
'sable': [
|
|
'/etc/init.d/squid3 reload',
|
|
'/etc/init.d/bind9 reload',
|
|
],
|
|
'sila': [
|
|
'/etc/init.d/bind9 reload',
|
|
],
|
|
'zamok': [
|
|
'/etc/init.d/postfix restart',
|
|
],
|
|
}.get(HOSTNAME, [])
|
|
|
|
# Adresses à pinguer pour tester la connexion
|
|
TEST_HOSTS = (
|
|
'91.121.84.138', # ovh.crans.org
|
|
'free.fr',
|
|
'google.com',
|
|
'yahoo.com',
|
|
)
|
|
|
|
#####################
|
|
# Fonctions utiles #
|
|
#####################
|
|
|
|
def cron(message):
|
|
"""Envoie le `message' a cron.
|
|
Ceci est un raccourci pour sys.stderr.write("%s\n" % message)"""
|
|
return sys.stderr.write("%s\n" % message)
|
|
|
|
def clobber(fichier, nouveau_contenu):
|
|
"""Réécrit `nouveau_contenu' dans `fichier'."""
|
|
fichier = open(fichier, 'w')
|
|
fichier.write(nouveau_contenu)
|
|
fichier.flush()
|
|
fichier.close()
|
|
|
|
def set_etat(etat, mode = None):
|
|
"""Écrit l'état de la connexion de secours sur l'hôte.
|
|
Si `mode' n'est pas None, alors c'est l'état maître qui est écrit."""
|
|
|
|
if not mode:
|
|
clobber(ETAT_HOTE, "%s\n" % etat)
|
|
else:
|
|
clobber(ETAT_MAITRE, "%s\n%s\n" % (etat, mode))
|
|
|
|
def get_etat(maitre = False):
|
|
"""Récupère l'état de la connexion de secours.
|
|
Renvoie une paire `etat', `mode'. Si `maitre' est True, l'état
|
|
maître et le mode sont récupérés. Sinon, `mode' est None"""
|
|
|
|
etat = None
|
|
mode = None
|
|
if not maitre:
|
|
try:
|
|
fichier = open(ETAT_HOTE)
|
|
except IOError:
|
|
pass
|
|
else:
|
|
etat = fichier.read().strip()
|
|
fichier.close()
|
|
else:
|
|
try:
|
|
fichier = open(ETAT_MAITRE)
|
|
except IOError:
|
|
pass
|
|
else:
|
|
lines = fichier.readlines()
|
|
fichier.close()
|
|
etat = lines[0].strip()
|
|
mode = lines[1].strip()
|
|
|
|
return etat, mode
|
|
|
|
def rewrite_config(fichier, comment, etat):
|
|
"""Modifie le fichier selon le nouvel état fourni.
|
|
Si `etat' est "normal" ("secours"), commente (décommente) les
|
|
lignes terminant par "#POUR SECOURS", ou les n lignes suivant une
|
|
ligne commençant par "#POUR SECOURS-n" (si le -n est omis, une
|
|
seule ligne est affectée)"""
|
|
|
|
signal = '#POUR SECOURS'
|
|
|
|
lines = open(fichier)
|
|
newlines = []
|
|
reste = 0 # Nombre de lignes restant à traiter
|
|
for line in lines:
|
|
sline = line.rstrip()
|
|
marqueur = signal in sline
|
|
avant = ""
|
|
apres = ""
|
|
if marqueur:
|
|
# sline = "".join((avant, signal, apres))
|
|
avant, apres = sline.split(signal, 2)
|
|
|
|
# Ici, nous sommes dans les lignes a changer en cas
|
|
# de connexion de secours :
|
|
# reste : lignes suivant une ligne #POUR SECOURS(-n)?
|
|
# marqueur and avant : ligne ayant des choses avant le marqueur
|
|
if reste or (marqueur and avant):
|
|
if not sline.startswith(comment) and etat == 'normal':
|
|
# On est actuellement configuré en secours
|
|
# Il faut passer en normal : commenter la ligne
|
|
newlines.append("%s%s" % (comment, line))
|
|
elif sline.startswith(comment) and etat == 'secours':
|
|
# On est actuellement configuré en normal
|
|
# Il faut passer en secours : décommenter la ligne
|
|
newlines.append(line.replace(comment, '', 1))
|
|
else:
|
|
# Rien à faire, on est bien configuré
|
|
newlines.append(line)
|
|
if reste:
|
|
reste -= 1
|
|
elif marqueur:
|
|
# On a une ligne qui commence par le marqueur,
|
|
# les n prochaines lignes sont à modifier.
|
|
if apres:
|
|
reste = int(apres[1:]) # on enlève le tiret
|
|
else:
|
|
reste = 1
|
|
newlines.append(line)
|
|
else:
|
|
# Ligne normale
|
|
newlines.append(line)
|
|
|
|
lines.close()
|
|
|
|
# Ecriture de la nouvelle version
|
|
clobber(fichier, "".join(newlines))
|
|
|
|
#####################
|
|
# Boucle Principale #
|
|
#####################
|
|
|
|
def connexion_ok():
|
|
"""Vérifie si la connexion fonctionne, à l'aide de pings.
|
|
Les hôtes testés sont ceux de `TEST_HOSTS'"""
|
|
|
|
pings = commands.getoutput('/usr/sbin/fping %s' % ' '.join(TEST_HOSTS))
|
|
print pings
|
|
|
|
# S'il y a autant de unreachable que de hosts,
|
|
# la connexion ne fonctionne pas
|
|
return (pings.count('is unreachable') + pings.count('address not found')) != len(TEST_HOSTS)
|
|
|
|
def new_etat_maitre(argument, mode_maitre_avant):
|
|
"""Renvoie l'état et le mode maitre selon l'argument passé au
|
|
script, le mode maître actuel, et le test de ping."""
|
|
|
|
mode_maitre_apres = mode_maitre_avant
|
|
|
|
if argument == 'secours':
|
|
print "Connexion de secours forcée."
|
|
etat_maitre_apres, mode_maitre_apres = 'secours', 'manuel'
|
|
|
|
elif argument == 'normal':
|
|
print "Connexion normale forcée."
|
|
etat_maitre_apres, mode_maitre_apres = 'normal', 'manuel'
|
|
|
|
elif argument == 'test':
|
|
if mode_maitre_avant != 'auto':
|
|
print 'Mode manuel, passer en mode auto pour tester'
|
|
sys.exit(1)
|
|
|
|
if argument in ('auto', 'test'):
|
|
# Test de la connexion nécessaire
|
|
if argument == 'auto':
|
|
print "Passage en mode automatique."
|
|
mode_maitre_apres = 'auto'
|
|
|
|
if connexion_ok():
|
|
etat_maitre_apres = 'normal'
|
|
else:
|
|
etat_maitre_apres = 'secours'
|
|
|
|
return etat_maitre_apres, mode_maitre_apres
|
|
|
|
def main():
|
|
"""Routine principale"""
|
|
etat_m_avant, mode_m_avant = get_etat(maitre = True)
|
|
|
|
# Valeurs par défaut pour le prochain état maître
|
|
etat_m_apres, mode_m_apres = etat_m_avant, mode_m_avant
|
|
|
|
#####
|
|
# Un argument a été passé, le script pourra changer le mode et l'état maitre
|
|
if len(sys.argv) == 2:
|
|
arg = sys.argv[1]
|
|
|
|
# Nouvel état et nouveau mode
|
|
etat_m_apres, mode_m_apres = new_etat_maitre(arg, mode_m_avant)
|
|
|
|
# On a récupéré toutes les informations, on peut changer le mode maître.
|
|
if etat_m_avant != etat_m_apres or mode_m_avant != mode_m_apres:
|
|
set_etat(etat_m_apres, mode_m_apres)
|
|
|
|
#####
|
|
# Changement de l'état local
|
|
etat_h_avant, _ = get_etat()
|
|
etat_h_apres = etat_m_apres
|
|
|
|
print "Mode %s" % mode_m_apres
|
|
|
|
if etat_h_apres == etat_h_avant:
|
|
print "L'hôte est déjà en état `%s'" % etat_h_avant
|
|
else:
|
|
cron("Passage de `%s' en etat `%s'" % (HOSTNAME, etat_h_apres))
|
|
|
|
# Réécriture des fichiers
|
|
for fichier, commentaire in FICHIERS.items():
|
|
try:
|
|
print 'Edition de %s' % fichier
|
|
rewrite_config(fichier, commentaire, etat_h_apres)
|
|
except IOError:
|
|
import traceback
|
|
traceback.print_exc(file = sys.stderr)
|
|
|
|
# Écriture de l'état local
|
|
set_etat(etat_h_apres)
|
|
|
|
# Exécution des commandes
|
|
for commande in COMMANDES:
|
|
os.system(commande)
|
|
|
|
if __name__ == "__main__":
|
|
if not (FICHIERS or COMMANDES):
|
|
print "Script sans effet sur cette machine."
|
|
sys.exit(1)
|
|
else:
|
|
main()
|