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)
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)
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'))
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