scripts/secours/secours.py
Xavier Lagorce 186f0ecacd Correction de bugs dans le passage en connexion de secours
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
2009-06-16 21:53:59 +02:00

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()