Projet

Général

Profil

gen_migration_sample.py

squelette de script pour génération de configuration de migration 2.4 - Bruno Boiget, 11/06/2014 15:03

Télécharger (11,5 ko)

 
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)