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

def backup(self, backup, volume_file, backup_metadata=True):

    volume = self.db.volume_get(self.context, backup.volume_id)
    updates = {}
    if (not backup.container):
        backup.container = self._ceph_backup_pool
        backup.save()
    LOG.debug("Starting    backup    of    volume='%s'.", volume.id)
    volume_file.seek(0)
    length = self._get_volume_size_gb(volume)
    do_full_backup = False
    if self._file_is_rbd(volume_file):
        LOG.debug('Volume    file    is    RBD:    attempting    incremental    backup.')
        try:
            updates = self._backup_rbd(backup, volume_file, volume.name, length)
        except exception.BackupRBDOperationFailed:
            LOG.debug('Forcing    full    backup    of    volume    %s.', volume.id)
            do_full_backup = True
    else:
        LOG.debug('Volume    file    is    NOT    RBD:    will    do    full    backup.')
        do_full_backup = True
    if do_full_backup:
        try:
            self._full_backup(backup, volume_file, volume.name, length)
        except exception.BackupOperationError:
            with excutils.save_and_reraise_exception():
                self.delete_backup(backup)
    if backup_metadata:
        try:
            self._backup_metadata(backup)
        except exception.BackupOperationError:
            with excutils.save_and_reraise_exception():
                self.delete_backup(backup)
    LOG.debug("Backup    '%(backup_id)s'    of    volume    %(volume_id)s    finished.", {'backup_id': backup.id, 'volume_id': volume.id})
    if ((not updates) and backup.parent_id):
        updates = {'parent_id': None}
    return updates
