RBD_IMAGE_OPTION_STRIPE_UNIT = _RBD_IMAGE_OPTION_STRIPE_UNIT
RBD_IMAGE_OPTION_STRIPE_COUNT = _RBD_IMAGE_OPTION_STRIPE_COUNT
RBD_IMAGE_OPTION_DATA_POOL = _RBD_IMAGE_OPTION_DATA_POOL
+RBD_IMAGE_OPTION_CLONE_FORMAT = _RBD_IMAGE_OPTION_CLONE_FORMAT
RBD_SNAP_NAMESPACE_TYPE_USER = _RBD_SNAP_NAMESPACE_TYPE_USER
RBD_SNAP_NAMESPACE_TYPE_GROUP = _RBD_SNAP_NAMESPACE_TYPE_GROUP
def clone(self, p_ioctx, p_name, p_snapname, c_ioctx, c_name,
features=None, order=None, stripe_unit=None, stripe_count=None,
- data_pool=None):
+ data_pool=None, clone_format=None):
"""
Clone a parent rbd snapshot into a COW sparse child.
:type stripe_count: int
:param data_pool: optional separate pool for data blocks
:type data_pool: str
+ :param clone_format: 1 (requires a protected snapshot), 2 (requires mimic+ clients)
+ :type clone_format: int
:raises: :class:`TypeError`
:raises: :class:`InvalidArgument`
:raises: :class:`ImageExists`
if data_pool is not None:
rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
data_pool)
+ if clone_format is not None:
+ rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_CLONE_FORMAT,
+ clone_format)
with nogil:
ret = rbd_clone3(_p_ioctx, _p_name, _p_snapname,
_c_ioctx, _c_name, opts)
def migration_prepare(self, ioctx, image_name, dest_ioctx, dest_image_name,
features=None, order=None, stripe_unit=None, stripe_count=None,
- data_pool=None):
+ data_pool=None, clone_format=None):
"""
Prepare an RBD image migration.
:type stripe_count: int
:param data_pool: optional separate pool for data blocks
:type data_pool: str
+ :param clone_format: if the source image is a clone, which clone format
+ to use for the destination image
+ :type clone_format: int
:raises: :class:`TypeError`
:raises: :class:`InvalidArgument`
:raises: :class:`ImageExists`
if data_pool is not None:
rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
data_pool)
+ if clone_format is not None:
+ rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_CLONE_FORMAT,
+ clone_format)
with nogil:
ret = rbd_migration_prepare(_ioctx, _image_name, _dest_ioctx,
_dest_image_name, opts)
@requires_not_closed
def deep_copy(self, dest_ioctx, dest_name, features=None, order=None,
- stripe_unit=None, stripe_count=None, data_pool=None):
+ stripe_unit=None, stripe_count=None, data_pool=None,
+ clone_format=None):
"""
Deep copy the image to another location.
:type stripe_count: int
:param data_pool: optional separate pool for data blocks
:type data_pool: str
+ :param clone_format: if the source image is a clone, which clone format
+ to use for the destination image
+ :type clone_format: int
:raises: :class:`TypeError`
:raises: :class:`InvalidArgument`
:raises: :class:`ImageExists`
if data_pool is not None:
rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
data_pool)
+ if clone_format is not None:
+ rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_CLONE_FORMAT,
+ clone_format)
with nogil:
ret = rbd_deep_copy(self.image, _dest_ioctx, _dest_name, opts)
finally:
RBD_MIRROR_IMAGE_DISABLED, MIRROR_IMAGE_STATUS_STATE_UNKNOWN,
RBD_MIRROR_IMAGE_MODE_JOURNAL, RBD_MIRROR_IMAGE_MODE_SNAPSHOT,
RBD_LOCK_MODE_EXCLUSIVE, RBD_OPERATION_FEATURE_GROUP,
+ RBD_OPERATION_FEATURE_CLONE_CHILD,
RBD_SNAP_NAMESPACE_TYPE_TRASH,
RBD_SNAP_NAMESPACE_TYPE_MIRROR,
RBD_IMAGE_MIGRATION_STATE_PREPARED, RBD_CONFIG_SOURCE_CONFIG,
self.rbd.remove(ioctx, dst_name)
@require_features([RBD_FEATURE_LAYERING])
- def test_deep_copy_clone(self):
- global ioctx
- global features
+ def test_deep_copy_clone_v1_to_v1(self):
self.image.write(b'a' * 256, 0)
self.image.create_snap('snap1')
self.image.write(b'b' * 256, 0)
self.image.protect_snap('snap1')
clone_name = get_temp_image_name()
dst_name = get_temp_image_name()
- self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name)
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name, features,
+ clone_format=1)
with Image(ioctx, clone_name) as child:
+ eq(0, child.op_features())
child.create_snap('snap1')
child.deep_copy(ioctx, dst_name, features=features,
order=self.image.stat()['order'],
stripe_unit=self.image.stripe_unit(),
stripe_count=self.image.stripe_count(),
- data_pool=None)
+ clone_format=1)
child.remove_snap('snap1')
with Image(ioctx, dst_name) as copy:
copy_data = copy.read(0, 256)
eq(b'a' * 256, copy_data)
+ eq(self.image.id(), copy.parent_id())
+ eq(0, copy.op_features())
copy.remove_snap('snap1')
self.rbd.remove(ioctx, dst_name)
self.rbd.remove(ioctx, clone_name)
self.image.unprotect_snap('snap1')
self.image.remove_snap('snap1')
+ @require_features([RBD_FEATURE_LAYERING])
+ def test_deep_copy_clone_v1_to_v2(self):
+ self.image.write(b'a' * 256, 0)
+ self.image.create_snap('snap1')
+ self.image.write(b'b' * 256, 0)
+ self.image.protect_snap('snap1')
+ clone_name = get_temp_image_name()
+ dst_name = get_temp_image_name()
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name, features,
+ clone_format=1)
+ with Image(ioctx, clone_name) as child:
+ eq(0, child.op_features())
+ child.create_snap('snap1')
+ child.deep_copy(ioctx, dst_name, features=features,
+ order=self.image.stat()['order'],
+ stripe_unit=self.image.stripe_unit(),
+ stripe_count=self.image.stripe_count(),
+ clone_format=2)
+ child.remove_snap('snap1')
+
+ with Image(ioctx, dst_name) as copy:
+ copy_data = copy.read(0, 256)
+ eq(b'a' * 256, copy_data)
+ eq(self.image.id(), copy.parent_id())
+ eq(RBD_OPERATION_FEATURE_CLONE_CHILD, copy.op_features())
+ copy.remove_snap('snap1')
+ self.rbd.remove(ioctx, dst_name)
+ self.rbd.remove(ioctx, clone_name)
+ self.image.unprotect_snap('snap1')
+ self.image.remove_snap('snap1')
+
+ @require_features([RBD_FEATURE_LAYERING])
+ def test_deep_copy_clone_v2_to_v1(self):
+ self.image.write(b'a' * 256, 0)
+ self.image.create_snap('snap1')
+ self.image.write(b'b' * 256, 0)
+ self.image.protect_snap('snap1')
+ clone_name = get_temp_image_name()
+ dst_name = get_temp_image_name()
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name, features,
+ clone_format=2)
+ with Image(ioctx, clone_name) as child:
+ eq(RBD_OPERATION_FEATURE_CLONE_CHILD, child.op_features())
+ child.create_snap('snap1')
+ child.deep_copy(ioctx, dst_name, features=features,
+ order=self.image.stat()['order'],
+ stripe_unit=self.image.stripe_unit(),
+ stripe_count=self.image.stripe_count(),
+ clone_format=1)
+ child.remove_snap('snap1')
+
+ with Image(ioctx, dst_name) as copy:
+ copy_data = copy.read(0, 256)
+ eq(b'a' * 256, copy_data)
+ eq(self.image.id(), copy.parent_id())
+ eq(0, copy.op_features())
+ copy.remove_snap('snap1')
+ self.rbd.remove(ioctx, dst_name)
+ self.rbd.remove(ioctx, clone_name)
+ self.image.unprotect_snap('snap1')
+ self.image.remove_snap('snap1')
+
+ @require_features([RBD_FEATURE_LAYERING])
+ def test_deep_copy_clone_v2_to_v2(self):
+ self.image.write(b'a' * 256, 0)
+ self.image.create_snap('snap1')
+ self.image.write(b'b' * 256, 0)
+ clone_name = get_temp_image_name()
+ dst_name = get_temp_image_name()
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name, features,
+ clone_format=2)
+ with Image(ioctx, clone_name) as child:
+ eq(RBD_OPERATION_FEATURE_CLONE_CHILD, child.op_features())
+ child.create_snap('snap1')
+ child.deep_copy(ioctx, dst_name, features=features,
+ order=self.image.stat()['order'],
+ stripe_unit=self.image.stripe_unit(),
+ stripe_count=self.image.stripe_count(),
+ clone_format=2)
+ child.remove_snap('snap1')
+
+ with Image(ioctx, dst_name) as copy:
+ copy_data = copy.read(0, 256)
+ eq(b'a' * 256, copy_data)
+ eq(self.image.id(), copy.parent_id())
+ eq(RBD_OPERATION_FEATURE_CLONE_CHILD, copy.op_features())
+ copy.remove_snap('snap1')
+ self.rbd.remove(ioctx, dst_name)
+ self.rbd.remove(ioctx, clone_name)
+ self.image.remove_snap('snap1')
+
def test_create_snap(self):
global ioctx
self.image.create_snap('snap1')
image.close()
RBD().remove(ioctx, image_name)
+ def test_clone_format(self):
+ clone_name2 = get_temp_image_name()
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
+ features, clone_format=1)
+ with Image(ioctx, clone_name2) as clone2:
+ eq(0, clone2.op_features())
+ self.rbd.remove(ioctx, clone_name2)
+
+ self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
+ features, clone_format=2)
+ with Image(ioctx, clone_name2) as clone2:
+ eq(RBD_OPERATION_FEATURE_CLONE_CHILD, clone2.op_features())
+ self.rbd.remove(ioctx, clone_name2)
def test_unprotected(self):
self.image.create_snap('snap2')
- global features
clone_name2 = get_temp_image_name()
- rados.conf_set("rbd_default_clone_format", "1")
+ # clone format 1 requires a protected snapshot
assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name,
- 'snap2', ioctx, clone_name2, features)
- rados.conf_set("rbd_default_clone_format", "auto")
+ 'snap2', ioctx, clone_name2, features, clone_format=1)
+ self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
+ features, clone_format=2)
+ self.rbd.remove(ioctx, clone_name2)
self.image.remove_snap('snap2')
def test_unprotect_with_children(self):
RBD().migration_commit(ioctx, image_name)
remove_image()
+ @require_features([RBD_FEATURE_LAYERING])
+ def test_migration_clone_v1_to_v1(self):
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_id = image.id()
+ image.create_snap('snap1')
+ image.protect_snap('snap1')
+ clone_name = get_temp_image_name()
+ RBD().clone(ioctx, image_name, 'snap1', ioctx, clone_name, features,
+ clone_format=1)
+ with Image(ioctx, clone_name) as clone:
+ eq(image_id, clone.parent_id())
+ eq(0, clone.op_features())
+
+ RBD().migration_prepare(ioctx, clone_name, ioctx, clone_name, features=63,
+ order=23, stripe_unit=1<<23, stripe_count=1,
+ clone_format=1)
+ RBD().migration_execute(ioctx, clone_name)
+ RBD().migration_commit(ioctx, clone_name)
+
+ with Image(ioctx, clone_name) as clone:
+ eq(image_id, clone.parent_id())
+ eq(0, clone.op_features())
+ RBD().remove(ioctx, clone_name)
+ with Image(ioctx, image_name) as image:
+ image.unprotect_snap('snap1')
+ image.remove_snap('snap1')
+ remove_image()
+
+ @require_features([RBD_FEATURE_LAYERING])
+ def test_migration_clone_v1_to_v2(self):
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_id = image.id()
+ image.create_snap('snap1')
+ image.protect_snap('snap1')
+ clone_name = get_temp_image_name()
+ RBD().clone(ioctx, image_name, 'snap1', ioctx, clone_name, features,
+ clone_format=1)
+ with Image(ioctx, clone_name) as clone:
+ eq(image_id, clone.parent_id())
+ eq(0, clone.op_features())
+
+ RBD().migration_prepare(ioctx, clone_name, ioctx, clone_name, features=63,
+ order=23, stripe_unit=1<<23, stripe_count=1,
+ clone_format=2)
+ RBD().migration_execute(ioctx, clone_name)
+ RBD().migration_commit(ioctx, clone_name)
+
+ with Image(ioctx, clone_name) as clone:
+ eq(image_id, clone.parent_id())
+ eq(RBD_OPERATION_FEATURE_CLONE_CHILD, clone.op_features())
+ RBD().remove(ioctx, clone_name)
+ with Image(ioctx, image_name) as image:
+ image.unprotect_snap('snap1')
+ image.remove_snap('snap1')
+ remove_image()
+
+ @require_features([RBD_FEATURE_LAYERING])
+ def test_migration_clone_v2_to_v1(self):
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_id = image.id()
+ image.create_snap('snap1')
+ image.protect_snap('snap1')
+ clone_name = get_temp_image_name()
+ RBD().clone(ioctx, image_name, 'snap1', ioctx, clone_name, features,
+ clone_format=2)
+ with Image(ioctx, clone_name) as clone:
+ eq(image_id, clone.parent_id())
+ eq(RBD_OPERATION_FEATURE_CLONE_CHILD, clone.op_features())
+
+ RBD().migration_prepare(ioctx, clone_name, ioctx, clone_name, features=63,
+ order=23, stripe_unit=1<<23, stripe_count=1,
+ clone_format=1)
+ RBD().migration_execute(ioctx, clone_name)
+ RBD().migration_commit(ioctx, clone_name)
+
+ with Image(ioctx, clone_name) as clone:
+ eq(image_id, clone.parent_id())
+ eq(0, clone.op_features())
+ RBD().remove(ioctx, clone_name)
+ with Image(ioctx, image_name) as image:
+ image.unprotect_snap('snap1')
+ image.remove_snap('snap1')
+ remove_image()
+
+ @require_features([RBD_FEATURE_LAYERING])
+ def test_migration_clone_v2_to_v2(self):
+ create_image()
+ with Image(ioctx, image_name) as image:
+ image_id = image.id()
+ image.create_snap('snap1')
+ clone_name = get_temp_image_name()
+ RBD().clone(ioctx, image_name, 'snap1', ioctx, clone_name, features,
+ clone_format=2)
+ with Image(ioctx, clone_name) as clone:
+ eq(image_id, clone.parent_id())
+ eq(RBD_OPERATION_FEATURE_CLONE_CHILD, clone.op_features())
+
+ RBD().migration_prepare(ioctx, clone_name, ioctx, clone_name, features=63,
+ order=23, stripe_unit=1<<23, stripe_count=1,
+ clone_format=2)
+ RBD().migration_execute(ioctx, clone_name)
+ RBD().migration_commit(ioctx, clone_name)
+
+ with Image(ioctx, clone_name) as clone:
+ eq(image_id, clone.parent_id())
+ eq(RBD_OPERATION_FEATURE_CLONE_CHILD, clone.op_features())
+ RBD().remove(ioctx, clone_name)
+ with Image(ioctx, image_name) as image:
+ image.remove_snap('snap1')
+ remove_image()
+
def test_migration_import(self):
create_image()
with Image(ioctx, image_name) as image: