scripts/secours/secours.py
Vincent Le Gallic d4453122d4 [secours.py] La variable pings n'existait pas toujours quand on en avait besoin.
Ignore-this: feba0d4009f499e4ceb771170b38e578

darcs-hash:20121205194601-2c9c1-139da3e782b8eee6085003d87ffecb4610b4e322.gz
2012-12-05 20:46:01 +01:00

307 lines
9.3 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 = {
'redisdead': {
'/etc/postfix/main.cf': '#',
},
'sable': {
'/etc/bind/named.conf.options': '//' ,
'/etc/squid3/squid.conf': '#',
},
'charybde': {
'/etc/bind/named.conf.options': '//' ,
},
'zamok': {
'/etc/postfix/main.cf': '#',
},
'komaz': {
'/proc/net/nf_condition/secours': '0',
}
}.get(HOSTNAME, {})
COMMANDES = {
'redisdead': [
'/etc/init.d/postfix restart',
],
'sable': [
'/etc/init.d/squid3 reload',
'/etc/init.d/bind9 reload',
],
'charybde': [
'/etc/init.d/bind9 reload',
],
'zamok': [
'/etc/init.d/postfix restart',
],
'komaz': [
'/etc/init.d/aiccu restart',
'/usr/sbin/monit monitor netacct-crans-sixxs2',
],
}.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)"""
if HOSTNAME=='komaz':
if etat=='secours':
clobber(fichier,'1')
elif etat=='normal':
clobber(fichier,'0')
return
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/bin/fping %s' % ' '.join(TEST_HOSTS))
print pings
# S'il y a autant de unreachable que de hosts,
# la connexion ne fonctionne pas
ok = (pings.count('is unreachable') + pings.count('address not found')) != len(TEST_HOSTS)
return ok, pings
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'
ok, pings = connexion_ok()
if ok:
etat_maitre_apres = 'normal'
else:
etat_maitre_apres = 'secours'
return etat_maitre_apres, mode_maitre_apres, pings
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
pings = "" # Pour que la variable existe même si la fonction new_etat_maitre n'est pas appelée
if len(sys.argv) == 2:
arg = sys.argv[1]
# Nouvel état et nouveau mode
etat_m_apres, mode_m_apres, pings = 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 HOSTNAME == 'komaz':
fichier, commentaire = FICHIERS.items()[0]
try:
rewrite_config(fichier, commentaire, etat_h_apres)
except IOError:
import traceback
traceback.print_exc(file = sys.stderr)
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))
cron(pings)
# 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
print "Execution des commandes %s" % ('; '.join(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()