Package zephir :: Package backend :: Module lib_backend
[hide private]
[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.utils.creolewrap import ZephirDict 
  17  from zephir.backend.uucp_utils import UUCP 
  18  from zephir.monitor.agentmanager.util import md5files 
  19  from twisted.internet.utils import getProcessOutputAndValue 
  20  import psycopg2 as PgSQL 
  21  import os, sys, time, string, traceback 
  22  import base64 
  23  from glob import glob 
  24  import xmlrpclib, os 
  25   
26 -class ResourceAuthError(Exception):
27 pass
28
29 -class CxPool(dict):
30 """dictionnaire des connexions postgresql en cours""" 31
32 - def create(self):
33 cx = PgSQL.connect(database=config.DB_NAME,user=config.DB_USER,password=config.DB_PASSWD) 34 cu = cx.cursor() 35 self[cu] = cx 36 return cu
37
38 - def commit(self,cu):
39 cu.close() 40 self[cu].commit() 41 self[cu].close() 42 del(self[cu])
43
44 - def rollback(self,cu):
45 cu.close() 46 self[cu].rollback() 47 self[cu].close() 48 del(self[cu])
49
50 - def close(self,cu):
51 cu.close() 52 self[cu].close() 53 del(self[cu])
54 55 cx_pool = CxPool() 56
57 -class Serveur:
58 """classe utilitaire pour récupérer diverses données sur un serveur""" 59
60 - def __init__(self, id_s, cu=None, data=None):
61 """initialise l'objet serveur et récupère les infos 62 """ 63 self.version = 'creole2' 64 # récupération des serveurs 65 self.id_s = int(id_s) 66 # date de dernière modification du serveur 67 self.modified=time.time() 68 # date de création de l'objet 69 self.created=time.time() 70 cursor = cu 71 if cu == None: 72 cursor = cx_pool.create() 73 try: 74 if data == None: 75 # le rne ne peut pas être modifié : on le stocke une fois pour toutes 76 cursor.execute("select rne from serveurs where id=%s" % str(self.id_s)) 77 data = cursor.fetchone() 78 self.rne = data[0] 79 # appel de la fonction update_data pour les données modifiables 80 self.update_data(cursor) 81 else: 82 self.rne = data[0] 83 self.confdir = os.path.join(config.PATH_ZEPHIR,'conf',self.rne, str(self.id_s)) 84 if cu == None: 85 cx_pool.close(cursor) 86 except: 87 traceback.print_exc() 88 if cu == None: 89 cx_pool.close(cursor)
90
91 - def update_data(self, cu=None, data=None):
92 """recharge les données des serveurs 93 """ 94 # récupération de l'établissement et de l'état 95 cursor = cu 96 if cu == None: 97 cursor = cx_pool.create() 98 try: 99 if data == None: 100 cursor.execute("select libelle, module_actuel, variante, timeout, etat, md5s, maj from serveurs where id=%s" % str(self.id_s)) 101 data = cursor.fetchone() 102 # données du serveur 103 self.libelle = data[0] 104 self.id_mod = int(data[1]) 105 self.id_var = int(data[2]) 106 try: 107 self.md5s = int(data[5]) 108 except: 109 self.md5s = -1 110 try: 111 self.maj = int(data[6]) 112 except: 113 self.maj = -1 114 try: 115 self.timeout = int(data[3]) 116 except: 117 self.timeout = 0 118 if data[4] == None: 119 self.status = -1 120 else: 121 self.status = int(data[4]) 122 # libelle etablissement 123 cursor.execute("select libelle from etablissements where rne='%s'" % self.rne) 124 data = cursor.fetchone() 125 self.etab = data[0] 126 # libelle module 127 cursor.execute("select libelle from modules where id='%s'" % self.id_mod) 128 data = cursor.fetchone() 129 self.module = data[0] 130 # détection des modules utilisant creole1 131 if self.module in config.OLD_MODULES: 132 self.version = 'creole1' 133 else: 134 self.version = 'creole2' 135 #libelle variante 136 cursor.execute("select libelle from variantes where id='%s'" % self.id_var) 137 data = cursor.fetchone() 138 self.variante = data[0] 139 self.modified = time.time() 140 if cu == None: 141 cx_pool.close(cursor) 142 except: 143 traceback.print_exc() 144 if cu == None: 145 cx_pool.close(cursor)
146
147 - def __repr__(self):
148 """affichage du serveur 149 """ 150 return """%s => %s (%s)""" % (self.id_s, self.module, self.rne)
151
152 - def __str__(self):
153 """affichage du serveur 154 """ 155 return """%s => %s (%s) - %s (%s)""" % (self.id_s, self.libelle, self.module, self.etab, self.rne)
156 157 ##################### 158 ## LECTURE DE DONNEES 159
160 - def get_libelle(self):
161 return self.libelle
162
163 - def get_rne(self):
164 return self.rne
165
166 - def get_etab(self):
167 return self.etab
168
169 - def get_module(self):
170 return self.module
171
172 - def get_variante(self):
173 return self.variante
174
175 - def get_timeout(self):
176 return self.timeout
177
178 - def get_confdir(self):
179 return self.confdir
180
181 - def get_config(self, mode="modif_config", encode=False):
182 """retourne la configuration eole du serveur (zephir.eol) 183 """ 184 # récupération du dictionnaire et des dicos locaux 185 if self.version == 'creole1': 186 # on recherche la liste des dictionnaires à charger 187 creole_files = [self.confdir+os.sep+'dictionnaire'] 188 # dictionnaire de la variante 189 creole_files.extend(glob(self.confdir+os.sep+'dicos/variante/*.eol')) 190 # dictionnaire locaux 191 creole_files.extend(glob(self.confdir+os.sep+'dicos/*.eol')) 192 dicos = [] 193 for dic in creole_files: 194 try: 195 # lecture du contenu des dictionnaires 196 fic = open(dic,'r') 197 lines = fic.readlines() 198 fic.close() 199 if encode == True: 200 data = [ unicode(line, 'ISO-8859-1').encode('UTF-8') for line in lines ] 201 else: 202 data = lines 203 # stockage du contenu du dictionnaire 204 dicos.append(data) 205 except OSError: 206 pass 207 208 if self.version == 'creole2': 209 # chargement de configuration pour un serveur Eole2 210 dicos = [] 211 for rep in ['module','local','variante']: 212 dicos.append(os.path.join(self.confdir,'dicos',rep)) 213 214 dico = ZephirDict(dicos, self.confdir, mode, self.version) 215 return dico
216
217 - def save_config(self, dico_zeph, mode='config', encode=False):
218 """sauvegarde la configuration eole du serveur (zephir.eol) 219 """ 220 try: 221 if mode in ['config','modif_config']: 222 fic_zephir = os.path.join(self.confdir,'zephir.eol') 223 key = 'config_ok' 224 elif mode in ['dico','modif_dico']: 225 # si gen_dico, sauvegarde de dico.eol dans la variante 226 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' 227 key = 'dico_ok' 228 dico_zeph.save(fic_zephir,encode=encode) 229 self.maj_params({key:1}) 230 self.check_md5conf() 231 except: 232 raise IOError("serveur %s : erreur de sauvegarde de %s" % (self.id_s, os.path.basename(fic_zephir))) 233 return ""
234
235 - def get_status(self):
236 """renvoie le dernier état enregistré du serveur 237 """ 238 try: 239 assert self.status in range (5) 240 except: 241 return -1 242 else: 243 return self.status
244
245 - def set_status(self, status):
246 """Met à jour l'état du serveur 247 """ 248 self.status = int(status) 249 cu = cx_pool.create() 250 try: 251 cu.execute("update serveurs set etat=%s where id=%s" % (self.status, self.id_s)) 252 cx_pool.commit(cu) 253 except: 254 traceback.print_exc() 255 cx_pool.rollback(cu) 256 return self.status
257
258 - def get_params(self, params=None):
259 """renvoie le champ paramètres du serveur 260 """ 261 try: 262 if params is None: 263 cu = cx_pool.create() 264 try: 265 cu.execute("select params from serveurs where id=%s" % str(self.id_s)) 266 data = cu.fetchone() 267 cx_pool.close(cu) 268 except: 269 traceback.print_exc() 270 cx_pool.close(cu) 271 params = {} 272 try: 273 params = eval(data[0]) 274 assert type(params) == dict and params.has_key('md5s') 275 except: 276 # met à jour le champ param si il n'est pas encore renseigné 277 params = self.update_params(params) 278 # sauvegarde du champs params 279 self.save_params(params) 280 else: 281 if type(params) == str: 282 params = eval(params) 283 284 # nombre de commandes et transferts uucp en attente 285 id_uucp = self.rne+'-'+str(self.id_s) 286 params['uucp_transfert']=0 287 params['uucp_cmd']=0 288 if os.path.isdir('/var/spool/uucp/%s/D.' % id_uucp): 289 params['uucp_transfert']=len(os.listdir('/var/spool/uucp/%s/D.' % id_uucp)) 290 if os.path.isdir('/var/spool/uucp/%s/D.X' % id_uucp): 291 params['uucp_cmd']=len(os.listdir('/var/spool/uucp/%s/D.X' % id_uucp)) 292 # si la configuration n'était pas présénte, on vérifie si c'est toujours le cas 293 if params['config_ok'] == 0: 294 if os.path.isfile(self.confdir+os.sep+'zephir.eol'): 295 params['config_ok']=1 296 if params['dico_ok'] == 0: 297 if os.path.isfile(self.confdir+os.sep+'dico.eol'): 298 params['dico_ok']=1 299 return params 300 except: 301 traceback.print_exc() 302 return {}
303
304 - def edit_serveur(self, dico_modifs):
305 # on vérifie que l'identifiant n'est pas modifié 306 if 'id' in dico_modifs.keys(): 307 return 0, config.u("""l'identifiant ne peut pas être modifié""") 308 # idem pour le rne et le module 309 if 'rne' in dico_modifs.keys(): 310 return 0, config.u("""le rne ne peut pas être modifié""") 311 # idem pour le rne et le module 312 if ('module_actuel' in dico_modifs.keys()) or ('module_initial' in dico_modifs.keys()): 313 return 0, config.u("""le module ne peut pas être modifié""") 314 # construction de la requête SQL de modification 315 if dico_modifs == {}: 316 return 1, config.u("""aucune modification demandée""") 317 else: 318 # cas spécial : si on modifie le module et pas la variante, mettre variante 0 par défaut 319 if ('module_actuel' in dico_modifs.keys()) and not ('variante' in dico_modifs.keys()): 320 dico_modifs['variante']=0 321 requete=["update serveurs set "] 322 for cle in dico_modifs.keys(): 323 requete.append(str(cle)) 324 requete.append("='") 325 requete.append(str(dico_modifs[cle]).replace("'","\\\'")) 326 requete.append("', ") 327 query="".join(requete)[:-2] 328 query += """ where id=%s""" % self.id_s 329 # modification dans la base et appel à la fonction de mise à jour dans l'arborescence 330 331 try: 332 cu = cx_pool.create() 333 cu.execute(query) 334 cx_pool.commit(cu) 335 except: 336 return 0, config.u("""Erreur lors de la modification dans la base""") 337 338 serveur_dir_ori = self.confdir 339 if 'variante' in dico_modifs.keys(): 340 if int(dico_modifs['variante']) != int(self.id_var): 341 # on loggue le changement de variante 342 try: 343 cu = cx_pool.create() 344 cu.execute("""insert into log_serveur (id_serveur,date,type,message,etat) values (%s,'%s','ZEPHIR','Modification de la variante (%s -> %s)',0)""" % (int(self.id_s),str(time.ctime()),str(self.id_var),str(dico_modifs['variante']))) 345 cx_pool.commit(cu) 346 except: 347 # on ne bloque pas si le log échoue 348 pass 349 350 # mise à jour des liens vers la variante 351 var_dir = os.path.abspath(config.PATH_MODULES)+os.sep+str(self.id_mod)+os.sep+'variantes'+os.sep+str(dico_modifs['variante']) 352 try: 353 os.unlink(serveur_dir_ori+os.sep+'dico.eol') 354 os.unlink(serveur_dir_ori+os.sep+'droits_variante') 355 except: 356 pass 357 358 retour = os.system('ln -s %s/dico.eol %s/dico.eol' % (var_dir, serveur_dir_ori)) 359 retour = os.system('ln -s %s/droits_zephir %s/droits_variante' % (var_dir, serveur_dir_ori)) 360 for rep in ['patchs','dicos','fichiers_perso','fichiers_zephir']: 361 if retour != 0: 362 break 363 if os.path.exists(serveur_dir_ori+'/%s/variante' % rep): 364 os.unlink(serveur_dir_ori+'/%s/variante' % rep) 365 retour = os.system('ln -s %s/%s %s/%s/variante' % (var_dir, rep, serveur_dir_ori, rep)) 366 367 if retour != 0: 368 # erreur lors de la création de l'arborescence 369 return 0, config.u("""erreur de création des liens de variante""") 370 371 self.update_data() 372 # tout s'est bien passé (on peut toujours rêver) 373 return 1, config.u('ok')
374
375 - def update_params(self,params_ori):
376 # le champ params n'est pas initialisé 377 params = {'agents':1 378 ,'dico_ok':0 379 ,'config_ok':0 380 ,'cle_ok':0 381 #,'uucp_transfert':0 382 #,'uucp_cmd':0 383 ,'maj_ok':[-2,''] 384 ,'reboot_ok':[-2,''] 385 ,'service_restart_ok':[-2,''] 386 ,'query_maj':[-2,''] 387 ,'reconfigure_ok':[-2,''] 388 ,'configure_ok':[-2,''] 389 ,'sauvegarde_ok':[-2,''] 390 ,'lock_ok':[1,''] 391 ,'timeout':[1,''] 392 ,'md5s':[-1,''] 393 ,'last_log':'journal vide' 394 } 395 try: 396 params['agents'] = params_ori['agents'] 397 except: 398 # le serveur n'a pas encore d'info sur les agents 399 pass 400 # présence de zephir.eol et clé d'enregistrement 401 if os.path.isfile(self.confdir+os.sep+'zephir.eol'): 402 params['config_ok']=1 403 if os.path.isfile(self.confdir+os.sep+'cle_publique'): 404 params['cle_ok']=1 405 # vérification des logs pour maj et reconfigure 406 query = """select type,etat,date,message from last_log_serveur where id_serveur = %s order by date desc,id desc limit 1""" % self.id_s 407 cu = cx_pool.create() 408 try: 409 cu.execute(query) 410 data = cu.fetchone() 411 # on stocke la date du dernier log remonté 412 if data is not None: 413 params['last_log']=str(data[2]) 414 query = """select type,etat,date,message from last_log_serveur where id_serveur = %s and type != 'SURVEILLANCE' order by date desc,id desc""" % self.id_s 415 cu.execute(query) 416 data = cu.fetchall() 417 cx_pool.close(cu) 418 # on parse les logs 419 not_found = ['maj','reconfigure','configure','lock','sauvegarde','reboot','service_restart','query_maj'] 420 for log in data: 421 # on regarde si le serveur n'a pas remonté un bloquage des tâches zephir 422 # (pb : procédure pour remonter la résolution du problème) 423 if log[0] == 'LOCK': 424 if 'lock' in not_found: 425 not_found.remove('lock') 426 if log[1] == 1: 427 params['lock_ok'] = [2,str(log[2]),log[3]] 428 # QUERY-MAJ : nombre de paquets à mettre à jour 429 elif log[0] == 'QUERY-MAJ': 430 if 'query_maj' in not_found: 431 not_found.remove('query_maj') 432 if log[1] == 0: 433 params['query_maj'] = [0,str(log[2])] 434 if 'paquets' in log[3]: 435 try: 436 nb_paquets = int(log[3].split()[0]) 437 params['query_maj'] = [nb_paquets,str(log[2])] 438 except: 439 params['query_maj'] = [-2,'erreur de lecture du résultat'] 440 elif log[1] > 0: 441 # Erreur de Query-Auto 442 params['query_maj'] = [-1, log[3]] 443 else: 444 for type_log in ['maj','configure','reconfigure','sauvegarde','reboot','service_restart']: 445 # état des actions 446 if log[0] == type_log.upper() and int(log[1]) != -2: 447 # on ne stocke que le dernier etat de chaque action 448 if type_log in not_found: 449 not_found.remove(type_log) 450 if log[1] == -1: 451 params['%s_ok' % type_log] = [2,str(log[2]),log[3]] 452 elif log[1] > 0: 453 params['%s_ok' % type_log] = [0,str(log[2]),log[3]] 454 elif log[1] == 0: 455 params['%s_ok' % type_log] = [1,str(log[2]),log[3]] 456 break 457 if not_found == []: 458 break 459 except: 460 traceback.print_exc() 461 cx_pool.close(cu) 462 return params
463
464 - def maj_params(self, param_updates):
465 """modifie le champ param d'un serveur 466 @param_updates : dictionnaire contenant les modifications 467 """ 468 params_ori = self.get_params() 469 params_ori.update(param_updates) 470 self.save_params(params_ori)
471
472 - def save_params(self, params):
473 params = str(params).replace("'","''") 474 query = """update serveurs set params='%s' where id=%s""" % (params, self.id_s) 475 cu = cx_pool.create() 476 try: 477 cu.execute(query) 478 cx_pool.commit(cu) 479 except: 480 traceback.print_exc() 481 cx_pool.rollback(cu)
482
483 - def check_md5conf(self):
484 # création de la liste des fichiers présents sur zephir 485 modifs = [] 486 fics=[] 487 for src, dst, pattern in md5files: 488 if os.path.isdir(os.path.join(self.confdir,dst)): 489 for fic in os.listdir(os.path.join(self.confdir,dst)): 490 if os.path.isfile(os.path.join(self.confdir,dst,fic)): 491 if pattern == None or fic.endswith(pattern): 492 fics.append(os.path.join(dst,fic)) 493 else: 494 fics.append(dst) 495 #final_md5 = [] 496 md5s = [] 497 md5file = os.path.join(os.path.abspath(config.PATH_ZEPHIR),'data','config%s.md5' % self.id_s) 498 if os.path.isfile(md5file): 499 # liste des fichiers remontés par le serveur 500 data = file(md5file).read().strip().split('\n') 501 # comparaison 502 for line in data: 503 fic = line.split()[1] 504 if fic not in fics: 505 modifs.append("absent sur zephir : %s" % fic) 506 else: 507 md5s.append(fic) 508 #final_md5.append(line) 509 # on réécrit le fichier de md5 sans les fichiers manquants (évite d'avoir une sortie stderr) 510 #fic_md5 = file(md5file,'w') 511 #fic_md5.write("\n".join(final_md5)) 512 #fic_md5.close() 513 for fic in fics: 514 if fic not in md5s: 515 # erreur : fichier non présent sur la machine distante 516 modifs.append("non envoyé : %s" % fic) 517 # test du contenu des fichiers 518 cmd_md5 = getProcessOutputAndValue("/usr/bin/md5sum", 519 args = ["-c",md5file], 520 path = self.confdir, 521 env = {'LC_ALL': 'C'}) 522 cmd_md5.addCallback(self._check_md5_res, md5s, modifs) 523 cmd_md5.addErrback(self._check_md5_res, md5s, modifs) 524 else: 525 # pas d'infos md5 pour ce serveur 526 self.maj_params({'md5s':[-1,""]}) 527 self.edit_serveur({'md5s':-1})
528
529 - def _check_md5_res(self, result, md5s, modifs):
530 # on effectue une vérification md5 des fichiers de configuration si disponible 531 out, err, code = result 532 self.edit_serveur({'md5s':1}) 533 self.maj_params({'md5s':[1,""]}) 534 for fic in md5s: 535 if not "%s: OK" % fic in out.split('\n'): 536 modifs.append("contenu modifié : %s" % fic) 537 md5_ok = False 538 if modifs != []: 539 self.edit_serveur({'md5s':0}) 540 self.maj_params({'md5s':[0,";".join(modifs).strip()]})
541
542 - def get_last_contact(self):
543 """renvoie la date de dernier contact avec le serveur 544 """ 545 cu = cx_pool.create() 546 try: 547 cu.execute("select last_contact from serveurs where id=%s" % str(self.id_s)) 548 data = cu.fetchone() 549 cx_pool.close(cu) 550 except: 551 traceback.print_exc() 552 cx_pool.close(cu) 553 return 0.0 554 return float(data[0])
555
556 - def migrate_data(self, check=False):
557 """fonction de récupération des données d'un serveur migré 558 """ 559 # liste des fichiers à copier 560 if check == True: 561 if config.migration_files.has_key(self.module): 562 return 1, True 563 else: 564 return 1, False 565 566 if not config.migration_files.has_key(self.module): 567 return 1, config.u("""pas de fichiers à migrer""") 568 dir_serv = os.path.join(self.confdir,'fichiers_zephir') 569 dir_bak = os.path.join(self.confdir+'-backup','fichiers_zephir') 570 if not os.path.isdir(dir_bak): 571 return 0, config.u("répertoire backup de migration non trouvé") 572 not_found = [] 573 errors = [] 574 # copie des fichiers avec conversion de l'encoding si besoin 575 for src, dst, convert in config.migration_files[self.module]['files']: 576 fic_src = os.path.join(dir_bak,src) 577 fic_dst = os.path.join(dir_serv,dst) 578 if os.path.isdir(fic_src): 579 # répertoire 580 os.path.walk(fic_src, self.tree_copy, (fic_src, fic_dst, convert, errors)) 581 elif os.path.isfile(fic_src): 582 self.copy_fic(fic_src, fic_dst, convert, errors) 583 else: 584 not_found.append(src) 585 # application des droits 586 for dest, options, user, group, mode in config.migration_files[self.module]['rights']: 587 cmd_rights = 'cd %s;chmod %s %s %s;chown %s %s.%s %s' % (dir_serv, options, mode, dest, options, user, group, dest) 588 res = os.system(cmd_rights) 589 if res != 0: 590 errors.append('application des droits impossible : %s' % dest) 591 return 1, config.u((not_found,errors))
592
593 - def tree_copy(self, args, dirname, fnames):
594 fic_src, fic_dst, convert, errors = args 595 for fic in fnames: 596 if fic not in config.migration_files[self.module]['exclude']: 597 src = os.path.join(dirname, fic) 598 dst = src.replace(fic_src, fic_dst) 599 self.copy_fic(src, dst, convert, errors)
600
601 - def copy_fic(self, src, dst, convert, errors):
602 """copie du fichier src sur dst 603 convert: si True, convertit le fichier d'ISO-8859-1 vers UTF-8 604 """ 605 if convert == True: 606 cmd = '/usr/bin/iconv -f ISO-8859-1 -t UTF-8 -o "%s" "%s"' % (dst, src) 607 else: 608 cmd = '/bin/cp -f "%s" "%s"' % (src, dst) 609 res = os.system(cmd) 610 if res != 0: 611 print "erreur lors de la migration des données, la commande suivante a échoué :" 612 print cmd 613 errors.append(src)
614 615
616 -class ServeurPool(dict):
617 """dictionnaire des serveurs existants gérant les autorisations d'accès 618 """ 619 620
621 - def __init__(self):
622 623 self._groupes_serveurs = {} 624 self._restrictions = {} 625 self.stats = {} 626 self.update_groupes() 627 # dictionnaire des variantes et leur module 628 self._mod_var = self.get_mod_var() 629 try: 630 self.update_auths() 631 except: 632 # pas encore de table de restriction sur cette version 633 pass
634
635 - def update_groupes(self):
636 cu = cx_pool.create() 637 # récupération des groupes de serveurs 638 try: 639 cu.execute("select id, libelle, serveurs from groupes_serveurs") 640 data = cu.fetchall() 641 for groupe in data: 642 id = int(groupe[0]) 643 libelle = str(groupe[1]) 644 serveurs = eval(groupe[2]) 645 timestamp = time.time() 646 date_creat = timestamp 647 if self._groupes_serveurs.has_key(id): 648 date_creat = self._groupes_serveurs[id][2][0] 649 self._groupes_serveurs[id] = [libelle, serveurs, [date_creat, timestamp]] 650 cx_pool.close(cu) 651 except: 652 traceback.print_exc() 653 cx_pool.close(cu)
654
655 - def get_mod_var(self):
656 """récupére les variantes et leur module""" 657 cu = cx_pool.create() 658 try: 659 cu.execute("select id,module from variantes") 660 data = cu.fetchall() 661 cx_pool.close(cu) 662 except: 663 data = [] 664 traceback.print_exc() 665 cx_pool.close(cu) 666 return dict(data)
667
668 - def update_auths(self, user=None):
669 if user != None: 670 user_clause = " where login = '%s'" % user 671 else: 672 user_clause = "" 673 674 cu = cx_pool.create() 675 try: 676 # récupération des types de contraintes spécifiées 677 cu.execute("select login, id_res, type_res from restrictions %s" % user_clause) 678 data = cu.fetchall() 679 for restriction in data: 680 login, id_res, type_res = restriction 681 if not self._restrictions.has_key(login): 682 self._restrictions[login] = {} 683 if self._restrictions[login].has_key(type_res): 684 self._restrictions[login][type_res].append(id_res) 685 else: 686 self._restrictions[login][type_res] = [id_res] 687 cx_pool.close(cu) 688 except: 689 traceback.print_exc() 690 cx_pool.close(cu)
691
692 - def add_restriction(self, credential, type_res, id_res):
693 """ajoute une restriction pour un utilisateur et une ressource donnés 694 type_res : nature des objets à restreindre 695 id_res ; identifiant de l'objet autorisé 696 les différents types reconnus sont : 'rne, group, id_mod, id_var' et les attributs 697 de la classe serveur (id_s, module, version, ...) 698 """ 699 # ajout dans la base 700 try: 701 assert str(id_res) in self._restrictions[credential][type_res] 702 except: 703 cu = cx_pool.create() 704 # validation de l'existence 705 libelle, table, field = config.type_res_label[type_res] 706 if field in ('rne','libelle'): 707 # données de type str 708 valid_query = "select * from %s where %s ilike '%s'" % (table, field, str(id_res)) 709 else: 710 # type entier 711 valid_query = "select * from %s where %s = %s" % (table, field, str(id_res)) 712 try: 713 cu.execute(valid_query) 714 # pas de données correspondante trouvée 715 if cu.fetchone() == None: 716 raise ValueError('identifiant de ressource inconnu') 717 except Exception, e: 718 cx_pool.close(cu) 719 return False 720 try: 721 cu.execute("""insert into restrictions (login, type_res, id_res) values ('%s','%s','%s')""" % (credential, type_res, str(id_res))) 722 except: 723 # erreur d'insertion dans la base 724 cx_pool.rollback(cu) 725 return False 726 cx_pool.commit(cu) 727 if not self._restrictions.has_key(credential): 728 self._restrictions[credential] = {} 729 if not self._restrictions[credential].has_key(type_res): 730 self._restrictions[credential][type_res] = [] 731 self._restrictions[credential][type_res].append(str(id_res)) 732 else: 733 # restriction déjà existante 734 pass 735 return True
736
737 - def get_restrictions(self, credential, type_res=None):
738 """renvoie la liste des restrictions d'un utilisateur 739 """ 740 if not self._restrictions.has_key(credential): 741 return [] 742 if type_res == None: 743 # toutes les restrictions : dictionnaire 744 return self._restrictions[credential] 745 else: 746 # si restrictions d'un type précis, on renvoie une liste de ressources 747 if self._restrictions[credential].has_key(type_res): 748 return self._restrictions[credential][type_res] 749 else: 750 return []
751
752 - def del_restriction(self, credential, type_res, id_res):
753 """enleve une restriction d'un utilisateur 754 """ 755 try: 756 liste_res = self._restrictions[credential][type_res] 757 assert str(id_res) in liste_res 758 except: 759 # restriction non existante 760 return False 761 # suppression dans la base 762 cu = cx_pool.create() 763 try: 764 cu.execute("""delete from restrictions where login='%s' and type_res='%s' and id_res='%s'""" \ 765 % (credential, type_res, str(id_res))) 766 cx_pool.commit(cu) 767 except: 768 traceback.print_exc() 769 cx_pool.rollback(cu) 770 return False 771 # supression en mémoire 772 self._restrictions[credential][type_res].remove(str(id_res)) 773 if self._restrictions[credential][type_res] == []: 774 del(self._restrictions[credential][type_res]) 775 return True
776
777 - def get_file_perms(self, data_dir, filepath=""):
778 """renvoie les informations de permissions associées à un fichier 779 """ 780 # lecture des informations sur le fichier si disponibles 781 permsfile = data_dir+os.sep+'droits_zephir' 782 if os.path.isfile(permsfile): 783 f_rights = file(permsfile) 784 data = f_rights.read().strip().split('\n') 785 f_rights.close() 786 else: 787 data = [] 788 mode = user = group = "" 789 recursive = False 790 if filepath != "": 791 result = {filepath:[mode, user, group, recursive]} 792 else: 793 result = {} 794 for line in data: 795 if line.startswith(filepath+'#') or filepath == "": 796 try: 797 filename, mode, user, group, recursive = line.split('#') 798 if recursive != '': 799 recursive = eval(recursive) 800 result[filename] = [mode, user, group, recursive] 801 if filepath != "": 802 break 803 except: 804 # fichier vide ? 805 pass 806 return result
807
808 - def set_file_perms(self, rights, data_dir):
809 """enregistre les informations de permissions associées à un(des) fichier(s) 810 @param data_dir: chemin ou trouver le fichier droits_zephir 811 """ 812 # lecture des données existantes 813 permsfile = data_dir+os.sep+'droits_zephir' 814 data = self.get_file_perms(data_dir) 815 data.update(rights) 816 # stockage des informations 817 lines = [] 818 for filepath in data.keys(): 819 # vérification des données 820 # XXX:FIXME 821 lines.append("%s#%s#%s#%s#%s" % (filepath, data[filepath][0], data[filepath][1], data[filepath][2],data[filepath][3])) 822 # écriture des informations 823 f_rights = file(permsfile,'w') 824 f_rights.write('\n'.join(lines)) 825 f_rights.close() 826 return True
827
828 - def del_file_perms(self, data_dir, filepath="", recurse=False):
829 """supprime les informations de permissions associées à un fichier (ou tous) 830 """ 831 # lecture des informations sur le fichier si disponibles 832 permsfile = data_dir+os.sep+'droits_zephir' 833 if not os.path.exists(permsfile): 834 # pas de permissions exitantes ou mauvais chemin 835 return False 836 if filepath != "": 837 f_rights = file(permsfile) 838 data = f_rights.read().strip().split('\n') 839 f_rights.close() 840 if recurse == True: 841 search_pattern = filepath 842 else: 843 search_pattern = filepath + '#' 844 for line in data: 845 if line.startswith(search_pattern): 846 data.remove(line) 847 else: 848 data = [] 849 # écriture des informations 850 try: 851 f_rights = file(permsfile,'w') 852 f_rights.write('\n'.join(data)) 853 f_rights.close() 854 except: 855 return False 856 return True
857
858 - def check_serv_credential(self, credential, key):
859 # vérification des restrictions sur les attributs du serveur 860 # (id_s, rne, id_mod, variante) 861 serv = dict.get(self,key) 862 if self._restrictions.has_key(credential): 863 for type_res, res in self._restrictions[credential].items(): 864 if res == []: 865 # ce cas ne devrait pas arriver car si on passe toujours par del_restriction 866 continue 867 res_ok = True 868 # si la ressource n'est pas autorisée 869 # cas particuliers : rne et groupes 870 if type_res == 'rne': 871 rne_ok = False 872 for rne in res: 873 # cas des rne avec wildcard (%) 874 if rne.endswith('%'): 875 if serv.rne.startswith(rne.split('%')[0]): 876 # ce rne correspond à l'expression 877 rne_ok = True 878 break 879 if not rne_ok: 880 if not serv.rne in res: 881 res_ok = False 882 elif type_res == 'groupe': 883 # test d'appartenance à un groupe autorisé 884 serv_in_groupes = False 885 for groupe in res: 886 if serv.id_s in self._groupes_serveurs[int(groupe)][1]: 887 serv_in_groupes = True 888 break 889 if serv_in_groupes == False: 890 res_ok = False 891 else: 892 # test attribut correspondant du serveur dans les valeurs autorisées 893 if not str(getattr(serv,type_res)) in [str(i) for i in res]: 894 res_ok = False 895 if not res_ok: 896 # on interdit l'accès 897 raise ResourceAuthError("unauthorized resource : %s" % key)
898
899 - def check_gr_credential(self, credential, groupe):
900 # validation des droits d'accès à un groupe 901 if self._restrictions.has_key(credential): 902 if self._restrictions[credential].has_key('groupe'): 903 if self._restrictions[credential]['groupe'] != []: 904 if str(groupe) not in self._restrictions[credential]['groupe']: 905 # accès refusé 906 # FIXME vérification suffisante ou regarder les serveurs ou rne du groupe ? 907 raise ResourceAuthError("unauthorized resource : %s" % groupe)
908
909 - def check_mod_credential(self, credential, id_mod):
910 # validation des droits d'accès à un module 911 if self._restrictions.has_key(credential): 912 if self._restrictions[credential].has_key('id_mod'): 913 if self._restrictions[credential]['id_mod'] != []: 914 if str(id_mod) not in self._restrictions[credential]['id_mod']: 915 # accès refusé 916 raise ResourceAuthError("unauthorized module : %s" % id_mod)
917
918 - def check_var_credential(self, credential, id_var):
919 # validation des droits d'accès à une variante 920 if self._restrictions.has_key(credential): 921 try: 922 var_mod = str(self._mod_var[int(id_var)]) 923 except: 924 # on met à jour si la variante n'est pas trouvée 925 self._mod_var = self.get_mod_var() 926 var_mod = str(self._mod_var[int(id_var)]) 927 # on commence par vérifier le module auquel la variante appartient 928 if self._restrictions[credential].has_key('id_mod'): 929 if self._restrictions[credential]['id_mod'] != []: 930 if var_mod not in self._restrictions[credential]['id_mod']: 931 raise ResourceAuthError("accès au module %s interdit" % var_mod) 932 # vérification de la variante elle-même 933 if self._restrictions[credential].has_key('id_var'): 934 if self._restrictions[credential]['id_var'] != []: 935 if str(id_var) not in self._restrictions[credential]['id_var']: 936 # accès refusé 937 raise ResourceAuthError("accès à la variante %s interdit" % id_var)
938
939 - def check_etab_credential(self, credential, rne):
940 # validation des droits d'accès à un établissement 941 rne_ok = True 942 if self._restrictions.has_key(credential): 943 if self._restrictions[credential].has_key('rne'): 944 if self._restrictions[credential]['rne'] != []: 945 if str(rne) not in self._restrictions[credential]['rne']: 946 rne_ok = False 947 for etab in self._restrictions[credential]['rne']: 948 if etab.endswith('%'): 949 if str(rne).startswith(etab.split('%')[0]): 950 rne_ok = True 951 if not rne_ok: 952 # accès refusé 953 raise ResourceAuthError("unauthorized module : %s" % rne)
954
955 - def get(self, credential, key):
956 if key in self.keys(): 957 self.check_serv_credential(credential, key) 958 else: 959 raise KeyError, "serveur inexistant" 960 return dict.get(self,key)
961
962 - def get_alertes(self, credential):
963 """renvoie la liste des serveurs en alerte 964 """ 965 servs=[] 966 for id_serv in self.keys(): 967 try: 968 serv = self.get(credential, int(id_serv)) 969 except ResourceAuthError: 970 # accès à ce serveur non autorisé 971 continue 972 if serv.get_status() not in [1, -1]: 973 servs.append(serv) 974 return servs
975 976
977 - def check_serveurs(self, credential, serveurs, last_check=None):
978 """vérifie si des serveurs ont été modifiés 979 @param serveurs : dictionnaires {idserveur:timestamp} 980 @param last_check : Si != None, on renvoie les serveurs créés après cette date (timestamp) 981 @return : liste d'id de serveurs 982 """ 983 modifs = {} 984 serv_pool = [] 985 for id_serv in [str(cle) for cle in self.keys()]: 986 try: 987 serv = self.get(credential, int(id_serv)) 988 except ResourceAuthError: 989 # serveur non existant ou non accessible 990 print "server %s not authorized" % id_serv 991 if serveurs.has_key(id_serv): 992 modifs[id_serv] = "deleted" 993 else: 994 serv_pool.append(str(serv.id_s)) 995 if serveurs.has_key(id_serv): 996 if serv.modified > serveurs[id_serv]: 997 # serveur modifié 998 modifs[id_serv] = serv.modified 999 elif last_check != None: 1000 if serv.created > last_check: 1001 # si le serveur a été créé après la dernière mise à jour du client 1002 # on l'ajoute comme nouveau serveur 1003 modifs[id_serv] = serv.modified 1004 1005 for id_serv in serveurs.keys(): 1006 if id_serv not in serv_pool: 1007 modifs[id_serv] = "deleted" 1008 1009 return modifs
1010
1011 - def check_groupes(self, credential, groupes, last_check=None):
1012 """vérifie si des groupes ont été modifiés 1013 @param groupes : dictionnaires {idgroupe:timestamp} 1014 @param last_check : Si != None, on renvoie les serveurs créés après cette date (timestamp) 1015 @return : dictionnaire {id_gr:[libelle, serveurs, [date_creat, date_modif]]} 1016 """ 1017 modifs = {} 1018 1019 for id_gr, data in self._groupes_serveurs.items(): 1020 try: 1021 group = self.get_groupes(credential,id_gr) 1022 except KeyError, ResourceAuthError: 1023 # groupe non existant ou non accessible 1024 print "group %s no longer exists or not authorized" % id_gr 1025 modifs[str(id_gr)] = "deleted" 1026 else: 1027 # la clé des dictionnaires doit être une chaine pour xmlrpc 1028 id_gr = str(id_gr) 1029 if groupes.has_key(id_gr): 1030 if data[2][1] > groupes[id_gr]: 1031 # groupe modifié 1032 modifs[id_gr] = data 1033 elif last_check != None: 1034 if data[2][0] > last_check: 1035 # nouveau groupe 1036 modifs[id_gr] = data 1037 1038 return modifs
1039
1040 - def add_groupe(self, credential, libelle, serveurs):
1041 query = """insert into groupes_serveurs (libelle,serveurs) values ('%s', '%s')""" % (libelle,str(serveurs)) 1042 cu = cx_pool.create() 1043 try: 1044 cu.execute(query) 1045 except: 1046 traceback.print_exc() 1047 cx_pool.roolback(cu) 1048 return False 1049 cx_pool.commit(cu) 1050 self.update_groupes() 1051 # ajout automatique du groupe pour l'utilisateur l'ayant créé si il a des restrictions de groupes 1052 if self._restrictions.has_key(credential): 1053 if self._restrictions[credential].has_key('groupe'): 1054 # on recherche l'id du groupe ajouté 1055 for id_gr in self._groupes_serveurs.keys(): 1056 if self._groupes_serveurs[id_gr][0] == libelle: 1057 self.add_restriction(credential,'groupe',str(id_gr)) 1058 return True
1059
1060 - def get_groupes(self, credential, id_groupe=None, alertes=False):
1061 if id_groupe != None: 1062 groupes = [int(id_groupe)] 1063 else: 1064 groupes = self._groupes_serveurs.keys() 1065 # vérification des restrictions sur les groupes 1066 res = [] 1067 for groupe in groupes: 1068 try: 1069 self.check_gr_credential(credential, groupe) 1070 except: 1071 # groupe non autorisé, on passe au suivant 1072 continue 1073 libelle, serveurs, timestamps = self._groupes_serveurs[groupe] 1074 # sinon on ajoute le groupe à la liste 1075 res.append([int(groupe), libelle, serveurs]) 1076 return res
1077
1078 - def edit_groupe(self, credential, id_groupe, libelle, serveurs):
1079 # vérification des droits sur le groupe 1080 id_groupe = int(id_groupe) 1081 self.check_gr_credential(credential, id_groupe) 1082 # mise à jour de la base 1083 cu = cx_pool.create() 1084 query = """update groupes_serveurs set libelle='%s', serveurs='%s' where id=%s""" % (libelle,str(serveurs),id_groupe) 1085 try: 1086 cu.execute(query) 1087 except: 1088 traceback.print_exc() 1089 cx_pool.rollback(cu) 1090 return False 1091 cx_pool.commit(cu) 1092 # mise à jour interne 1093 date_creat = self._groupes_serveurs[id_groupe][2][0] 1094 self._groupes_serveurs[id_groupe] = [libelle, serveurs, [date_creat,time.time()]] 1095 return True
1096
1097 - def del_groupe(self, credential, id_groupe):
1098 # vérification des droits sur le groupe 1099 id_groupe = int(id_groupe) 1100 self.check_gr_credential(credential, id_groupe) 1101 # on supprime le groupe des groupes surveillés par les utilisateurs 1102 d = {'id':id_groupe} 1103 # recherche des utilisateurs surveillant le groupe 1104 rech_groupe = """groupes like '[%(id)s]' or groupes like '[%%%(id)s,%%]' or groupes like '%%, %(id)s]'""" % d 1105 cursor = cx_pool.create() 1106 try: 1107 cursor.execute("""select login,groupes from users where %s""" % rech_groupe) 1108 data=cursor.fetchall() 1109 # pour chaque utilisateur 1110 for user in data: 1111 # modification de la liste des groupes 1112 groupes = eval(user[1]) 1113 if id_groupe in groupes: 1114 groupes.remove(id_groupe) 1115 sql_update = """update users set groupes='%s' where login='%s'""" % (str(groupes),user[0]) 1116 cursor.execute(sql_update) 1117 # suppression du groupe dans la base 1118 cursor.execute("""delete from groupes_serveurs where id = %s""" % str(id_groupe)) 1119 cx_pool.commit(cursor) 1120 except: 1121 traceback.print_exc() 1122 cx_pool.rollback(cursor) 1123 return False 1124 # suppression du groupe en mémoire 1125 del(self._groupes_serveurs[id_groupe]) 1126 return True
1127 1128
1129 - def extend_groupe(self, credential, id_groupe, serveurs):
1130 # vérification des droits sur le groupe 1131 id_groupe = int(id_groupe) 1132 self.check_gr_credential(credential, id_groupe) 1133 # extension des serveurs du groupe 1134 # dans le dictionnaire interne 1135 for serv in serveurs: 1136 if serv in self._groupes_serveurs[id_groupe][1]: 1137 serveurs.remove(serv) 1138 self._groupes_serveurs[id_groupe][1].extend(serveurs) 1139 self._groupes_serveurs[id_groupe][2] = [time.time(),time.time()] 1140 cu = cx_pool.create() 1141 # dans la base de données 1142 try: 1143 query="""update groupes_serveurs set serveurs='%s' where id=%s""" % (str(self._groupes_serveurs[id_groupe][1]),id_groupe) 1144 cu.execute(query) 1145 cx_pool.commit(cu) 1146 except: 1147 traceback.print_exc() 1148 cx_pool.rollback(cu)
1149
1150 - def add_serveur(self, credential, rne, libelle, materiel, processeur, disque_dur, date_install, installateur, tel, remarques, module_initial, module_actuel, variante, timestamp_serveur, timeout):
1151 # on vérifie si l'accès au rne , module et variante est permis 1152 if self._restrictions.has_key(credential): 1153 if self._restrictions[credential].has_key('rne'): 1154 self.check_etab_credential(credential, rne) 1155 if self._restrictions[credential].has_key('id_mod'): 1156 if module_actuel not in self._restrictions[credential]['id_mod']: 1157 raise ResourceAuthError("unauthorized resource %s" % module_actuel) 1158 if self._restrictions[credential].has_key('variante'): 1159 if variante not in self._restrictions[credential]['variante']: 1160 raise ResourceAuthError("unauthorized resource %s" % variante) 1161 # insertion dans la base de données 1162 cu = cx_pool.create() 1163 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)""" \ 1164 % (rne,libelle.replace("'","\\\'"),materiel.replace("'","\\\'"),processeur.replace("'","\\\'"),disque_dur.replace("'","\\\'"),date_install.replace("'","\\\'"),installateur.replace("'","\\\'"),tel,remarques.replace("'","\\\'"),module_initial,module_actuel,variante,timestamp_serveur,timeout) 1165 cu.execute(query) 1166 # récupération de l'identifiant attribué par la base 1167 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'""" % (rne, libelle.replace("'","\\\'"), date_install.replace("'","\\\'"), installateur.replace("'","\\\'"), remarques.replace("'","\\\'"), module_actuel, variante, timestamp_serveur) 1168 cu.execute(query) 1169 id_serveur = int(cu.fetchone()[0]) 1170 self[id_serveur] = Serveur(id_serveur,cu) 1171 cx_pool.commit(cu) 1172 return id_serveur
1173
1174 - def del_serveur(self, credential, id_serveur):
1175 """Supression d'un serveur de la base 1176 """ 1177 # vérification des restrictions 1178 self.check_serv_credential(credential, id_serveur) 1179 cu = cx_pool.create() 1180 # on supprime le serveur dans la base 1181 try: 1182 cu.execute("""delete from serveurs where id=%s""" % str(id_serveur)) 1183 cx_pool.commit(cu) 1184 # supression interne 1185 del(self[id_serveur]) 1186 except: 1187 traceback.print_exc() 1188 cx_pool.rollback(cu)
1189
1190 - def edit_serveur(self, id_serveur, dico_modifs):
1191 # modification d'un serveur dans la base 1192 serv = self[id_serveur] 1193 return serv.edit_serveur(dico_modifs)
1194
1195 - def update_contact(self, id_serveur):
1196 """mise à jour de la date de contact d'un serveur""" 1197 id_serveur = int(id_serveur) 1198 cu = cx_pool.create() 1199 try: 1200 # on met à jour le serveur dans la base 1201 query = """update serveurs set last_contact='%s' where id=%s""" % (str(time.time()),id_serveur) 1202 cu.execute(query) 1203 cx_pool.commit(cu) 1204 # mise à jour des statistiques 1205 if int(id_serveur) in self.stats['no_contact']: 1206 self.stats['no_contact'].remove(int(id_serveur)) 1207 except: 1208 traceback.print_exc() 1209 cx_pool.rollback(cu)
1210 1211
1212 -def serveur_pool():
1213 """crée un pool de tous les serveurs existants 1214 """ 1215 # création du pool de serveurs en mémoire 1216 pool=ServeurPool() 1217 cu = cx_pool.create() 1218 try: 1219 cu.execute("select id, rne, libelle, module_actuel, variante, timeout, etat, md5s, maj, last_contact from serveurs") 1220 data = cu.fetchall() 1221 nbserv = len(data) 1222 no_contact = [] 1223 # Instanciation des serveurs 1224 for serv in data: 1225 # liste des serveurs n'ayant jamais contacté zephir 1226 if serv[9] == None: 1227 no_contact.append(int(serv[0])) 1228 # nombre total de serveurs 1229 pool[serv[0]] = Serveur(serv[0], cu, serv[1:2]) 1230 pool[serv[0]].update_data(cu, serv[2:9]) 1231 pool.stats['no_contact'] = no_contact 1232 pool.stats['nb_serv'] = len(data) 1233 cx_pool.close(cu) 1234 except: 1235 traceback.print_exc() 1236 cx_pool.close(cu) 1237 return pool
1238 1239 # utility functions 1240 1241 text_characters = "".join(map(chr, range(32, 255)) + list("\n\r\t\b")) 1242 _null_trans = string.maketrans("", "") 1243
1244 -def istextfile(filename, blocksize = 1024):
1245 return istext(open(filename).read(blocksize))
1246
1247 -def istext(s):
1248 if "\0" in s: 1249 return 0 1250 1251 if not s: # Empty files are considered text 1252 return 1 1253 1254 # Get the non-text characters (maps a character to itself then 1255 # use the 'remove' option to get rid of the text characters.) 1256 t = s.translate(_null_trans, text_characters) 1257 1258 # If more than 10% non-text characters, then 1259 # this is considered a binary file 1260 if len(t)/len(s) > 0.10: 1261 return 0 1262 return 1
1263