]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
pybind/rbd: expose RBD_IMAGE_OPTION_FLATTEN option
authorIlya Dryomov <idryomov@gmail.com>
Wed, 1 May 2024 13:49:47 +0000 (15:49 +0200)
committerIlya Dryomov <idryomov@gmail.com>
Tue, 7 May 2024 08:37:12 +0000 (10:37 +0200)
It takes effect with deep_copy() and migration_prepare().

Fixes: https://tracker.ceph.com/issues/65624
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
(cherry picked from commit 39f207ff65a4eb10f67a2d4a8a4af1bad0d2e412)

Conflicts:
PendingReleaseNotes [ moved to >=18.2.4 section ]

PendingReleaseNotes
src/pybind/rbd/c_rbd.pxd
src/pybind/rbd/mock_rbd.pxi
src/pybind/rbd/rbd.pyx
src/test/pybind/test_rbd.py

index 1c2bc48ac3f02b1a4d6d4e3f571bc782fcdb5c8b..5505ded733200794b9d07b07a23a260fd04d4532 100644 (file)
@@ -4,6 +4,8 @@
 * RBD: `RBD_IMAGE_OPTION_CLONE_FORMAT` option has been exposed in Python
   bindings via `clone_format` optional parameter to `clone`, `deep_copy` and
   `migration_prepare` methods.
+* RBD: `RBD_IMAGE_OPTION_FLATTEN` option has been exposed in Python bindings via
+  `flatten` optional parameter to `deep_copy` and `migration_prepare` methods.
 
 >=18.2.2
 --------
index decc417ef17ef47303813aab32881af814c56df8..8b604764a20d146d3ddff23de503a2ff0f235fc1 100644 (file)
@@ -44,6 +44,7 @@ cdef extern from "rbd/librbd.h" nogil:
         _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_FLATTEN "RBD_IMAGE_OPTION_FLATTEN"
         _RBD_IMAGE_OPTION_CLONE_FORMAT "RBD_IMAGE_OPTION_CLONE_FORMAT"
 
         RBD_MAX_BLOCK_NAME_SIZE
index 6e533c265dfad81b5a7c4524211cf6e0003981de..3c3c358fd04bfecf72d1b9e3967e1257b82ac80c 100644 (file)
@@ -44,6 +44,7 @@ cdef nogil:
         _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_FLATTEN "RBD_IMAGE_OPTION_FLATTEN"
         _RBD_IMAGE_OPTION_CLONE_FORMAT "RBD_IMAGE_OPTION_CLONE_FORMAT"
 
         RBD_MAX_BLOCK_NAME_SIZE
index a2b42c8c0aa674ed6951b8ae831c7d246a820085..aa07ab172cb92cc79f2eeea83a3bd722e1513e6b 100644 (file)
@@ -114,6 +114,7 @@ RBD_IMAGE_OPTION_ORDER = _RBD_IMAGE_OPTION_ORDER
 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_FLATTEN = _RBD_IMAGE_OPTION_FLATTEN
 RBD_IMAGE_OPTION_CLONE_FORMAT = _RBD_IMAGE_OPTION_CLONE_FORMAT
 
 RBD_SNAP_NAMESPACE_TYPE_USER = _RBD_SNAP_NAMESPACE_TYPE_USER
@@ -961,7 +962,7 @@ class RBD(object):
 
     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, clone_format=None):
+                          data_pool=None, clone_format=None, flatten=False):
         """
         Prepare an RBD image migration.
 
@@ -986,6 +987,9 @@ class RBD(object):
         :param clone_format: if the source image is a clone, which clone format
                              to use for the destination image
         :type clone_format: int
+        :param flatten: if the source image is a clone, whether to flatten the
+                        destination image or make it a clone of the same parent
+        :type flatten: bool
         :raises: :class:`TypeError`
         :raises: :class:`InvalidArgument`
         :raises: :class:`ImageExists`
@@ -1021,6 +1025,7 @@ class RBD(object):
             if clone_format is not None:
                 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_CLONE_FORMAT,
                                              clone_format)
+            rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FLATTEN, flatten)
             with nogil:
                 ret = rbd_migration_prepare(_ioctx, _image_name, _dest_ioctx,
                                             _dest_image_name, opts)
@@ -3450,7 +3455,7 @@ cdef class Image(object):
     @requires_not_closed
     def deep_copy(self, dest_ioctx, dest_name, features=None, order=None,
                   stripe_unit=None, stripe_count=None, data_pool=None,
-                  clone_format=None):
+                  clone_format=None, flatten=False):
         """
         Deep copy the image to another location.
 
@@ -3471,6 +3476,9 @@ cdef class Image(object):
         :param clone_format: if the source image is a clone, which clone format
                              to use for the destination image
         :type clone_format: int
+        :param flatten: if the source image is a clone, whether to flatten the
+                        destination image or make it a clone of the same parent
+        :type flatten: bool
         :raises: :class:`TypeError`
         :raises: :class:`InvalidArgument`
         :raises: :class:`ImageExists`
@@ -3504,6 +3512,7 @@ cdef class Image(object):
             if clone_format is not None:
                 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_CLONE_FORMAT,
                                              clone_format)
+            rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FLATTEN, flatten)
             with nogil:
                 ret = rbd_deep_copy(self.image, _dest_ioctx, _dest_name, opts)
         finally:
index 1bd492bd2d4b6701a0676ff7842454113a7a9255..55a5fc5722faba099aedb57b560366f832137a84 100644 (file)
@@ -949,6 +949,66 @@ class TestImage(object):
         self.rbd.remove(ioctx, clone_name)
         self.image.remove_snap('snap1')
 
+    @require_features([RBD_FEATURE_LAYERING])
+    def test_deep_copy_clone_v1_flatten(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(),
+                            flatten=True)
+            child.remove_snap('snap1')
+
+        with Image(ioctx, dst_name) as copy:
+            copy_data = copy.read(0, 256)
+            eq(b'a' * 256, copy_data)
+            assert_raises(ImageNotFound, 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_flatten(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(),
+                            flatten=True)
+            child.remove_snap('snap1')
+
+        with Image(ioctx, dst_name) as copy:
+            copy_data = copy.read(0, 256)
+            eq(b'a' * 256, copy_data)
+            assert_raises(ImageNotFound, 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.remove_snap('snap1')
+
     def test_create_snap(self):
         global ioctx
         self.image.create_snap('snap1')
@@ -2975,6 +3035,9 @@ class TestMigration(object):
             image.remove_snap('snap1')
         remove_image()
 
+    # TODO: add test_migration_clone_v{1,2}_flatten tests once
+    # https://tracker.ceph.com/issues/65743 is addressed
+
     def test_migration_import(self):
         create_image()
         with Image(ioctx, image_name) as image: