Пример #1
0
    def _rbd_diff_transfer(self,
                           src_name,
                           src_pool,
                           dest_name,
                           dest_pool,
                           src_user,
                           src_conf,
                           dest_user,
                           dest_conf,
                           src_snap=None,
                           from_snap=None):
        """Copy only extents changed between two points.

        If no snapshot is provided, the diff extents will be all those changed
        since the rbd volume/base was created, otherwise it will be those
        changed since the snapshot was created.
        """
        LOG.debug(
            _("performing differential transfer from '%(src)s' to "
              "'%(dest)s'") % {
                  'src': src_name,
                  'dest': dest_name
              })

        # NOTE(dosaboy): Need to be tolerant of clusters/clients that do
        # not support these operations since at the time of writing they
        # were very new.

        src_ceph_args = self._ceph_args(src_user, src_conf, pool=src_pool)
        dest_ceph_args = self._ceph_args(dest_user, dest_conf, pool=dest_pool)

        cmd = ['rbd', 'export-diff'] + src_ceph_args
        if from_snap is not None:
            cmd.extend(['--from-snap', from_snap])
        if src_snap:
            path = self._utf8("%s/%s@%s" % (src_pool, src_name, src_snap))
        else:
            path = self._utf8("%s/%s" % (src_pool, src_name))
        cmd.extend([path, '-'])
        try:
            out, err = self._execute(*cmd)
        except (processutils.ProcessExecutionError,
                processutils.UnknownArgumentError) as exc:
            msg = _("rbd export-diff failed - %s") % (str(exc))
            LOG.info(msg)
            raise exception.BackupRBDOperationFailed(msg)

        cmd = ['rbd', 'import-diff'] + dest_ceph_args
        cmd.extend(['-', self._utf8("%s/%s" % (dest_pool, dest_name))])
        try:
            out, err = self._execute(*cmd, process_input=out)
        except (processutils.ProcessExecutionError,
                processutils.UnknownArgumentError) as exc:
            msg = _("rbd import-diff failed - %s") % (str(exc))
            LOG.info(msg)
            raise exception.BackupRBDOperationFailed(msg)
Пример #2
0
    def _rbd_diff_transfer(self,
                           src_name,
                           src_pool,
                           dest_name,
                           dest_pool,
                           src_user,
                           src_conf,
                           dest_user,
                           dest_conf,
                           src_snap=None,
                           from_snap=None):
        """Copy only extents changed between two points.

        If no snapshot is provided, the diff extents will be all those changed
        since the rbd volume/base was created, otherwise it will be those
        changed since the snapshot was created.
        """
        LOG.debug(
            _("Performing differential transfer from '%(src)s' to "
              "'%(dest)s'") % {
                  'src': src_name,
                  'dest': dest_name
              })

        # NOTE(dosaboy): Need to be tolerant of clusters/clients that do
        # not support these operations since at the time of writing they
        # were very new.

        src_ceph_args = self._ceph_args(src_user, src_conf, pool=src_pool)
        dest_ceph_args = self._ceph_args(dest_user, dest_conf, pool=dest_pool)

        cmd1 = ['rbd', 'export-diff'] + src_ceph_args
        if from_snap is not None:
            cmd1.extend(['--from-snap', from_snap])
        if src_snap:
            path = strutils.safe_encode("%s/%s@%s" %
                                        (src_pool, src_name, src_snap))
        else:
            path = strutils.safe_encode("%s/%s" % (src_pool, src_name))

        cmd1.extend([path, '-'])

        cmd2 = ['rbd', 'import-diff'] + dest_ceph_args
        rbd_path = strutils.safe_encode("%s/%s" % (dest_pool, dest_name))
        cmd2.extend(['-', rbd_path])

        ret, stderr = self._piped_execute(cmd1, cmd2)
        if ret:
            msg = (_("RBD diff op failed - (ret=%(ret)s stderr=%(stderr)s)") %
                   ({
                       'ret': ret,
                       'stderr': stderr
                   }))
            LOG.info(msg)
            raise exception.BackupRBDOperationFailed(msg)
Пример #3
0
 def mock_rbd_diff_transfer_side_effect(src_name, src_pool,
                                        dest_name, dest_pool,
                                        src_user, src_conf,
                                        dest_user, dest_conf,
                                        src_snap, from_snap):
     raise exception.BackupRBDOperationFailed(_('mock'))
Пример #4
0
    def _backup_rbd(self, backup_id, volume_id, volume_file, volume_name,
                    length):
        """Create a incremental backup from an RBD image."""
        rbd_user = volume_file.rbd_user
        rbd_pool = volume_file.rbd_pool
        rbd_conf = volume_file.rbd_conf
        source_rbd_image = volume_file.rbd_image

        # Identify our --from-snap point (if one exists)
        from_snap = self._get_most_recent_snap(source_rbd_image)
        LOG.debug(_("using --from-snap '%s'") % from_snap)

        backup_name = self._get_backup_base_name(volume_id, diff_format=True)
        image_created = False
        force_full_backup = False
        with drivers.rbd.RADOSClient(self, self._ceph_backup_pool) as client:
            # If from_snap does not exist in the destination, this implies a
            # previous backup has failed. In this case we will force a full
            # backup.
            #
            # TODO(dosaboy): find a way to repair the broken backup
            #
            if backup_name not in self.rbd.RBD().list(ioctx=client.ioctx):
                # If a from_snap is defined then we cannot proceed (see above)
                if from_snap is not None:
                    force_full_backup = True

                # Create new base image
                self._create_base_image(backup_name, length, client)
                image_created = True
            else:
                # If a from_snap is defined but does not exist in the back base
                # then we cannot proceed (see above)
                if not self._snap_exists(backup_name, from_snap, client):
                    force_full_backup = True

        if force_full_backup:
            errmsg = (_("snap='%(snap)s' does not exist in base "
                        "image='%(base)s' - aborting incremental backup") % {
                            'snap': from_snap,
                            'base': backup_name
                        })
            LOG.info(errmsg)
            # Raise this exception so that caller can try another
            # approach
            raise exception.BackupRBDOperationFailed(errmsg)

        # Snapshot source volume so that we have a new point-in-time
        new_snap = self._get_new_snap_name(backup_id)
        LOG.debug(_("creating backup snapshot='%s'") % (new_snap))
        source_rbd_image.create_snap(new_snap)

        # Attempt differential backup. If this fails, perhaps because librbd
        # or Ceph cluster version does not support it, do a full backup
        # instead.
        #
        # TODO(dosaboy): find a way to determine if the operation is supported
        #                rather than brute force approach.
        try:
            before = time.time()
            self._rbd_diff_transfer(volume_name,
                                    rbd_pool,
                                    backup_name,
                                    self._ceph_backup_pool,
                                    src_user=rbd_user,
                                    src_conf=rbd_conf,
                                    dest_user=self._ceph_backup_user,
                                    dest_conf=self._ceph_backup_conf,
                                    src_snap=new_snap,
                                    from_snap=from_snap)

            LOG.debug(
                _("differential backup transfer completed in %.4fs") %
                (time.time() - before))

            # We don't need the previous snapshot (if there was one) anymore so
            # delete it.
            if from_snap:
                source_rbd_image.remove_snap(from_snap)

        except exception.BackupRBDOperationFailed:
            LOG.debug(_("differential backup transfer failed"))

            # Clean up if image was created as part of this operation
            if image_created:
                self._try_delete_base_image(backup_id,
                                            volume_id,
                                            base_name=backup_name)

            # Delete snapshot
            LOG.debug(_("deleting backup snapshot='%s'") % (new_snap))
            source_rbd_image.remove_snap(new_snap)

            # Re-raise the exception so that caller can try another approach
            raise