zephir.backend.uucp_utils

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

# Source Code for Module zephir.backend.uucp_utils

      1  #! /usr/bin/env python
      2  # -*- coding: UTF-8 -*-
      3  ###########################################################################
      4  # Eole NG - 2007
      5  # Copyright Pole de Competence Eole  (Ministere Education - Academie Dijon)
      6  # Licence CeCill  cf /root/LicenceEole.txt
      7  # eole@ac-dijon.fr
      8  #
      9  # uucp_utils.py
     10  #
     11  # fonctions utilitaires pour la gestion d'uucp
     12  #
     13  ###########################################################################
     14
     15  import os,time,shutil,tempfile
     16
     17  UUCP_DIR = "/var/spool/uucp"
     18  CMD_UUX = "/usr/bin/uux2"
     19  CMD_UUCP = "/usr/bin/uucp2"
     20  LOG_FILE = "/tmp/rapport.zephir"
     21  LOCK = "/var/spool/uucp/lock"
     22  IGNORE_LIST = ['.ssh','.Status','.Temp','.Received','.bash_history','dead.letter']
     23
     24  # commandes connues: dictionnaires {"commande uucp" : (libellé de la commande, rejouable, transfert_rejouable)}
     25  # dans le cas où une commande n'est pas rejouable, il faut écraser son  appel précédent
     26  # lorsqu'elle est appelée plusieurs fois (concerne surtout les commandes liées à un transfert de fichier)
     27  # si le transfert est rejouable, on ne supprime pas le transfert associé à l'ancienne commande
     28  # lors d'un écrasement d'appel (par exemple si le fichier peut varier à chaque appel)
     29  COMMANDS = {"zephir_client save_files": ("Sauvegarde complète", True, False),
     30              "zephir_client save_files 0": ("Sauvegarde (complète)", True, False),
     31              "zephir_client save_files 1": ("Sauvegarde (configuration)", True, False),
     32              "zephir_client save_files 2": ("Sauvegarde (configuration/patchs/dicos)", True, False),
     33              "zephir_client save_files 3": ("Sauvegarde (fichiers divers)", True, False),
     34              "zephir_client maj_auto": ("Mise à jour", True, False),
     35              "zephir_client maj_auto E": ("Mise à jour complète", True, False),
     36              "zephir_client maj_client": ("Mise à jour de zephir-client", True, False),
     37              "zephir_client download_upgrade": ("Préchargement des paquets (Upgrade-Auto)", True, False),
     38              "zephir_client configure": ("Envoi de configuration", False, False),
     39              "zephir_client reboot": ("Redémarrage du serveur", True, False),
     40              "zephir_client service_restart": ("Redémarrage du service", True, False),
     41              "zephir_client reconfigure": ("Reconfiguration", True, False),
     42              "zephir_client update_key regen_certs": ("Renouvellement des clés d'enregistrement et certificats ssl", False, False),
     43              "zephir_client update_key": ("Renouvellement des clés d'enregistrement", False, False),
     44              "zephir_client update_replication": ("Regénération de la configuration de réplication LDAP", False, False),
     45              "zephir_client change_ip": ("Préparation au changement d'adresse de zephir", False, False),
     46              "zephir_client purge_ip": ("Annulation du changement d'adresse de zephir", True, False),
     47              "zephir_client import_aaf": ("Prise en compte des fichiers d'import AAF", False, True),
     48             }
     49
     50 -class UUCPError(Exception): pass
     51
     52  NUMBERS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z','a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
     53
     54  # fonction de conversion
     55 -def convert_num(chaine):
     56      result=0
     57      digits = range(len(chaine))
     58      # on parcourt la chaine en partant de la fin
     59      digits.reverse()
     60      for i in range(len(chaine)):
     61          # valeur du digit en cours
     62          digit = NUMBERS.index(chaine[digits[i]])
     63          # valeur * longueur_base^position
     64          result = result + digit * len(NUMBERS).__pow__(i)
     65      return result
     66
     67 -def get_lock():
     68      # on regarde si on a le droit d'écrire
     69      timeout = 0
     70      while os.path.isfile(LOCK):
     71          # si non on attend la libération des ressources
     72          timeout += 1
     73          # si plus de 5 secondes d'attente, il y a surement un problème
     74          if timeout > 10:
     75              # print "Problème d'accès concurrent, fichier %s présent" % LOCK
     76              raise UUCPError("Problème d'accès concurrent, fichier %s présent" % LOCK)
     77          time.sleep(0.5)
     78      # on bloque les autres appels à cette fonction en cas de demandes simultanées
     79      lock=file(LOCK,"w")
     80      lock.close()
     81
     82 -def free_lock():
     83      # supprime le fichier de lock si il existe
     84      if os.path.isfile(LOCK):
     85          os.unlink(LOCK)
     86
     87 -class UUCP:
     88      """wrapper d'uucp pour permettre la gestion des files dans zephir"""
     89
     90 -    def __init__(self,peers=None):
     91          """initialisation de l'objet"""
     92          self.peers_ori=peers
     93          self.pool={}
     94          self._scan_pool()
     95
     96 -    def _scan_pool(self, peer=None):
     97          """crée la liste d'attente en mémoire"""
     98          # on crée la liste des commandes et transferts actuels
     99          # si pas de noms de machines spécifiées, on recherche dans tous les systèmes
    100          if peer is not None:
    101              peers = [peer]
    102          else:
    103              if self.peers_ori is None:
    104                  peers = os.listdir(UUCP_DIR)
    105                  for peer in IGNORE_LIST:
    106                      if peer in peers:
    107                          peers.remove(peer)
    108              else:
    109                  peers = self.peers_ori
    110          # parcours de /var/spool/uucp
    111          for peer in peers:
    112              # répertoire uucp de ce correspondant
    113              peer_dir = os.path.abspath(os.path.join(UUCP_DIR, peer))
    114              # liste des actions en attente
    115              lst={}
    116              if os.path.isdir(os.path.join(peer_dir, "C.")):
    117                  for fic in os.listdir(os.path.join(peer_dir, "C.")):
    118                      # on récupère la date de création de la commande (timestamp)
    119                      date_creat = os.stat(os.path.join(peer_dir, "C.", fic)).st_ctime
    120                      f=file(os.path.join(peer_dir, "C.", fic))
    121                      l=f.read().strip().split('\n')[0]
    122                      f.close()
    123                      # séparation des paramètres
    124                      # ex de lignes:
    125                      # S /var/lib/zephir/conf/0210056X/4/config-zephir.tar ~ root -Cd D.0008 0644
    126                      # S D.X0007 X.zephirN0007 root -C D.X0007 0666
    127                      args=l.split()[1]
    128                      script=l.split()[5]
    129                      seq_number=convert_num(script[-4:])
    130                      # si on a une commande
    131                      type_cmd = "transfert"
    132                      orig_cmd = ""
    133                      if args.startswith("D.X"):
    134                          type_cmd = "execute"
    135                          # on regarde laquelle dans le fichier correspondant
    136                          f=file(os.path.join(peer_dir, "D.X", script))
    137                          l=f.read().strip().split('\n')
    138                          f.close()
    139                          for line in l:
    140                              line = line.strip()
    141                              if line[:2] == "C ":
    142                                  args = line[2:]
    143                                  orig_cmd = line[2:]
    144                                  # on regarde si c'est une commande connue
    145                                  cmds = COMMANDS.keys()
    146                                  cmds.sort(reverse=True)
    147                                  for cmd in cmds:
    148                                      if line[2:].startswith(cmd):
    149                                          args = line[2:].replace(cmd, COMMANDS[cmd][0])
    150                                          break
    151
    152                      lst[seq_number]=(type_cmd,args,script,fic,date_creat,orig_cmd)
    153
    154              self.pool[peer]=lst
    155
    156
    157 -    def add_cmd(self,peer,commande):
    158          """met en place une commande distante"""
    159          peer_dir = os.path.abspath(os.path.join(UUCP_DIR, peer))
    160          get_lock()
    161          # on recherche le grade à affecter à la commande (pour assurer l'ordre d'execution)
    162          self._scan_pool(peer)
    163          used_grades = []
    164          if peer in self.pool:
    165              cmd_files = [uucmd[3] for uucmd in self.pool[peer].values()]
    166              for uucmd in cmd_files:
    167                  cmd_grade = convert_num(uucmd[2])
    168                  if cmd_grade not in used_grades:
    169                      used_grades.append(cmd_grade)
    170          if used_grades != []:
    171              new_grade = max(used_grades) + 1
    172          else:
    173              # grade de départ pour les commandes : 'O' (les transferts de fichiers ont 'N' par défaut)
    174              new_grade = 24
    175          # si on est déjà au grade maximum, on le conserve (ne devrait pas arriver sauf si file d'attente bloquée longtemps ?)
    176          if new_grade >= len(NUMBERS):
    177              new_grade = NUMBERS[-1]
    178          else:
    179              new_grade = NUMBERS[new_grade]
    180          old_calls = []
    181          if commande in COMMANDS and COMMANDS[commande][1] == False:
    182              # on recherche d'éventuels appels précédents à supprimer si la commande n'est pas rejouable
    183              for old_cmd in self.list_cmd(peer)[peer]:
    184                  # pour les commandes, il faut comparer à l'appel réel et non au libellé
    185                  if self.pool[peer][old_cmd[0]][5] == commande:
    186                      old_calls.append((old_cmd[0], COMMANDS[commande][2]))
    187          # construction de l'appel à uux
    188          cmd = "%s -g %s -r '%s!%s >%s!%s'" % (CMD_UUX, new_grade, peer, commande, peer, LOG_FILE)
    189          try:
    190              # appel systeme
    191              res=os.system(cmd)
    192              # on ajoute la commande à la liste des commandes de ce peer
    193              if res == 0:
    194                  # recherche du fichier correspondant (dernier fichier créé dans "C.")
    195                  tempbuf=tempfile.mktemp()
    196                  os.system("/bin/ls -t %s > %s" % (os.path.join(peer_dir, "C./"), tempbuf))
    197                  output=file(tempbuf)
    198                  res2=output.read()
    199                  output.close()
    200                  os.unlink(tempbuf)
    201                  # on récupère le nom du fichier le plus récent
    202                  filename = res2.split()[0]
    203                  # nom du script correspondant
    204                  f=file(os.path.join(peer_dir, "C.", filename))
    205                  l=f.read().strip().split('\n')[0]
    206                  f.close()
    207                  script = l.split()[5]
    208                  # on déduit le n° dans la file à partir du nom de script
    209                  seq_num=convert_num(script[-4:])
    210                  if not self.pool.has_key(peer):
    211                      self.pool[peer]={}
    212                  date_creat = os.stat(os.path.join(peer_dir, "C.", filename)).st_ctime
    213                  self.pool[peer][seq_num]=("execute", commande, script, filename, date_creat, commande)
    214                  # on libère la ressource
    215                  free_lock()
    216                  # on supprime les éventuels fichiers précédents
    217                  if old_calls != []:
    218                      for call_id, keep_transfert in old_calls:
    219                          self.remove_cmd(peer, call_id, keep_transfert)
    220                  return seq_num
    221              else:
    222                  free_lock()
    223                  raise Exception("uux2 a retourné une erreur")
    224          except Exception,e:
    225              free_lock()
    226              # print """erreur lors de la préparation de l'exécution de %s : %s""" % (commande,e)
    227              raise UUCPError("""erreur lors de la préparation de l'exécution de %s : %s""" % (commande, str(e)))
    228
    229 -    def add_file(self,peer,fichier,destination="~"):
    230          """prépare l'envoi d'un fichier"""
    231          peer_dir = os.path.abspath(os.path.join(UUCP_DIR, peer))
    232          # Si un transfert est déjà stocké pour ce fichier, on prépare son annulation
    233          old_trans = []
    234          for transfert in self.list_files(peer)[peer]:
    235              if transfert[1] == fichier:
    236                  old_trans.append(transfert[0])
    237          # construction de l'appel à uucp
    238          cmd = "%s -r %s %s\\!%s" % (CMD_UUCP,fichier,peer,destination)
    239          get_lock()
    240          try:
    241              # appel systeme à uucp
    242              res=os.system(cmd)
    243              # on ajoute le transfert à la liste des transferts de ce peer
    244              if res == 0:
    245                  # recherche du fichier correspondant
    246                  tempbuf=tempfile.mktemp()
    247                  os.system("/bin/ls -t %s > %s" % (os.path.join(peer_dir, "C./"), tempbuf))
    248                  output=file(tempbuf)
    249                  res2=output.read()
    250                  output.close()
    251                  os.unlink(tempbuf)
    252                  # on récupère le nom du fichier le plus récent
    253                  filename = res2.split()[0]
    254                  # nom du script correspondant
    255                  f=file(os.path.join(peer_dir, "C.", filename))
    256                  l=f.read().strip().split('\n')[0]
    257                  f.close()
    258                  script = l.split()[5]
    259                  # on déduit le n° dans la file à partir du nom de script
    260                  seq_num=convert_num(script[-4:])
    261                  if not self.pool.has_key(peer):
    262                      self.pool[peer]={}
    263                  date_creat = os.stat(os.path.join(peer_dir, "C.", filename)).st_ctime
    264                  self.pool[peer][seq_num] = ("transfert",fichier,script,filename,time.time(),date_creat,fichier)
    265                  # on libère la ressource
    266                  free_lock()
    267                  # on supprime les éventuels fichiers précédents
    268                  if old_trans != []:
    269                      for trans_id in old_trans:
    270                          self.remove_cmd(peer, trans_id)
    271                  return 0
    272              else:
    273                  free_lock()
    274                  raise UUCPError("""echec à l'exécution de uucp""")
    275          except Exception,e:
    276              free_lock()
    277              raise UUCPError("""erreur lors de la préparation du transfert de %s : %s""" % (fichier,e))
    278
    279 -    def _create_liste(self,type_cmd,peers):
    280          """fonction interne qui liste les actions d'un type particulier"""
    281          cmds = {}
    282          # pour chaque peer
    283          for peer in peers:
    284              cmds[peer] = []
    285              if not self.pool.has_key(peer):
    286                  continue
    287              # on parcourt la liste des actions en attente
    288              numeros = self.pool[peer].keys()
    289              numeros.sort()
    290              for n in numeros:
    291                  action = self.pool[peer][n]
    292                  # si c'est une commande distante
    293                  if action[0] == type_cmd:
    294                      # on l'ajoute à la liste
    295                      cmds[peer].append((n, action[1]))
    296          return cmds
    297
    298 -    def check_timeout(self,max_time,peer=None):
    299          """vérifie si il existe des commandes plus anciennes
    300          que max_time pour un serveur donné (ou tous)
    301          @param max_time: age maximum en seconde accepté pour une commande
    302          retourne un dictionnaire {serveur:liste des ids de commande trop anciens}"""
    303          dic_res = {}
    304          if peer is not None:
    305              peers=[peer]
    306          else:
    307              self._scan_pool()
    308              peers=self.pool.keys()
    309          for serveur in peers:
    310              timeouts = []
    311              if self.pool.has_key(serveur):
    312                  for seq_num, data in self.pool[serveur].items():
    313                      test_time = time.localtime(float(data[-2] + max_time))
    314                      # si timeout (date de création + délai > date actuelle)
    315                      if test_time < time.localtime():
    316                          # on renvoie le n° de commande et sa date de création
    317                          timeouts.append((seq_num, time.ctime(data[-2])))
    318              if timeouts != []:
    319                  dic_res[serveur] = timeouts
    320          return dic_res
    321
    322 -    def list_cmd(self,peer=None):
    323          """renvoie la liste des commandes en attente"""
    324          if peer is not None:
    325              self._scan_pool(peer)
    326              peers=[peer]
    327          else:
    328              self._scan_pool()
    329              peers=self.pool.keys()
    330          return self._create_liste("execute",peers)
    331
    332 -    def list_files(self,peer=None):
    333          """renvoie la liste des transferts en attente"""
    334          if peer is not None:
    335              self._scan_pool(peer)
    336              peers=[peer]
    337          else:
    338              self._scan_pool()
    339              peers=self.pool.keys()
    340          return self._create_liste("transfert",peers)
    341
    342 -    def remove_cmd(self, peer, num_cmd, keep_transfert=False):
    343          """supprime une commande ou un transfert"""
    344          num_cmd = int(num_cmd)
    345          type_cmd,fichier,script,filename,date_creat,fichier_orig = self.pool[peer][num_cmd]
    346          peer_dir = os.path.abspath(os.path.join(UUCP_DIR, peer))
    347          get_lock()
    348          # supression du fichier correspondant dans C.
    349          try:
    350              if os.path.isfile(os.path.join(peer_dir, 'C.', filename)):
    351                  os.unlink(os.path.join(peer_dir, 'C.', filename))
    352              if type_cmd == "transfert":
    353                  # supression de la copie du fichier dans D.
    354                  if os.path.isfile(os.path.join(peer_dir, 'D.', script)):
    355                      os.unlink(os.path.join(peer_dir, 'D.', script))
    356              else:
    357                  # supression du script correspondant dans D.X
    358                  if os.path.isfile(os.path.join(peer_dir, 'D.X', script)):
    359                      os.unlink(os.path.join(peer_dir, 'D.X', script))
    360              # on libère la ressource
    361              free_lock()
    362          except:
    363              # print "erreur lors de la suppression des fichiers"
    364              free_lock()
    365              raise UUCPError("erreur lors de la suppression des fichiers")
    366
    367          del(self.pool[peer][num_cmd])
    368          # dans le cas d'une commande avec fichier, on supprime le transfert associé si on le trouve
    369          if fichier_orig in COMMANDS and COMMANDS[fichier_orig][1] == False and not keep_transfert:
    370              previous_cmd = self.pool[peer].get(num_cmd-1,[''])
    371              if previous_cmd[0] == "transfert":
    372                  self.remove_cmd(peer, num_cmd-1)
    373          return 0
    374
    375 -    def flush(self,peers=None):
    376          """supprime toute la file d'attente"""
    377          if peers is None:
    378              for i in os.listdir(UUCP_DIR):
    379                  if i not in IGNORE_LIST:
    380                      try:
    381                          # on supprime le répertoire d'échange
    382                          if os.path.isdir(os.path.join(UUCP_DIR, i)):
    383                              shutil.rmtree(os.path.join(UUCP_DIR, i))
    384                          self.pool[i]={}
    385                      except:
    386                          raise UUCPError("""erreur de suppression de la file d'attente de %s""" % i)
    387          else:
    388              try:
    389                  for peer in peers:
    390                      if os.path.isdir(os.path.join(UUCP_DIR, peer)):
    391                          shutil.rmtree(os.path.join(UUCP_DIR, peer))
    392                      self.pool[peer]={}
    393              except:
    394                  raise UUCPError("""erreur de supression de la file d'attente""")
    395          return 0
    396
    397  uucp_pool = UUCP()
    398
    399  if __name__ == '__main__':
    400      peers = ["0210056X-1","0210056X-2","0210056X-3","0210056X-4","0210056X-5"]
    401      uucp=UUCP(peers)
    402      peer = ""
    403      while not peer in peers:
    404          peer = raw_input("\nvoir la file d'attente de : ")
    405      print "\ncommandes :\n"
    406      for cmd in uucp.list_cmd(peer)[peer]:
    407          print "  "+str(cmd)
    408
    409      print "\ntransferts :\n"
    410      for cmd in uucp.list_files(peer)[peer]:
    411          print "  "+str(cmd)
    412      print '\n'
    413