1 # low_backup_manager module
2 # Authors: Nicholas Alexander && Otacilio Lacerda
3 # Backup_manager class:
4 # Class responsible for backup functions like creating and removing backups
11 from PyQt4.QtCore import *
14 from pcsbackupparser import *
15 from pcsbackupinfo import *
16 from pcsbackupmanager import *
17 from pcsbackupxml import *
19 import pcsbackuputils as utils
21 HOME = os.path.expanduser("~")
22 BACKUP_FILES_PATH = os.path.join(HOME, ".pcsuite/Backup")
23 DEFAULT_CONFIG_PATH = "%s/.pcsuite/config" % HOME
24 BACKUPS_FILE = os.path.join(HOME, ".pcsuite/Backup/.backups")
27 class PcsPcBackupManager(PcsBackupManager):
30 PcsBackupManager.__init__(self)
32 self.backupInProgress = False
33 self.restoreInProgress = False
34 self.copyInProgress = False
36 self.myDocsPath = "Root/home/user/MyDocs/"
38 self.currentCategory = ""
41 def loadBackups(self):
42 # 'XX Loading the backup list available in the PC'
44 if os.path.exists(BACKUPS_FILE):
45 file = open(BACKUPS_FILE)
46 self._backupList = pickle.load(file)
49 print "IOError while loading the PC backups"
51 #raise Exception("Error while reading backups")
56 def saveBackups(self):
57 # 'XX Saving the backup list in the config file'
59 obj = self._backupList
60 file = open(BACKUPS_FILE, "w")
61 pickle.dump(obj, file)
64 #raise Exception("Error while saving backups")
69 def getBackupList(self):
70 """Return a list with the name of all done backups. """
72 return self._backupList[:]
74 def createBackup(self, backup_name, path, host_ip, categories, comment=""):
75 self.backupThread = NewBackupThread(self, backup_name, path, host_ip,
77 self.backupThread.start()
80 def renameBackup(self, backupName, newName):
83 backupInfo = self.getBackupInfo(backupName)
84 if backupInfo != None:
86 old = os.path.join(str(backupInfo.getPath()), str(backupName))
87 new = os.path.join(str(backupInfo.getPath()), str(newName))
89 backupInfo.setName(newName)
91 print "Error while changing backup name"
100 def removeBackup(self, backupName):
102 Remove a backup from pc and from _backupList.
105 backupInfo = self.getBackupInfo(backupName)
106 completePath = os.path.join(str(backupInfo.getPath()),
107 str(backupInfo.getName()))
108 if os.path.exists(completePath):
109 utils.removePath(completePath)
110 self._backupList.remove(backupInfo)
115 def copyBackupToDevice(self, deviceIp, backupName, memoryStick):
116 """ Copy a backup in the PC to some memory card inside the chosen device.
119 String deviceIp - String with ip address of device.
120 String backupName - Name of backup to be copied.
121 Int memoryStick - Integer representing which memory stick backup
123 0 - Removable memory stick
124 1 - Internal memory stick
128 mountPoint = os.path.join(DEVICES_POINT, "%s/Root" % deviceIp)
129 utils.mountDevice(USER_HOST, deviceIp, mountPoint)
131 backup = self.getBackupInfo(backupName)
132 backupPath = os.path.join(str(backup.getPath()), str(backupName))
133 if memoryStick != 0 and memoryStick != 1:
135 destination = os.path.join(mountPoint, 'media/mmc%s' %
136 (memoryStick+1), 'backups', backupName)
138 self.setCopyInProgress(True)
139 if self.copy(backupPath, destination) == 0:
141 self.setCopyInProgress(False)
143 utils.unmountDevice(mountPoint)
146 def getBackupInfo(self, backupName):
147 for backupInfo in self._backupList:
148 if backupInfo.getName() == backupName:
152 def startBackupRestore(self, backupInfo, device_ip, categories):
153 return self.restoreBackup(backupInfo, device_ip, categories)
155 def setCopyInProgress(self, status):
156 self.copyInProgress = status
158 def setBackupInProgress(self, status):
159 self.backupInProgress = status
162 def setRestoreInProgress(self, status):
163 self.restoreInProgress = status
165 # FIXME: rewrite this method. Add error handling, some more reliable code
166 def _runCreateBackup(self, backup_name, path, host_ip, categories, comment=""):
167 """Create a backup and add it to _backupList.
169 Backup all files and directories of categories chosen. The device
170 system file is mounted on PC and them files are compressed in a zip
171 format. All zip files from each category are saved in the given path.
174 backup_name -- Backup's name. This name will be the name of the backup
176 path -- Location to save Backup file
177 host_ip -- The device IP address.
178 categories -- A python dictonary where the keys are the categories from
179 osso-backup, and the value can be either True or False, to identify
180 which categories to backup.
181 comment -- Any comment about the backup. It will be saved in Backup
187 self.setBackupInProgress(True)
188 self.currentCategory = ""
191 # Mount device folders
193 mountPoint = os.path.join(DEVICES_POINT, "%s/Root" % host_ip)
194 utils.mountDevice(USER_HOST, host_ip, mountPoint)
196 # Create backup folder in the given path
197 self.backupPath = os.path.join(str(path), backup_name)
198 self._createBackupFolder(self.backupPath)
200 # Copying osso-backup configuration files to TabletSuite folder and
201 # parsing backup information from these files.
202 # ( Information = which paths should be in each backup category)
203 utils.copyOssoBackupConfigFiles(DEFAULT_CONFIG_PATH, mountPoint)
204 xmlParser = PcsBackupParser()
205 xmlParser.fillLocationsDict(DEFAULT_CONFIG_PATH)
206 backupItemDict = xmlParser.getLocationsDict()
208 # Changing work directory to ease file search. Keeping previous
209 # folder to come back after backup process.
210 current_dir = os.getcwd()
211 workPath = DEVICES_POINT + str(host_ip)
214 # Computing total size that this backup will have.
215 self.totalSize = self._getTotalSize(backupItemDict, categories)
218 # Start backup process. Create backup of paths for each locations
220 self.backedUpNumber = 0
222 for category in backupItemDict.keys():
223 self.currentCategory = category
225 for backupItem in backupItemDict[category]:
227 # If current backupInfo category is valid and it's value is
228 # True in categories dictionary, add it to current backup.
229 if (category in categories) and (categories[category]):
230 categoryFilesNumber = 0
231 categoryFile = os.path.join(self.backupPath,
234 if os.path.exists(categoryFile):
235 zipfile = utils.openZip(categoryFile, 'a')
237 zipfile = utils.openZip(categoryFile, 'w')
239 objPath = backupItem.path.replace('/','Root/',1)
240 backupFolder = os.path.join(workPath, objPath)
241 categoryFilesNumber = self.zipFolder(backupFolder,
244 if categoryFilesNumber == -1:
245 os.chdir(current_dir)
247 self.backedUpNumber += categoryFilesNumber
250 # Update the filesByCategory dictionary with the
251 # current number of files from current category that
252 # were already copied.
253 self._updateCategoryCount(filesByCategory, category,
255 utils.closeZip(zipfile)
256 # If category had no file to copy remove its folder
257 if int(utils.getSize(categoryFile) == 0):
258 os.remove(categoryFile)
261 # Copying media files from device user folder if media category is
262 # True in categories dictionary.
263 if ("media" in categories) and (categories["media"]):
264 self.currentCategory = "media"
265 result = self._backupMedias(workPath)
267 os.chdir(current_dir)
269 # Update backup files number count
270 self.backedUpNumber += result
271 self._updateCategoryCount(filesByCategory, "media", result)
272 zipPath = os.path.join(self.backupPath,'media.zip')
273 if int(utils.getSize(zipPath) == 0):
277 # Copying documents files from device user folder if documents
278 # category is True in categories dictionary.
279 if ("documents" in categories) and (categories["documents"]):
280 self.currentCategory = "documents"
281 result = self._backupDocuments(workPath)
282 # Update backup files number count
283 self.backedUpNumber += result
285 os.chdir(current_dir)
287 self._updateCategoryCount(filesByCategory, "documents",
289 zipPath = os.path.join(self.backupPath, 'documents.zip')
290 if int(utils.getSize(zipPath) == 0):
294 # Change to previous work directory
295 os.chdir(current_dir)
297 if self.backedUpNumber == 0:
298 utils.removePath(self.backupPath)
301 # Create Backup Object, add it to this manager backup list and save
302 # the list in the backup file.
303 backup_size = utils.getSize(self.backupPath)
304 backupInfo = PcsBackupInfo(backup_name, path, backup_size, comment)
305 backupInfo.setFilesNumber(self.backedUpNumber)
306 self._backupList.append(backupInfo)
309 createXml(backupInfo, filesByCategory, host_ip)
310 self.setBackupInProgress(False)
312 utils.unmountDevice(mountPoint)
317 def _backupDocuments(self, workPath):
318 """ Create backup of documents files in user folder. """
319 categoryFilesNumber = 0
320 destinationPath = os.path.join(self.backupPath,'documents.zip')
321 if os.path.exists(destinationPath):
322 zipfile = utils.openZip(destinationPath, 'a')
324 zipfile = utils.openZip(destinationPath, 'w')
326 docsPath = os.path.join(self.myDocsPath, ".documents")
328 if os.path.exists(docsPath):
329 backupPath = os.path.join(workPath, docsPath)
330 categoryFilesNumber = self.zipFolder(backupPath, zipfile, docsPath,
333 utils.closeZip(zipfile)
334 return categoryFilesNumber
337 def _backupMedias(self, workPath):
338 """ Create backup of media files in user folder. """
339 categoryFilesNumber = 0
340 destinationPath = os.path.join(self.backupPath,'media.zip')
341 if os.path.exists(destinationPath):
342 zipfile = utils.openZip(destinationPath, 'a')
344 zipfile = utils.openZip(destinationPath, 'w')
346 userFilesPath = self.myDocsPath
347 if os.path.exists(os.path.join(userFilesPath)):
348 for folder in os.listdir(userFilesPath):
349 if folder != '.documents':
350 objPath = os.path.join(userFilesPath, folder)
351 backupDir = os.path.join(workPath, objPath)
352 result = self.zipFolder(backupDir, zipfile,
355 categoryFilesNumber += result
359 utils.closeZip(zipfile)
360 return categoryFilesNumber
363 def _createBackupFolder(self, newBackupPath):
364 if not utils.createFolder(newBackupPath):
368 def _emitProgress(self):
369 currentSize = utils.getSize(self.backupPath)
370 percentage = self.computePercentage(self.totalSize, currentSize)
371 self.emit(SIGNAL("backupProgress"), (percentage, self.currentCategory))
374 def _getTotalSize(self, backupFileDict, categories):
376 for category in backupFileDict.keys():
377 for backupItem in backupFileDict[category]:
378 if (category in categories) and (categories[category]):
379 objPath = backupItem.path.replace('/','Root/',1)
380 size += utils.getSize(objPath)
382 if categories["documents"]:
383 size += utils.getSize(os.path.join(self.myDocsPath,
385 if categories["media"]:
386 for folder in os.listdir(self.myDocsPath):
387 if folder != '.documents':
388 objPath = os.path.join(self.myDocsPath, folder)
389 size += utils.getSize(objPath)
392 def _updateCategoryCount(self, filesByCategory, category, backed_up_number):
393 if str(category) not in filesByCategory.keys():
394 filesByCategory[category] = backed_up_number
396 filesByCategory[category] += backed_up_number
399 def _verify_backup_name(self, backup_name):
400 """ Check if already exists any backup with the given name inside the
401 pc backup list. In case there is one, this function will return another
402 name to be set as the new backup name, else it will return the same
406 if self.getBackupInfo(backup_name) != None:
408 new_name = backup_name + "%02d" % counter
409 while(self.getBackupInfo(new_name)) != None:
411 new_name = backup_name + "%02d" % counter
413 backup_name = new_name
416 def zipFolder(self, path, zipfile, obj_path, category):
417 # Compress the folder from the given path, with all directories and
419 # zipfile: The ZipFile object to append the folder
420 # obj_path: The ZipFile path
423 if os.path.exists(path):
424 if os.path.isdir(path):
425 files = os.listdir(path)
427 if os.path.isdir(os.path.join(path, node)):
428 zipCount = self.zipFolder(os.path.join(path, node), zipfile,
429 os.path.join(obj_path, node), category)
435 # Check if backup was canceled and return -1 case positive
436 if not self.backupInProgress:
437 utils.removePath(self.backupPath)
440 if utils.zip(zipfile, os.path.join(obj_path, node)):
444 # Check if backup was canceled and return -1 case positive
445 if not self.backupInProgress:
446 utils.removePath(self.backupPath)
449 if utils.zip(zipfile, obj_path):
456 class NewBackupThread(QThread):
457 def __init__(self, manager, backupName, path, hostIp, categories, comment):
458 QThread.__init__(self)
460 self.manager = manager
461 self.backupName = backupName
464 self.categories = categories
465 self.comment = comment
468 ret = self.manager._runCreateBackup (self.backupName, self.path,
469 self.hostIp, self.categories,
471 self.manager.emit(SIGNAL("backupDone"), ret,
472 (self.manager.totalSize, self.manager.backedUpNumber))