zephir.backend.dictpool

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

# Source Code for Module zephir.backend.dictpool

      1  # -*- coding: UTF-8 -*-
      2  ###########################################################################
      3  # Eole NG - 2012
      4  # Copyright Pole de Competence Eole  (Ministere Education - Academie Dijon)
      5  # Licence CeCill  cf /root/LicenceEole.txt
      6  # eole@ac-dijon.fr
      7  #
      8  # dictpool.py
      9  #
     10  # classes de gestion d'un pool de dictionnaires Creole
     11  # avec liaison au niveau module/variante/serveur
     12  #
     13  ###########################################################################
     14  """classes de gestion d'un pool de dictionnaires Creole
     15  """
     16  from zephir.backend import config
     17  from zephir.backend.config import log
     18  from zephir.backend.lib_backend import cx_pool, PgSQL
     19  import os, sys, time, traceback, shutil
     20  import base64
     21  from glob import glob
     22
     23  RESOURCE_TYPES = ('module', 'variante', 'serveur')
     24
     25 -class DictPool:
     26      """
     27      Classe de gestion des dictionnaires par module/variante/serveur (eole 2.3 et plus)
     28      """
     29
     30 -    def __init__(self, eole_versions, serveur_pool):
     31          """
     32          précharge une liste de dictionnaires disponibles pour
     33          une version donnée de la distribution Eole
     34          """
     35          self.eole_versions = eole_versions
     36          self.dict_dir = {}
     37          for eole_version in self.eole_versions:
     38              self.dict_dir[eole_version] = os.path.join(config.ROOT_DIR, 'dictionnaires', config.DISTRIBS[int(eole_version)][1])
     39          self.serveur_pool = serveur_pool
     40          self.update_data()
     41          self.reset_modules()
     42
     43 -    def update_data(self):
     44          """
     45          met à jour la structure interne depuis l'aborescence
     46          des dictionnaires eole/locaux
     47          """
     48          self.paqs = {}
     49          self.dicos = {}
     50          self.variantes = {}
     51          self.modules = {}
     52          for eole_version in self.eole_versions:
     53              self.paqs[eole_version] = {}
     54              self.dicos[eole_version] = {}
     55              # chargement des différents paquets et dictionnaires existants
     56              for dict_type in ('eole', 'local'):
     57                  self.update_dicts(eole_version, dict_type)
     58              # chargement de la liste des modules/variantes compatibles
     59              req = """select modules.id, modules.libelle, variantes.id from variantes, modules where modules.id=variantes.module and modules.version=%s"""
     60              cu = cx_pool.create()
     61              cu.execute(req, (eole_version,))
     62              data = cu.fetchall()
     63              cx_pool.close(cu)
     64              # on ne prend en compte que les modules pour lesquels au moins
     65              # un paquet est défini dans le fichier de description du module
     66              mod_defaults = {}
     67              for mod, label, var in data:
     68                  if mod not in self.modules:
     69                      # il faut remplir self.modules avant d'utiliser get_module_defaults
     70                      # l'entrée est supprimée ensuite si aucun paquet n'est défini
     71                      self.modules[mod] = (label, eole_version)
     72                      if len(self.get_module_defaults(mod)) == 0:
     73                          # pas de paquets déclarés, on ne gère pas ce module avec le pool
     74                          del(self.modules[mod])
     75                      else:
     76                          print "Utilisation du pool de dictionnaires pour le module: %s" % label
     77                  vars = self.variantes.get(mod, [])
     78                  vars.append(var)
     79                  self.variantes[mod] = vars
     80
     81 -    def update_dicts(self, eole_version, dict_type):
     82          """rechargement de la représentation de l'arborescence des dictionnaires
     83          dict_type : dictionnaires de type 'local' ou 'eole'
     84          """
     85          self.paqs[eole_version][dict_type] = {}
     86          self.dicos[eole_version][dict_type] = {}
     87          dict_dir = os.path.join(self.dict_dir[eole_version], dict_type)
     88          if not os.path.isdir(dict_dir):
     89              os.makedirs(dict_dir)
     90          paq_eole = os.listdir(dict_dir)
     91          # dictionnaires gérés par des paquets
     92          for paq_dir in paq_eole:
     93              if os.path.isdir(os.path.join(self.dict_dir[eole_version], dict_type, paq_dir)):
     94                  self.paqs[eole_version][dict_type][paq_dir] = glob(os.path.join(self.dict_dir[eole_version], dict_type, paq_dir, '*.xml'))
     95          # dictionnaires isolés
     96          dicos = glob(os.path.join(self.dict_dir[eole_version], dict_type,'*.xml'))
     97          for dict_path in dicos:
     98              self.dicos[eole_version][dict_type][os.path.basename(dict_path)] = dict_path
     99
    100 -    def get_paq_dict(self, eole_version, liste_paqs, full_path=True):
    101          """renvoie une liste de dictionnaires
    102          à partir d'une liste de paquets
    103          """
    104          dicos_mod = []
    105          for paq in liste_paqs:
    106              # on vérifie qu'on n'a pas une chaine vide (fichier sans paquet) ou une ligne commentée
    107              if paq and not paq.startswith('#'):
    108                  type_paq, paq_name = os.path.split(paq)
    109                  if paq_name in self.paqs[eole_version].get(type_paq, {}):
    110                      if full_path:
    111                          dicos_mod.extend(self.paqs[eole_version][type_paq][paq_name])
    112                      else:
    113                          dicos_mod.append(paq_name)
    114          return dicos_mod
    115
    116 -    def check_dict(self, eole_version, dict_type, dict_name):
    117          """
    118          vérifie si un dictionnaire donné est bien référencé
    119          """
    120          if dict_name.endswith('.xml'):
    121              if dict_name not in self.dicos[eole_version][dict_type]:
    122                  raise ValueError, "dictionnaire non référencé : %s" % dict_name
    123          elif dict_name not in self.paqs[eole_version][dict_type]:
    124              raise ValueError, "paquet inconnu : %s" % dict_name
    125
    126 -    def check_type_res(self, res_type):
    127          """
    128          vérifie que le type de ressource demandée est reconnu
    129          """
    130          if res_type not in RESOURCE_TYPES:
    131              raise ValueError, "Type de ressource inconnu : %s" % res_type
    132
    133 -    def check_module(self, id_module):
    134          """vérifie si un numéro de module est géré par le pool
    135          """
    136          return int(id_module) in self.modules
    137
    138 -    def get_database_dict(self, eole_version, type_res, id_res, full_path=True):
    139          """ récupère les dictionnaires/paquets additionnels
    140          stockés en base de données
    141          type_res : type de ressource (serveur, variante, module)
    142          id_res : identifiant de la ressource
    143          """
    144          self.check_type_res(type_res)
    145          tablename = "dict_%s" % type_res
    146          req = "select dict_type, dict_name from " + tablename + " where id_resource=%s"""
    147          cursor = cx_pool.create()
    148          try:
    149              cursor.execute(req, (int(id_res),))
    150              data = cursor.fetchall()
    151          except:
    152              raise ValueError('%s introuvable : %s' % (type_res, id_res))
    153          cx_pool.close(cursor)
    154          dicos_mod = []
    155          for dict_type, dict_name in data:
    156              if full_path:
    157                  # renvoie le chemin complet des dictionnaires associés (+ dictionnaires des paquets)
    158                  if dict_name in self.paqs[eole_version][dict_type]:
    159                      dicos_mod.extend(self.paqs[eole_version][dict_type][dict_name])
    160                  elif dict_name.endswith('.xml'):
    161                      dicos_mod.append(os.path.join(self.dict_dir[eole_version], dict_type, dict_name))
    162              else:
    163                  # on renvoie juste le nom de paquet/dictionnaire
    164                  dicos_mod.append(dict_name)
    165          return dicos_mod
    166
    167 -    def set_database_dict(self, eole_version, type_res, id_res, dict_type, dict_path):
    168          """
    169          associe un(des) dictionnaire(s) à une ressource en base de données
    170          type_res : 'module', 'variante' ou 'serveur'
    171          dict_type : type de dictionnaire (eole ou local)
    172          dict_path : nom du dictionnaire ou du paquet
    173          """
    174          self.check_dict(eole_version, dict_type, dict_path)
    175          # vérification de la présence du dictionnaire dans le pool interne
    176          assert dict_path in self.dicos[eole_version][dict_type] or dict_path in self.paqs[eole_version][dict_type]
    177          self.check_type_res(type_res)
    178          # on détermine le type de dictionnaire en fonction du chemin de la ressource
    179          tablename = "dict_%s" % type_res
    180          req = "insert into " + tablename + " values (%s, %s, %s)"
    181          cursor = cx_pool.create()
    182          try:
    183              cursor.execute(req, (int(id_res), dict_type, dict_path))
    184              cx_pool.commit(cursor)
    185          except PgSQL.IntegrityError:
    186              cx_pool.rollback(cursor)
    187              raise ValueError('%s %s : utilise déjà %s' % (type_res, str(id_res), dict_path))
    188          except:
    189              traceback.print_exc()
    190              cx_pool.rollback(cursor)
    191              raise ValueError('%s introuvable : %s' % (type_res, id_res))
    192
    193 -    def del_database_dict(self, eole_version, type_res, id_res, dict_type, dict_path):
    194          """
    195          supprime l'association entre un dictionnaire et une ressource de la base
    196          """
    197          self.check_dict(eole_version, dict_type, dict_path)
    198          self.check_type_res(type_res)
    199          tablename = "dict_%s" % type_res
    200          req = "delete from " + tablename + " where id_resource=%s and dict_type=%s and dict_name=%s"
    201          cursor = cx_pool.create()
    202          try:
    203              cursor.execute(req, (int(id_res), dict_type, dict_path))
    204              cx_pool.commit(cursor)
    205          except:
    206              traceback.print_exc()
    207              cx_pool.rollback(cursor)
    208              raise ValueError('Entrée introuvable dans la table %s' % tablename)
    209
    210      ###################################
    211      # Gestion du pool de dictionnaires
    212 -    def get_dict_links(self, eole_version, dict_type, dict_name):
    213          """
    214          renvoie la liste des liens établis pour un dictionnaire/paquet donné
    215          """
    216          return self.get_dict_resources(eole_version, dict_type, dict_name, True)
    217
    218 -    def get_dict_resources(self, eole_version, dict_type, dict_name, get_links=False):
    219          """
    220          liste toutes les associations liées à un dictionnaire particulier
    221          retourne la liste des liens par type de ressource
    222          get_paths : si False, renvoie la liste des ressources associées
    223                      si True, renvoie la liste des liens symboliques existants
    224          """
    225          dict_resources =  {}
    226          for type_res in RESOURCE_TYPES:
    227              dict_resources[type_res] = []
    228              tablename = 'dict_%s' % type_res
    229              req = "select id_resource from " + tablename + " where dict_type=%s and dict_name=%s"
    230              cursor = cx_pool.create()
    231              try:
    232                  cursor.execute(req, (dict_type, dict_name))
    233                  data = cursor.fetchall()
    234                  cx_pool.close(cursor)
    235              except:
    236                  cx_pool.close(cursor)
    237                  return dict_resources
    238              for id_entry in data:
    239                  # calcul du chemin du lien symbolique créé pour la ressource
    240                  id_res = id_entry[0]
    241                  if get_links:
    242                      res_links = self.get_link_path(int(eole_version), id_res, type_res, dict_type, dict_name)
    243                      if res_links:
    244                          for res_link in res_links:
    245                              if os.path.islink(res_link):
    246                                  dict_resources[type_res].append(res_link)
    247                  else:
    248                      dict_resources[type_res].append(id_res)
    249          return dict_resources
    250
    251 -    def get_link_path(self, eole_version, id_res, type_res, dict_type, dict_name):
    252          """
    253          calcule le chemin d'un lien vers un dictionnaire pour une ressource particulière
    254          """
    255          if dict_name in self.paqs[eole_version][dict_type]:
    256              # paquet : on récupère la liste des dictionnaires
    257              dicts = [os.path.basename(paq) for paq in self.paqs[eole_version][dict_type][dict_name]]
    258              serveur_dir = var_dir = 'package'
    259          else:
    260              dicts = [dict_name]
    261              serveur_dir = 'local'
    262              var_dir = 'dicos'
    263          path_link = None
    264          if type_res == 'module':
    265              if int(id_res) in self.modules:
    266                  path_link = os.path.join(config.PATH_MODULES, str(id_res), 'dicos')
    267          elif type_res == 'variante':
    268              id_mod = None
    269              for mod, vars in self.variantes.items():
    270                  if int(id_res) in vars:
    271                      id_mod = mod
    272              # on ne prend que les variantes correspondant aux modules gérés
    273              if id_mod:
    274                  path_link = os.path.join(config.PATH_MODULES, str(id_mod), 'variantes', str(id_res), var_dir)
    275          elif type_res == 'serveur':
    276              try:
    277                  serveur = self.serveur_pool[int(id_res)]
    278                  assert serveur.id_mod in self.modules
    279              except:
    280                  # serveur non trouvé ou mauvaise version de module
    281                  return None
    282              path_link = os.path.join(serveur.get_confdir(), 'dicos', serveur_dir)
    283          if path_link is not None:
    284              return [os.path.join(path_link, dict_file) for dict_file in dicts]
    285          return None
    286
    287 -    def add_dict(self, eole_version, dict_name, content, paq_name=""):
    288          """
    289          ajoute/met à jour un dictionnaire dans le pool local
    290          paq_name : nom du paquet intégrant le dictionnaire ou rien si
    291                     le dictionnaire est isolé
    292          """
    293          # création du répertoire du paquet si besoin
    294          try:
    295              if paq_name:
    296                  dict_dir = os.path.join(self.dict_dir[eole_version], 'local', paq_name)
    297              else:
    298                  dict_dir = os.path.join(self.dict_dir[eole_version], 'local')
    299              if not os.path.isdir(dict_dir):
    300                  os.makedirs(dict_dir)
    301              # sauvegarde du contenu du dictionnaire. On l'écrase si il existe déjà
    302              dict_file = open(os.path.join(dict_dir, dict_name), 'w')
    303              dict_file.write(base64.decodestring(content))
    304              dict_file.close()
    305          except:
    306              # erreur d'écriture ?
    307              traceback.print_exc()
    308              return False
    309          # ajout du dictionnaire/paquet dans la liste interne si tout s'est bien passé
    310          self.update_dicts(eole_version, 'local')
    311          return True
    312
    313 -    def get_dict(self, eole_version, type_dict, dict_name, paq_name=""):
    314          """
    315          renvoie le contenu d'un dictionnaire
    316          dict_name : nom du dictionnaire à récupérer
    317          paq_name : nom du paquet contenant le dictionnaire ou rien
    318          """
    319          content = ""
    320          if paq_name:
    321              dict_path = os.path.join(self.dict_dir[eole_version], type_dict, paq_name, dict_name)
    322          else:
    323              dict_path = os.path.join(self.dict_dir[eole_version], type_dict, dict_name)
    324          assert os.path.exists(dict_path), "Fichier non retrouvé"
    325          return base64.encodestring(open(dict_path).read())
    326
    327 -    def remove_dict(self, eole_version, dict_name, paq_name=""):
    328          """
    329          supprimer un dictionnaire/paquet du pool local
    330          dict_name : nom du dictionnaire / paquet à supprimer
    331          paq_name : nom du paquet contenant le dictionnaire ou rien
    332          """
    333          # cas possibles :
    334          #   1) dictionnaire isolé / paquet : rechercher tous les liens et les supprimer
    335          #   2) dictionnaire dans un paquet : supprimer seulement le dictionnaire
    336          if not paq_name:
    337              # récupération de tous les liens sur ce dictionnaire / paquet
    338              dict_links = self.get_dict_links(eole_version, 'local', dict_name)
    339              for links in dict_links.values():
    340                  for dict_link in links:
    341                      if os.path.islink(dict_link):
    342                          os.unlink(dict_link)
    343          else:
    344              # cas 2 : obligatoirement fichier .xml
    345              assert dict_name.endswith('.xml'), "fichier invalide : dictionnaire xml requis"
    346          # dans tous les cas : suppression du dictionnaire
    347          try:
    348              dict_path = os.path.join(self.dict_dir[eole_version], 'local', paq_name, dict_name)
    349              assert os.path.exists(dict_path)
    350              if os.path.isdir(dict_path):
    351                  shutil.rmtree(dict_path)
    352              else:
    353                  os.unlink(dict_path)
    354          except:
    355              return False
    356          # suppression globale dans la base (cas 1)
    357          if not paq_name:
    358              for type_res in RESOURCE_TYPES:
    359                  tablename = 'dict_%s' % type_res
    360                  req = "delete from " + tablename + " where dict_type='local' and dict_name=%s"
    361                  cursor = cx_pool.create()
    362                  try:
    363                      cursor.execute(req, (dict_name,))
    364                      cx_pool.commit(cursor)
    365                  except:
    366                      traceback.print_exc()
    367                      cx_pool.rollback(cursor)
    368                      return False
    369          self.update_dicts(eole_version, 'local')
    370          return True
    371
    372      #############################################
    373      # gestion des dictionnaires au niveau module
    374
    375 -    def list_module(self, id_module, full_path=True):
    376          """
    377          liste les dictionnaires/paquets associés
    378          à un module dans la base de données
    379          """
    380          # récupération du libellé du module
    381          try:
    382              libelle, module_version = self.modules[int(id_module)]
    383          except:
    384              raise KeyError('numéro de module inconnu ou non géré')
    385          dicos_mod = self.get_module_defaults(id_module, full_path)
    386          # récupération des données dans la base
    387          dicos_mod.extend(self.get_database_dict(module_version, 'module', id_module, full_path))
    388          return dicos_mod
    389
    390 -    def get_module_defaults(self, id_module, full_path=True):
    391          """
    392          retourne la liste des paquets définis par défaut pour un module particulier
    393          """
    394          try:
    395              label, module_version = self.modules[int(id_module)]
    396          except:
    397              raise KeyError('numéro de module inconnu ou non géré')
    398          # récupération des paquets par défaut d'un module
    399          fic_module = os.path.join(config.ROOT_DIR, 'default_modules', str(module_version), label)
    400          liste_paqs = open(fic_module).read()
    401          liste_paqs = liste_paqs.strip().split('\n')
    402          return self.get_paq_dict(module_version, liste_paqs, full_path)
    403
    404 -    def update_module(self, id_module):
    405          """
    406          met à jour la liste les liens vers les dictionnaires
    407          associés à un module
    408          """
    409          mod_dir = os.path.join(config.PATH_MODULES, str(id_module), 'dicos')
    410          # bascule depuis l'ancien mode de gestion si le répertoire est un lien
    411          if os.path.islink(mod_dir):
    412              os.unlink(mod_dir)
    413          if not os.path.isdir(mod_dir):
    414              os.makedirs(mod_dir)
    415          # lecture de la liste des dictionnaires du module
    416          dicts = self.list_module(id_module)
    417          # suppression et regénération de tous les liens symboliques
    418          old_dicts = glob(os.path.join(mod_dir, '*.xml'))
    419          for old_dict in old_dicts:
    420              os.unlink(old_dict)
    421          for new_dict in dicts:
    422              if os.path.exists(new_dict):
    423                  os.symlink(new_dict, os.path.join(mod_dir, os.path.basename(new_dict)))
    424
    425 -    def add_module_dict(self, id_module, dict_type, dict_path):
    426          """
    427          ajoute un dictionnaire à un module
    428          """
    429          # vérification du n° de module
    430          if not int(id_module) in self.modules:
    431              raise ValueError('module incompatible (distribution non gérée)')
    432          module_version = self.modules[int(id_module)][1]
    433          self.set_database_dict(module_version, 'module', id_module, dict_type, dict_path)
    434          # mise à jour des liens
    435          self.update_module(id_module)
    436
    437 -    def del_module_dict(self, id_module, dict_type, dict_path):
    438          """
    439          supprime un dictionnaire d'un module
    440          """
    441          # vérification du n° de module
    442          try:
    443              libelle, module_version = self.modules[int(id_module)]
    444          except KeyError:
    445              raise ValueError('module incompatible (version non gérée : %s)' % config.DISTRIBS[module_version][1])
    446          # on supprime toutes les entrées dans la base
    447          self.del_database_dict(module_version, 'module', id_module, dict_type, dict_path)
    448
    449          # XXX FIXME : interdire la supression dans les fichiers ? (au moins pour les modules Eole)
    450
    451          # module : on regarde si le dictionnaire n'est pas dans le fichier de description du module ??
    452          # récupération des paquets par défaut du module
    453          fic_module = os.path.join(config.ROOT_DIR, 'default_modules', str(module_version), libelle)
    454          liste_paqs = open(fic_module).read()
    455          liste_paqs = liste_paqs.strip().split('\n')
    456          if dict_path in liste_paqs:
    457              # suppression de l'entrée dans le fichier
    458              liste_paqs.remove(dict_path)
    459              f_mod = open(fic_module, 'w')
    460              f_mod.write('\n'.join(liste_paqs))
    461              f_mod.close()
    462          self.update_module(id_module)
    463
    464      ###############################################
    465      # gestion des dictionnaires au niveau variante
    466
    467 -    def get_var_mod(self, id_variante):
    468          """
    469          vérifie que la distribution de la variante est gérée et renvoie son n° de module
    470          """
    471          id_mod = None
    472          for mod, vars in self.variantes.items():
    473              if int(id_variante) in vars:
    474                  id_mod = mod
    475          if id_mod is None:
    476              raise ValueError("Variante inconnue ou incompatible (distribution non gérée)")
    477          module_version = self.modules[id_mod][1]
    478          return id_mod, module_version
    479
    480 -    def check_passvar(self, id_variante, cred_user, pass_var):
    481          """Vérification du mot de passe/propriétaire de la variante
    482          A appeler avant add(del)_variante_dict
    483          """
    484          query = "select owner, passmd5 from variantes where id=%s"
    485          cursor = cx_pool.create()
    486          try:
    487              cursor.execute(query, (int(id_variante),))
    488              owner, passmd5 = cursor.fetchone()
    489          except:
    490              raise ValueError('variante introuvable : %s' % str(id_variante))
    491          cx_pool.close(cursor)
    492          if owner != cred_user:
    493              # vérification du mot de passe
    494              if passmd5 != pass_var and passmd5 not in [None,'', BLANK_MD5]:
    495                  # mauvais mot de passe
    496                  return False
    497          return True
    498
    499 -    def list_variante(self, id_variante, full_path=True):
    500          """
    501          liste les dictionnaires/paquets associés
    502          à une variante dans la base de données
    503          """
    504          # vérification de la version de distribution
    505          id_mod, module_version = self.get_var_mod(id_variante)
    506          return self.get_database_dict(module_version, 'variante', id_variante, full_path)
    507
    508 -    def update_variante(self, id_variante, id_mod = None):
    509          """
    510          met à jour la liste les liens vers les dictionnaires
    511          associés à une variante
    512          """
    513          if id_mod is None:
    514              id_mod, module_version = self.get_var_mod(id_variante)
    515          else:
    516              module_version = self.modules[id_mod][1]
    517          dicts = self.get_database_dict(module_version, 'variante', int(id_variante))
    518          var_dir = os.path.join(config.PATH_MODULES, str(id_mod), 'variantes', str(id_variante), 'dicos')
    519          if not os.path.isdir(var_dir):
    520              os.makedirs(var_dir)
    521          var_paq_dir = os.path.join(config.PATH_MODULES, str(id_mod), 'variantes', str(id_variante), 'package')
    522          if not os.path.isdir(var_paq_dir):
    523              os.makedirs(var_paq_dir)
    524          # suppression et regénération de tous les liens symboliques
    525          old_dicts = glob(os.path.join(var_dir, '*.xml'))
    526          old_dicts.extend(glob(os.path.join(var_paq_dir, '*.xml')))
    527          # XXX FIXME gérer les erreurs
    528          for old_dict in old_dicts:
    529              os.unlink(old_dict)
    530          for new_dict in dicts:
    531              dirname = new_dict.split(os.sep)[-2]
    532              if dirname in ('eole', 'local'):
    533                  # on a affaire à un dictionnaire isolé
    534                  dest_dir = var_dir
    535              else:
    536                  # dictionnaire de paquet, on ne l'ajoute pas local pour éviter
    537                  # les doublons avec ceux isntallés par le paquet
    538                  dest_dir = var_paq_dir
    539              if not os.path.isdir(dest_dir):
    540                  os.makedirs(dest_dir)
    541              os.symlink(new_dict, os.path.join(dest_dir, os.path.basename(new_dict)))
    542          self.sync_variante_paqs(id_variante)
    543
    544 -    def sync_variante_paqs(self, id_variante):
    545          """synchronise les paquets additonnels et les paquets de dictonnaires d'une variante
    546          """
    547          module, module_version = self.get_var_mod(int(id_variante))
    548          f_perso = os.path.join(config.PATH_MODULES, str(module), 'variantes', str(id_variante), 'fichiers_zephir', 'fichiers_variante')
    549          variantes_files, variante_paqs = self.serveur_pool.get_fic_perso(f_perso)
    550          # paquets définis en base
    551          var_dicts = self.list_variante(id_variante, False)
    552          # on ne garde que les paquets (pas les dictionnaires isolés) ?
    553          paq_dicts = set([ paq for paq in var_dicts if not paq.endswith('.xml') ])
    554          # récupération de l'ensemble des paquets de dictionnaires connus pour
    555          # la distribution de ce serveur
    556          dict_paqs_eole = set(self.paqs[module_version]['eole'].keys())
    557          dict_paqs_local = set(self.paqs[module_version]['local'].keys())
    558          # recherche des paquets de dictionnaires non mis en base
    559          local_paqs = dict_paqs_local.intersection(variante_paqs)
    560          for paq in local_paqs:
    561              if paq not in paq_dicts:
    562                  self.add_variante_dict(id_variante, 'local', paq)
    563          eole_paqs = dict_paqs_eole.intersection(variante_paqs)
    564          for paq in eole_paqs:
    565              if paq not in paq_dicts:
    566                  self.add_variante_dict(id_variante, 'eole', paq)
    567          # recherche des paquets en base non installés par la variante
    568          new_paqs = []
    569          for paq in paq_dicts:
    570              if paq not in variante_paqs:
    571                  new_paqs.append(paq)
    572          if new_paqs:
    573              variante_paqs.extend(new_paqs)
    574              self.serveur_pool.save_fic_perso(variantes_files, variante_paqs, f_perso)
    575
    576 -    def add_variante_dict(self, id_variante, dict_type, dict_path):
    577          """
    578          ajoute un dictionnaire à un module
    579          """
    580          # récupération de l'identifiant du module
    581          id_mod, module_version = self.get_var_mod(id_variante)
    582          self.set_database_dict(module_version, 'variante', id_variante, dict_type, dict_path)
    583          # mise à jour des liens
    584          self.update_variante(id_variante, id_mod)
    585
    586 -    def del_variante_dict(self, id_variante, dict_type, dict_path):
    587          """
    588          supprime un dictionnaire d'une variante
    589          """
    590          # vérification du n° de module/variante
    591          id_mod, module_version = self.get_var_mod(id_variante)
    592          # on supprime toutes les entrées dans la base
    593          self.del_database_dict(module_version, 'variante', id_variante, dict_type, dict_path)
    594          # si paquet de dictionnaires supprimé, on le supprime aussi de fichiers_variante
    595          f_perso = os.path.join(config.PATH_MODULES, str(id_mod), 'variantes', str(id_variante), 'fichiers_zephir', 'fichiers_variante')
    596          fic_var, paq_var = self.serveur_pool.get_fic_perso(f_perso)
    597          if dict_path in paq_var:
    598              paq_var.remove(dict_path)
    599              self.serveur_pool.save_fic_perso(fic_var, paq_var, f_perso)
    600          # mise à jour des liens sur les dictionnaires du pool
    601          self.update_variante(id_variante, id_mod)
    602
    603      ##############################################
    604      # gestion des dictionnaires au niveau serveur
    605
    606 -    def get_serveur(self, id_serveur):
    607          """
    608          vérifie que la distribution du serveur est gérée et renvoie l'objet Serveur associé
    609          """
    610          try:
    611              serveur = self.serveur_pool[int(id_serveur)]
    612              # on vérifie la version du serveur
    613              assert serveur.id_mod in self.modules
    614          except:
    615              raise KeyError('Serveur incompatible (distribution non gérée)')
    616          return serveur
    617
    618 -    def list_serveur(self, id_serveur, full_path=True):
    619          """
    620          liste les dictionnaires/paquets associés
    621          à un serveur dans la base de données
    622          """
    623          serveur = self.get_serveur(id_serveur)
    624          return self.get_database_dict(self.modules[serveur.id_mod][1], 'serveur', serveur.id_s, full_path)
    625
    626 -    def update_serveur(self, id_serveur):
    627          """
    628          met à jour la liste les liens vers les dictionnaires
    629          associés à un serveur
    630          """
    631          serveur = self.get_serveur(id_serveur)
    632          self.check_dirs(serveur)
    633          # lecture de la liste des dictionnaires du serveur
    634          dicts = self.get_database_dict(self.modules[serveur.id_mod][1], 'serveur', serveur.id_s)
    635          serveur_dir = os.path.join(serveur.get_confdir(), 'dicos', 'local')
    636          paq_dir = os.path.join(serveur.get_confdir(), 'dicos', 'package')
    637          # suppression et regénération de tous les liens symboliques
    638          old_dicts = glob(os.path.join(serveur_dir, '*.xml'))
    639          old_dicts.extend(glob(os.path.join(paq_dir, '*.xml')))
    640          # XXX FIXME gérer les erreurs
    641          for old_dict in old_dicts:
    642              # on ne touche pas aux dictionnaires stockés 'physiquement' dans 'dicos/local' du serveur
    643              if os.path.islink(old_dict):
    644                  os.unlink(old_dict)
    645          for new_dict in dicts:
    646              dirname = new_dict.split(os.sep)[-2]
    647              if dirname in ('eole', 'local'):
    648                  # on a affaire à un dictionnaire isolé
    649                  dest_dir = serveur_dir
    650              else:
    651                  # dictionnaire de paquet, on ne l'ajoute pas local pour éviter
    652                  # les doublons avec ceux isntallés par le paquet
    653                  dest_dir = paq_dir
    654              if os.path.isfile(os.path.join(dest_dir, os.path.basename(new_dict))):
    655                  log.msg('serveur %s : lien vers %s non créé, dictionnaire local existant' % (str(id_serveur), new_dict))
    656              elif not os.path.isdir(dest_dir):
    657                  os.makedirs(dest_dir)
    658              os.symlink(new_dict, os.path.join(dest_dir, os.path.basename(new_dict)))
    659
    660 -    def check_dirs(self, serveur):
    661          """vérifie qu'un serveur possède bien tous les liens nécessaires
    662          à la gestion des dictionnaires par dictpool
    663
    664          serveur: objet serveur du pool de serveur Zéphir
    665
    666          les répertoires de dictionnaires sont :
    667          local       : dictionnaires particuliers au serveur
    668          package     : dictionnaires livrés par des paquets particulier au serveur
    669          variante    : dictionnaires particuliers à la variante
    670          var_package : dictionnaires livrés par des paquets définis dans la variante
    671          module      : dictionnaires définis au niveau module (et paquets du module)
    672          (pour le module, on ne fait pas de distinction pour les dictionnaire livrés
    673          par des paquets, aucun de ces dictionnaires n'étant envoyés au serveur)
    674          """
    675          # vérification des liens sur le module et la variante
    676          mod_dir = os.path.join(config.PATH_MODULES, str(serveur.id_mod))
    677          var_dir = os.path.join(mod_dir, 'variantes', str(serveur.id_var))
    678          if not os.path.isdir(os.path.join(serveur.get_confdir(), 'dicos', 'package')):
    679              os.makedirs(os.path.join(serveur.get_confdir(), 'dicos', 'package'))
    680          if not os.path.islink(os.path.join(serveur.get_confdir(), 'dicos', 'module')):
    681              os.symlink(os.path.join(mod_dir, 'dicos'), os.path.join(serveur.get_confdir(), 'dicos', 'module'))
    682          if not os.path.islink(os.path.join(serveur.get_confdir(), 'dicos', 'variante')):
    683              os.symlink(os.path.join(var_dir, 'dicos'), os.path.join(serveur.get_confdir(), 'dicos', 'variante'))
    684          if not os.path.islink(os.path.join(serveur.get_confdir(), 'dicos', 'var_package')):
    685              os.symlink(os.path.join(var_dir, 'package'), os.path.join(serveur.get_confdir(), 'dicos', 'var_package'))
    686
    687 -    def sync_serveur_packages(self, id_serveur):
    688          """synchronise l'état des paquets installés avec
    689          les dictionnaires activés
    690          """
    691          serveur = self.get_serveur(id_serveur)
    692          module_version = self.modules[serveur.id_mod][1]
    693          # liste des paquets de dictionnaires installés par défaut sur ce module
    694          module_paqs = set(self.get_module_defaults(serveur.id_mod, False))
    695          # récupération de l'ensemble des paquets de dictionnaires connus pour
    696          # la distribution de ce serveur
    697          dict_paqs_eole = set(self.paqs[module_version]['eole'].keys())
    698          dict_paqs_local = set(self.paqs[module_version]['local'].keys())
    699          # vérification des paquets installés remontés par le serveur
    700          pkg_file = os.path.join(os.path.abspath(config.PATH_ZEPHIR),'data','packages%s.list' % serveur.id_s)
    701          serveur_paqs = []
    702          serveur_dicts = []
    703          new_paqs = []
    704          new_dicts = []
    705          # if not os.path.isfile(pkg_file):
    706          # le serveur n'a jamais remonté sa liste de paquets
    707          # on considère qu'il possède les paquets de base du module et de la variante
    708          # les paquets additionnels au niveau module sont ajoutés aux paquets du serveur
    709          for dict_name in self.get_database_dict(module_version, 'variante', serveur.id_var, False):
    710              if not dict_name.endswith('.xml'):
    711                  serveur_paqs.append(dict_name)
    712          serveur_paqs = set(serveur_paqs)
    713          # FIXME : détection automatique des paquets déjà installés ?
    714          #else:
    715          #    for line in open(pkg_file):
    716          #        serveur_paqs.append(line.split()[0])
    717          #    serveur_paqs = set(serveur_paqs)
    718          #    # ajout des paquets installés en base si besoin
    719          #    for eole_paq in dict_paqs_eole.intersection(serveur_paqs):
    720          #        serveur_dicts.append(('eole', eole_paq))
    721          #    for local_paq in dict_paqs_local.intersection(serveur_paqs):
    722          #        serveur_dicts.append(('local', local_paq))
    723          #    log.msg('*** SYNC SERVEUR: paquets déjà installés: %s' % str(serveur_dicts))
    724
    725          # si des paquets sont en attente d'installation au niveau serveur, on vérifie qu'ils sont activés
    726          f_perso = os.path.join(serveur.get_confdir(), 'fichiers_zephir', 'fichiers_zephir')
    727          fic_serv, paq_serv = self.serveur_pool.get_fic_perso(f_perso)
    728          for dict_name in dict_paqs_eole.intersection(set(paq_serv)):
    729              if dict_name not in serveur_paqs:
    730                  serveur_dicts.append(('eole', dict_name))
    731          for dict_name in dict_paqs_local.intersection(set(paq_serv)):
    732              if dict_name not in serveur_paqs:
    733                  serveur_dicts.append(('local', dict_name))
    734          # liste des paquets de dictionnaires sélectionnés
    735          # et ajout de paquets à installer sur le serveur si besoin
    736          dict_paqs_serveur = self.get_database_dict(module_version, 'serveur', serveur.id_s, False)
    737          # pas de prise en compte des paquets ajoutés au niveau module : à calculer
    738          # dynamiquement au moment de l'envoi de conf au serveur ?
    739          # dict_paqs_serveur.extend(self.get_database_dict(module_version, 'module', serveur.id_mod,False))
    740          dict_paqs_serveur = set([paq for paq in dict_paqs_serveur if not paq.endswith('.xml')])
    741          # recherche des paquets référencés en base pour le serveur et non déclarés dans fichiers_zephir
    742          for eole_paq in dict_paqs_serveur.difference(serveur_paqs):
    743              new_paqs.append(eole_paq)
    744          # ajout des nouvelles entrées en base si besoin
    745          for dict_type, dict_path in serveur_dicts:
    746              # les paquets de base du module sont ignorés
    747              if dict_path not in dict_paqs_serveur and dict_path not in module_paqs:
    748                  self.add_serveur_dict(serveur.id_s, dict_type, dict_path, False)
    749                  new_dicts.append(dict_path)
    750          # mise à jour des liens vers les dictionnaires
    751          self.update_serveur(id_serveur)
    752          # ajout de paquets supplémentaires à installer sur le serveur
    753          added_paqs = serveur.add_packages(new_paqs)
    754          return added_paqs, new_dicts
    755
    756 -    def add_serveur_dict(self, id_serveur, dict_type, dict_path, update=True):
    757          """
    758          ajoute un dictionnaire à un module
    759          update: pas de mise à jour des liens du serveur si False
    760                   (à utiliser pour les traitements par lot)
    761          """
    762          serveur = self.get_serveur(id_serveur)
    763          module_version = self.modules[serveur.id_mod][1]
    764          self.set_database_dict(module_version, 'serveur', serveur.id_s, dict_type, dict_path)
    765          if update:
    766              # mise à jour des liens
    767              self.sync_serveur_packages(id_serveur)
    768
    769 -    def del_serveur_dict(self, id_serveur, dict_type, dict_path, update=True):
    770          """
    771          supprime un dictionnaire d'un serveur
    772          update: pas de mise à jour des liens du serveur si False
    773                   (à utiliser pour les traitements par lot)
    774          """
    775          serveur = self.get_serveur(id_serveur)
    776          module_version = self.modules[serveur.id_mod][1]
    777          # on ne gère que les dictionnaires du pool (les dictionnaires stockés
    778          # au niveau serveur sont gérés indépendamment)
    779          # on supprime toutes les entrées dans la base
    780          self.del_database_dict(module_version, 'serveur', id_serveur, dict_type, dict_path)
    781          # si paquet de dictionnaires supprimé, on le supprime aussi de fichiers_du serveur
    782          f_perso = os.path.join(serveur.get_confdir(), 'fichiers_zephir', 'fichiers_zephir')
    783          fic_serv, paq_serv = self.serveur_pool.get_fic_perso(f_perso)
    784          if dict_path in paq_serv:
    785              paq_serv.remove(dict_path)
    786              self.serveur_pool.save_fic_perso(fic_serv, paq_serv, f_perso)
    787          if update:
    788              # mise à jour des liens sur les dictionnaires du pool
    789              self.sync_serveur_packages(id_serveur)
    790
    791 -    def list_local_serveur(self, id_serveur):
    792          """
    793          Renvoie la liste des dictionnaire à envoyer et des paquets à installer pour un serveur
    794          """
    795          serveur = self.get_serveur(id_serveur)
    796          module_version = self.modules[serveur.id_mod][1]
    797          local_serv = {'dicos':[], 'paquets':[]}
    798          for type_res, id_res in (('module',serveur.id_mod), ('variante',serveur.id_var), ('serveur',serveur.id_s)):
    799              # création de la liste des dictionnaires 'isolés'
    800              for dict_res in self.get_database_dict(module_version, type_res, id_res):
    801                  if dict_res.split(os.sep)[-2] in ('local', 'eole'):
    802                      # dictionnaire isolé : à envoyer au serveur
    803                      local_serv['dicos'].append(dict_res)
    804                  else:
    805                      paq_name = dict_res.split(os.sep)[-2]
    806                      if paq_name not in local_serv['paquets']:
    807                          local_serv['paquets'].append(paq_name)
    808          # ajout des autres dictionnaires stockés dans le répertoire 'dicos' du serveur
    809          for dict_serveur in glob(os.path.join(serveur.get_confdir(), 'dicos', 'local' ,'*.xml')):
    810              if not os.path.islink(dict_serveur):
    811                  local_serv['dicos'].append(dict_serveur)
    812          return local_serv
    813
    814      ####################################
    815      # fonctions utilitaires pour Zéphir
    816
    817 -    def reset_modules(self):
    818          """
    819          met à jour les liens des dictionnaires pour l'ensemble des modules/variantes gérés
    820          """
    821          for id_mod, variantes in self.variantes.items():
    822              if id_mod in self.modules:
    823                  log.msg("Réinitialisation des dictionnaires de module / variantes : %s" % self.modules[id_mod][0])
    824                  self.update_module(id_mod)
    825                  for id_var in variantes:
    826                      self.update_variante(id_var)
    827
    828 -def init_dictpool(serveur_pool):
    829      """
    830      fonction d'initialisation du pool de dictionnaire
    831      """
    832      # détection des version de distribution disponibles dans le pool
    833      versions = []
    834      for eole_version in config.DISTRIBS:
    835          # on ne regarde que les version supérieures à eole 2.3
    836          if eole_version >= 5:
    837              dict_path = os.path.join(config.ROOT_DIR, 'dictionnaires', config.DISTRIBS[int(eole_version)][1])
    838              if os.path.isdir(os.path.join(dict_path, 'eole')):
    839                  # répertoire de paquets présents, on gère les dictionnaires de cette distribution
    840                  # on ajoute le répertoire 'local' si il n'existe pas
    841                  if not os.path.isdir(os.path.join(dict_path, 'local')):
    842                      os.makedirs(os.path.join(dict_path, 'local'))
    843                  versions.append(eole_version)
    844      return DictPool(versions, serveur_pool)
    845