Example #1
0
 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
Example #2
0
 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
Example #3
0
 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)}))
Example #4
0
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)}))
Example #5
0
 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]:
Example #6
0
                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}),