gen_migration_sample.py
1 |
#!/usr/bin/env python
|
---|---|
2 |
# -*- coding: UTF-8 -*-
|
3 |
###########################################################################
|
4 |
# Eole - 2014
|
5 |
# Copyright Pole de Competence Eole (Ministere Education - Academie Dijon)
|
6 |
# Licence CeCill cf /root/LicenceEole.txt
|
7 |
# eole@ac-dijon.fr
|
8 |
#
|
9 |
# migration_2.4.py
|
10 |
#
|
11 |
# squelette de script permettant de générer des configurations de migration
|
12 |
# en mode batch (via l'API XMLRPC de Zéphir)
|
13 |
#
|
14 |
# Ce script génère une configuration de la même façon qu'en utilisant
|
15 |
# le bouton "générer un configuration de migration' de l'application web.
|
16 |
#
|
17 |
# Il est possible de personnaliser la fonction update_conf pour gérer
|
18 |
# des variables supplémentaires non gérées par la fonction d'origine.
|
19 |
# (variables déclarées au niveau des variantes / serveurs).
|
20 |
#
|
21 |
###########################################################################
|
22 |
|
23 |
import csv,getpass,os |
24 |
import dico,base64,xmlrpclib |
25 |
from zephir.utils.creolewrap import ZephirDict |
26 |
import sys |
27 |
reload(sys)
|
28 |
|
29 |
# force l'encodage par défaut à utf-8 pour les chaînes unicode
|
30 |
sys.setdefaultencoding('utf-8')
|
31 |
|
32 |
# à redéfinir si utilisé à distance
|
33 |
adresse_zephir='localhost'
|
34 |
|
35 |
# délimiteur de champ pour le fichier CSV décrivant les serveurs à traiter
|
36 |
delimiter = ';'
|
37 |
# contenu des champs du fichier csv:
|
38 |
# id_serveur;id_variante_destination
|
39 |
|
40 |
# !! mettre à True pour vérifier les valeurs lors de tests (la configuration ne sera pas sauvegardée) !!
|
41 |
debug = False
|
42 |
|
43 |
#######################################################
|
44 |
## FONCTIONS UTILITAIRES POUR LE TRAITEMENT DES VALEURS
|
45 |
|
46 |
def is_empty(value): |
47 |
# renvoie True si value est 'vide' :
|
48 |
# liste vide / chaine vide / liste de chaines vides
|
49 |
return len(u"".join(value)) == 0 |
50 |
|
51 |
def is_equal(dico, nom_var, test): |
52 |
"""renvoie True si la valeur de la variable nom_var est équivalente à test
|
53 |
gère le cas des chaines unicode ou non et des listes 'vides'
|
54 |
"""
|
55 |
if nom_var in dico.liste_vars: |
56 |
if test in ([], [''], [u'']): |
57 |
if dico.get_value(nom_var) in ([], [''], [u'']): |
58 |
return True |
59 |
if dico.get_value(nom_var) == test or \ |
60 |
dico.get_value(nom_var) == [test] or \
|
61 |
dico.get_value(nom_var) == [unicode(test)]:
|
62 |
return True |
63 |
return False |
64 |
else:
|
65 |
print u"Appel de is_equal sur une variable inconnue : {0}".format(nom_var) |
66 |
|
67 |
def copy_val(dico, nom_var, value): |
68 |
""" définit une valeur sur une variable en transformant
|
69 |
les chaînes non unicode en unicode dico est une configuration 2.4
|
70 |
"""
|
71 |
if nom_var in dico.liste_vars: |
72 |
if value == "[u'']" or value == "['']": |
73 |
value = [] |
74 |
if value != '[]' and type(value) != list: |
75 |
if value.startswith(u"["): |
76 |
value = eval(value)
|
77 |
# si le dictionnaire de destination est de type 'creole3' (eole 2.4),
|
78 |
# on transforme les type 'str' en 'unicode'
|
79 |
if dico.version == 'creole3': |
80 |
if type(value) == list: |
81 |
for index ,val in enumerate(value): |
82 |
if type(val) == str: |
83 |
value[index] = unicode(val, 'utf-8') |
84 |
elif type(value) == str: |
85 |
value = unicode(value, 'utf-8') |
86 |
dico.get_var(nom_var) |
87 |
dico.set_value(value) |
88 |
else:
|
89 |
print u"Appel de copy_val sur une variable inconnue : {0}".format(nom_var) |
90 |
|
91 |
def read_val(dico, nom_var): |
92 |
if nom_var in dico.liste_vars: |
93 |
value = dico.get_value(nom_var) |
94 |
if value == "[u'']" or value == "['']" or value == ['']: |
95 |
value = [] |
96 |
if value != '[]' and type(value) != list: |
97 |
if value.startswith(u"["): |
98 |
value = eval(value)
|
99 |
if type(value) == list: |
100 |
for index,v in enumerate(value): |
101 |
if type(v) == str: |
102 |
value[index] = unicode(v, 'utf-8') |
103 |
return value
|
104 |
else:
|
105 |
return unicode(dico.get_value(nom_var), 'utf-8') |
106 |
else:
|
107 |
print u"Appel de read_val sur une variable inconnue : {0}".format(nom_var) |
108 |
|
109 |
######################################
|
110 |
## FONCTION PERSONNALISEE DE MIGRATION
|
111 |
|
112 |
def update_conf(d_eole1, d_eole2, variante, proxy): |
113 |
"""
|
114 |
traitement automatique des variables
|
115 |
|
116 |
met à jour la configuration de migration en fonction de la configuration actuelle
|
117 |
|
118 |
d_eole1 : dictionnaire avec la configuration d'origine du serveur
|
119 |
d_eole2 : dictionnaire qui sera sauvegardé sur Zéphir comme 'migration.eol'
|
120 |
(configuration à appliquer sur le serveur migré)
|
121 |
variante: identifiant de la variante du serveur après migration
|
122 |
proxy: proxy XMLRPC utilisable pour faire des appels à l'API Zéphir
|
123 |
|
124 |
lire la valeur d'une variable (dico 1 ou 2):
|
125 |
|
126 |
d_eoleX.get_value('nom_var')
|
127 |
!ATTENTION! : get_value renvoie toujours une liste de valeur.
|
128 |
En cas de test sur une valeur, récupérer la valeur voulue
|
129 |
ex: if read_val(d_eole1,'activer_squid_auth')[0] == 'pedago':
|
130 |
|
131 |
copier la valeur d'une variable de dico1 vers une variabel de dico2:
|
132 |
copy_val(d_eole2,'nom_variable2.4',d_eole1.get_value('nom_variable2.2'))
|
133 |
|
134 |
modifier la valeur d'une variable du dictionnaire eole 2:
|
135 |
|
136 |
copy_val(d_eole2,'nom_var',u'valeur')
|
137 |
|
138 |
pour une variable multiple : copy_val(d_eole2,'nom_var',[u'valeur1',u'valeur2', ...])
|
139 |
|
140 |
vérifier si une variable est présente dans un dictionnaire:
|
141 |
|
142 |
if 'nom_variable' in d_eoleX.liste_vars:
|
143 |
.....
|
144 |
|
145 |
une bonne pratique peut être de créer des sous fonctions spécifiques à chaque variante
|
146 |
|
147 |
Pour plus d'informations sur l'utilisation de l'API de Zéphir:
|
148 |
https://<adresse_ip_zephir>:8070/aide/devel
|
149 |
https://<adresse_ip_zephir>:8070/aide/api
|
150 |
"""
|
151 |
|
152 |
# ----- inclure les traitements des variables ici
|
153 |
# --- !! pour éviter des problèmes d'indentation, configurez l'éditeur pour remplacer
|
154 |
# --- les tabulations par 4 espaces (ou éditez le fichier avec vi sur un module eole)
|
155 |
#
|
156 |
# --- Dans la mesure du possible, utilisez de préférence la modification des valeurs par défaut
|
157 |
# --- des modules/variantes eole NG dans l'application web si les valeurs à renseigner sont les mêmes
|
158 |
# --- pour tous les serveurs d'un(e) variante/module.
|
159 |
|
160 |
|
161 |
### Attention, pour la migration vers EOLE 2.4 :
|
162 |
#
|
163 |
# pour set_value sur d_eole2, il faut utiliser des chaînes unicode pour les chaînes de caractères :
|
164 |
# par exemple:
|
165 |
# - d_eole2.get_var('passerelle_smtp') : sélectionne la variable
|
166 |
# - d_eole2.set_value(u'smtp.in.ac-dijon.fr') : applique la valeur (unicode)
|
167 |
#
|
168 |
# utiliser de préférence les fonction copy_val et read_val, qui convertissent les chaînes si besoin :
|
169 |
# copy_val(d_eole2, 'passerelle_smtp', u'smtp.in.ac-dijon.fr') : ici, u est optionnel
|
170 |
###
|
171 |
|
172 |
# exemple1 : mettre le nombre de cartes à 3 si le modèle de pare-feu dans la configuration amon-1.5
|
173 |
# contient la chaine '3zones'
|
174 |
#
|
175 |
#if 'type_amon' in d_eole1.liste_vars:
|
176 |
# if '3zones' in d_eole1.get_value('type_amon'):
|
177 |
# d_eole2.dico.set_value('nombre_interfaces','3')
|
178 |
|
179 |
# exemple2 : pour la variante 8, activer le serveur sso si le frontend ead-web est activé
|
180 |
#
|
181 |
#if variante == 8:
|
182 |
# if isequal(d_eole2, 'ead_web', 'oui'):
|
183 |
# copy_val(d_eole2,'adresse_ip_sso', read_val(d_eole1,'adresse_ip_eth0'))
|
184 |
|
185 |
# ---- fin des traitements
|
186 |
|
187 |
return d_eole2
|
188 |
|
189 |
######################
|
190 |
## FONCTION PRINCIPALE
|
191 |
|
192 |
def migrate_serveurs(fic_csv_name="migration.csv"): |
193 |
"""
|
194 |
boucle principal de parcours des serveurs
|
195 |
"""
|
196 |
login = raw_input('login zephir :') |
197 |
passwd = getpass.getpass('mot de passe :')
|
198 |
print "" |
199 |
# lecture du fichier csv
|
200 |
if fic_csv_name == "": |
201 |
fic_csv_name = raw_input('emplacement du fichier csv : ') |
202 |
fic_csv = open(fic_csv_name)
|
203 |
parser = csv.reader(fic_csv,delimiter=delimiter) |
204 |
if adresse_zephir in ('localhost', '127.0.0.1'): |
205 |
# si lancé sur Zéphir, on utilise le port XMLRPC local (sans TLS, plus rapide)
|
206 |
proxy = xmlrpclib.ServerProxy('http://%s:%s@%s:7081' % (login,passwd,adresse_zephir))
|
207 |
else:
|
208 |
proxy = xmlrpclib.ServerProxy('https://%s:%s@%s:7080' % (login,passwd,adresse_zephir))
|
209 |
# informations sur les module et variantes
|
210 |
|
211 |
# parsing des données
|
212 |
erreurs = [] |
213 |
while True: |
214 |
try:
|
215 |
# lecture d'une ligne du fichier csv
|
216 |
data_serveur = parser.next() |
217 |
except StopIteration: |
218 |
# fin du fichier
|
219 |
fic_csv.close() |
220 |
break
|
221 |
# chaque ligne indique un numéro de serveur et le n° de la variante à utiliser après migration
|
222 |
id_serveur, variante = data_serveur |
223 |
id_serveur = int(id_serveur)
|
224 |
variante = int(variante)
|
225 |
# migration du serveur (équivalent de 'générer la 'générer les données de migration' dans l'appli web)
|
226 |
try:
|
227 |
# récupération du dictionnaire creole du serveur dans sa version actuelle
|
228 |
code, data_ori = proxy.serveurs.get_dico(id_serveur,'modif_config',True) |
229 |
d_eole1 = ZephirDict(mode='')
|
230 |
d_eole1.init_from_zephir(data_ori) |
231 |
# initialisation du dictionnaire creole2
|
232 |
code, data_migration = proxy.serveurs.migrate_conf(int(id_serveur), 'migration', int(variante)) |
233 |
if code == 0: |
234 |
raise Exception, data_migration |
235 |
## IMPORTANT, pour la migration vers eole 2.4, bien spécifier version='creole3'
|
236 |
d_eole2 = ZephirDict(mode='', version='creole3') |
237 |
# initialisation du dictionnaire depuis les valeurs renvoyées par Zéphir
|
238 |
d_eole2.init_from_zephir(data_migration) |
239 |
# déblocage temporaire des variables cachées et vérouillées ?
|
240 |
#settings = d_eole2.dico.cfgimpl_get_settings()
|
241 |
#settings.remove('disabled')
|
242 |
#settings.remove('frozen')
|
243 |
# mise à jour du dictionnaire de migration
|
244 |
if main_loop == False: |
245 |
return d_eole1, d_eole2
|
246 |
|
247 |
# APPEL DE LA FONCTION DE MIGRATION PERSONNALISEE
|
248 |
d_eole2 = update_conf(d_eole1, d_eole2, int(variante), proxy)
|
249 |
|
250 |
if debug:
|
251 |
print "\n--- configuration du serveur %s\n" % id_serveur |
252 |
for var in d_eole2.liste_vars: |
253 |
print var, '-->', d_eole2.get_value(var) |
254 |
else:
|
255 |
save_data = d_eole2.save(force=False) #,eol_file='/tmp/test.txt') |
256 |
# sauvegarde de la conf sur zephir (migration.eol)
|
257 |
code, detail = proxy.serveurs.save_conf(id_serveur, save_data, 'migration')
|
258 |
print "serveur modifié : %s" % id_serveur |
259 |
except Exception, e: |
260 |
#si besoin, décommenter pour avoir la trace complète
|
261 |
#import traceback
|
262 |
#traceback.print_exc()
|
263 |
print "erreur de modification du serveur %s : %s" % (id_serveur, str(e)) |
264 |
erreurs.append(delimiter.join(data_serveur)) |
265 |
|
266 |
# on stocke dans un fichier csv les serveurs non ajoutés
|
267 |
if len(erreurs) > 0: |
268 |
f=open("error.csv","w") |
269 |
f.write('\n'.join(erreurs))
|
270 |
f.close() |
271 |
print "\nDes erreurs ont été rencontrées :\n" |
272 |
print "- Vous pouvez consulter les logs du service zephir pour plus d'informations:\n" |
273 |
print " /var/log/rsyslog/local/zephir_backend/zephir_backend.info.log\n" |
274 |
print "- un fichier error.csv a été généré pour relancer ce script sur les serveurs en erreur\n" |
275 |
|
276 |
if __name__ == '__main__': |
277 |
try:
|
278 |
# on regarde si un fichier csv est passé en argument
|
279 |
assert os.path.isfile(sys.argv[1]) |
280 |
fic_csv = sys.argv[1]
|
281 |
except:
|
282 |
fic_csv = ""
|
283 |
migrate_serveurs(fic_csv, main_loop=True)
|