def _full_backup(self, backup_id, volume_id, src_volume, src_name, length): """Perform a full backup of src volume. First creates a base backup image in our backup location then performs an chunked copy of all data from source volume to a new backup rbd image. """ backup_name = self._get_backup_base_name(volume_id, backup_id) with rbd_driver.RADOSClient(self, self._ceph_backup_pool) as client: # First create base backup image old_format, features = self._get_rbd_support() LOG.debug(_("Creating base image='%s'") % (backup_name)) self.rbd.RBD().create(ioctx=client.ioctx, name=backup_name, size=length, old_format=old_format, features=features, stripe_unit=self.rbd_stripe_unit, stripe_count=self.rbd_stripe_count) LOG.debug(_("Copying data")) dest_rbd = self.rbd.Image(client.ioctx, backup_name) try: rbd_meta = rbd_driver.RBDImageMetadata(dest_rbd, self._ceph_backup_pool, self._ceph_backup_user, self._ceph_backup_conf) rbd_fd = rbd_driver.RBDImageIOWrapper(rbd_meta) self._transfer_data(src_volume, src_name, rbd_fd, backup_name, length) finally: dest_rbd.close()
def _full_restore(self, backup_id, volume_id, dest_file, dest_name, length, src_snap=None): """Restore volume using full copy i.e. all extents. This will result in all extents being copied from source to destination. """ with rbd_driver.RADOSClient(self, self._ceph_backup_pool) as client: # If a source snapshot is provided we assume the base is diff # format. if src_snap: diff_format = True else: diff_format = False backup_name = self._get_backup_base_name(volume_id, backup_id=backup_id, diff_format=diff_format) # Retrieve backup volume src_rbd = self.rbd.Image(client.ioctx, backup_name, snapshot=src_snap, read_only=True) try: rbd_meta = rbd_driver.RBDImageMetadata(src_rbd, self._ceph_backup_pool, self._ceph_backup_user, self._ceph_backup_conf) rbd_fd = rbd_driver.RBDImageIOWrapper(rbd_meta) self._transfer_data(rbd_fd, backup_name, dest_file, dest_name, length) finally: src_rbd.close()
def test_backup_volume_from_rbd_fail2(self, mock_popen, mock_fnctl): """Test of when an exception occurs in an exception handler. In backup(), after an exception.BackupOperationError occurs in self._backup_metadata(), we want to check the process when the second exception occurs in self.delete(). """ backup_name = self.service._get_backup_base_name(self.backup_id, diff_format=True) def mock_write_data(): self.volume_file.seek(0) data = self.volume_file.read(self.data_length) self.callstack.append('write') checksum.update(data) test_file.write(data) def mock_read_data(): self.callstack.append('read') return self.volume_file.read(self.data_length) self._setup_mock_popen(mock_popen, ['out', 'err'], p1hook=mock_read_data, p2hook=mock_write_data) self.mock_rbd.RBD.list = mock.Mock() self.mock_rbd.RBD.list.return_value = [backup_name] with contextlib.nested( mock.patch.object(self.service, 'get_backup_snaps'), mock.patch.object(self.service, '_rbd_diff_transfer'), mock.patch.object(self.service, '_full_backup'), mock.patch.object( self.service, '_backup_metadata')) as (_unused1, _u2, _u3, mock_backup_metadata): def mock_backup_metadata_side_effect(backup): raise exception.BackupOperationError(_('mock')) # Raise a pseudo exception.BackupOperationError. mock_backup_metadata.side_effect = mock_backup_metadata_side_effect with mock.patch.object(self.service, 'delete') as mock_delete: def mock_delete_side_effect(backup): raise self.service.rbd.ImageBusy() # Raise a pseudo exception rbd.ImageBusy. mock_delete.side_effect = mock_delete_side_effect with tempfile.NamedTemporaryFile() as test_file: checksum = hashlib.sha256() image = self.service.rbd.Image() meta = rbddriver.RBDImageMetadata(image, 'pool_foo', 'user_foo', 'conf_foo') rbdio = rbddriver.RBDImageIOWrapper(meta) # We expect that the second exception is # notified. self.assertRaises(self.service.rbd.ImageBusy, self.service.backup, self.backup, rbdio)
def test_backup_volume_from_rbd(self): self._create_volume_db_entry(self.volume_id, 1) backup = db.backup_get(self.ctxt, self.backup_id) self._set_common_backup_stubs(self.service) backup_name = self.service._get_backup_base_name(self.backup_id, diff_format=True) self.stubs.Set(self.service, '_try_delete_base_image', lambda *args, **kwargs: None) self.stubs.Set(fcntl, 'fcntl', lambda *args, **kwargs: 0) with tempfile.NamedTemporaryFile() as test_file: checksum = hashlib.sha256() def write_data(): self.volume_file.seek(0) data = self.volume_file.read(self.length) self.called.append('write') checksum.update(data) test_file.write(data) def read_data(): self.called.append('read') return self.volume_file.read(self.length) def rbd_list(inst, ioctx): self.called.append('list') return [backup_name] self._setup_mock_popen(self, ['out', 'err'], p1hook=read_data, p2hook=write_data) self.stubs.Set(self.service.rbd.RBD, 'list', rbd_list) self.stubs.Set(self.service, '_discard_bytes', lambda *args: None) meta = rbddriver.RBDImageMetadata(self.service.rbd.Image(), 'pool_foo', 'user_foo', 'conf_foo') rbd_io = rbddriver.RBDImageIOWrapper(meta) self.service.backup(backup, rbd_io) self.assertEqual(self.called, [ 'list', 'popen_init', 'read', 'popen_init', 'write', 'stdout_close', 'communicate' ]) # Ensure the files are equal self.assertEqual(checksum.digest(), self.checksum.digest())
def test_backup_volume_from_rbd(self, mock_popen, mock_fnctl): backup_name = self.service._get_backup_base_name(self.backup_id, diff_format=True) def mock_write_data(): self.volume_file.seek(0) data = self.volume_file.read(self.data_length) self.callstack.append('write') checksum.update(data) test_file.write(data) def mock_read_data(): self.callstack.append('read') return self.volume_file.read(self.data_length) self._setup_mock_popen(mock_popen, ['out', 'err'], p1hook=mock_read_data, p2hook=mock_write_data) self.mock_rbd.RBD.list = mock.Mock() self.mock_rbd.RBD.list.return_value = [backup_name] with mock.patch.object(self.service, '_backup_metadata'): with mock.patch.object(self.service, 'get_backup_snaps') as \ mock_get_backup_snaps: with mock.patch.object(self.service, '_full_backup') as \ mock_full_backup: with mock.patch.object(self.service, '_try_delete_base_image'): with tempfile.NamedTemporaryFile() as test_file: checksum = hashlib.sha256() image = self.service.rbd.Image() meta = rbddriver.RBDImageMetadata(image, 'pool_foo', 'user_foo', 'conf_foo') rbdio = rbddriver.RBDImageIOWrapper(meta) self.service.backup(self.backup, rbdio) self.assertEqual(self.callstack, ['popen_init', 'read', 'popen_init', 'write', 'stdout_close', 'communicate']) self.assertFalse(mock_full_backup.called) self.assertTrue(mock_get_backup_snaps.called) # Ensure the files are equal self.assertEqual(checksum.digest(), self.checksum.digest())
def setUp(self): super(RBDImageIOWrapperTestCase, self).setUp() self.meta = mock.Mock() self.meta.user = '******' self.meta.conf = 'mock_conf' self.meta.pool = 'mock_pool' self.meta.image = mock.Mock() self.meta.image.read = mock.Mock() self.meta.image.write = mock.Mock() self.meta.image.size = mock.Mock() self.mock_rbd_wrapper = driver.RBDImageIOWrapper(self.meta) self.data_length = 1024 self.full_data = 'abcd' * 256
def test_backup_volume_from_rbd(self): self._create_volume_db_entry(self.volume_id, 1) backup = db.backup_get(self.ctxt, self.backup_id) self._set_common_backup_stubs(self.service) backup_name = self.service._get_backup_base_name(self.backup_id, diff_format=True) self.stubs.Set(self.service, '_try_delete_base_image', lambda *args, **kwargs: None) with tempfile.NamedTemporaryFile() as test_file: checksum = hashlib.sha256() def write_data(inst, data, offset): checksum.update(data) test_file.write(data) def read_data(inst, offset, length): return self.volume_file.read(self.length) def rbd_list(inst, ioctx): return [backup_name] self.stubs.Set(self.service.rbd.Image, 'read', read_data) self.stubs.Set(self.service.rbd.Image, 'write', write_data) self.stubs.Set(self.service.rbd.RBD, 'list', rbd_list) meta = rbddriver.RBDImageMetadata(self.service.rbd.Image(), 'pool_foo', 'user_foo', 'conf_foo') rbd_io = rbddriver.RBDImageIOWrapper(meta) self.service.backup(backup, rbd_io) # Ensure the files are equal self.assertEqual(checksum.digest(), self.checksum.digest())
def test_backup_volume_from_rbd_fail(self, mock_popen, mock_fnctl): """Test of when an exception occurs in an exception handler. In _backup_rbd(), after an exception.BackupRBDOperationFailed occurs in self._rbd_diff_transfer(), we want to check the process when the second exception occurs in self._try_delete_base_image(). """ backup_name = self.service._get_backup_base_name(self.backup_id, diff_format=True) def mock_write_data(): self.volume_file.seek(0) data = self.volume_file.read(self.data_length) self.callstack.append('write') checksum.update(data) test_file.write(data) def mock_read_data(): self.callstack.append('read') return self.volume_file.read(self.data_length) self._setup_mock_popen(mock_popen, ['out', 'err'], p1hook=mock_read_data, p2hook=mock_write_data) self.mock_rbd.RBD.list = mock.Mock() self.mock_rbd.RBD.list.return_value = [backup_name] with mock.patch.object(self.service, 'get_backup_snaps'), \ mock.patch.object(self.service, '_rbd_diff_transfer') as \ mock_rbd_diff_transfer: 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')) # Raise a pseudo exception.BackupRBDOperationFailed. mock_rbd_diff_transfer.side_effect \ = mock_rbd_diff_transfer_side_effect with mock.patch.object(self.service, '_full_backup'), \ mock.patch.object(self.service, '_try_delete_base_image') as \ mock_try_delete_base_image: def mock_try_delete_base_image_side_effect( backup_id, volume_id, base_name): raise self.service.rbd.ImageNotFound(_('mock')) # Raise a pesudo exception rbd.ImageNotFound. mock_try_delete_base_image.side_effect \ = mock_try_delete_base_image_side_effect with mock.patch.object(self.service, '_backup_metadata'): with tempfile.NamedTemporaryFile() as test_file: checksum = hashlib.sha256() image = self.service.rbd.Image() meta = rbddriver.RBDImageMetadata( image, 'pool_foo', 'user_foo', 'conf_foo') rbdio = rbddriver.RBDImageIOWrapper(meta) # We expect that the second exception is # notified. self.assertRaises(self.service.rbd.ImageNotFound, self.service.backup, self.backup, rbdio)
def _get_wrapped_rbd_io(self, rbd_image): rbd_meta = rbddriver.RBDImageMetadata(rbd_image, 'pool_foo', 'user_foo', 'conf_foo') return rbddriver.RBDImageIOWrapper(rbd_meta)