Fix RBD incremental backup

Since the default get_backup_device method doesn't
return enough Ceph connection information, RBD
incremental backup will fail, resulting in full backups
being performed when an incremental is requested.

Closes-Bug: #1578036
Co-Author: Gorka Eguileor <geguileo@redhat.com>
Change-Id: I41d1ec84db58f4bf0f7362cec022f2c3380e5ee2
This commit is contained in:
Eric Harney 2018-04-26 15:18:35 -04:00
parent 910f534382
commit 53522164dc
2 changed files with 64 additions and 0 deletions
cinder
tests/unit/volume/drivers
volume/drivers

@ -17,6 +17,7 @@
import math
import os
import tempfile
import uuid
import castellan
import ddt
@ -26,6 +27,7 @@ from oslo_utils import imageutils
from oslo_utils import units
from cinder import context
from cinder import db
from cinder import exception
import cinder.image.glance
from cinder.image import image_utils
@ -183,6 +185,7 @@ class RBDTestCase(test.TestCase):
self.cfg.rbd_store_chunk_size = 4
self.cfg.rados_connection_retries = 3
self.cfg.rados_connection_interval = 5
self.cfg.backup_use_temp_snapshot = False
mock_exec = mock.Mock()
mock_exec.return_value = ('', '')
@ -2147,6 +2150,53 @@ class RBDTestCase(test.TestCase):
'rbd', 'import', '--pool', 'rbd', '--order', 22,
'/imgfile', self.volume_c.name)
@mock.patch('cinder.objects.Volume.get_by_id')
@mock.patch('cinder.db.volume_glance_metadata_get', return_value={})
@common_mocks
def test_get_backup_device_ceph(self, mock_gm_get, volume_get_by_id):
# Use the same volume for backup (volume_a)
volume_get_by_id.return_value = self.volume_a
driver = self.driver
self._create_backup_db_entry(fake.BACKUP_ID, self.volume_a['id'], 1)
backup = objects.Backup.get_by_id(self.context, fake.BACKUP_ID)
backup.service = 'asdf#ceph'
ret = driver.get_backup_device(self.context, backup)
self.assertEqual(ret, (self.volume_a, False))
def _create_backup_db_entry(self, backupid, volid, size,
userid=str(uuid.uuid4()),
projectid=str(uuid.uuid4())):
backup = {'id': backupid, 'size': size, 'volume_id': volid,
'user_id': userid, 'project_id': projectid}
return db.backup_create(self.context, backup)['id']
@mock.patch('cinder.volume.driver.BaseVD._get_backup_volume_temp_snapshot')
@mock.patch('cinder.volume.driver.BaseVD._get_backup_volume_temp_volume')
@mock.patch('cinder.objects.Volume.get_by_id')
@mock.patch('cinder.db.volume_glance_metadata_get', return_value={})
@common_mocks
def test_get_backup_device_other(self,
mock_gm_get,
volume_get_by_id,
mock_get_temp_volume,
mock_get_temp_snapshot):
# Use a cloned volume for backup (volume_b)
self.volume_a.previous_status = 'in-use'
mock_get_temp_volume.return_value = self.volume_b
mock_get_temp_snapshot.return_value = (self.volume_b, False)
volume_get_by_id.return_value = self.volume_a
driver = self.driver
self._create_backup_db_entry(fake.BACKUP_ID, self.volume_a['id'], 1)
backup = objects.Backup.get_by_id(self.context, fake.BACKUP_ID)
backup.service = 'asdf'
ret = driver.get_backup_device(self.context, backup)
self.assertEqual(ret, (self.volume_b, False))
class ManagedRBDTestCase(test_driver.BaseDriverTestCase):
driver_name = "cinder.volume.drivers.rbd.RBDDriver"

@ -36,6 +36,7 @@ from cinder import exception
from cinder.i18n import _
from cinder.image import image_utils
from cinder import interface
from cinder import objects
from cinder.objects import fields
from cinder import utils
from cinder.volume import configuration
@ -1770,3 +1771,16 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
volume.set_snap(None)
if not children and volume.is_protected_snap(snapshot.name):
volume.unprotect_snap(snapshot.name)
def get_backup_device(self, context, backup):
"""Get a backup device from an existing volume.
To support incremental backups on Ceph to Ceph we don't clone
the volume.
"""
if not backup.service.endswith('ceph') or backup.snapshot_id:
return super(RBDDriver, self).get_backup_device(context, backup)
volume = objects.Volume.get_by_id(context, backup.volume_id)
return (volume, False)