[secours/secours.py] Réécriture du script de passage en connexion de secours
Ignore-this: e3ed5bde757a7629bc56cbf4e2755851 - UTF-8 - Fichiers dans /usr/scripts/var/secours - Factorisation des opérations courantes (ex: lecture/écriture de l'état) - Commentaires des chemins d'exécution - ... darcs-hash:20090418054726-ffbb2-1104104531e42cbe0444c14ae1a3a69a28c6bb24.gz
This commit is contained in:
parent
eac6b0be8e
commit
4fa805460a
1 changed files with 263 additions and 144 deletions
|
@ -1,164 +1,283 @@
|
||||||
#! /usr/bin/env python
|
#! /usr/bin/env python
|
||||||
# -*- coding: iso-8859-15 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
|
"""Reconfiguration des services pour la connexion de secours
|
||||||
|
|
||||||
# $Id: secours.py,v 1.5 2007-08-27 12:22:07 salles Exp $
|
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
|
||||||
|
|
||||||
""" Script de reconfigure des services en connexion de secours
|
Sans argument, configure les services selon l'état maître
|
||||||
Arguments :
|
|
||||||
test : teste la connexion et passe en secours si besoin
|
Auteurs :
|
||||||
normal : force la connexion normale
|
- Frédéric Pauget, Août 2005 (Implémentation initiale)
|
||||||
secours : force la connexion de secours
|
- Nicolas Dandrimont, Avril 2009 (Réécriture pour nettoyages)
|
||||||
auto : permet la modification automatique de l'état
|
|
||||||
Sans argument configure les services comme indiqué dans etat_maitre
|
|
||||||
|
|
||||||
Frédéric Pauget août 2005
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys, re, os, commands
|
import commands
|
||||||
|
import os
|
||||||
from socket import gethostname
|
from socket import gethostname
|
||||||
hostname = gethostname().split(".")[0]
|
import sys
|
||||||
path = os.path.dirname(sys.argv[0])
|
|
||||||
|
|
||||||
### Fichiers à modifier, chaine indiquant un commentaire dans ceux-ci
|
HOSTNAME = gethostname().split(".")[0]
|
||||||
### et commandes à excécuter après édition
|
SECOURS_PATH = "/usr/scripts/var/secours"
|
||||||
if hostname == 'rouge' :
|
|
||||||
fichiers = { '/etc/bind/named.conf.options' : '//' ,
|
|
||||||
'/etc/postfix/main.cf' : '#' }
|
|
||||||
cmds = [ '/etc/init.d/postfix restart' , '/etc/init.d/bind9 reload' ]
|
|
||||||
elif hostname == 'sila' :
|
|
||||||
fichiers = { '/etc/bind/named.conf.options' : '//' ,
|
|
||||||
'/etc/squid/squid.conf' : '#' }
|
|
||||||
cmds = [ '/etc/init.d/squid reload' , '/etc/init.d/bind9 reload' ]
|
|
||||||
elif hostname == 'sable' :
|
|
||||||
fichiers = { '/etc/squid/squid.conf' : '#' }
|
|
||||||
cmds = [ '/etc/init.d/squid reload' ]
|
|
||||||
elif hostname == 'zamok' :
|
|
||||||
fichiers = { '/etc/postfix/main.cf' : '#' }
|
|
||||||
cmds = [ '/etc/init.d/postfix restart' ]
|
|
||||||
else :
|
|
||||||
print "Script sans effet sur cette machine."
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# Machines à pinguer pour tester la connexion
|
ETAT_HOTE = os.path.join(SECOURS_PATH, "etat_%s" % HOSTNAME)
|
||||||
hosts = ( '91.121.84.138', '216.239.57.104', '213.228.0.42', '217.12.3.11')
|
ETAT_MAITRE = os.path.join(SECOURS_PATH, "etat_maitre")
|
||||||
|
|
||||||
####################
|
### Fichiers à modifier, chaine indiquant un commentaire dans ceux-ci
|
||||||
# Fonctions utiles #
|
### et commandes à excécuter après édition
|
||||||
####################
|
FICHIERS = {
|
||||||
|
'rouge': {
|
||||||
|
'/etc/bind/named.conf.options': '//',
|
||||||
|
'/etc/postfix/main.cf': '#',
|
||||||
|
},
|
||||||
|
'sila': {
|
||||||
|
'/etc/bind/named.conf.options': '//' ,
|
||||||
|
'/etc/squid/squid.conf': '#',
|
||||||
|
},
|
||||||
|
'sable': {
|
||||||
|
'/etc/squid/squid.conf': '#',
|
||||||
|
},
|
||||||
|
'zamok': {
|
||||||
|
'/etc/postfix/main.cf': '#',
|
||||||
|
},
|
||||||
|
}.get(HOSTNAME, {})
|
||||||
|
|
||||||
|
COMMANDES = {
|
||||||
|
'rouge': [
|
||||||
|
'/etc/init.d/postfix restart',
|
||||||
|
'/etc/init.d/bind9 reload',
|
||||||
|
],
|
||||||
|
'sila': [
|
||||||
|
'/etc/init.d/squid reload',
|
||||||
|
'/etc/init.d/bind9 reload',
|
||||||
|
],
|
||||||
|
'sable': [
|
||||||
|
'/etc/init.d/squid 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)"""
|
||||||
|
|
||||||
def normal_ok() :
|
|
||||||
""" Teste le connexion normale à l'aide de pings
|
|
||||||
Retourne False si il y a un problème sur la connexion normale
|
|
||||||
Retourne True si la connexion normale est fonctionelle
|
|
||||||
"""
|
|
||||||
pings = commands.getoutput('/usr/sbin/fping %s' % ' '.join(hosts))
|
|
||||||
print pings
|
|
||||||
if pings.count('is unreachable') == len(hosts) :
|
|
||||||
return False
|
|
||||||
else :
|
|
||||||
return True
|
|
||||||
|
|
||||||
def edit(file,comment,etat) :
|
|
||||||
""" Edite le fichier fourni en commentant (mode normal)
|
|
||||||
ou décommentant (mode secours) les lignes signalées :
|
|
||||||
* celles se terminant avec #POUR SECOURS
|
|
||||||
* ou les n lignes (<10)suivant une qui commande par #POUR SECOURS-n
|
|
||||||
(si le -n est omis une seule ligne est affectée)"""
|
|
||||||
|
|
||||||
signal = '#POUR SECOURS'
|
signal = '#POUR SECOURS'
|
||||||
l = len(signal)
|
|
||||||
|
lines = open(fichier)
|
||||||
fd = open(file)
|
newlines = []
|
||||||
line = fd.readline()
|
reste = 0 # Nombre de lignes restant à traiter
|
||||||
new = ''
|
for line in lines:
|
||||||
reste = 0 # Nombre de lignes restant à traiter
|
sline = line.rstrip()
|
||||||
while line :
|
marqueur = signal in sline
|
||||||
l = line.rstrip()
|
avant = ""
|
||||||
mo = re.match('^(.*)'+signal+'(|-.)$',l)
|
apres = ""
|
||||||
if (mo and len(mo.group(1)) > 1) or reste:
|
if marqueur:
|
||||||
# Ligne pour secours
|
# sline = "".join((avant, signal, apres))
|
||||||
if not re.match('^' + comment,l) and etat == 'normal':
|
avant, apres = sline.split(signal, 2)
|
||||||
# On est actuellement configuré en secours
|
|
||||||
# Il faut passer en normal
|
# Ici, nous sommes dans les lignes a changer en cas
|
||||||
new += comment + line
|
# de connexion de secours :
|
||||||
elif re.match('^' + comment,l) and etat == 'secours' :
|
# reste : lignes suivant une ligne #POUR SECOURS(-n)?
|
||||||
# On est actuellement configuré en normal
|
# marqueur and avant : ligne ayant des choses avant le marqueur
|
||||||
# Il faut passer en secours
|
if reste or (marqueur and avant):
|
||||||
new += line.replace(comment,'',1)
|
if not sline.startswith(comment) and etat == 'normal':
|
||||||
else :
|
# On est actuellement configuré en secours
|
||||||
# Rien à faire, on est bien configuré
|
# Il faut passer en normal : commenter la ligne
|
||||||
new += line
|
newlines.append("%s%s" % (comment, line))
|
||||||
if reste :
|
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
|
reste -= 1
|
||||||
elif mo and len(mo.group(1)) == 0:
|
elif marqueur:
|
||||||
# On a une ligne avec secours uniquement, c'est les n
|
# On a une ligne qui commence par le marqueur,
|
||||||
# prochaines lignes qui font foi
|
# les n prochaines lignes sont à modifier.
|
||||||
try : reste = int(mo.group(2)[1:])
|
if apres:
|
||||||
except : reste = 1
|
reste = int(apres[1:]) # on enlève le tiret
|
||||||
new += line
|
else:
|
||||||
else :
|
reste = 1
|
||||||
|
newlines.append(line)
|
||||||
|
else:
|
||||||
# Ligne normale
|
# Ligne normale
|
||||||
new += line
|
newlines.append(line)
|
||||||
|
|
||||||
line = fd.readline()
|
lines.close()
|
||||||
|
|
||||||
fd.close()
|
|
||||||
|
|
||||||
# Ecriture de la nouvelle version
|
# Ecriture de la nouvelle version
|
||||||
open(file,'w').write(new)
|
clobber(fichier, "".join(newlines))
|
||||||
|
|
||||||
if __name__ == '__main__' :
|
#####################
|
||||||
# Etat dans lequel on devrai être
|
# Boucle Principale #
|
||||||
etat_maitre, mode = map(str.strip,open(path+'/etat_maitre').readlines())
|
#####################
|
||||||
nouvel_etat = etat_maitre
|
|
||||||
try:
|
|
||||||
etat_actuel = open('%s/etat_%s' % (path,hostname)).readline().strip()
|
|
||||||
except :
|
|
||||||
etat_actuel = None
|
|
||||||
|
|
||||||
if len(sys.argv) == 2 :
|
|
||||||
if sys.argv[1] == 'secours' :
|
|
||||||
print "Connexion de secours forcée."
|
|
||||||
nouvel_etat = 'secours'
|
|
||||||
mode = 'manuel'
|
|
||||||
open(path+'/etat_maitre','w').write('%s\n%s'% (nouvel_etat,mode))
|
|
||||||
|
|
||||||
elif sys.argv[1] == 'normal' :
|
def connexion_ok():
|
||||||
print "Connexion normale forcée."
|
"""Vérifie si la connexion fonctionne, à l'aide de pings.
|
||||||
nouvel_etat = 'normal'
|
Les hôtes testés sont ceux de `TEST_HOSTS'"""
|
||||||
mode = 'manuel'
|
|
||||||
open(path+'/etat_maitre','w').write('%s\n%s'% (nouvel_etat,mode))
|
pings = commands.getoutput('/usr/sbin/fping %s' % ' '.join(TEST_HOSTS))
|
||||||
|
print pings
|
||||||
elif sys.argv[1] in ( 'auto', 'test' ) :
|
|
||||||
if sys.argv[1] == 'auto' :
|
# S'il y a autant de unreachable que de hosts,
|
||||||
mode = 'auto'
|
# la connexion ne fonctionne pas
|
||||||
|
return pings.count('is unreachable') != len(TEST_HOSTS)
|
||||||
if mode != 'auto' :
|
|
||||||
print 'Mode manuel, passer en mode auto pour tester'
|
def new_etat_maitre(argument, mode_maitre_avant):
|
||||||
sys.exit(1)
|
"""Renvoie l'état et le mode maitre selon l'argument passé au
|
||||||
|
script, le mode maître actuel, et le test de ping."""
|
||||||
# On choisi le mode de connexion
|
|
||||||
if normal_ok() : nouvel_etat = 'normal'
|
mode_maitre_apres = mode_maitre_avant
|
||||||
else : nouvel_etat = 'secours'
|
|
||||||
|
if argument == 'secours':
|
||||||
if nouvel_etat != etat_maitre or sys.argv[1] == 'auto' :
|
print "Connexion de secours forcée."
|
||||||
open(path + '/etat_maitre','w').write('%s\n%s'% (nouvel_etat,mode))
|
etat_maitre_apres, mode_maitre_apres = 'secours', 'manuel'
|
||||||
|
|
||||||
print "Mode %s" % mode
|
elif argument == 'normal':
|
||||||
|
print "Connexion normale forcée."
|
||||||
if nouvel_etat == etat_actuel :
|
etat_maitre_apres, mode_maitre_apres = 'normal', 'manuel'
|
||||||
print etat_actuel
|
|
||||||
else :
|
elif argument == 'test':
|
||||||
# Il faut changer
|
if mode_maitre_avant != 'auto':
|
||||||
sys.stderr.write("Passage en mode %s\n" % nouvel_etat) # Ecriture sur stderr pour le cron
|
print 'Mode manuel, passer en mode auto pour tester'
|
||||||
for f, c in fichiers.items() :
|
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:
|
try:
|
||||||
print 'Edition de %s' % f
|
print 'Edition de %s' % fichier
|
||||||
edit(f,c,nouvel_etat)
|
rewrite_config(fichier, commentaire, etat_h_apres)
|
||||||
except:
|
except IOError:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc(file = sys.stderr)
|
||||||
open('%s/etat_%s' % (path,hostname),'w').write(nouvel_etat)
|
|
||||||
for c in cmds :
|
# Écriture de l'état local
|
||||||
os.system(c)
|
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()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue