基于ceph的cinder backup功能代码分析

1、cinder/backup/api.py 239 create687

def create(self, context, name, description, volume_id,
           container, incremental=False, availability_zone=None,
           force=False, snapshot_id=None):

检查磁盘状态,磁盘快照以及快照状态以及in-use时是否加了force,检查backup配额,由于我们使用的是ceph的driver,获取dirver的过程就不看了主要看看实现的地方
2、cinder/backup/drivers/ceph.py 840 backup函数

def backup(self, backup, volume_file, backup_metadata=True):
        """Backup volume and metadata (if available) to Ceph object store.

        If the source volume is an RBD we will attempt to do an
        incremental/differential backup, otherwise a full copy is performed.
        If this fails we will attempt to fall back to full copy.
        """
        backup_id = backup['id']
        volume = self.db.volume_get(self.context, backup['volume_id'])
        volume_id = volume['id']
        volume_name = volume['name']

        LOG.debug("Starting backup of volume='%s'.", volume_id)

        # Ensure we are at the beginning of the volume
        volume_file.seek(0)
        length = self._get_volume_size_gb(volume)

        do_full_backup = False
        if self._file_is_rbd(volume_file):
            # If volume an RBD, attempt incremental backup.
            try:
                self._backup_rbd(backup_id, volume_id, volume_file,
                                 volume_name, length)
            except exception.BackupRBDOperationFailed:
                LOG.debug("Forcing full backup of volume %s.", volume_id)
                do_full_backup = True
        else:
            do_full_backup = True

        if do_full_backup:
            self._full_backup(backup_id, volume_id, volume_file,
                              volume_name, length)

        backup.container = self._ceph_backup_pool
        backup.save()

        if backup_metadata:
            try:
                self._backup_metadata(backup)
            except exception.BackupOperationError:
                with excutils.save_and_reraise_exception():
                    # Cleanup.
                    self.delete(backup)

        LOG.debug("Backup '%(backup_id)s' of volume %(volume_id)s finished.",
                  {'backup_id': backup_id, 'volume_id': volume_id})

3、cinder/backup/drivers/ceph.py 589 _backup_rbd函数

    def _backup_rbd(self, backup_id, volume_id, volume_file, volume_name,
                    length):
        """Create an 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 '%(snap)s' for incremental backup of "
                  "volume %(volume)s.",
                  {'snap': from_snap, 'volume': volume_id})

        base_name = self._get_backup_base_name(volume_id, diff_format=True)
        image_created = False
        with rbd_driver.RADOSClient(self, self._ceph_backup_pool) as client:
            # If from_snap does not exist at the destination (and the
            # destination exists), 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 base_name not in self.rbd.RBD().list(ioctx=client.ioctx):
                # If a from_snap is defined but the base does not exist, we
                # ignore it since it is stale and waiting to be cleaned up.
                if from_snap:
                    LOG.debug("Source snapshot '%(snapshot)s' of volume "
                              "%(volume)s is stale so deleting.",
                              {'snapshot': from_snap, 'volume': volume_id})
                    source_rbd_image.remove_snap(from_snap)
                    from_snap = None

                # Create new base image
                #创建基础镜像用于后期数据拷贝过来
                self._create_base_image(base_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(base_name, from_snap, client):
                    errmsg = (_("Snapshot='%(snap)s' does not exist in base "
                                "image='%(base)s' - aborting incremental "
                                "backup") %
                              {'snap': from_snap, 'base': base_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, base_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:
            with excutils.save_and_reraise_exception():
                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=base_name)

                # Delete snapshot
                LOG.debug("Deleting diff backup snapshot='%(snapshot)s' of "
                          "source volume='%(volume)s'.",
                          {'snapshot': new_snap, 'volume': volume_id})
                source_rbd_image.remove_snap(new_snap)

4、cinder/backup/drivers/ceph.py 515 _rbd_diff_transfer函数
这里才是真正干活的地方

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 = utils.convert_str("%s/%s@%s"
                                     % (src_pool, src_name, src_snap))
        else:
            path = utils.convert_str("%s/%s" % (src_pool, src_name))
        cmd1.extend([path, '-'])

        cmd2 = ['rbd', 'import-diff'] + dest_ceph_args
        rbd_path = utils.convert_str("%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)

其实基于ceph后端的cinder backup最终重要的是两个三个地方:
1、对volume先做一个全量拷贝
2、对volume的拷贝做快照

 rbd create voulmes/test
 rbd snap create volumes/test@snap

3、在快照的基础上做增量快照

导出增量部分
rbd export-diff volumes/test@snap snap1
创建一个新的镜像
rbd create backup/volume-$volume-id.backup.base
导入增量部分
rbd import-diff snap1 backup/volume-$volume-id.backup.base

完成上述步骤即可完成增量导入

你可能感兴趣的:(openstack,python,cinder)