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