ceph backup support for EXCLUSIVE_LOCK and JOURNALING features

Ceph Jewel has introduced the ability to mirror its pools. If configured
to do so, the rbd-mirror daemon will replay an image's journals to
replicate the image at a remote Ceph store. In order for an image to be
mirrored, the 'exclusive-lock' and 'journaling' feature bits need to be
enabled per image.

Ceph documentation recommends using "rbd features default = 125" in the
Ceph configuration to enable these feature bits for all newly created
images. However, if an image has an explicit set of feature bits asked
for during its creation, that request overrides what is set for "rbd
features default". The Ceph backup driver is specifcally asking for
"stripingv2" and "layering"; thus ignoring what is set at "rbd features
default".

This patch adds a new configuration option, 'backup_ceph_image_journals'
which adds these feature bits to the 'features' set returned by
_get_rbd_support(). If 'backup_ceph_image_journals' is set to True and
the underlying installation of RBD does not support either JOURNALING or
EXCLUSIVE_LOCK, an error is logged and 'BackupCephInvalidArgs' is raised
whenever an operation attempts to create a new Cepd object.

Change-Id: Iea9dc18ab68891c99d008157220365d184caf508
Implements: blueprint backup-ceph-driver-journaling-exculsive-lock-features
This commit is contained in:
Eric M Gonzalez 2016-12-20 10:43:21 -06:00
parent 0ed3a80d6c
commit dc96c948f7
3 changed files with 66 additions and 0 deletions

View File

@ -88,6 +88,9 @@ service_opts = [
help='RBD stripe unit to use when creating a backup image.'),
cfg.IntOpt('backup_ceph_stripe_count', default=0,
help='RBD stripe count to use when creating a backup image.'),
cfg.BoolOpt('backup_ceph_image_journals', default=False,
help='If True, apply JOURNALING and EXCLUSIVE_LOCK feature '
'bits to the backup RBD objects to allow mirroring'),
cfg.BoolOpt('restore_discard_excess_bytes', default=True,
help='If True, always discard excess bytes when restoring '
'volumes i.e. pad with zeroes.')
@ -222,6 +225,16 @@ class CephBackupDriver(driver.BackupDriver):
"""Determine if striping is supported by our version of librbd."""
return hasattr(self.rbd, 'RBD_FEATURE_STRIPINGV2')
@property
def _supports_exclusive_lock(self):
"""Determine if exclusive-lock is supported by librbd."""
return hasattr(self.rbd, 'RBD_FEATURE_EXCLUSIVE_LOCK')
@property
def _supports_journaling(self):
"""Determine if journaling is supported by our version of librbd."""
return hasattr(self.rbd, 'RBD_FEATURE_JOURNALING')
def _get_rbd_support(self):
"""Determine RBD features supported by our version of librbd."""
old_format = True
@ -233,6 +246,25 @@ class CephBackupDriver(driver.BackupDriver):
old_format = False
features |= self.rbd.RBD_FEATURE_STRIPINGV2
# journaling requires exclusive_lock; check both together
if CONF.backup_ceph_image_journals:
if self._supports_exclusive_lock and self._supports_journaling:
old_format = False
features |= (self.rbd.RBD_FEATURE_EXCLUSIVE_LOCK |
self.rbd.RBD_FEATURE_JOURNALING)
else:
# FIXME (tasker): when the backup manager supports loading the
# driver during its initialization, this exception should be
# moved to the driver's initialization so that it can stop
# the service from starting when the underyling RBD does not
# support the requested features.
LOG.error(_LE("RBD journaling not supported - unable to "
"support per image mirroring in backup pool"))
raise exception.BackupInvalidCephArgs(
_("Image Journaling set but RBD backend does "
"not support journaling")
)
return (old_format, features)
def _connect_to_rados(self, pool=None):

View File

@ -22,6 +22,7 @@ import uuid
import mock
from os_brick.initiator import linuxrbd
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_serialization import jsonutils
from oslo_utils import units
import six
@ -42,6 +43,8 @@ from cinder.tests.unit import fake_constants as fake
# NOTE: this must be initialised in test setUp().
RAISED_EXCEPTIONS = []
CONF = cfg.CONF
class MockException(Exception):
@ -202,8 +205,13 @@ class BackupCephTestCase(test.TestCase):
def test_get_rbd_support(self):
del self.service.rbd.RBD_FEATURE_LAYERING
del self.service.rbd.RBD_FEATURE_STRIPINGV2
del self.service.rbd.RBD_FEATURE_EXCLUSIVE_LOCK
del self.service.rbd.RBD_FEATURE_JOURNALING
self.assertFalse(hasattr(self.service.rbd, 'RBD_FEATURE_LAYERING'))
self.assertFalse(hasattr(self.service.rbd, 'RBD_FEATURE_STRIPINGV2'))
self.assertFalse(hasattr(self.service.rbd,
'RBD_FEATURE_EXCLUSIVE_LOCK'))
self.assertFalse(hasattr(self.service.rbd, 'RBD_FEATURE_JOURNALING'))
oldformat, features = self.service._get_rbd_support()
self.assertTrue(oldformat)
@ -221,6 +229,28 @@ class BackupCephTestCase(test.TestCase):
self.assertFalse(oldformat)
self.assertEqual(1 | 2, features)
# initially, backup_ceph_image_journals = False. test that
# the flags are defined, but that they are not returned.
self.service.rbd.RBD_FEATURE_EXCLUSIVE_LOCK = 4
oldformat, features = self.service._get_rbd_support()
self.assertFalse(oldformat)
self.assertEqual(1 | 2, features)
self.service.rbd.RBD_FEATURE_JOURNALING = 64
oldformat, features = self.service._get_rbd_support()
self.assertFalse(oldformat)
self.assertEqual(1 | 2, features)
# test that the config setting properly sets the FEATURE bits.
# because journaling requires exclusive-lock, these are set
# at the same time.
CONF.set_override("backup_ceph_image_journals", True)
oldformat, features = self.service._get_rbd_support()
self.assertFalse(oldformat)
self.assertEqual(1 | 2 | 4 | 64, features)
@common_mocks
def test_get_most_recent_snap(self):
last = 'backup.%s.snap.9824923.1212' % (uuid.uuid4())

View File

@ -0,0 +1,4 @@
---
features:
- Added new BoolOpt ``backup_ceph_image_journals`` for enabling the Ceph
image features required to support RBD mirroring of Cinder backup pool.