def parseCommand(self): """Parse options to retrieve the correct command""" self.options = self.getOptions(self.config) if self.options['DestinationType'] == 'remote (ssh)': prefs = config.PrefsConf(create=True) tempDir = prefs.get('Preferences', 'TempDir') or tempfile.gettempdir() self.dest = os.path.join(tempDir, os.path.split(fwbackups.escapeQuotes(self.dest, 1))[1]) if self.options['Engine'] == 'rsync': command = 'rsync -g -o -p -t -R' if self.options['Incremental']: command += ' -u --del' if self.options['Recursive']: command += ' -r' if not self.options['BackupHidden']: command += ' --exclude=.*' if self.options['Sparse']: command += ' -S' if self.options['FollowLinks']: command += ' -L' else: command += ' -l' if self.options['Excludes'] != None and self.options['Excludes'] != "": for i in self.options['Excludes'].split('\n'): command += ' --exclude="%s"' % i elif self.options['Engine'] == 'tar': command = "tar rf '%s'" % fwbackups.escapeQuotes(self.dest, 1) elif self.options['Engine'] == 'tar.gz': # DON'T rfz - Can't use r (append) and z (gzip) together command = "tar cfz '%s'" % fwbackups.escapeQuotes(self.dest, 1) elif self.options['Engine'] == 'tar.bz2': # DON'T rfz - Can't use r (append) and j (bzip2) together command = "tar cfj '%s'" % fwbackups.escapeQuotes(self.dest, 1) # -- if self.options['Engine'] in ['tar', 'tar.gz', 'tar.bz2']: # they share command options if not self.options['Recursive']: command += ' --no-recursion' if not self.options['BackupHidden']: command += ' --exclude=.*' if self.options['Sparse']: command += ' -S' if self.options['FollowLinks']: command += ' -h' if self.options['Excludes']: for i in self.options['Excludes'].split('\n'): command += ' --exclude="%s"' % i # Finally... return command
def parseCommand(self, config): """Parse options to retrieve the correct command""" self.options = self.getOptions(self.config) if self.options["DestinationType"] == "remote (ssh)": tempDir = tempfile.gettempdir() self.dest = os.path.join(tempDir, os.path.split(fwbackups.escapeQuotes(self.dest, 1))[1]) if self.options["Engine"] == "rsync": command = "rsync -g -o -p -t -R" if self.options["Incremental"]: command += " -u --del" if self.options["Recursive"]: command += " -r" if not self.options["BackupHidden"]: command += " --exclude=.*" if self.options["Sparse"]: command += " -S" if self.options["FollowLinks"]: command += " -L" else: command += " -l" if self.options["Excludes"] != None and self.options["Excludes"] != "": for i in self.options["Excludes"].split("\n"): command += ' --exclude="%s"' % i elif self.options["Engine"] == "tar": command = "tar rf '%s'" % fwbackups.escapeQuotes(self.dest, 1) elif self.options["Engine"] == "tar.gz": # DON'T rfz - Can't use r (append) and z (gzip) together command = "tar cfz '%s'" % fwbackups.escapeQuotes(self.dest, 1) elif self.options["Engine"] == "tar.bz2": # DON'T rfz - Can't use r (append) and j (bzip2) together command = "tar cfj '%s'" % fwbackups.escapeQuotes(self.dest, 1) # -- if self.options["Engine"] in ["tar", "tar.gz", "tar.bz2"]: # they share command options if not self.options["Recursive"]: command += " --no-recursion" if not self.options["BackupHidden"]: command += " --exclude=.*" if self.options["Sparse"]: command += " -S" if self.options["FollowLinks"]: command += " -h" if self.options["Excludes"]: for i in self.options["Excludes"].split("\n"): command += ' --exclude="%s"' % i # Finally... return command
def backupPaths(self, paths, command): """Does the actual copying dirty work""" # this is in common self._current = 0 if len(paths) == 0: return True self._total = len(paths) self._status = STATUS_BACKING_UP wasAnError = False if self.options['Engine'] == 'tar': if MSWINDOWS: self.logger.logmsg('INFO', _('Using %s on Windows: Cancel function will only take effect after a path has been completed.' % self.options['Engine'])) import tarfile fh = tarfile.open(self.dest, 'w') for i in paths: self.ifCancel() self._current += 1 self.logger.logmsg('DEBUG', _('Backing up path %(a)i/%(b)i: %(c)s' % {'a': self._current, 'b': self._total, 'c': i})) fh.add(i, recursive=self.options['Recursive']) fh.close() else: # not MSWINDOWS for i in paths: i = fwbackups.escapeQuotes(i, 1) self.ifCancel() self._current += 1 self.logger.logmsg('DEBUG', _("Running command: nice -n %(a)i %(b)s '%(c)s'" % {'a': self.options['Nice'], 'b': command, 'c': i})) sub = fwbackups.executeSub("nice -n %i %s '%s'" % (self.options['Nice'], command, i), env=self.environment, shell=True) self.pids.append(sub.pid) self.logger.logmsg('DEBUG', _('Starting subprocess with PID %s') % sub.pid) # track stdout errors = [] # use nonblocking I/O fl = fcntl.fcntl(sub.stderr, fcntl.F_GETFL) fcntl.fcntl(sub.stderr, fcntl.F_SETFL, fl | os.O_NONBLOCK) while sub.poll() in ["", None]: time.sleep(0.01) try: errors += sub.stderr.readline() except IOError, description: pass self.pids.remove(sub.pid) retval = sub.poll() self.logger.logmsg('DEBUG', _('Subprocess with PID %(a)s exited with status %(b)s' % {'a': sub.pid, 'b': retval})) # Something wrong? if retval != EXIT_STATUS_OK and retval != 2: wasAnError = True self.logger.logmsg('ERROR', 'An error occurred while backing up path \'%s\'.\nPlease check the error output below to determine if any files are incomplete or missing.' % str(i)) self.logger.logmsg('ERROR', _('Process exited with status %(a)s. Errors: %(b)s' % {'a': retval, 'b': ''.join(errors)}))
class BackupOperation(operations.Common): """A parent class for all backups operations.""" def __init__(self, logger=None): """Initalizes the class. If no logger is specified, a new one will be created.""" operations.Common.__init__(self, logger) def getOptions(self, conf): """Loads all the configuration options from a restore configuration file and returns them in a dictionary""" def _bool(value): return value in [1, '1', True, 'True'] options = conf.getOptions() if not options['RemotePort']: options['RemotePort'] = 22 else: options['RemotePort'] = int(options['RemotePort']) options['Nice'] = int(options['Nice']) options['RemotePassword'] = options['RemotePassword'].decode('base64') for option in ['Recursive', 'PkgListsToFile', 'DiskInfoToFile', 'BackupHidden', 'FollowLinks', 'Sparse']: options[option] = _bool(options[option]) return options def parsePaths(self, config): """Get the list of paths in the configuration file. Returns a list of paths to backup""" paths = [] for path in config.getPaths(): paths.append(path) return paths def createPkgLists(self): """Create the pkg lists in tempdir""" # Start as a dictionary so we can keep track of which items we have already # processed. See comment at retur for more info. pkgListFiles = {} for path in os.environ['PATH'].split(':'): if os.path.exists(os.path.join(path, 'rpm')) and not pkgListFiles.has_key('rpm'): fd, path = tempfile.mkstemp(suffix='.txt', prefix="%s - tmp" % _('rpm - Package list')) fh = os.fdopen(fd, "w") # try with rpm-python, but if not just execute it like the rest try: import rpm pyrpm = True except ImportError: pyrpm = False if pyrpm: ts=rpm.ts() # Equivalent to rpm -qa mi=ts.dbMatch() for hdr in mi: fh.write("%s-%s-%s.%s\n" % (hdr['name'], hdr['version'], hdr['release'], hdr['arch'])) else: retval, stdout, stderr = fwbackups.execute('rpm -qa', env=self.environment, shell=True, stdoutfd=fh) pkgListFiles['rpm'] = path fh.close() if os.path.exists(os.path.join(path, 'pacman')) and not pkgListFiles.has_key('pacman'): fd, path = tempfile.mkstemp(suffix='.txt', prefix="%s - tmp" % _('Pacman - Package list')) fh = os.fdopen(fd, 'w') retval, stdout, stderr = fwbackups.execute('pacman -Qq', env=self.environment, shell=True, stdoutfd=fh) pkgListFiles['pacman'] = path fh.close() if os.path.exists(os.path.join(path, 'dpkg')) and not pkgListFiles.has_key('dpkg'): fd, path = tempfile.mkstemp(suffix='.txt', prefix="%s - tmp" % _('dpkg - Package list')) fh = os.fdopen(fd, 'w') retval, stdout, stderr = fwbackups.execute('dpkg -l', env=self.environment, shell=True, stdoutfd=fh) pkgListFiles['dpkg'] = path fh.close() # We want to return a list of only the filenames return pkgListFiles.values() def createDiskInfo(self): """Print disk info to a file in tempdir""" fd, path = tempfile.mkstemp(suffix='.txt', prefix="%s - tmp" % _('Disk Information')) fh = os.fdopen(fd, 'w') retval, stdout, stderr = fwbackups.execute('fdisk -l', env=self.environment, shell=True, stdoutfd=fh) fh.close() return path def parseCommand(self): """Parse options to retrieve the correct command""" self.options = self.getOptions(self.config) if self.options['DestinationType'] == 'remote (ssh)': prefs = config.PrefsConf(create=True) tempDir = prefs.get('Preferences', 'TempDir') or tempfile.gettempdir() self.dest = os.path.join(tempDir, os.path.split(fwbackups.escapeQuotes(self.dest, 1))[1]) if self.options['Engine'] == 'rsync': command = 'rsync -g -o -p -t -R' if self.options['Incremental']: command += ' -u --del' if self.options['Recursive']: command += ' -r' if not self.options['BackupHidden']: command += ' --exclude=.*' if self.options['Sparse']: command += ' -S' if self.options['FollowLinks']: command += ' -L' else: command += ' -l' if self.options['Excludes'] != None and self.options['Excludes'] != "": for i in self.options['Excludes'].split('\n'): command += ' --exclude="%s"' % i elif self.options['Engine'] == 'tar': command = "tar rf '%s'" % fwbackups.escapeQuotes(self.dest, 1) elif self.options['Engine'] == 'tar.gz': # DON'T rfz - Can't use r (append) and z (gzip) together command = "tar cfz '%s'" % fwbackups.escapeQuotes(self.dest, 1) elif self.options['Engine'] == 'tar.bz2': # DON'T rfz - Can't use r (append) and j (bzip2) together command = "tar cfj '%s'" % fwbackups.escapeQuotes(self.dest, 1) # -- if self.options['Engine'] in ['tar', 'tar.gz', 'tar.bz2']: # they share command options if not self.options['Recursive']: command += ' --no-recursion' if not self.options['BackupHidden']: command += ' --exclude=.*' if self.options['Sparse']: command += ' -S' if self.options['FollowLinks']: command += ' -h' if self.options['Excludes']: for i in self.options['Excludes'].split('\n'): command += ' --exclude="%s"' % i # Finally... return command def addListFilesToBackup(self, pkgListfiles, command, engine, paths): """Adds the pkglist and diskinfo to the backup""" self.ifCancel() for file in pkgListfiles: paths.append(file) def deleteListFiles(self, pkgListfiles): """Delete the list files in the tempdir""" self.ifCancel() for file in pkgListfiles: os.remove(file) def checkRemoteServer(self): """Checks if a connection to the remote server can be established""" self.logger.logmsg('DEBUG', _('Attempting to connect to server %s...') % self.options['RemoteHost']) thread = fwbackups.runFuncAsThread(sftp.testConnection, self.options['RemoteHost'], self.options['RemoteUsername'], self.options['RemotePassword'], self.options['RemotePort'], self.options['RemoteFolder']) while thread.retval == None: time.sleep(0.1) # Check for errors, if any import paramiko import socket if thread.retval == True: self.logger.logmsg('DEBUG', _('Attempt to connect succeeded.')) return True elif type(thread.exception) == IOError: self.logger.logmsg('ERROR', _('The backup destination was either not ' + \ 'found or it cannot be written to due to insufficient permissions.')) return False elif type(thread.exception) == paramiko.AuthenticationException: self.logger.logmsg('ERROR', _('A connection was established, but authentication ' + \ 'failed. Please verify the username and password ' + \ 'and try again.')) return False elif type(thread.exception) == socket.gaierror or type(thread.exception) == socket.error: self.logger.logmsg('ERROR', _('A connection to the server could not be established.\n' + \ 'Error %(a)s: %(b)s' % {'a': type(thread.exception), 'b': str(thread.exception)} + \ '\nPlease verify your settings and try again.')) return False elif type(thread.exception) == socket.timeout: self.logger.logmsg('ERROR', _('A connection to the server has timed out. ' + \ 'Please verify your settings and try again.')) return False elif type(thread.exception) == paramiko.SSHException: self.logger.logmsg('ERROR', _('A connection to the server could not be established ' + \ 'because an error occurred: %s' % str(thread.exception) + \ '\nPlease verify your settings and try again.')) return False else: # not remote, just pass return True def backupPaths(self, paths, command): """Does the actual copying dirty work""" # this is in common self._current = 0 if len(paths) == 0: return True self._total = len(paths) self._status = STATUS_BACKING_UP wasAnError = False if self.options['Engine'] == 'tar': if MSWINDOWS: self.logger.logmsg('INFO', _('Using %s on Windows: Cancel function will only take effect after a path has been completed.' % self.options['Engine'])) import tarfile fh = tarfile.open(self.dest, 'w') for i in paths: self.ifCancel() self._current += 1 self.logger.logmsg('DEBUG', _('Backing up path %(a)i/%(b)i: %(c)s' % {'a': self._current, 'b': self._total, 'c': i})) fh.add(i, recursive=self.options['Recursive']) fh.close() else: # not MSWINDOWS for i in paths: i = fwbackups.escapeQuotes(i, 1) self.ifCancel() self._current += 1 self.logger.logmsg('DEBUG', _("Running command: nice -n %(a)i %(b)s '%(c)s'" % {'a': self.options['Nice'], 'b': command, 'c': i})) sub = fwbackups.executeSub("nice -n %i %s '%s'" % (self.options['Nice'], command, i), env=self.environment, shell=True) self.pids.append(sub.pid) self.logger.logmsg('DEBUG', _('Starting subprocess with PID %s') % sub.pid) # track stdout errors = [] # use nonblocking I/O fl = fcntl.fcntl(sub.stderr, fcntl.F_GETFL) fcntl.fcntl(sub.stderr, fcntl.F_SETFL, fl | os.O_NONBLOCK) while sub.poll() in ["", None]: time.sleep(0.01) try: errors += sub.stderr.readline() except IOError, description: pass self.pids.remove(sub.pid) retval = sub.poll() self.logger.logmsg('DEBUG', _('Subprocess with PID %(a)s exited with status %(b)s' % {'a': sub.pid, 'b': retval})) # Something wrong? if retval != EXIT_STATUS_OK and retval != 2: wasAnError = True self.logger.logmsg('ERROR', 'An error occurred while backing up path \'%s\'.\nPlease check the error output below to determine if any files are incomplete or missing.' % str(i)) self.logger.logmsg('ERROR', _('Process exited with status %(a)s. Errors: %(b)s' % {'a': retval, 'b': ''.join(errors)})) elif self.options['Engine'] == 'tar.gz': self._total = 1 if MSWINDOWS: self.logger.logmsg('INFO', _('Using %s on Windows: Cancel function will only take effect after a path has been completed.' % self.options['Engine'])) import tarfile fh = tarfile.open(self.dest, 'w:gz') for i in paths: self._current += 1 self.ifCancel() self.logger.logmsg('DEBUG', _('Backing up path %(a)i/%(b)i: %(c)s' % {'a': self._current, 'b': self._total, 'c': i})) fh.add(i, recursive=self.options['Recursive']) self.logger.logmsg('DEBUG', _('Adding path `%s\' to the archive' % i)) fh.close() else: # not MSWINDOWS self._current = 1 escapedPaths = [fwbackups.escapeQuotes(i, 1) for i in paths] # This is a fancy way for getting i = "'one' 'two' 'three'" i = "'%s'" % "' '".join(escapedPaths) self.logger.logmsg('INFO', _('Using %s: Must backup all paths at once - Progress notification will be disabled.' % self.options['Engine'])) self.logger.logmsg('DEBUG', _('Backing up path %(a)i/%(b)i: %(c)s') % {'a': self._current, 'b': self._total, 'c': i.replace("'", '')}) self.logger.logmsg('DEBUG', _("Running command: nice -n %(a)i %(b)s %(c)s" % {'a': self.options['Nice'], 'b': command, 'c': i})) # Don't wrap i in quotes; we did this above already when mering the paths sub = fwbackups.executeSub("nice -n %i %s %s" % (self.options['Nice'], command, i), env=self.environment, shell=True) self.pids.append(sub.pid) self.logger.logmsg('DEBUG', _('Starting subprocess with PID %s') % sub.pid) # track stdout errors = [] # use nonblocking I/O fl = fcntl.fcntl(sub.stderr, fcntl.F_GETFL) fcntl.fcntl(sub.stderr, fcntl.F_SETFL, fl | os.O_NONBLOCK) while sub.poll() in ["", None]: time.sleep(0.01) try: errors += sub.stderr.readline() except IOError, description: pass self.pids.remove(sub.pid) retval = sub.poll() self.logger.logmsg('DEBUG', _('Subprocess with PID %(a)s exited with status %(b)s' % {'a': sub.pid, 'b': retval})) # Something wrong? if retval != EXIT_STATUS_OK and retval != 2: wasAnError = True self.logger.logmsg('ERROR', 'An error occurred while backing up path \'%s\'.\nPlease check the error output below to determine if any files are incomplete or missing.' % str(i)) self.logger.logmsg('ERROR', _('Process exited with status %(a)s. Errors: %(b)s' % {'a': retval, 'b': ''.join(errors)}))
elif self.options['Engine'] == 'tar.bz2': self._total = 1 if MSWINDOWS: self.logger.logmsg('INFO', _('Using %s on Windows: Cancel function will only take effect after a path has been completed.' % self.options['Engine'])) import tarfile fh = tarfile.open(self.dest, 'w:bz2') for i in paths: self._current += 1 self.ifCancel() self.logger.logmsg('DEBUG', _('Backing up path %(a)i/%(b)i: %(c)s' % {'a': self._current, 'b': self._total, 'c': i})) fh.add(i, recursive=self.options['Recursive']) self.logger.logmsg('DEBUG', _('Adding path `%s\' to the archive' % i)) fh.close() else: # not MSWINDOWS self._current = 1 escapedPaths = [fwbackups.escapeQuotes(i, 1) for i in paths] # This is a fancy way for getting i = "'one' 'two' 'three'" i = "'%s'" % "' '".join(escapedPaths) self.logger.logmsg('INFO', _('Using %s: Must backup all paths at once - Progress notification will be disabled.' % self.options['Engine'])) self.logger.logmsg('DEBUG', _('Backing up path %(a)i/%(b)i: %(c)s') % {'a': self._current, 'b': self._total, 'c': i}) self.logger.logmsg('DEBUG', _("Running command: nice -n %(a)i %(b)s %(c)s" % {'a': self.options['Nice'], 'b': command, 'c': i})) # Don't wrap i in quotes; we did this above already when mering the paths sub = fwbackups.executeSub("nice -n %i %s %s" % (self.options['Nice'], command, i), env=self.environment, shell=True) self.pids.append(sub.pid) self.logger.logmsg('DEBUG', _('Starting subprocess with PID %s') % sub.pid) # track stdout errors = [] # use nonblocking I/O fl = fcntl.fcntl(sub.stderr, fcntl.F_GETFL) fcntl.fcntl(sub.stderr, fcntl.F_SETFL, fl | os.O_NONBLOCK) while sub.poll() in ["", None]:
import tarfile fh = tarfile.open(self.dest, "w:bz2") for i in paths: self._current += 1 self.ifCancel() self.logger.logmsg( "DEBUG", _("Backing up path %(a)i/%(b)i: %(c)s" % {"a": self._current, "b": self._total, "c": i}), ) fh.add(i, recursive=self.options["Recursive"]) self.logger.logmsg("DEBUG", _("Adding path `%s' to the archive" % i)) fh.close() else: # not MSWINDOWS self._current = 1 escapedPaths = [fwbackups.escapeQuotes(i, 1) for i in paths] # This is a fancy way for getting i = "'one' 'two' 'three'" i = "'%s'" % "' '".join(escapedPaths) self.logger.logmsg( "INFO", _( "Using %s: Must backup all paths at once - Progress notification will be disabled." % self.options["Engine"] ), ) self.logger.logmsg( "DEBUG", _("Backing up path %(a)i/%(b)i: %(c)s") % {"a": self._current, "b": self._total, "c": i} ) self.logger.logmsg( "DEBUG", _("Running command: nice -n %(a)i %(b)s %(c)s" % {"a": self.options["Nice"], "b": command, "c": i}),