ScolaSync  1.0
usbThread.py
Aller à la documentation de ce fichier.
00001 # -*- coding: utf-8 -*-    
00002 # $Id: usbThread.py 47 2011-06-13 10:20:14Z georgesk $  
00003 
00004 licenceEn="""
00005     file usbThread.py
00006     this file is part of the project scolasync
00007     
00008     Copyright (C) 2010 Georges Khaznadar <georgesk@ofset.org>
00009 
00010     This program is free software: you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation, either version3 of the License, or
00013     (at your option) any later version.
00014 
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018     GNU General Public License for more details.
00019 
00020     You should have received a copy of the GNU General Public License
00021     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00022 """
00023 
00024 import subprocess, threading, re, os.path, time
00025 
00026 _threadNumber=0
00027 
00028 ##
00029 # 
00030 #     Une classe pour tenir un registre des threads concernant les baladeurs.
00031 #     
00032 class ThreadRegister:
00033 
00034     ##
00035     # 
00036     #         Le constructure met en place un dictionnaire
00037     #         
00038     def __init__(self):
00039         self.dico={}
00040 
00041     def __str__(self):
00042         return "ThreadRegister: %s" %self.dico
00043         
00044     ##
00045     # 
00046     #         @param ud un disque
00047     #         @param thread un thread
00048     #         Empile un thread pour le baladeur ud
00049     #         
00050     def push(self, ud, thread):
00051         if ud.owner not in self.dico.keys():
00052             self.dico[ud.owner]=[thread]
00053         else:
00054             self.dico[ud.owner].append(thread)
00055 
00056     ##
00057     # 
00058     #         @param ud un disque
00059     #         @param thread un thread
00060     #         Dépile un thread pour le baladeur ud
00061     #         
00062     def pop(self, ud, thread):
00063         self.dico[ud.owner].remove(thread)
00064 
00065     ##
00066     # 
00067     #         Indique si le disque est occupé par des threads
00068     #         @param owner le propriétaire du disque
00069     #         @return les données associées par le dictionnaire
00070     #         
00071     def busy(self, owner):
00072         if owner in self.dico.keys():
00073             return self.dico[owner]
00074         return []
00075 
00076     ##
00077     # 
00078     #         renvoie l'ensemble des threads actifs
00079     #         
00080     def threadSet(self):
00081         result=set()
00082         for o in self.dico.keys():
00083             for t in self.dico[o]:
00084                 result.add(t)
00085         return result
00086             
00087 globalThreads=ThreadRegister()
00088 
00089 ##
00090 # 
00091 #     Évite d'avoir des <i>slashes</i> dans un nom de thread
00092 #     @return la fin du nom de chemin, après le dernier <i>slash</i> ;
00093 #     si le chemin ne finit pas bien, remplace les <i>slashes</i> par
00094 #     des sous-tirets "_".
00095 #     
00096 def _sanitizePath(path):
00097     pattern=re.compile(".*([^/]+)")
00098     m=pattern.match(str(path))
00099     if m:
00100         return m.group(1)
00101     else:
00102         return str(path).replace('/','_')
00103 
00104 ##
00105 # 
00106 #     fabrique un nom de thread commençant par th_, suivi d'un nombre unique,
00107 #     suivi d'une chaîne relative à la clé USB
00108 #     @param ud une instance de uDisk
00109 #     @return un nom de thread unique
00110 #     
00111 def _threadName(ud):
00112     global _threadNumber
00113     name="th_%04d_%s" %(_threadNumber,_sanitizePath(ud.path))
00114     _threadNumber+=1
00115     return name
00116 
00117 ##
00118 # 
00119 #     Renvoie la date et l'heure dans un format court
00120 #     @return une chaîne donnée par strftime et le format %Y/%m/%d-%H:%M:%S
00121 #     
00122 def _date():
00123     return time.strftime("%Y/%m/%d-%H:%M:%S")
00124 
00125 ##
00126 # 
00127 #     Lance une commande dans un shell, et journalise la réussite ou l'échec
00128 #     @param cmd la commande shell, si cette commande contient des points-virgules elle sera découpée en plusieurs sous-commandes, chacune traitée séparément.
00129 #     @param logfile le fichier de journalisation
00130 #     
00131 def _call(cmd, logfile):
00132     for command in cmd.split(";"):
00133         okToLog="echo [%s] Success: %s >> %s" %(_date(), command, logfile)
00134         koToLog="echo [%s] Error: %s >> %s" %(_date(), command, logfile)
00135         cmd1="(%s && %s) || %s" %(command, okToLog, koToLog)
00136         subprocess.call(cmd1, shell=True)
00137 
00138 ##
00139 # 
00140 #     Une classe abstraite
00141 #      Cette classe sert de creuset pour les classe servant aux copies
00142 #      et aux effacement.
00143 #     
00144 class abstractThreadUSB(threading.Thread):
00145     ##
00146     # 
00147     #         Constructeur
00148     #         Crée un thread pour copier une liste de fichiers vers une clé USB.
00149     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00150     #         @param fileList la liste des fichiers à traiter
00151     #         @param subdir un sous-répertoire de la clé USB
00152     #         @param dest un répertoire de destination si nécessaire, None par défaut
00153     #         @param logfile un fichier de journalisation, /dev/null par défaut
00154     #         
00155     def __init__(self,ud, fileList, subdir, dest=None, logfile="/dev/null"):
00156         threading.Thread.__init__(self,target=self.toDo,
00157                                   args=(ud, fileList, subdir, dest, logfile),
00158                                   name=_threadName(ud))        
00159         self.cmd=u"echo This is an abstract method, don't call it"
00160         self.ud=ud
00161         ud.threadRunning=True
00162         self.fileList=fileList
00163         self.subdir=subdir
00164         self.dest=dest
00165         self.logfile=logfile
00166 
00167     ##
00168     # 
00169     #         Renvoie une chaîne informative sur le thread
00170     #         @return une chaine donnant des informations sur ce qui va
00171     #         se passer dans le thread qui a été créé.
00172     #         
00173     def __str__(self):
00174         result="%s(\n" %self.threadType()
00175         result+="  ud       = %s\n" %self.ud
00176         result+="  fileList = %s\n" %self.fileList
00177         result+="  subdir   = %s\n" %self.subdir
00178         result+="  dest     = %s\n" %self.dest
00179         result+="  logfile  = %s\n" %self.logfile
00180         result+="  cmd      = %s\n" %self.cmd
00181         result+="\n"
00182         return result
00183 
00184     ##
00185     # 
00186     #         @return une chaîne courte qui informe sur le type de thread
00187     #         
00188     def threadType(self):
00189         return "abstractThreadUSB"
00190 
00191     ##
00192     # 
00193     #         La fonction abstraite pour les choses à faire
00194     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00195     #         @param fileList la liste des fichiers à traiter
00196     #         @param subdir un sous-répertoire de la clé USB
00197     #         @param dest un répertoire de destination
00198     #         @param logfile un fichier de journalisation
00199     #         
00200     def toDo(self, ud, fileList, subdir, dest, logfile):
00201         # ça ne fait rien du tout pour un thread abstrait
00202         pass
00203     
00204 ##
00205 # 
00206 #     Classe pour les threads copiant vers les clés USB
00207 #     
00208 class threadCopyToUSB(abstractThreadUSB):
00209     ##
00210     # 
00211     #         Constructeur
00212     #         Crée un thread pour copier une liste de fichiers vers une clé USB.
00213     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00214     #         @param fileList la liste des fichiers à copier
00215     #         @param subdir le sous-répertoire de la clé USB où faire la copie
00216     #         @param logfile un fichier de journalisation, /dev/null par défaut
00217     #         
00218     def __init__(self,ud, fileList, subdir, logfile="/dev/null"):
00219         abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=None, logfile=logfile)
00220         self.cmd=u"mkdir -p '{toDir}'; cp -R '{fromFile}' '{toFile}'"
00221 
00222     ##
00223     # 
00224     #         @return une chaîne courte qui informe sur le type de thread
00225     #         
00226     def threadType(self):
00227         return "threadCopyToUSB"
00228 
00229     ##
00230     # 
00231     #         Copie une liste de fichiers vers une clé USB sous un répertoire donné.
00232     #          Ce répertoire est composé de ud.visibleDir() joint au
00233     #          sous-répertoire subdir.
00234     #          À chaque fichier ou répertoire copié, une ligne est journalisée dans le
00235     #          fichier de journal de l'application.
00236     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00237     #         @param fileList la liste des fichiers à copier
00238     #         @param logfile un fichier de journalisation
00239     #         @param subdir le sous-répertoire de la clé USB où faire la copie
00240     #         
00241     def toDo(self, ud, fileList, subdir, dest, logfile):
00242         global globalThreads
00243         globalThreads.push(ud, self)
00244         while subdir[0]=='/':
00245             subdir=subdir[1:]
00246         destpath=os.path.join(ud.ensureMounted(),ud.visibleDir(),subdir)
00247         for f in fileList:
00248             fileName=os.path.basename(f)
00249             cmd=self.cmd.format(fromFile=f,
00250                                 toDir=destpath,
00251                                 toFile=os.path.join(destpath,fileName))
00252             _call(cmd,logfile)
00253         globalThreads.pop(ud, self)
00254 
00255 ##
00256 # 
00257 #     Classe pour les threads copiant depuis les clés USB
00258 #     
00259 class threadCopyFromUSB(abstractThreadUSB):
00260     ##
00261     # 
00262     #         Constructeur
00263     #         Crée un thread pour copier une liste de fichiers depuis une clé USB
00264     #         vers un répertoire de disque.
00265     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00266     #         @param fileList la liste des fichiers à copier
00267     #         @param subdir le sous-répertoire de la clé USB d'où faire la copie
00268     #         @param dest un répertoire de destination
00269     #         @param logfile un fichier de journalisation, /dev/null par défaut
00270     #         
00271     def __init__(self,ud, fileList, subdir=".", dest="/tmp",
00272                  rootPath="/", logfile="/dev/null"):
00273         abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=dest,
00274                                    logfile=logfile)
00275         self.rootPath=rootPath
00276         self.cmd=u"mkdir -p '{toDir}'; cp -R '{fromPath}' '{toPath}'"
00277 
00278     ##
00279     # 
00280     #         Copie une liste de fichiers d'une clé USB sous un répertoire donné.
00281     #          À chaque fichier ou répertoire copié, une ligne est journalisée
00282     #          dans le fichier de journal de l'application.
00283     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00284     #         @param fileList la liste des fichiers à copier
00285     #         @param dest un répertoire de destination
00286     #         @param logfile un fichier de journalisation
00287     #         @param subdir le sous-répertoire de la clé USB où faire la copie
00288     #         
00289     def toDo(self, ud, fileList, subdir, dest, logfile):
00290         global globalThreads
00291         globalThreads.push(ud, self)
00292         for f in fileList:
00293             ## prend le fichier ou le répertoire sur le disque courant
00294             fromPath=os.path.join(ud.ensureMounted(), f)
00295             owner=ud.ownerByDb()
00296             ## personnalise le nom de la destination
00297             newName=u"%s_%s" %(owner,f)
00298             ## calcule le point de copie et le répertoire à créer s'il le faut
00299             toPath=os.path.join(dest,newName)
00300             toDir=os.path.dirname(toPath)
00301             cmd=self.cmd.format(fromPath=fromPath, toPath=toPath, toDir=toDir)
00302             _call(cmd,logfile)
00303         globalThreads.pop(ud, self)
00304             
00305 ##
00306 # 
00307 #     Classe pour les threads effaçant des sous-arbres dans les clés USB
00308 #     
00309 class threadDeleteInUSB(abstractThreadUSB):
00310     ##
00311     # 
00312     #         Constructeur
00313     #          Crée un thread pour supprimer une liste de fichiers dans une clé USB.
00314     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00315     #         @param fileList la liste des fichiers à supprimer
00316     #         @param subdir le sous-répertoire de la clé USB où faire les suppressions
00317     #         @param logfile un fichier de journalisation, /dev/null par défaut
00318     #         
00319     def __init__(self,ud, fileList, subdir, logfile="/dev/null"):
00320         abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=None, logfile=logfile)
00321         self.cmd=u"rm -rf '{toDel}'"
00322 
00323     ##
00324     # 
00325     #         Supprime une liste de fichiers dans une clé USB.
00326     #          La liste est prise sous un répertoire donné. Le répertoire visible
00327     #          qui dépend du constructuer d ela clé est pris en compte.
00328     #          À chaque fichier ou répertoire supprimé, une ligne est
00329     #          journalisée dans le fichier de journal de l'application.
00330     #         @param l'instance uDisk correspondant à une partition de clé USB
00331     #         @param fileList la liste des fichiers à copier
00332     #         @param dest un répertoire de destination
00333     #         @param logfile un fichier de journalisation
00334     #         @param subdir le sous-répertoire de la clé USB où faire la copie
00335     #         
00336     def toDo(self, ud, fileList, subdir, dest, logfile):
00337         global globalThreads
00338         globalThreads.push(ud, self)
00339         for f in fileList:
00340             toDel=os.path.join(ud.ensureMounted(), f)
00341             cmd=self.cmd.format(toDel=toDel)
00342             _call(cmd,logfile)
00343         globalThreads.pop(ud, self)
00344 
 Tout Classes Espaces de nommage Fichiers Fonctions Variables