Package zephir :: Package backend :: Module lib_backend
[frames] | no frames]

Source Code for Module zephir.backend.lib_backend

   1  # -*- coding: UTF-8 -*- 
   2  ########################################################################### 
   3  # Eole NG - 2007 
   4  # Copyright Pole de Competence Eole  (Ministere Education - Academie Dijon) 
   5  # Licence CeCill  cf /root/LicenceEole.txt 
   6  # eole@ac-dijon.fr 
   7  # 
   8  # lib_backend.py 
   9  # 
  10  # classes de base pour la gestion des serveurs sous zephir 
  11  # 
  12  ########################################################################### 
  13  """fonctions utiles pour l'accès à la base de données (via adbapi) 
  14  """ 
  15  from zephir.backend import config 
  16  from zephir.backend.config import log 
  17  try: 
  18      from migration_perso import migration_perso, destinations_perso 
  19  except: 
  20      migration_perso = {} 
  21      destinations_perso = [] 
  22  from zephir.lib_zephir import lock, unlock, is_locked, lock_dir 
  23  from zephir.utils.creolewrap import ZephirDict 
  24  from zephir.monitor.agentmanager.util import md5files 
  25  from twisted.internet.utils import getProcessOutputAndValue 
  26  import psycopg2 as PgSQL 
  27  import os, sys, time, string, traceback, apt_pkg, re, shutil 
  28  import base64 
  29  from hashlib import md5 
  30  from ConfigParser import ConfigParser 
  31  from glob import glob 
  32  from cStringIO import StringIO 
  33  import xmlrpclib, cjson 
  34   
35 -class ResourceAuthError(Exception):
36 pass
37
38 -class ParamsLock:
39 - def __init__(self, id_serveur):
40 self.lockname = 'server_params_%s' % str(id_serveur)
41 - def __enter__(self):
42 wait_time = 0 43 while is_locked(self.lockname) and (wait_time <= 20): 44 # on attend au maximum 20 secondes que la ressource soit libérée 45 # après 20 secondes, on considére que l'appel concurrent est en échec 46 time.sleep(0.25) 47 wait_time += 0.25 48 # on interdit temporairement d'autres modification des paramètres du serveur 49 lock(self.lockname)
50 - def __exit__(self, type, value, traceback):
51 unlock(self.lockname)
52
53 -class CxPool(dict):
54 """dictionnaire des connexions postgresql en cours""" 55
56 - def create(self):
57 cx = PgSQL.connect(database=config.DB_NAME,user=config.DB_USER,password=config.DB_PASSWD) 58 cu = cx.cursor() 59 self[cu] = cx 60 return cu
61
62 - def commit(self,cu):
63 cu.close() 64 self[cu].commit() 65 self[cu].close() 66 del(self[cu])
67
68 - def rollback(self,cu):
69 cu.close() 70 self[cu].rollback() 71 self[cu].close() 72 del(self[cu])
73
74 - def close(self,cu):
75 cu.close() 76 self[cu].close() 77 del(self[cu])
78
79 -class AptChecker:
80 """gestionnaire de vérification d'état des paquets d'un serveur 81 """
82 - def __init__(self):
83 self.pkg_file = {} 84 self.packages = {} 85 self.timestamp = {} 86 for zephir_version, data in config.DISTRIBS.items(): 87 if zephir_version != 1: 88 codename, version, maintained = data 89 # lecture des fichiers générés pour les différents serveurs de mise à jour 90 for serv_maj in config.SERVEURS_MAJ_CLIENTS: 91 if zephir_version not in self.pkg_file: 92 self.pkg_file[zephir_version] = {} 93 self.packages[zephir_version] = {} 94 self.timestamp[zephir_version] = {} 95 self.pkg_file[zephir_version][serv_maj] = os.path.join(os.path.abspath(config.PATH_ZEPHIR),'packages_%s_%s.ini' % (codename, serv_maj)) 96 self.packages[zephir_version][serv_maj] = ConfigParser() 97 if not os.path.isfile(self.pkg_file[zephir_version][serv_maj]): 98 f = file(self.pkg_file[zephir_version][serv_maj],'w') 99 f.close() 100 # la liste des paquets n'a jamais été vérifiée, on lance la procédure de recherche 101 log.msg("** récupération de la liste des paquets disponibles en mise à jour", serv_maj) 102 os.system('/usr/share/eole/check_maj_eole.py') 103 self.timestamp[zephir_version][serv_maj] = os.path.getmtime(self.pkg_file[zephir_version][serv_maj]) 104 self.packages[zephir_version][serv_maj].read(self.pkg_file[zephir_version][serv_maj]) 105 # création d'une regexp compilée pour les paquets à ignorer 106 if len(config.held_packages) > 0: 107 self.held_pattern = re.compile("|".join(config.held_packages)) 108 else: 109 self.held_pattern = None
110
111 - def check_min_version(self, current_version, min_version):
112 apt_pkg.InitSystem() 113 if apt_pkg.VersionCompare(current_version, min_version) >= 0: 114 return True 115 return False
116
117 - def check_packages(self, maj_infos, pkg_file, debnames, version, serv_maj, type_maj = 'complete', show_installed = False):
118 """vérifie l'état de mise à jour des paquets 119 type_maj : type de mise à jour (minimale/complete) 120 show_installed : si True, renvoie la liste de tous les paquets installés 121 par défaut, seulement les paquets non à jour 122 """ 123 # si serveur de maj inconnu, on utilise le premier de la liste 124 if serv_maj not in self.packages[version]: 125 config.log.msg("Serveur de mise à jour", serv_maj, "inconnu : utilisation de", config.SERVEURS_MAJ_CLIENTS[0]) 126 serv_maj = config.SERVEURS_MAJ_CLIENTS[0] 127 depot_changed = True 128 client_changed = True 129 if os.path.isfile(pkg_file): 130 md5_packs = md5(file(pkg_file).read()).hexdigest() 131 else: 132 # pas de fichier remonté par le serveur ! 133 if maj_infos is not None: 134 return maj_infos[0], maj_infos 135 else: 136 return [], None 137 138 depot_timestamp = os.path.getmtime(self.pkg_file[version][serv_maj]) 139 if maj_infos: 140 # on regarde si les données ont changé depuis la dernière vérification de ce serveur 141 if maj_infos[1] == type_maj and maj_infos[2] == md5_packs and maj_infos[3] == serv_maj: 142 client_changed = False 143 if maj_infos[4] == depot_timestamp: 144 depot_changed = False 145 146 if debnames == [] and not show_installed: 147 # vérification globale de l'état de mise à jour du serveur 148 # si on a déjà des infos et que rien n'a changé, on ne fait pas de traitements 149 if not depot_changed and not client_changed: 150 return maj_infos[0], maj_infos 151 152 # creation de la liste des paquets installés (/à vérifier si debnames non vide) 153 pkg_liste = [] 154 if os.path.isfile(pkg_file): 155 for line in file(pkg_file): 156 pak_info = line.split() 157 if debnames != []: 158 # travail sur des paquets particuliers 159 if pak_info[0] in debnames: 160 pkg_liste.append(pak_info) 161 if len(pkg_liste) == len(debnames): 162 # on a trouvé tous les paquets demandés : on sort 163 break 164 else: 165 pkg_liste.append(line.split()) 166 # md5 des paquets installés 167 check_needed = False 168 if maj_infos is None or client_changed == True or debnames != [] or show_installed == True: 169 # Des informations ne sont pas à jour ou on a demandé les infos sur des paquets particuliers 170 check_needed = True 171 if depot_changed: 172 # la liste des paquets dispos a changé, on la recharge 173 self.packages[version][serv_maj].read(self.pkg_file[version][serv_maj]) 174 self.timestamp[version][serv_maj] = depot_timestamp 175 check_needed = True 176 liste_pack = [] 177 if check_needed: 178 outdated = [] 179 apt_pkg.InitSystem() 180 for pkg_name, pkg_version in pkg_liste: 181 if (self.held_pattern is None) or (not self.held_pattern.match(pkg_name)): 182 try: 183 # les paquets n'ayant jamais eu de mise à jour ne sont pas présents 184 if self.packages[version][serv_maj].has_section(pkg_name): 185 try: 186 maj_version = self.packages[version][serv_maj].get(pkg_name, type_maj) 187 except: 188 if type_maj != 'minimum': 189 log.msg('Informations de version du paquet %s non trouvées pour %s' % (pkg_name, serv_maj)) 190 traceback.print_exc() 191 if apt_pkg.VersionCompare(maj_version, pkg_version) > 0: 192 # on renvoie, le paquet, la version installée, et la version sur le serveur de maj 193 liste_pack.append((pkg_name, pkg_version, maj_version)) 194 outdated.append((pkg_name, pkg_version, maj_version)) 195 continue 196 elif show_installed == True: 197 liste_pack.append((pkg_name, pkg_version, maj_version)) 198 continue 199 except: 200 traceback.print_exc() 201 # listing des paquets installés, on renvoie le paquet 202 # même si il n'est pas dans les repository de maj 203 if show_installed == True: 204 liste_pack.append((pkg_name, pkg_version, pkg_version)) 205 maj_infos = (outdated, type_maj, md5_packs, serv_maj, depot_timestamp) 206 else: 207 # pas de changement, on reprend la liste précédente 208 liste_pack = maj_infos[0] 209 return liste_pack, maj_infos
210 211 cx_pool = CxPool() 212 # maj_checker = AptChecker() 213
214 -class Serveur:
215 """classe utilitaire pour récupérer diverses données sur un serveur""" 216
217 - def __init__(self, pool, id_s, cu=None, data=None):
218 """initialise l'objet serveur et récupère les infos 219 """ 220 self.version = 'creole2' 221 # récupération des serveurs 222 self.id_s = int(id_s) 223 # date de dernière modification du serveur 224 self.modified = time.time() 225 # date de création de l'objet 226 self.created = time.time() 227 # serveur inactif ? (pas d'alerte) 228 self.no_alert = False 229 # lien sur le pool de serveurs 230 self.pool = pool 231 # liste des paquets non à jour 232 self.maj_infos = None 233 # cache de la configuration Creole 234 self.dico = None 235 self.dico_mtime = None 236 self.last_mode = None 237 cursor = cu 238 if cu == None: 239 cursor = cx_pool.create() 240 try: 241 if data == None: 242 # le rne ne peut pas être modifié : on le stocke une fois pour toutes 243 cursor.execute("select rne from serveurs where id=%s", (int(self.id_s),)) 244 data = cursor.fetchone() 245 self.rne = data[0] 246 # appel de la fonction update_data pour les données modifiables 247 self.update_data(cursor) 248 else: 249 self.rne = data[0] 250 self.confdir = os.path.join(config.PATH_ZEPHIR,'conf',self.rne, str(self.id_s)) 251 if cu == None: 252 cx_pool.close(cursor) 253 except: 254 traceback.print_exc() 255 if cu == None: 256 cx_pool.close(cursor)
257
258 - def update_data(self, cu=None, data=None):
259 """recharge les données des serveurs 260 """ 261 # récupération de l'établissement et de l'état 262 cursor = cu 263 if cu == None: 264 cursor = cx_pool.create() 265 try: 266 if data == None: 267 cursor.execute("select libelle, module_actuel, variante, timeout, etat, md5s, maj, no_alert from serveurs where id=%s", (int(self.id_s),)) 268 data = cursor.fetchone() 269 # données du serveur 270 self.libelle = data[0] 271 self.id_mod = int(data[1]) 272 self.id_var = int(data[2]) 273 try: 274 self.md5s = int(data[5]) 275 except: 276 self.md5s = -1 277 try: 278 self.maj = int(data[6]) 279 except: 280 self.maj = -1 281 try: 282 self.timeout = int(data[3]) 283 except: 284 self.timeout = 0 285 if data[4] == None: 286 self.status = -1 287 else: 288 self.status = int(data[4]) 289 try: 290 if str(data[7]) == '1': 291 self.no_alert = True 292 else: 293 self.no_alert = False 294 except: 295 pass 296 # libelle etablissement 297 cursor.execute("select libelle from etablissements where rne=%s", (self.rne,)) 298 data = cursor.fetchone() 299 self.etab = data[0] 300 # libelle module 301 cursor.execute("select libelle, version from modules where id=%s", (int(self.id_mod),)) 302 data = cursor.fetchone() 303 self.module = data[0] 304 # numéro de distribution Eole (1:Eole 1.X, 2:Eole 2.0, 3:Eole 2.1, ...) 305 try: 306 self.module_version = int(data[1]) 307 except: 308 self.module_version = 5 309 # détection de la version de creole utilisée 310 self.version = config.CREOLE_VERSIONS[self.module_version] 311 # libelle variante 312 cursor.execute("select libelle from variantes where id=%s", (int(self.id_var),)) 313 data = cursor.fetchone() 314 self.variante = data[0] 315 self.modified = time.time() 316 if cu == None: 317 cx_pool.close(cursor) 318 except: 319 traceback.print_exc() 320 if cu == None: 321 cx_pool.close(cursor)
322
323 - def __repr__(self):
324 """affichage du serveur 325 """ 326 return """%s => %s (%s)""" % (self.id_s, self.module, self.rne)
327
328 - def __str__(self):
329 """affichage du serveur 330 """ 331 return """%s => %s (%s) - %s (%s)""" % (self.id_s, self.libelle, self.module, self.etab, self.rne)
332 333 ##################### 334 ## LECTURE DE DONNEES 335
336 - def get_libelle(self):
337 return self.libelle
338
339 - def get_rne(self):
340 return self.rne
341
342 - def get_etab(self):
343 return self.etab
344
345 - def get_module(self):
346 return self.module
347
348 - def get_variante(self):
349 return self.variante
350
351 - def get_timeout(self):
352 return self.timeout
353
354 - def get_confdir(self):
355 return self.confdir
356
357 - def get_config(self, mode="modif_config", encode=False):
358 """retourne la configuration eole du serveur (zephir.eol) 359 """ 360 # actualisation si des dictionnaires ont changé 361 dico = self.check_dict(mode, encode) 362 # vérification des valeurs chargées en mémoire 363 return self.load_conf(mode, dico)
364
365 - def check_dict(self, mode, encode=False):
366 """récupération des dictionnaires de configuration du serveur 367 """ 368 # vérification de la date de mise à jour des dictionnaires 369 if self.version == 'creole1': 370 # on recherche la liste des dictionnaires à charger 371 dicts = [self.confdir+os.sep+'dictionnaire'] 372 # dictionnaire de la variante 373 dicts.extend(glob(self.confdir+os.sep+'dicos/variante/*.eol')) 374 # dictionnaire locaux 375 dicts.extend(glob(self.confdir+os.sep+'dicos/*.eol')) 376 else: 377 dicts = glob('%s/dicos/*/*.xml' % self.confdir) 378 last_mtime = 0.0 379 # on enregistre la liste des dictionnaires pris en compte 380 # et leur dernière date de modification 381 mtimes = set() 382 for d in dicts: 383 d_mtime = os.stat(d).st_mtime 384 mtimes.add((d, d_mtime)) 385 if config.CREOLE_CACHE: 386 # gestion de la date de dernière mise à jour du cache creole 387 if self.dico_mtime is None or mtimes != self.dico_mtime: 388 self.dico_mtime = mtimes 389 # dictionnaires non à jour, on réinitialise 390 self.dico = None 391 if self.dico is None or config.CREOLE_CACHE == False: 392 if self.version == 'creole1': 393 dicos = [] 394 for dic in dicts: 395 try: 396 # lecture du contenu des dictionnaires 397 fic = open(dic,'r') 398 lines = fic.readlines() 399 fic.close() 400 if encode == True: 401 data = [ unicode(line, 'ISO-8859-1').encode('UTF-8') for line in lines ] 402 else: 403 data = lines 404 # stockage du contenu du dictionnaire 405 dicos.append(data) 406 except OSError: 407 pass 408 else: 409 # Des dictionnaires ont été modifiés : on réinitialise l'objet dictionnaire 410 # + chargement de la configuration dans le bon mode 411 dicos = [] 412 for rep in ['module','variante','local','package','var_package']: 413 if os.path.exists(os.path.join(self.confdir,'dicos',rep)): 414 dicos.append(os.path.join(self.confdir,'dicos',rep)) 415 dico = ZephirDict(dicos, self.confdir, mode, self.version) 416 if config.CREOLE_CACHE: 417 self.dico = dico 418 return dico 419 else: 420 # le dictionnaire est déjà en cache 421 return self.dico
422
423 - def parsedico(self, mode='modif_config', separator="| ", encode=False):
424 if self.dico is not None: 425 if config.CREOLE_CACHE and self.dico.mode == mode: 426 # dans tous les cas, on appelle check_dict 427 # au cas ou un dictionnaires aurait été modifié 428 dico = self.check_dict(mode, encode) 429 return dico.parsedico(separator=separator) 430 if config.CREOLE_CACHE == False and self.last_mode == mode: 431 if separator != "| ": 432 # cache partiel, les valeurs sont stockées avec le séparateur "| " 433 final_dict = {} 434 for varname, values in self.dico.items(): 435 final_dict[varname] = separator.join([val.strip() for val in values.split("| ")]) 436 return final_dict 437 return self.dico 438 dico = self.get_config(mode, encode) 439 return dico.parsedico(separator=separator)
440
441 - def load_conf(self, mode, dico=None):
442 """vérifie que les valeurs déjà chargées correspondent au mode demandé 443 """ 444 if config.CREOLE_CACHE: 445 if self.dico.mode != mode or not mode.startswith('modif'): 446 # rechargement des valeurs dans le mode demandé 447 # on recharge si on change de mode ou si on veut repartir des 448 # valeurs d'origine (le fichier de module/variante est susceptible d'avoir changé) 449 self.dico.load_values(mode) 450 return self.dico 451 else: 452 if self.last_mode != mode or not mode.startswith('modif'): 453 dico.load_values(mode) 454 # mode de cache réduit, on ne conserve que le dictionnaire de valeurs 455 self.dico = dico.parsedico(separator="| ") 456 self.last_mode = mode 457 return dico
458
459 - def save_config(self, dico_zeph, mode='config', encode=False, force=False, new_method=True):
460 """sauvegarde la configuration eole du serveur (zephir.eol) 461 force : pas de validation des contraintes (pour une variante, la conf est incomplète) 462 new_method : utiliser le nouveau calcul du md5 de la configuration 463 """ 464 try: 465 if mode in ['config','modif_config']: 466 fic_zephir = os.path.join(self.confdir,'zephir.eol') 467 key = 'config_ok' 468 dict_mode = 'modif_config' 469 elif mode in ['dico','modif_dico']: 470 # si gen_dico, sauvegarde de dico.eol dans la variante 471 fic_zephir = os.path.abspath(config.PATH_MODULES)+os.sep+str(self.id_mod)+os.sep+'variantes'+os.sep+str(self.id_var)+os.sep+'dico.eol' 472 key = 'dico_ok' 473 dict_mode = 'modif_dico' 474 elif mode in ['migration','modif_migration']: 475 fic_zephir = os.path.join(self.confdir,'migration.eol') 476 key = 'migration_ok' 477 dict_mode = 'modif_migration' 478 # sauvegarde des données dans le fichier de configuration 479 dico_zeph.save(fic_zephir,encode=encode,force=force) 480 # mise à jour du cache de configuration du serveur 481 if config.CREOLE_CACHE: 482 self.dico.load_values(dict_mode) 483 else: 484 self.dico = dico_zeph.parsedico(separator="| ") 485 self.last_mode = dict_mode 486 if key != '': 487 self.maj_params({key:1}) 488 self.check_md5conf(new_method) 489 except: 490 traceback.print_exc() 491 raise IOError("serveur %s : erreur de sauvegarde de %s" % (self.id_s, os.path.basename(fic_zephir))) 492 return ""
493
494 - def get_status(self):
495 """renvoie le dernier état enregistré du serveur 496 """ 497 try: 498 assert self.status in range (5) 499 except: 500 return -1 501 else: 502 return self.status
503
504 - def set_status(self, status):
505 """Met à jour l'état du serveur 506 """ 507 self.status = int(status) 508 cu = cx_pool.create() 509 try: 510 cu.execute("update serveurs set etat=%s where id=%s", (int(self.status), int(self.id_s))) 511 cx_pool.commit(cu) 512 except: 513 traceback.print_exc() 514 cx_pool.rollback(cu) 515 return self.status
516
517 - def update_ip_pub(self, ip_publique):
518 """Conserve la dernière adresse de connexion du serveur 519 """ 520 cu = cx_pool.create() 521 try: 522 cu.execute("update serveurs set ip_publique=%s where id=%s", (ip_publique, int(self.id_s))) 523 cx_pool.commit(cu) 524 except: 525 traceback.print_exc() 526 cx_pool.rollback(cu) 527 return False 528 return True
529
530 - def get_params(self, params=None):
531 """renvoie le champ paramètres du serveur 532 """ 533 try: 534 if params is None: 535 cu = cx_pool.create() 536 try: 537 cu.execute("select params from serveurs where id=%s", (int(self.id_s),)) 538 data = cu.fetchone() 539 cx_pool.close(cu) 540 except: 541 traceback.print_exc() 542 cx_pool.close(cu) 543 params = {} 544 try: 545 params = eval(data[0]) 546 # RQE : si ajout de nouveaux champs dans params, 547 # remplacer le nom du champ ci-dessous par le dernier ajouté 548 assert type(params) == dict and params.has_key('perso_ok') 549 except: 550 # met à jour le champ param si il n'est pas encore renseigné 551 params = self.update_params(params) 552 # sauvegarde du champs params 553 self.save_params(params) 554 else: 555 if type(params) == str: 556 params = eval(params) 557 558 # nombre de commandes et transferts uucp en attente 559 id_uucp = self.rne+'-'+str(self.id_s) 560 params['uucp_transfert']=0 561 params['uucp_cmd']=0 562 if os.path.isdir('/var/spool/uucp/%s/D.' % id_uucp): 563 params['uucp_transfert']=len(os.listdir('/var/spool/uucp/%s/D.' % id_uucp)) 564 if os.path.isdir('/var/spool/uucp/%s/D.X' % id_uucp): 565 params['uucp_cmd']=len(os.listdir('/var/spool/uucp/%s/D.X' % id_uucp)) 566 # si la configuration n'était pas présénte, on vérifie si c'est toujours le cas 567 if params['config_ok'] == 0: 568 if os.path.isfile(self.confdir+os.sep+'zephir.eol'): 569 params['config_ok'] = 1 570 if params['dico_ok'] == 0: 571 if os.path.isfile(self.confdir+os.sep+'dico.eol'): 572 params['dico_ok'] = 1 573 if os.path.isfile(self.confdir+os.sep+'new_key.pub'): 574 if os.path.isfile(os.path.join(self.confdir,'new_addr')): 575 params['new_key'] = (2,file(os.path.join(self.confdir,'new_addr')).read().strip()) 576 elif os.path.isfile(os.path.join(self.confdir,'new_addr_ok')): 577 params['new_key'] = (3,file(os.path.join(self.confdir,'new_addr_ok')).read().strip()) 578 else: 579 params['new_key'] = (1, '') 580 else: 581 params['new_key'] = (0, '') 582 if self.module_version not in config.allowed_migrations: 583 #if self.version != 'creole1': 584 params['migration_ok'] = 1 585 elif os.path.isfile(self.confdir+os.sep+'migration.eol'): 586 params['migration_ok'] = 0 587 else: 588 params['migration_ok'] = -1 589 return params 590 except: 591 traceback.print_exc() 592 return {}
593
594 - def edit_serveur(self, dico_modifs):
595 # on vérifie que l'identifiant n'est pas modifié 596 if 'id' in dico_modifs.keys(): 597 return 0, config.u("""L'identifiant ne peut pas être modifié""") 598 # idem pour le rne et le module 599 if 'rne' in dico_modifs.keys(): 600 return 0, config.u("""Le rne ne peut pas être modifié""") 601 if 'module_initial' in dico_modifs.keys(): 602 return 0, config.u("""Le module initial ne peut pas être modifié""") 603 if 'module_actuel' in dico_modifs.keys() and int(dico_modifs['module_actuel']) != self.id_mod: 604 # modification amon <-> amonecole permis 605 mod_name = self.module[:self.module.rindex('-')] 606 allowed_edits = config.allowed_mod_edits.get(self.module_version, {}) 607 if mod_name in allowed_edits: 608 # recherche de la variante par défaut et du libellé pour le modules de destination 609 query = """select modules.libelle, variantes.id, modules.version from modules, variantes where modules.id=%s and modules.id=variantes.module and variantes.libelle='standard'""" 610 cu = cx_pool.create() 611 try: 612 cu.execute(query, (int(dico_modifs['module_actuel']),)) 613 data = cu.fetchone() 614 cx_pool.close(cu) 615 except: 616 traceback.print_exc() 617 cx_pool.close(cu) 618 return 0, config.u("""Le module n'a pas été retrouvé""") 619 if (not data[0][:data[0].rindex('-')] in allowed_edits[mod_name]) or (int(data[2]) != int(self.module_version)): 620 return 0, config.u("""Le module demandé n'est pas compatible avec ce serveur""") 621 # cas spécial : si on modifie le module et pas la variante, mettre variante standard par défaut 622 if not 'variante' in dico_modifs.keys(): 623 dico_modifs['variante'] = data[1] 624 else: 625 return 0, config.u("""Le module ne peut pas être modifié""") 626 # construction de la requête SQL de modification 627 if dico_modifs == {}: 628 return 1, config.u("""Aucune modification demandée""") 629 else: 630 params = [] 631 requete=["update serveurs set "] 632 for cle in dico_modifs.keys(): 633 requete.append(str(cle)) 634 requete.append("=%s, ") 635 if cle in ('module_initial', 'module_actuel', 'variante', 'timeout', 'etat', 'maj', 'md5s', 'no_alert'): 636 params.append(int(dico_modifs[cle])) 637 else: 638 params.append(str(dico_modifs[cle])) 639 query = "".join(requete)[:-2] 640 query += """ where id=%s""" 641 params.append(int(self.id_s)) 642 # modification dans la base et appel à la fonction de mise à jour dans l'arborescence 643 try: 644 cu = cx_pool.create() 645 cu.execute(query, params) 646 cx_pool.commit(cu) 647 except: 648 return 0, config.u("""Erreur lors de la modification dans la base""") 649 650 serveur_dir_ori = self.confdir 651 if 'variante' in dico_modifs.keys(): 652 if int(dico_modifs['variante']) != int(self.id_var): 653 # on logue le changement de variante 654 try: 655 params = (int(self.id_s), str(time.ctime()), int(self.id_var), int(dico_modifs['variante'])) 656 cu = cx_pool.create() 657 cu.execute("""insert into log_serveur (id_serveur,date,type,message,etat) values (%s,%s,'ZEPHIR','Modification de la variante (%s -> %s)',0)""", params) 658 cx_pool.commit(cu) 659 except: 660 # on ne bloque pas si le log échoue 661 pass 662 663 if 'module_actuel' in dico_modifs.keys() and int(dico_modifs['module_actuel']) != self.id_mod: 664 code, msg = self._cree_arbo_serveur(new_module=int(dico_modifs['module_actuel']),new_variante=int(dico_modifs['variante'])) 665 else: 666 code, msg = self._cree_arbo_serveur(new_variante=int(dico_modifs['variante'])) 667 if code == 0: 668 return 0, config.u(msg) 669 670 # mise à jour des statistiques des variantes 671 self.pool.stats['serv_variantes'][str(self.id_var)] = self.pool.stats['serv_variantes'][str(self.id_var)] - 1 672 self.pool.stats['serv_variantes'][str(dico_modifs['variante'])] = self.pool.stats['serv_variantes'].get(str(dico_modifs['variante']), 0) + 1 673 if 'module_actuel' in dico_modifs.keys() and int(dico_modifs['module_actuel']) != self.id_mod: 674 # mise à jour des statistiques des modules 675 self.pool.stats['serv_modules'][str(self.id_mod)] = self.pool.stats['serv_modules'][str(self.id_mod)] - 1 676 self.pool.stats['serv_modules'][str(dico_modifs['module_actuel'])] = self.pool.stats['serv_modules'].get(str(dico_modifs['module_actuel']), 0) + 1 677 self.update_data() 678 # tout s'est bien passé (on peut toujours rêver) 679 return 1, config.u('ok')
680
681 - def _cree_arbo_serveur(self,new_module=None,new_variante=None):
682 """crée l'arborescence zephir d'un nouveau serveur""" 683 # création des différents répertoires du serveur sur zéphir 684 edit_mode = False 685 if new_module is not None: 686 edit_mode = True 687 module = new_module 688 else: 689 module = self.id_mod 690 if new_variante is not None: 691 edit_mode = True 692 variante = new_variante 693 else: 694 variante = self.id_var 695 serveur_dir = self.confdir 696 697 if not edit_mode: 698 try: 699 os.makedirs(serveur_dir) 700 except: 701 return 0, """erreur de création du répertoire du serveur""" 702 # création du répertoire de stockage des fichiers spécifiques au module 703 try: 704 os.makedirs(serveur_dir+os.sep+'fichiers_zephir') 705 os.makedirs(serveur_dir+os.sep+'fichiers_perso') 706 os.makedirs(serveur_dir+os.sep+'dicos') 707 os.makedirs(serveur_dir+os.sep+'patchs') 708 os.makedirs(serveur_dir+os.sep+'uucp') 709 except: 710 shutil.rmtree(serveur_dir) 711 return 0, """erreur de création du répertoire des fichiers spécifiques au module""" 712 713 if (not edit_mode) or new_module: 714 # création du lien vers le dictionnaire du module 715 try: 716 if os.path.isdir(os.path.abspath(config.PATH_MODULES)+os.sep+str(module)+'/dicos'): 717 # module Eole2 718 if not edit_mode: 719 os.makedirs(serveur_dir+os.sep+'dicos/local') 720 assert os.system('ln -nsf '+os.path.abspath(config.PATH_MODULES)+os.sep+str(module)+'/dicos '+serveur_dir+'/dicos/module') == 0 721 assert os.system('ln -nsf '+os.path.abspath(config.PATH_MODULES)+os.sep+str(module)+'/module.eol '+serveur_dir+'/module.eol') == 0 722 else: 723 # module Eole1 724 assert os.system('ln -nsf '+os.path.abspath(config.PATH_MODULES)+os.sep+str(module)+'/dictionnaire'+' '+serveur_dir+'/dictionnaire') == 0 725 except: 726 shutil.rmtree(serveur_dir) 727 return 0, """erreur de lien sur le dictionnaire""" 728 729 if (not edit_mode) or new_variante: 730 # création des liens vers les patchs et dicos des variantes (ou du module si pas de variante) 731 # dico.eol 732 try: 733 assert os.system('ln -nsf '+os.path.abspath(config.PATH_MODULES)+os.sep+str(module)+'/variantes/'+str(variante)+os.sep+'dico.eol'+' '+serveur_dir+os.sep+'dico.eol') == 0 734 assert os.system('ln -nsf '+os.path.abspath(config.PATH_MODULES)+os.sep+str(module)+'/variantes/'+str(variante)+os.sep+'droits_zephir'+' '+serveur_dir+os.sep+'droits_variante') == 0 735 # patchs 736 assert os.system('ln -nsf '+os.path.abspath(config.PATH_MODULES)+os.sep+str(module)+'/variantes/'+str(variante)+'/patchs'+' '+serveur_dir+'/patchs/variante') == 0 737 # dicos 738 assert os.system('ln -nsf '+os.path.abspath(config.PATH_MODULES)+os.sep+str(module)+'/variantes/'+str(variante)+'/dicos'+' '+serveur_dir+'/dicos/variante') == 0 739 if os.path.isdir(os.path.join(config.PATH_MODULES, str(module), 'variantes', str(variante), 'package')): 740 assert os.system('ln -nsf '+os.path.abspath(config.PATH_MODULES)+os.sep+str(module)+'/variantes/'+str(variante)+'/package'+' '+serveur_dir+'/dicos/var_package') == 0 741 # fichiers additionnels (définis dans les dictionnaires locaux) 742 assert os.system('ln -nsf '+os.path.abspath(config.PATH_MODULES)+os.sep+str(module)+'/variantes/'+str(variante)+'/fichiers_perso'+' '+serveur_dir+'/fichiers_perso/variante') == 0 743 # fichiers spécifiques au module (définis dans le dictionnaire zephir) 744 assert os.system('ln -nsf '+os.path.abspath(config.PATH_MODULES)+os.sep+str(module)+'/variantes/'+str(variante)+'/fichiers_zephir'+' '+serveur_dir+'/fichiers_zephir/variante') == 0 745 except: 746 shutil.rmtree(serveur_dir) 747 return 0, """erreur de création des liens vers la variante""" 748 return 1, 'ok'
749
750 - def update_params(self,params_ori):
751 # le champ params n'est pas initialisé 752 if self.module_version in config.allowed_migrations: 753 #if self.version == 'creole1': 754 migration_ok = -1 755 else: 756 # pour les serveurs sans migration, on considère que la migration est déjà faite 757 migration_ok = 1 758 params = {'agents':1 759 ,'dico_ok':0 760 ,'config_ok':0 761 ,'migration_ok':migration_ok 762 ,'cle_ok':0 763 ,'new_key':(0,'') 764 #,'uucp_transfert':0 765 #,'uucp_cmd':0 766 ,'maj_ok':[-2,''] 767 ,'reboot_ok':[-2,''] 768 ,'service_restart_ok':[-2,''] 769 ,'query_maj':[-2,''] 770 ,'reconfigure_ok':[-2,''] 771 ,'configure_ok':[-2,''] 772 ,'sauvegarde_ok':[-2,''] 773 ,'upgrade_ok':[-2,''] 774 ,'perso_ok':[-2,''] 775 ,'lock_ok':[1,''] 776 ,'timeout':[1,''] 777 ,'md5s':[-1,''] 778 ,'last_log':'journal vide' 779 } 780 try: 781 params['agents'] = params_ori['agents'] 782 except: 783 # le serveur n'a pas encore d'info sur les agents 784 pass 785 # présence de zephir.eol et clé d'enregistrement 786 if os.path.isfile(os.path.join(self.confdir,'zephir.eol')): 787 params['config_ok']=1 788 if os.path.isfile(os.path.join(self.confdir,'cle_publique')): 789 params['cle_ok']=1 790 if os.path.isfile(os.path.join(self.confdir,'new_key.pub')): 791 if os.path.isfile(os.path.join(self.confdir,'new_addr')): 792 params['new_key'] = (2,file(os.path.join(self.confdir,'new_addr')).read().strip()) 793 elif os.path.isfile(os.path.join(self.confdir,'new_addr_ok')): 794 params['new_key'] = (3,file(os.path.join(self.confdir,'new_addr_ok')).read().strip()) 795 else: 796 params['new_key'] = (1,'') 797 # vérification des logs pour maj et reconfigure 798 query = """select type,etat,date,message from last_log_serveur where id_serveur = %s order by date desc,id desc limit 1""" 799 cu = cx_pool.create() 800 try: 801 cu.execute(query, (int(self.id_s),)) 802 data = cu.fetchone() 803 # on stocke la date du dernier log remonté 804 if data is not None: 805 params['last_log']=str(data[2]) 806 query = """select type,etat,date,message from last_log_serveur where id_serveur = %s and type != 'SURVEILLANCE' order by date desc,id desc""" 807 cu.execute(query, (int(self.id_s),)) 808 data = cu.fetchall() 809 cx_pool.close(cu) 810 # on parse les logs 811 not_found = ['maj','reconfigure','configure','lock','sauvegarde','reboot','service_restart','upgrade','perso'] 812 for log in data: 813 # on regarde si le serveur n'a pas remonté un bloquage des tâches zephir 814 # (pb : procédure pour remonter la résolution du problème) 815 if log[0] == 'LOCK': 816 if 'lock' in not_found: 817 not_found.remove('lock') 818 if log[1] == 1: 819 params['lock_ok'] = [2,str(log[2]),log[3]] 820 else: 821 for type_log in ['maj','configure','reconfigure','sauvegarde','reboot','service_restart','upgrade','perso']: 822 # état des actions 823 if log[0] == type_log.upper() and int(log[1]) != -2: 824 # on ne stocke que le dernier etat de chaque action 825 if type_log in not_found: 826 not_found.remove(type_log) 827 if log[1] == -1: 828 params['%s_ok' % type_log] = [2,str(log[2]),log[3]] 829 elif log[1] > 0: 830 params['%s_ok' % type_log] = [0,str(log[2]),log[3]] 831 elif log[1] == 0: 832 params['%s_ok' % type_log] = [1,str(log[2]),log[3]] 833 break 834 if not_found == []: 835 break 836 except: 837 traceback.print_exc() 838 cx_pool.close(cu) 839 return params
840
841 - def maj_params(self, param_updates):
842 """modifie le champ param d'un serveur 843 @param_updates : dictionnaire contenant les modifications 844 """ 845 # on interdit d'autres modifications sur les paramètres de ce serveur 846 # pendant la procédure de mise à jour 847 with ParamsLock(self.id_s): 848 params_ori = self.get_params() 849 params_ori.update(param_updates) 850 self.save_params(params_ori)
851
852 - def save_params(self, params):
853 query = """update serveurs set params=%s where id=%s""" 854 sql_params = (str(params), int(self.id_s)) 855 cu = cx_pool.create() 856 try: 857 cu.execute(query, sql_params) 858 cx_pool.commit(cu) 859 except: 860 traceback.print_exc() 861 cx_pool.rollback(cu)
862
863 - def check_md5conf(self, new_method=True):
864 # création de la liste des fichiers présents sur zephir 865 modifs = [] 866 fics=[] 867 files=md5files[self.module_version] 868 # cas particulier : zephir.eol 869 # creole 1 : suppression des lignes sans variables 870 # creole 2 : valeurs précédentes ignorées 871 orig_eol = os.path.join(self.confdir,'zephir.eol') 872 var_eol = os.path.join(self.confdir,'variables.eol') 873 if os.path.isfile(orig_eol): 874 f_var = file(var_eol, 'w') 875 if self.version == 'creole3': 876 # création d'un fichier ordonné variable:valeur 877 conf_orig = cjson.decode(file(orig_eol).read()) 878 eole_vars = conf_orig.keys() 879 eole_vars.sort() 880 for var_name in eole_vars: 881 if var_name != 'mode_zephir': 882 var_data = u"{0}:{1}\n".format(var_name, conf_orig[var_name].get('val')) 883 f_var.write(var_data.encode(config.charset)) 884 elif self.version == 'creole2': 885 if new_method: 886 # on crée un fichier avec variable:valeur ordonné par nom de variable 887 conf = ConfigParser() 888 conf.read(orig_eol) 889 var_names = conf.sections() 890 var_names.sort() 891 for var_name in var_names: 892 # 1 ligne par variable avec valeurs concaténées 893 # (évite les problèmes avec les chaines vide) 894 # on ne tient pas compte de mode_zephir qui est 895 # à oui seulement sur Zéphir 896 if var_name != 'mode_zephir': 897 f_var.write("%s:%s\n" % (var_name, ''.join(eval(conf.get(var_name, 'val'))))) 898 else: 899 for line in file(orig_eol).readlines(): 900 # ancienne méthode 901 if not "valprec =" in line: 902 f_var.write(line) 903 elif self.version == 'creole1': 904 for line in file(orig_eol).readlines(): 905 if "@@" in line: 906 f_var.write(line) 907 f_var.close() 908 for src, dst, pattern in files: 909 if os.path.isdir(os.path.join(self.confdir,dst)): 910 for fic in os.listdir(os.path.join(self.confdir,dst)): 911 if os.path.isfile(os.path.join(self.confdir,dst,fic)): 912 if pattern == None or fic.endswith(pattern): 913 fics.append(os.path.join(dst,fic)) 914 else: 915 fics.append(dst) 916 md5s = [] 917 md5file = os.path.join(os.path.abspath(config.PATH_ZEPHIR),'data','config%s.md5' % self.id_s) 918 if os.path.isfile(md5file): 919 # liste des fichiers remontés par le serveur 920 data = file(md5file).read().strip().split('\n') 921 # comparaison 922 for line in data: 923 fic = line.split()[1] 924 if fic not in fics: 925 modifs.append("absent sur zephir : %s" % fic) 926 else: 927 md5s.append(fic) 928 for fic in fics: 929 if fic not in md5s: 930 # erreur : fichier non présent sur la machine distante 931 modifs.append("non envoyé : %s" % fic) 932 # test du contenu des fichiers 933 cmd_md5 = getProcessOutputAndValue("/usr/bin/md5sum", 934 args = ["-c",md5file], 935 path = self.confdir, 936 env = {'LC_ALL': 'C'}) 937 cmd_md5.addCallback(self._check_md5_res, md5s, modifs) 938 cmd_md5.addErrback(self._check_md5_res, md5s, modifs) 939 else: 940 # pas d'infos md5 pour ce serveur 941 self.maj_params({'md5s':[-1,""]}) 942 self.edit_serveur({'md5s':-1})
943
944 - def _check_md5_res(self, result, md5s, modifs):
945 # on effectue une vérification md5 des fichiers de configuration si disponible 946 out, err, code = result 947 for fic in md5s: 948 if not "%s: OK" % fic in out.split('\n'): 949 if fic == 'variables.eol': 950 # cas particulier des variables creole 1, on indique une modification sur zephir.eol 951 modifs.append("contenu modifié : zephir.eol") 952 else: 953 modifs.append("contenu modifié : %s" % fic) 954 md5_ok = False 955 if modifs != []: 956 self.edit_serveur({'md5s':0}) 957 self.maj_params({'md5s':[0,";".join(modifs).strip()]}) 958 else: 959 self.edit_serveur({'md5s':1}) 960 self.maj_params({'md5s':[1,""]})
961
962 - def check_maj_status(self, maj_checker, show_installed = False, debnames = []):
963 """vérifie l'état de mise à jour d'un serveur 964 """ 965 if self.version == 'creole1': 966 return [] 967 # liste des paquets remontés par le serveur 968 if type(debnames) != list: 969 debnames = [debnames] 970 liste_pkgs = [] 971 # on regarde le type de mise à jour configurée (minimale/complète) et le serveur de mise à jour 972 dico = self.parsedico() 973 type_maj = dico['type_maj'].split('| ')[0] 974 serv_maj = dico['serveur_maj'].split('| ')[0] 975 version = self.module_version 976 pkg_file = os.path.join(os.path.abspath(config.PATH_ZEPHIR),'data','packages%s.list' % self.id_s) 977 # on compare les paquets installés avec ceux disponibles sur le serveur de maj 978 liste_pkgs, maj_infos = maj_checker.check_packages(self.maj_infos, pkg_file, debnames, version, serv_maj, type_maj, show_installed) 979 if maj_infos is not None and not debnames and not show_installed and maj_infos != self.maj_infos: 980 # la liste des paquets non à jour a changé, on met à jour l'état du serveur 981 try: 982 nb_pack = len(maj_infos[0]) 983 if len(maj_infos[0]) > 0: 984 self.edit_serveur({'maj':len(maj_infos[0])}) 985 self.maj_params({'query_maj':[len(maj_infos[0]),time.ctime()]}) 986 else: 987 self.edit_serveur({'maj':0}) 988 self.maj_params({'query_maj':[0,time.ctime()]}) 989 self.maj_infos = maj_infos 990 except: 991 traceback.print_exc() 992 pass 993 return liste_pkgs
994
995 - def check_min_version(self, maj_checker, debname, min_version):
996 # recherche de la version actuelle du paquet sur ce serveur 997 if maj_checker: 998 pkg_file = os.path.join(os.path.abspath(config.PATH_ZEPHIR),'data','packages%s.list' % self.id_s) 999 if os.path.isfile(pkg_file): 1000 for line in file(pkg_file): 1001 pak_name, pak_version = line.split() 1002 if pak_name == debname: 1003 return maj_checker.check_min_version(pak_version, min_version) 1004 # information non trouvée, on retourne False par défaut 1005 return False
1006
1007 - def regen_key(self, new_addr=None):
1008 """prépare une nouvelle clé ssh pour la connexion uucp à zephir 1009 """ 1010 new_key = os.path.join(self.confdir, 'new_key') 1011 addr_file = os.path.join(self.confdir, 'new_addr') 1012 if os.path.exists(new_key): 1013 os.unlink(new_key) 1014 if os.path.exists("%s.pub" % new_key): 1015 os.unlink("%s.pub" % new_key) 1016 for f_addr in [addr_file, '%s_ok' % addr_file]: 1017 if os.path.exists(f_addr): 1018 os.unlink(f_addr) 1019 zephir_addr = new_addr or config.ADRESSE_ZEPHIR 1020 res = os.system("""/usr/bin/ssh-keygen -N "" -b 1024 -t rsa -f %s -C uucp@%s""" % (new_key, zephir_addr)) 1021 # si nouvelle adresse à envoyer, on crée un fichier new_addr 1022 if new_addr: 1023 f_adr = open(addr_file, 'w') 1024 f_adr.write(new_addr) 1025 f_adr.close() 1026 return res
1027
1028 - def get_key(self, old_key, new_key, confirm_ip):
1029 """envoie une nouvelle clé à un serveur. 1030 les 2 clés publiques (ancienne et nouvelles) doivent être passées en paramètre. 1031 """ 1032 try: 1033 old_key_path = os.path.join(self.confdir, 'cle_publique') 1034 new_key_path = os.path.join(self.confdir, 'new_key.pub') 1035 new_key_priv_path = os.path.join(self.confdir, 'new_key') 1036 new_addr_path = os.path.join(self.confdir, 'new_addr') 1037 old_data = file(old_key_path).read().strip() 1038 old_data = base64.encodestring(old_data[old_data.index('ssh-rsa'):]) 1039 if old_data == old_key: 1040 # la clé correspond, on renvoie les nouvelles clés 1041 data_keys = [] 1042 new_data = file(new_key_path).read().strip() 1043 new_data = base64.encodestring(new_data[new_data.index('ssh-rsa'):]) 1044 if new_data != new_key: 1045 return 0, "nouvelle clé publique non reconnue" 1046 if confirm_ip == True: 1047 if os.path.isfile(new_addr_path): 1048 os.rename(new_addr_path, '%s_ok' % new_addr_path) 1049 return 1, '' 1050 else: 1051 return 0, "changement d'adresse non détecté sur zephir" 1052 new_data_priv = base64.encodestring(file(new_key_priv_path).read().strip()) 1053 os.unlink(new_key_priv_path) 1054 return 1, new_data_priv 1055 else: 1056 return 0, "ancienne clé publique non reconnue" 1057 except: 1058 return 0, "erreur de récupération des données"
1059
1060 - def get_last_contact(self):
1061 """renvoie la date de dernier contact avec le serveur 1062 """ 1063 cu = cx_pool.create() 1064 try: 1065 cu.execute("select last_contact from serveurs where id=%s", (int(self.id_s),)) 1066 data = cu.fetchone() 1067 cx_pool.close(cu) 1068 except: 1069 traceback.print_exc() 1070 cx_pool.close(cu) 1071 return 0.0 1072 return float(data[0])
1073
1074 - def migrate_config(self,module_dest,variante_dest,mode="migration",dest_version=None):
1075 """crée un dictionnaire creole2 et importe les valeurs du serveur""" 1076 assert self.module_version in config.allowed_migrations 1077 assert int(module_dest) >= 1 1078 assert int(variante_dest) >= 1 1079 # on stocke la variante choisie dans le répertoire de conf 1080 f_var = open(os.path.join(self.confdir, 'variante_migration'), 'w') 1081 f_var.write(str(variante_dest)) 1082 f_var.close() 1083 # chemin vers les dictionnaires du module 1084 creoledirs = [os.path.join(os.path.abspath(config.PATH_MODULES),str(module_dest),'dicos')] 1085 creoledirs.append(os.path.join(os.path.abspath(config.PATH_MODULES),str(module_dest),'variantes',str(variante_dest),'dicos')) 1086 # création du dictionnaire 1087 dico = ZephirDict(creoledirs, self.confdir, mode, dest_version or self.version) 1088 return dico
1089
1090 - def migrate_data(self, check=False):
1091 """fonction de récupération des données d'un serveur migré 1092 check : vérifie seulement si des données sont migrables sur ce serveur 1093 """ 1094 # fichiers à migrer : fichiers par défaut 1095 migration_files = {} 1096 module = self.module[:self.module.rindex('-')] 1097 if module in config.migration_files: 1098 migration_files.update(config.migration_files[module]) 1099 # définitions supplémentaires 1100 if module in migration_perso: 1101 for section in migration_perso[module].keys(): 1102 migration_files[section].extend(migration_perso[module][section]) 1103 dir_serv = os.path.join(self.confdir,'fichiers_zephir') 1104 dir_bak = os.path.join(self.confdir+'-backup','fichiers_zephir') 1105 # mode check : indique si des fichiers sont gérés pour ce serveur 1106 if check == True: 1107 if len(migration_files.keys()) > 0 and os.path.isdir(dir_bak): 1108 return 1, True 1109 else: 1110 return 1, False 1111 # liste des fichiers à copier 1112 if not len(migration_files.keys()) > 0: 1113 return 1, config.u("""pas de fichiers à migrer""") 1114 if not os.path.isdir(dir_bak): 1115 return 0, config.u("répertoire backup de migration non trouvé") 1116 not_found = [] 1117 errors = [] 1118 # copie des fichiers avec conversion de l'encoding si besoin 1119 for src, dst, convert in migration_files['files']: 1120 fic_src = os.path.join(dir_bak,src) 1121 fic_dst = os.path.join(dir_serv,dst) 1122 if os.path.isdir(fic_src): 1123 # répertoire 1124 os.path.walk(fic_src, self.tree_copy, (fic_src, fic_dst, convert, errors, migration_files)) 1125 elif os.path.isfile(fic_src): 1126 self.copy_fic(fic_src, fic_dst, convert, errors) 1127 else: 1128 not_found.append(src) 1129 # application des droits 1130 droits_zephir = [] 1131 dests=[] 1132 for dest, options, user, group, mode in migration_files['rights']: 1133 if dest.endswith("*"): 1134 # cas "repertoire/*" : on recherche tous les fichiers 1135 dests = glob(os.path.join(dir_serv,dest)) 1136 else: 1137 if os.path.exists(os.path.join(dir_serv,dest)): 1138 dests = [os.path.join(dir_serv,dest)] 1139 for fic_dest in dests: 1140 if os.path.basename(fic_dest) not in migration_files['exclude']: 1141 cmd_rights = 'chmod %s %s %s;chown %s %s.%s %s' % (options, mode, fic_dest, options, user, group, fic_dest) 1142 res = os.system(cmd_rights) 1143 if res != 0: 1144 errors.append('application des droits impossible : %s' % fic_dest) 1145 else: 1146 # ajout de la ligne pour droits_zephir 1147 droits_zephir.append("%s#%s#%s#%s#%s" % (fic_dest[fic_dest.index('fichiers_zephir'):],mode,user,group,'-R' in options)) 1148 1149 # création du fichier droits_zephir 1150 fic_droits = open(os.path.join(self.confdir, 'droits_zephir'),'w') 1151 fic_droits.write('\n'.join(droits_zephir)) 1152 fic_droits.close() 1153 # mise en place du fichier fichiers_zephir (copie de fichiers_module + destinations personnalisées) 1154 # on utilise toujours le fichier de la version 2.0 pour une migration 1155 module_ng_orig = '%s-2.0' % self.module[:self.module.rindex('-')] 1156 module_file = '/usr/share/zephir/dictionnaires/fichiers_%s' % module_ng_orig 1157 serveur_file = os.path.join(self.confdir,'fichiers_zephir','fichiers_zephir') 1158 if os.path.isfile(module_file): 1159 # lecture du fichier du module 1160 data = file(module_file).read().strip() 1161 if module in destinations_perso: 1162 data += "\n# fichiers ajoutés lors de la migration Eole 1 vers Eole NG" 1163 for dest in destinations_perso[module]: 1164 data += "\n%s" % dest 1165 1166 # écriture en utilisant les templates définis dans config.py 1167 final_data = "%s\n%s\n%%%%\n%s" % (config.FILE_SECTION,data,config.RPM_SECTION) 1168 1169 # écriture du fichier final 1170 dests = file(serveur_file,'w') 1171 dests.write(final_data) 1172 dests.close() 1173 1174 return 1, config.u((not_found,errors))
1175
1176 - def variante_migration(self):
1177 path_var = os.path.join(self.confdir, 'variante_migration') 1178 if os.path.isfile(path_var): 1179 return open(path_var).read().strip() 1180 else: 1181 return ''
1182
1183 - def add_packages(self, paq_names):
1184 """ajoute un(des) paquet(s) aux paquets à installer d'un serveur 1185 """ 1186 added = [] 1187 f_perso = os.path.join(self.get_confdir(), 'fichiers_zephir', 'fichiers_zephir') 1188 fichiers, paqs = self.pool.get_fic_perso(f_perso) 1189 # ajout des nouveau paquet 1190 for paq in paq_names: 1191 # on ajoute le paquet si il n'est pas présent 1192 if paq not in paqs: 1193 paqs.append(paq) 1194 added.append(paq) 1195 self.pool.save_fic_perso(fichiers, paqs, f_perso) 1196 return added
1197
1198 - def remove_packages(self, paq_names):
1199 """ajoute un paquet aux paquets à installer d'un serveur 1200 """ 1201 removed = [] 1202 f_perso = os.path.join(self.get_confdir(), 'fichiers_zephir', 'fichiers_zephir') 1203 fichiers, paqs = self.pool.get_fic_perso(f_perso) 1204 # ajout des nouveau paquet 1205 for paq in paq_names: 1206 # on ajoute le paquet si il n'est pas présent 1207 if paq in paqs: 1208 paqs.remove(paq) 1209 removed.append(paq) 1210 else: 1211 log.msg("Suppression d'un paquet non défini sur le serveur %s : %s" % (str(self.id_s), paq)) 1212 self.pool.save_fic_perso(fichiers, paqs, f_perso) 1213 return removed
1214
1215 - def add_info_etab(self, rne, id_client, f_conf):
1216 # lecture du fichier d'info etablissments 1217 repl_dir = os.path.join(self.confdir, 'replication') 1218 data_etab = ConfigParser() 1219 data_etab.read(os.path.join(repl_dir, 'etabs.ini')) 1220 # ajout d'informations sur l'établissement et le portail 1221 serv_client = dict.get(self.pool, id_client) 1222 if not data_etab.has_section(rne): 1223 data_etab.add_section(rne) 1224 data = self.pool.get_info_etab(serv_client.rne) 1225 if len(data) > 0: 1226 data_etab.set(rne, "type_etab", data[0][0]) 1227 data_etab.set(rne, "libelle_etab", data[0][1]) 1228 else: 1229 # infos non trouvées dans la base (mauvais rne renseigné ?) 1230 # Dans ce cas, on utilise le commentaire inseré par scribe 1231 # pour le libellé établissement 1232 log.msg("etabs.ini : les informations sur l'établissement %s ne sont pas disponibles" % rne) 1233 repl_lines = open(f_conf).strip().split('\n') 1234 libelle = '' 1235 for line in repl_lines: 1236 if line.startswith('#'): 1237 libelle = line 1238 break 1239 data_etab.set(rne, "libelle_etab", libelle) 1240 data_etab.set(rne, "type_etab", "") 1241 # recherche de l'adresse du portail dans la configuration 1242 # des serveurs de cet établissement 1243 portail_etab = self.pool.get_portail_etab(id_client) 1244 if portail_etab: 1245 if not portail_etab.startswith('http'): 1246 portail_etab = "https://%s" % portail_etab 1247 data_etab.set(rne, "portail_etab", portail_etab) 1248 # sauvegarde des infos etablissement 1249 fichier_etab = open(os.path.join(repl_dir, 'etabs.ini'), 'w') 1250 data_etab.write(fichier_etab) 1251 fichier_etab.close()
1252
1253 - def remove_info_etab(self, rne):
1254 # lecture du fichier d'info etablissments 1255 repl_dir = os.path.join(self.confdir, 'replication') 1256 data_etab = ConfigParser() 1257 data_etab.read(os.path.join(repl_dir, 'etabs.ini')) 1258 if data_etab.has_section(rne): 1259 # infos à supprimer trouvées 1260 data_etab.remove_section(rne) 1261 # sauvegarde 1262 fichier_etab = open(os.path.join(repl_dir, 'etabs.ini'), 'w') 1263 data_etab.write(fichier_etab) 1264 fichier_etab.close()
1265
1266 - def add_replication(self, rne, content, id_client):
1267 """ajoute une configuration de réplication LDAP 1268 """ 1269 # chemin de sauvegarde de l'archive contenant la conf de réplication 1270 repl_dir = os.path.join(self.confdir, 'replication') 1271 if not os.path.exists(repl_dir): 1272 os.makedirs(repl_dir) 1273 script = os.path.join(repl_dir, 'replication-%s.conf' % rne) 1274 try: 1275 bin_file = StringIO() 1276 data = base64.decodestring(content) 1277 fd = open(script,'wb') 1278 # sauvegarde du fichier 1279 bin_file.write(data) 1280 bin_file.seek(0) 1281 fd.write(bin_file.read()) 1282 fd.close() 1283 except: 1284 traceback.print_exc() 1285 return 0, config.u("erreur de l'écriture du fichier %s" % script) 1286 # stockage des infos sur l'établissement pour envoi sur le serveur de réplication 1287 try: 1288 self.add_info_etab(rne.upper(), id_client, script) 1289 except: 1290 log.msg('Erreur lors de la mise à jour du fichier etabs.ini') 1291 traceback.print_exc() 1292 return 1, "OK"
1293
1294 - def del_replication(self, filename):
1295 repl_dir = os.path.join(self.confdir, 'replication') 1296 try: 1297 assert os.path.isfile(os.path.join(repl_dir, filename)) 1298 os.unlink(os.path.join(repl_dir, filename)) 1299 # création d'un fichier .modified pour indiquer que la conf est modifiée manuellement 1300 file(os.path.join(self.confdir, 'replication', '.modified'), 'w').close() 1301 except: 1302 return 0, config.u("Erreur lors de la suppression du fichier %s" % filename) 1303 try: 1304 # supression des eventuelles informations dans etabs.ini 1305 rne = filename[filename.index('-')+1:filename.rindex('.')] 1306 self.remove_info_etab(rne.upper()) 1307 except: 1308 log.msg('Erreur lors de la supression des informations dans etabs.ini') 1309 traceback.print_exc() 1310 1311 return 1, "OK"
1312
1313 - def check_replication(self):
1314 # vérification du répertoire 1315 if not os.path.isdir(os.path.join(self.confdir, 'replication')): 1316 repl_state = 0 1317 elif os.path.isfile(os.path.join(self.confdir, 'replication', '.modified')): 1318 repl_state = 2 1319 else: 1320 repl_state = 1 1321 return 1, repl_state
1322
1323 - def get_replication(self):
1324 # définition du répertoire du serveur 1325 conf_repl = [] 1326 # lecture de la liste des fichiers enregistrés 1327 if os.path.isdir(os.path.join(self.confdir, 'replication')): 1328 search_path = os.path.join(self.confdir, 'replication', 'replication-*.conf') 1329 try: 1330 for conf_file in glob(search_path): 1331 conf_repl.append(os.path.basename(conf_file)) 1332 except: 1333 return 0, config.u("Erreur lors de la recherche des configurations de réplication") 1334 return 1, config.u(conf_repl)
1335
1336 - def get_replication_infos(self):
1337 """renvoie les données du fichier etabs.ini 1338 """ 1339 data_etabs = "" 1340 fic_etab = os.path.join(self.confdir, 'replication', 'etabs.ini') 1341 if os.path.isfile(fic_etab): 1342 data_etabs = open(fic_etab).read() 1343 return data_etabs
1344 1345
1346 - def tree_copy(self, args, dirname, fnames):
1347 fic_src, fic_dst, convert, errors, migration_files = args 1348 if not os.path.isdir(fic_dst): 1349 os.makedirs(fic_dst) 1350 for fic in fnames: 1351 if fic not in migration_files['exclude']: 1352 src = os.path.join(dirname, fic) 1353 dst = src.replace(fic_src, fic_dst) 1354 if os.path.exists(src): 1355 self.copy_fic(src, dst, convert, errors)
1356
1357 - def copy_fic(self, src, dst, convert, errors):
1358 """copie du fichier src sur dst 1359 convert: si True, convertit le fichier d'ISO-8859-1 vers UTF-8 1360 """ 1361 if convert == True: 1362 cmd = '/usr/bin/iconv -f ISO-8859-1 -t UTF-8 -o "%s" "%s"' % (dst, src) 1363 else: 1364 cmd = '/bin/cp -f "%s" "%s"' % (src, dst) 1365 res = os.system(cmd) 1366 if res != 0: 1367 log.msg("erreur lors de la migration des données, la commande suivante a échoué :", cmd) 1368 errors.append(src)
1369 1370
1371 -class ServeurPool(dict):
1372 """dictionnaire des serveurs existants gérant les autorisations d'accès 1373 """ 1374
1375 - def __init__(self):
1376 1377 self._groupes_serveurs = {} 1378 self._restrictions = {} 1379 self.stats = {} 1380 # self.update_groupes() 1381 # dictionnaire des variantes et leur module 1382 self.purge_locks() 1383 self._mod_var = self.get_mod_var() 1384 try: 1385 self.update_auths() 1386 except: 1387 # pas encore de table de restriction sur cette version 1388 pass
1389
1390 - def purge_locks(self):
1391 # nettoie les eventuels locks non supprimés à l'initialisation 1392 for lock_file in glob(os.path.join(lock_dir, 'server_params_*')): 1393 os.unlink(lock_file)
1394
1395 - def update_groupes(self):
1396 cu = cx_pool.create() 1397 # récupération des groupes de serveurs 1398 try: 1399 cu.execute("select id, libelle, serveurs from groupes_serveurs") 1400 data = cu.fetchall() 1401 need_commit = False 1402 for groupe in data: 1403 id = int(groupe[0]) 1404 libelle = str(groupe[1]) 1405 serveurs = [] 1406 # suppression des éventuels serveurs inexistants 1407 update_gr = False 1408 for serveur in eval(groupe[2]): 1409 if serveur in self: 1410 serveurs.append(serveur) 1411 else: 1412 update_gr = True 1413 if update_gr: 1414 # des serveurs ne sont plus valides, on les supprime du groupe dans la base de données 1415 cu.execute("update groupes_serveurs set serveurs=%s where id=%s", (str(serveurs), int(id))) 1416 need_commit = True 1417 timestamp = time.time() 1418 date_creat = timestamp 1419 if self._groupes_serveurs.has_key(id): 1420 date_creat = self._groupes_serveurs[id][2][0] 1421 self._groupes_serveurs[id] = [libelle, serveurs, [date_creat, timestamp]] 1422 if need_commit: 1423 cx_pool.commit(cu) 1424 else: 1425 cx_pool.close(cu) 1426 except: 1427 traceback.print_exc() 1428 cx_pool.close(cu)
1429
1430 - def get_mod_var(self):
1431 """récupére les variantes et leur module""" 1432 cu = cx_pool.create() 1433 try: 1434 cu.execute("select id, module from variantes") 1435 data = cu.fetchall() 1436 cx_pool.close(cu) 1437 except: 1438 data = [] 1439 traceback.print_exc() 1440 cx_pool.close(cu) 1441 return dict(data)
1442
1443 - def update_auths(self, user=None):
1444 params = [] 1445 if user != None: 1446 user_clause = " where login=%s" 1447 params.append(user) 1448 else: 1449 user_clause = "" 1450 1451 cu = cx_pool.create() 1452 try: 1453 # récupération des types de contraintes spécifiées 1454 cu.execute("select login, id_res, type_res from restrictions %s" % user_clause, params) 1455 data = cu.fetchall() 1456 for restriction in data: 1457 login, id_res, type_res = restriction 1458 if not self._restrictions.has_key(login): 1459 self._restrictions[login] = {} 1460 if self._restrictions[login].has_key(type_res): 1461 self._restrictions[login][type_res].append(id_res) 1462 else: 1463 self._restrictions[login][type_res] = [id_res] 1464 cx_pool.close(cu) 1465 except: 1466 traceback.print_exc() 1467 cx_pool.close(cu)
1468
1469 - def add_restriction(self, credential, type_res, id_res):
1470 """ajoute une restriction pour un utilisateur et une ressource donnés 1471 type_res : nature des objets à restreindre 1472 id_res ; identifiant de l'objet autorisé 1473 les différents types reconnus sont : 'rne, group, id_mod, id_var' et les attributs 1474 de la classe serveur (id_s, module, version, ...) 1475 """ 1476 # ajout dans la base 1477 id_res = str(id_res) 1478 try: 1479 assert id_res in self._restrictions[credential][type_res] 1480 except: 1481 cu = cx_pool.create() 1482 # validation de l'existence 1483 libelle, table, field = config.type_res_label[type_res] 1484 if field in ('rne','libelle'): 1485 # données de type str 1486 query = "select * from %s where %s ilike %%s" % (table, field) 1487 params = (id_res,) 1488 else: 1489 # type entier 1490 query = "select * from %s where %s = %%s" % (table, field) 1491 params = (int(id_res),) 1492 try: 1493 cu.execute(query, params) 1494 # pas de données correspondante trouvée 1495 if cu.fetchone() == None: 1496 raise ValueError('identifiant de ressource inconnu') 1497 except Exception, e: 1498 cx_pool.close(cu) 1499 return False 1500 try: 1501 params = (credential, type_res, id_res) 1502 cu.execute("""insert into restrictions (login, type_res, id_res) values (%s,%s,%s)""", params) 1503 except: 1504 # erreur d'insertion dans la base 1505 cx_pool.rollback(cu) 1506 return False 1507 cx_pool.commit(cu) 1508 if not self._restrictions.has_key(credential): 1509 self._restrictions[credential] = {} 1510 if not self._restrictions[credential].has_key(type_res): 1511 self._restrictions[credential][type_res] = [] 1512 self._restrictions[credential][type_res].append(id_res) 1513 else: 1514 # restriction déjà existante 1515 pass 1516 return True
1517
1518 - def get_restrictions(self, credential, type_res=None):
1519 """renvoie la liste des restrictions d'un utilisateur 1520 """ 1521 if not self._restrictions.has_key(credential): 1522 return [] 1523 if type_res == None: 1524 # toutes les restrictions : dictionnaire 1525 return self._restrictions[credential] 1526 else: 1527 # si restrictions d'un type précis, on renvoie une liste de ressources 1528 if self._restrictions[credential].has_key(type_res): 1529 return self._restrictions[credential][type_res] 1530 else: 1531 return []
1532
1533 - def del_restriction(self, credential, type_res, id_res):
1534 """enleve une restriction d'un utilisateur 1535 """ 1536 id_res = str(id_res) 1537 try: 1538 liste_res = self._restrictions[credential][type_res] 1539 assert id_res in liste_res 1540 except: 1541 # restriction non existante 1542 return False 1543 # suppression dans la base 1544 cu = cx_pool.create() 1545 try: 1546 cu.execute("""delete from restrictions where login=%s and type_res=%s and id_res=%s""", (credential, type_res, id_res)) 1547 cx_pool.commit(cu) 1548 except: 1549 traceback.print_exc() 1550 cx_pool.rollback(cu) 1551 return False 1552 # supression en mémoire 1553 self._restrictions[credential][type_res].remove(id_res) 1554 if self._restrictions[credential][type_res] == []: 1555 del(self._restrictions[credential][type_res]) 1556 return True
1557
1558 - def get_file_perms(self, data_dir, filepath=""):
1559 """renvoie les informations de permissions associées à un fichier 1560 """ 1561 # lecture des informations sur le fichier si disponibles 1562 permsfile = data_dir+os.sep+'droits_zephir' 1563 if os.path.isfile(permsfile): 1564 f_rights = file(permsfile) 1565 data = f_rights.read().strip().split('\n') 1566 f_rights.close() 1567 else: 1568 data = [] 1569 mode = user = group = "" 1570 recursive = False 1571 if filepath != "": 1572 result = {filepath:[mode, user, group, recursive]} 1573 else: 1574 result = {} 1575 for line in data: 1576 if line.startswith(filepath+'#') or filepath == "": 1577 try: 1578 filename, mode, user, group, recursive = line.split('#') 1579 if recursive != '': 1580 recursive = eval(recursive) 1581 result[filename] = [mode, user, group, recursive] 1582 if filepath != "": 1583 break 1584 except: 1585 # fichier vide ? 1586 pass 1587 return result
1588
1589 - def get_fic_perso(self, fic_perso):
1590 """renvoie la liste des fichiers personnalisés et paquets d'un serveur 1591 """ 1592 # on reprend la liste des paquets supplémentaires définis 1593 try: 1594 f = open(fic_perso) 1595 old_content = f.read() 1596 f.close() 1597 fichiers = old_content.split('%%\n')[0].strip() 1598 paqs = old_content.split('%%\n')[1].strip() 1599 except: 1600 fichiers = config.FILE_SECTION 1601 paqs = config.RPM_SECTION 1602 fichiers = fichiers.split('\n') 1603 paqs = paqs.split('\n') 1604 return fichiers, paqs
1605
1606 - def save_fic_perso(self, fichiers, paquets, fic_perso):
1607 """sauvegarde la liste des fichiers personnalisés et paquets d'un serveur 1608 """ 1609 # écriture du contenu du fichier 1610 f=open(fic_perso, 'w') 1611 f.write('\n'.join(fichiers) + "\n%%\n" + '\n'.join(paquets) + '\n') 1612 f.close()
1613
1614 - def set_file_perms(self, rights, data_dir):
1615 """enregistre les informations de permissions associées à un(des) fichier(s) 1616 @param data_dir: chemin ou trouver le fichier droits_zephir 1617 """ 1618 # lecture des données existantes 1619 permsfile = data_dir+os.sep+'droits_zephir' 1620 data = self.get_file_perms(data_dir) 1621 data.update(rights) 1622 # stockage des informations 1623 lines = [] 1624 for filepath in data.keys(): 1625 # vérification des données 1626 lines.append("%s#%s#%s#%s#%s" % (filepath, data[filepath][0], data[filepath][1], data[filepath][2],data[filepath][3])) 1627 # écriture des informations 1628 f_rights = file(permsfile,'w') 1629 f_rights.write('\n'.join(lines)) 1630 f_rights.close() 1631 return True
1632
1633 - def del_file_perms(self, data_dir, filepath="", recurse=False):
1634 """supprime les informations de permissions associées à un fichier (ou tous) 1635 """ 1636 # lecture des informations sur le fichier si disponibles 1637 permsfile = data_dir+os.sep+'droits_zephir' 1638 if not os.path.exists(permsfile): 1639 # pas de permissions exitantes ou mauvais chemin 1640 return False 1641 if filepath != "": 1642 f_rights = file(permsfile) 1643 data = f_rights.read().strip().split('\n') 1644 f_rights.close() 1645 if recurse == True: 1646 search_pattern = filepath 1647 else: 1648 search_pattern = filepath + '#' 1649 for line in data: 1650 if line.startswith(search_pattern): 1651 data.remove(line) 1652 else: 1653 data = [] 1654 # écriture des informations 1655 try: 1656 f_rights = file(permsfile,'w') 1657 f_rights.write('\n'.join(data)) 1658 f_rights.close() 1659 except: 1660 return False 1661 return True
1662
1663 - def get_allowed_servers(self, credential):
1664 """récupération de la liste des serveurs accessibles par un utilisateur 1665 Vérifie les restrictions définies pour l'utilisateur 'credential' 1666 en terme de groupe/etablissement/serveur/module/variante 1667 """ 1668 serveurs = [] 1669 # récupération de la liste de tous les serveurs 1670 gr_denied = set() 1671 restrictions = {} 1672 # groupe, id_serveur, module, rne, variante 1673 if self._restrictions.has_key(credential): 1674 # liste des serveurs interdits par groupe 1675 restrictions = self._restrictions[credential] 1676 for id_gr in restrictions.get('groupe',[]): 1677 if self._groupes_serveurs.has_key(int(id_gr)): 1678 gr_denied.update(self._groupes_serveurs[int(id_gr)][1]) 1679 # création de la liste des serveurs autorisés 1680 for id_serv, infos in self.items(): 1681 if id_serv not in gr_denied: 1682 # vérification des attributs du serveur 1683 if infos.rne not in restrictions.get('rne', [infos.rne]): 1684 continue 1685 if str(infos.id_s) not in restrictions.get('id_s', [str(infos.id_s)]): 1686 continue 1687 if str(infos.id_mod) not in restrictions.get('id_mod', [str(infos.id_mod)]): 1688 # serveur non accessible, on passe au serveur suivant 1689 continue 1690 if str(infos.id_var) not in restrictions.get('id_var', [str(infos.id_var)]): 1691 continue 1692 # pas de restrictions pour ce serveur, on le valide 1693 serveurs.append(id_serv) 1694 return serveurs
1695
1696 - def check_serv_credential(self, credential, key):
1697 # vérification des restrictions sur les attributs du serveur 1698 # (id_s, rne, id_mod, variante) 1699 serv = dict.get(self, key) 1700 if self._restrictions.has_key(credential): 1701 for type_res, res in self._restrictions[credential].items(): 1702 if res == []: 1703 # ce cas ne devrait pas arriver car si on passe toujours par del_restriction 1704 continue 1705 res_ok = True 1706 # si la ressource n'est pas autorisée 1707 # cas particuliers : rne et groupes 1708 if type_res == 'rne': 1709 rne_ok = False 1710 for rne in res: 1711 # cas des rne avec wildcard (%) 1712 if rne.endswith('%'): 1713 if serv.rne.startswith(rne.split('%')[0]): 1714 # ce rne correspond à l'expression 1715 rne_ok = True 1716 break 1717 if not rne_ok: 1718 if not serv.rne in res: 1719 res_ok = False 1720 elif type_res == 'groupe': 1721 # test d'appartenance à un groupe autorisé 1722 serv_in_groupes = False 1723 for groupe in res: 1724 if serv.id_s in self._groupes_serveurs[int(groupe)][1]: 1725 serv_in_groupes = True 1726 break 1727 if serv_in_groupes == False: 1728 res_ok = False 1729 else: 1730 # test attribut correspondant du serveur dans les valeurs autorisées 1731 if not str(getattr(serv,type_res)) in [str(i) for i in res]: 1732 res_ok = False 1733 if not res_ok: 1734 # on interdit l'accès 1735 raise ResourceAuthError("Serveur interdit : %s" % key)
1736
1737 - def check_gr_credential(self, credential, groupe):
1738 # validation des droits d'accès à un groupe 1739 if self._restrictions.has_key(credential): 1740 if self._restrictions[credential].has_key('groupe'): 1741 if self._restrictions[credential]['groupe'] != []: 1742 if str(groupe) not in self._restrictions[credential]['groupe']: 1743 # accès refusé 1744 # FIXME vérification suffisante ou regarder les serveurs ou rne du groupe ? 1745 raise ResourceAuthError("Groupe interdit : %s" % groupe)
1746
1747 - def check_mod_credential(self, credential, id_mod):
1748 # validation des droits d'accès à un module 1749 if self._restrictions.has_key(credential): 1750 if self._restrictions[credential].has_key('id_mod'): 1751 if self._restrictions[credential]['id_mod'] != []: 1752 if str(id_mod) not in self._restrictions[credential]['id_mod']: 1753 # accès refusé 1754 raise ResourceAuthError("Module interdit : %s" % id_mod)
1755
1756 - def check_var_credential(self, credential, id_var):
1757 # validation des droits d'accès à une variante 1758 if self._restrictions.has_key(credential): 1759 try: 1760 var_mod = str(self._mod_var[int(id_var)]) 1761 except: 1762 # on met à jour si la variante n'est pas trouvée 1763 self._mod_var = self.get_mod_var() 1764 var_mod = str(self._mod_var[int(id_var)]) 1765 # on commence par vérifier le module auquel la variante appartient 1766 if self._restrictions[credential].has_key('id_mod'): 1767 if self._restrictions[credential]['id_mod'] != []: 1768 if var_mod not in self._restrictions[credential]['id_mod']: 1769 raise ResourceAuthError("module %s interdit" % var_mod) 1770 # vérification de la variante elle-même 1771 if self._restrictions[credential].has_key('id_var'): 1772 if self._restrictions[credential]['id_var'] != []: 1773 if str(id_var) not in self._restrictions[credential]['id_var']: 1774 # accès refusé 1775 raise ResourceAuthError("variante %s interdite" % id_var)
1776
1777 - def check_etab_credential(self, credential, rne):
1778 # validation des droits d'accès à un établissement 1779 rne_ok = True 1780 if self._restrictions.has_key(credential): 1781 if self._restrictions[credential].has_key('rne'): 1782 if self._restrictions[credential]['rne'] != []: 1783 if str(rne) not in self._restrictions[credential]['rne']: 1784 rne_ok = False 1785 for etab in self._restrictions[credential]['rne']: 1786 if etab.endswith('%'): 1787 if str(rne).startswith(etab.split('%')[0]): 1788 rne_ok = True 1789 if not rne_ok: 1790 # accès refusé 1791 raise ResourceAuthError("Etablissement interdit : %s" % rne)
1792
1793 - def get(self, credential, key):
1794 if key in self.keys(): 1795 self.check_serv_credential(credential, key) 1796 else: 1797 raise KeyError, "serveur inexistant" 1798 return dict.get(self,key)
1799
1800 - def get_stats(self):
1801 self.stats['nb_serv'] = len(self) 1802 return self.stats
1803
1804 - def get_alertes(self, credential):
1805 """renvoie la liste des serveurs en alerte 1806 """ 1807 servs=[] 1808 for id_serv in self.keys(): 1809 try: 1810 serv = self.get(credential, int(id_serv)) 1811 except ResourceAuthError: 1812 # accès à ce serveur non autorisé 1813 continue 1814 if serv.get_status() not in [1, -1]: 1815 servs.append(serv) 1816 return servs
1817
1818 - def get_migration_status(self, credential):
1819 """renvoie la liste des serveurs en cours de migration/non migrés 1820 """ 1821 migration_st = {} 1822 for id_serv in self.keys(): 1823 try: 1824 serv = self.get(credential, int(id_serv)) 1825 except ResourceAuthError: 1826 # accès à ce serveur non autorisé 1827 continue 1828 if serv.module_version in config.allowed_migrations: 1829 params = serv.get_params() 1830 if params['migration_ok'] == 0: 1831 servs = migration_st.get(str(serv.module_version), [[],[]]) 1832 servs[0].append(serv) 1833 migration_st[str(serv.module_version)] = servs 1834 elif params['migration_ok'] == -1: 1835 servs = migration_st.get(str(serv.module_version), [[],[]]) 1836 servs[1].append(serv) 1837 migration_st[str(serv.module_version)] = servs 1838 return migration_st
1839
1840 - def check_serveurs(self, credential, serveurs, last_check=None):
1841 """vérifie si des serveurs ont été modifiés 1842 @param serveurs : dictionnaires {idserveur:timestamp} 1843 @param last_check : Si != None, on renvoie les serveurs créés après cette date (timestamp) 1844 @return : liste d'id de serveurs 1845 """ 1846 modifs = {} 1847 serv_pool = [] 1848 for id_serv in [str(cle) for cle in self.keys()]: 1849 try: 1850 serv = self.get(credential, int(id_serv)) 1851 except ResourceAuthError: 1852 # serveur non existant ou non accessible 1853 log.msg("server %s not authorized" % id_serv) 1854 if serveurs.has_key(id_serv): 1855 modifs[id_serv] = "deleted" 1856 else: 1857 serv_pool.append(str(serv.id_s)) 1858 if serveurs.has_key(id_serv): 1859 if serv.modified > serveurs[id_serv]: 1860 # serveur modifié 1861 modifs[id_serv] = serv.modified 1862 elif last_check != None: 1863 if serv.created > last_check: 1864 # si le serveur a été créé après la dernière mise à jour du client 1865 # on l'ajoute comme nouveau serveur 1866 modifs[id_serv] = serv.modified 1867 1868 for id_serv in serveurs.keys(): 1869 if id_serv not in serv_pool: 1870 modifs[id_serv] = "deleted" 1871 1872 return modifs
1873
1874 - def check_groupes(self, credential, groupes, last_check=None):
1875 """vérifie si des groupes ont été modifiés 1876 @param groupes : dictionnaires {idgroupe:timestamp} 1877 @param last_check : Si != None, on renvoie les serveurs créés après cette date (timestamp) 1878 @return : dictionnaire {id_gr:[libelle, serveurs, [date_creat, date_modif]]} 1879 """ 1880 modifs = {} 1881 1882 for id_gr, data in self._groupes_serveurs.items(): 1883 try: 1884 group = self.get_groupes(credential, id_gr) 1885 except KeyError, ResourceAuthError: 1886 # groupe non existant ou non accessible 1887 log.msg("group %s no longer exists or not authorized" % id_gr) 1888 modifs[str(id_gr)] = "deleted" 1889 else: 1890 # la clé des dictionnaires doit être une chaine pour xmlrpc 1891 id_gr = str(id_gr) 1892 if groupes.has_key(id_gr): 1893 if data[2][1] > groupes[id_gr]: 1894 # groupe modifié 1895 modifs[id_gr] = data 1896 elif last_check != None: 1897 if data[2][0] > last_check: 1898 # nouveau groupe 1899 modifs[id_gr] = data 1900 return modifs
1901
1902 - def add_groupe(self, credential, libelle, serveurs):
1903 query = """insert into groupes_serveurs (libelle,serveurs) values (%s, %s)""" 1904 params = (libelle, str(serveurs)) 1905 cu = cx_pool.create() 1906 try: 1907 cu.execute(query, params) 1908 except: 1909 traceback.print_exc() 1910 cx_pool.rollback(cu) 1911 return False 1912 cx_pool.commit(cu) 1913 self.update_groupes() 1914 # ajout automatique du groupe pour l'utilisateur l'ayant créé si il a des restrictions de groupes 1915 if self._restrictions.has_key(credential): 1916 if self._restrictions[credential].has_key('groupe'): 1917 # on recherche l'id du groupe ajouté 1918 for id_gr in self._groupes_serveurs.keys(): 1919 if self._groupes_serveurs[id_gr][0] == libelle: 1920 self.add_restriction(credential,'groupe',str(id_gr)) 1921 return True
1922
1923 - def get_groupes(self, credential, id_groupe=None, alertes=False):
1924 if id_groupe != None: 1925 groupes = [int(id_groupe)] 1926 else: 1927 groupes = self._groupes_serveurs.keys() 1928 # vérification des restrictions sur les groupes 1929 res = [] 1930 for groupe in groupes: 1931 try: 1932 self.check_gr_credential(credential, groupe) 1933 except: 1934 # groupe non autorisé, on passe au suivant 1935 continue 1936 libelle, serveurs, timestamps = self._groupes_serveurs[groupe] 1937 # sinon on ajoute le groupe à la liste 1938 res.append([int(groupe), libelle, serveurs]) 1939 return res
1940
1941 - def edit_groupe(self, credential, id_groupe, libelle, serveurs):
1942 # vérification des droits sur le groupe 1943 id_groupe = int(id_groupe) 1944 self.check_gr_credential(credential, id_groupe) 1945 # mise à jour de la base 1946 cu = cx_pool.create() 1947 query = """update groupes_serveurs set libelle=%s, serveurs=%s where id=%s""" 1948 params = (libelle,str(serveurs),int(id_groupe)) 1949 try: 1950 cu.execute(query, params) 1951 except: 1952 traceback.print_exc() 1953 cx_pool.rollback(cu) 1954 return False 1955 cx_pool.commit(cu) 1956 # mise à jour interne 1957 date_creat = self._groupes_serveurs[id_groupe][2][0] 1958 self._groupes_serveurs[id_groupe] = [libelle, serveurs, [date_creat,time.time()]] 1959 return True
1960
1961 - def del_groupe(self, credential, id_groupe):
1962 # vérification des droits sur le groupe 1963 id_groupe = int(id_groupe) 1964 self.check_gr_credential(credential, id_groupe) 1965 # on supprime le groupe des groupes surveillés par les utilisateurs 1966 rech_params = ('[%d]' % id_groupe, '[%%, %d, %%]' % id_groupe, '[%%, %d]' % id_groupe, '[%d, %%]' % id_groupe) 1967 cursor = cx_pool.create() 1968 try: 1969 cursor.execute("""select login,groupes from users where groupes like %s or groupes like %s or groupes like %s or groupes like %s""", rech_params) 1970 data=cursor.fetchall() 1971 # pour chaque utilisateur 1972 for user in data: 1973 # modification de la liste des groupes 1974 groupes = eval(user[1]) 1975 if id_groupe in groupes: 1976 groupes.remove(id_groupe) 1977 sql_update = """update users set groupes=%s where login=%s""" 1978 params = (str(groupes), user[0]) 1979 cursor.execute(sql_update, params) 1980 # suppression du groupe dans la base 1981 cursor.execute("""delete from groupes_serveurs where id = %s""", (int(id_groupe),)) 1982 cx_pool.commit(cursor) 1983 except: 1984 traceback.print_exc() 1985 cx_pool.rollback(cursor) 1986 return False 1987 # suppression du groupe en mémoire 1988 del(self._groupes_serveurs[id_groupe]) 1989 return True
1990 1991
1992 - def extend_groupe(self, credential, id_groupe, serveurs):
1993 # vérification des droits sur le groupe 1994 id_groupe = int(id_groupe) 1995 self.check_gr_credential(credential, id_groupe) 1996 # extension des serveurs du groupe 1997 # dans le dictionnaire interne 1998 for serv in serveurs: 1999 if serv not in self._groupes_serveurs[id_groupe][1]: 2000 self._groupes_serveurs[id_groupe][1].append(serv) 2001 self._groupes_serveurs[id_groupe][2] = [time.time(),time.time()] 2002 cu = cx_pool.create() 2003 # dans la base de données 2004 try: 2005 query = """update groupes_serveurs set serveurs=%s where id=%s""" 2006 params = (str(self._groupes_serveurs[id_groupe][1]), id_groupe) 2007 cu.execute(query, params) 2008 cx_pool.commit(cu) 2009 except: 2010 traceback.print_exc() 2011 cx_pool.rollback(cu)
2012
2013 - def add_serveur(self, credential, rne, libelle, materiel, processeur, disque_dur, date_install, installateur, tel, remarques, module_initial, module_actuel, variante, timestamp_serveur, timeout):
2014 # on vérifie si l'accès au rne , module et variante est permis 2015 if self._restrictions.has_key(credential): 2016 if self._restrictions[credential].has_key('rne'): 2017 self.check_etab_credential(credential, rne) 2018 if self._restrictions[credential].has_key('id_mod'): 2019 if module_actuel not in self._restrictions[credential]['id_mod']: 2020 raise ResourceAuthError("Module interdit : %s" % module_actuel) 2021 if self._restrictions[credential].has_key('variante'): 2022 cu = cx_pool.create() 2023 cu.execute('select libelle from variantes where id=%s', (int(variante),)) 2024 libel_var = cu.fetchone()[0] 2025 cx_pool.close(cu) 2026 # dans le cas d'une variante standard, on l'autorise toujours 2027 if libel_var != 'standard' and variante not in self._restrictions[credential]['variante']: 2028 raise ResourceAuthError("Variante interdit : %s" % variante) 2029 # insertion dans la base de données 2030 cu = cx_pool.create() 2031 query = """insert into serveurs (rne,libelle,materiel,processeur,disque_dur,date_install,installateur,tel,remarques,module_initial,module_actuel,variante,timestamp,timeout) values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)""" 2032 params = (rne, libelle, materiel, processeur, disque_dur, date_install, installateur, tel, remarques, int(module_initial), int(module_actuel), int(variante), timestamp_serveur, int(timeout)) 2033 cu.execute(query, params) 2034 # récupération de l'identifiant attribué par la base 2035 query = """select id from serveurs where rne=%s and libelle=%s and date_install=%s and installateur=%s and remarques=%s and module_actuel=%s and variante=%s and timestamp=%s""" 2036 params = (rne, libelle, date_install, installateur, remarques, int(module_actuel), int(variante), timestamp_serveur) 2037 cu.execute(query, params) 2038 id_serveur = int(cu.fetchone()[0]) 2039 self[id_serveur] = Serveur(self, id_serveur,cu) 2040 self.stats['no_contact'].append(id_serveur) 2041 self.stats['serv_modules'][str(module_actuel)] = self.stats['serv_modules'].get(str(module_actuel),0) + 1 2042 self.stats['serv_variantes'][str(variante)] = self.stats['serv_variantes'].get(str(variante),0) + 1 2043 cx_pool.commit(cu) 2044 return self[id_serveur]
2045
2046 - def del_serveur(self, credential, id_serveur):
2047 """Supression d'un serveur de la base 2048 """ 2049 # vérification des restrictions 2050 self.check_serv_credential(credential, id_serveur) 2051 cu = cx_pool.create() 2052 # on supprime le serveur dans la base 2053 try: 2054 cu.execute("""delete from serveurs where id=%s""", (int(id_serveur),)) 2055 cx_pool.commit(cu) 2056 # supression interne 2057 self.stats['serv_modules'][str(self[id_serveur].id_mod)] = self.stats['serv_modules'][str(self[id_serveur].id_mod)] - 1 2058 self.stats['serv_variantes'][str(self[id_serveur].id_var)] = self.stats['serv_variantes'][str(self[id_serveur].id_var)] - 1 2059 del(self[id_serveur]) 2060 if int(id_serveur) in self.stats['no_contact']: 2061 self.stats['no_contact'].remove(int(id_serveur)) 2062 except: 2063 traceback.print_exc() 2064 cx_pool.rollback(cu)
2065
2066 - def edit_serveur(self, id_serveur, dico_modifs):
2067 # modification d'un serveur dans la base 2068 serv = self[id_serveur] 2069 return serv.edit_serveur(dico_modifs)
2070
2071 - def update_contact(self, id_serveur):
2072 """mise à jour de la date de contact d'un serveur""" 2073 id_serveur = int(id_serveur) 2074 cu = cx_pool.create() 2075 try: 2076 # on met à jour le serveur dans la base 2077 query = """update serveurs set last_contact=%s where id=%s""" 2078 params = (str(time.time()), int(id_serveur)) 2079 cu.execute(query, params) 2080 cx_pool.commit(cu) 2081 # mise à jour des statistiques 2082 if int(id_serveur) in self.stats['no_contact']: 2083 self.stats['no_contact'].remove(int(id_serveur)) 2084 except: 2085 traceback.print_exc() 2086 cx_pool.rollback(cu)
2087
2088 - def get_portail_etab(self, id_client):
2089 """recherche l'adresse du portail etablissement dans la configuration d'un serveur 2090 variables recherchées (cf var_portail dans config.py commun) 2091 """ 2092 client = dict.get(self, id_client) 2093 portail_etab = self._get_portail_etab(client.id_s) 2094 if portail_etab == '': 2095 # pas de portail trouvé, on vérifie les autres serveurs de l'établissement 2096 for serv_id in self.etabs[client.rne]: 2097 if serv_id != id_client: 2098 portail_etab = self._get_portail_etab(serv_id) 2099 if portail_etab: 2100 # portail trouvé 2101 break 2102 return portail_etab
2103
2104 - def _get_portail_etab(self, id_client):
2105 client = dict.get(self, id_client) 2106 for mod in config.mod_portail: 2107 if client.module.startswith(mod): 2108 # recherche des variables renseignant l'adresse du portail 2109 config_serv = client.parsedico() 2110 for v_portail in config.var_portail: 2111 if config_serv.get(v_portail, '') != '': 2112 # les variables sont en priorité décroissante, on s'arrête 2113 # à la première occurence trouvée 2114 return config_serv.get(v_portail) 2115 return ''
2116
2117 - def get_info_etab(self, rne):
2118 query = """select distinct types_etab.libelle, etablissements.libelle from types_etab, etablissements where \ 2119 types_etab.id = etablissements.type and etablissements.rne = %s""" 2120 sql_params = (rne,) 2121 cu = cx_pool.create() 2122 try: 2123 cu.execute(query, sql_params) 2124 data = cu.fetchall() 2125 cx_pool.close(cu) 2126 except: 2127 traceback.print_exc() 2128 data = [] 2129 return data
2130
2131 - def check_replication_infos(self, id_serv):
2132 """Génère un fichier d'information sur les établissements répliqués 2133 sur le serveur id_serv (réplication LDAP) 2134 --> type / libelle / adresse du portail si disponible 2135 """ 2136 # serveur de réplication 2137 serv = dict.get(self,id_serv) 2138 # lecture du fichier d'information sur les établissements répliqués 2139 2140 # récupération des RNEs des établissements répliqués. Nécessite qu'ils aient été saisis correctement 2141 search_path = os.path.join(serv.confdir, 'replication', 'replication-*.conf') 2142 f_confs = glob(search_path) 2143 for f_conf in f_confs: 2144 # on recherche le rne dans le fichier de replication 2145 rne = "" 2146 for line in file(f_conf): 2147 if line.strip().startswith('searchbase='): 2148 rne = line[line.index('ou=')+3:] 2149 rne = rne[:rne.index(',')] 2150 break 2151 if rne == "": 2152 # RNE non trouvé dans le fichier de réplication, ne devrait pas arriver 2153 # dans ce cas on utilise celui présent dans le nom du fichier 2154 rne = f_conf[f_conf.index('-')+1:f_conf.rindex('.')] 2155 # vérification des informations stockées dans etabs.ini 2156 self.check_info_etab(serv, rne, f_conf)
2157
2158 - def check_info_etab(self, serv, rne, f_conf):
2159 """vérifie la disponibilité des informations sur le portail/établissement répliqué 2160 """ 2161 repl_dir = os.path.join(serv.confdir, 'replication') 2162 data_etab = ConfigParser() 2163 data_etab.read(os.path.join(repl_dir, 'etabs.ini')) 2164 rne = rne.upper() 2165 if not data_etab.has_section(rne): 2166 # on appelle la fonction de mise à jour des infos etablissement 2167 # sur le premier serveur trouvé dans l'établissement 2168 try: 2169 id_client = self.etabs[rne][0] 2170 serv.add_info_etab(rne, id_client, f_conf) 2171 except: 2172 log.msg("Aucun serveur trouvé pour l'établissement %s" % rne) 2173 return
2174
2175 -def serveur_pool():
2176 """crée un pool de tous les serveurs existants 2177 """ 2178 # création du pool de serveurs en mémoire 2179 pool = ServeurPool() 2180 cu = cx_pool.create() 2181 try: 2182 cu.execute("select id, rne, libelle, module_actuel, variante, timeout, etat, md5s, maj, no_alert, last_contact from serveurs") 2183 data = cu.fetchall() 2184 nbserv = len(data) 2185 no_contact = [] 2186 # Instanciation des serveurs 2187 pool.stats['serv_modules'] = {} 2188 pool.stats['serv_variantes'] = {} 2189 pool.etabs = {} 2190 # initialisation des statistiques des modules/variantes 2191 for var, module in pool._mod_var.items(): 2192 if str(module) not in pool.stats['serv_modules']: 2193 pool.stats['serv_modules'][str(module)] = 0 2194 if str(var) not in pool.stats['serv_variantes']: 2195 pool.stats['serv_variantes'][str(var)] = 0 2196 for serv in data: 2197 # liste des serveurs n'ayant jamais contacté zephir 2198 if serv[10] == None: 2199 no_contact.append(int(serv[0])) 2200 # nombre total de serveurs 2201 pool[serv[0]] = Serveur(pool, serv[0], cu, serv[1:2]) 2202 pool[serv[0]].update_data(cu, serv[2:10]) 2203 # on conserve une liste des serveurs par établissement 2204 rne_serv = pool[serv[0]].rne 2205 if rne_serv in pool.etabs: 2206 pool.etabs[rne_serv].append(serv[0]) 2207 else: 2208 pool.etabs[rne_serv] = [serv[0]] 2209 # décompte par module et variante 2210 pool.stats['serv_modules'][str(serv[3])] = pool.stats['serv_modules'].get(str(serv[3]),0) + 1 2211 pool.stats['serv_variantes'][str(serv[4])] = pool.stats['serv_variantes'].get(str(serv[4]),0) + 1 2212 pool.stats['no_contact'] = no_contact 2213 pool.stats['nb_serv'] = len(pool) 2214 # chargement des groupes 2215 pool.update_groupes() 2216 cx_pool.close(cu) 2217 except: 2218 traceback.print_exc() 2219 cx_pool.close(cu) 2220 return pool
2221 2222 # utility functions 2223 2224 text_characters = "".join(map(chr, range(32, 255)) + list("\n\r\t\b")) 2225 _null_trans = string.maketrans("", "") 2226
2227 -def istextfile(filename, blocksize = 1024):
2228 return istext(open(filename).read(blocksize))
2229
2230 -def istext(s):
2231 if "\0" in s: 2232 return 0 2233 2234 if not s: # Empty files are considered text 2235 return 1 2236 2237 # Get the non-text characters (maps a character to itself then 2238 # use the 'remove' option to get rid of the text characters.) 2239 t = s.translate(_null_trans, text_characters) 2240 2241 # If more than 10% non-text characters, then 2242 # this is considered a binary file 2243 if len(t)/len(s) > 0.10: 2244 return 0 2245 return 1
2246