From 0b296db086913c6b03cc78e1f9ade9ab1c5123cf Mon Sep 17 00:00:00 2001 From: Imran Imtiaz Date: Wed, 1 Jul 2026 16:31:52 +0100 Subject: [PATCH] mgr/dashboard: support clone_by_snap_id flag in clone API for group snapshots The clone API endpoint POST /api/block/image/{image_spec}/snap/{snapshot_name}/clone only accepted a snapshot name, which uses rbd_clone3 (clone by name). This only works for snapshots in the user namespace. Group snapshots (with .group prefix, in RBD_SNAP_NAMESPACE_TYPE_GROUP namespace) require rbd_clone4 (clone by snap ID). The rbd Python binding already supports this: passing an int for p_snapshot triggers rbd_clone4. Add an optional clone_by_snap_id boolean flag to the clone endpoint. When set, the path parameter is treated as a numeric snap ID and passed directly as an int to rbd.RBD().clone(). Clone format 2 is enforced because this flag is intended for non-user namespace snapshots which cannot be protected (a prerequisite for clone format 1). Usage: POST /api/block/image/{image_spec}/snap/{snap_id}/clone {"clone_by_snap_id": true, ...} Fixes: https://tracker.ceph.com/issues/77875 Signed-off-by: Imran Imtiaz Assisted-by: IBM Bob --- src/pybind/mgr/dashboard/controllers/rbd.py | 21 ++++++++++++++++++--- src/pybind/mgr/dashboard/openapi.yaml | 3 +++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/pybind/mgr/dashboard/controllers/rbd.py b/src/pybind/mgr/dashboard/controllers/rbd.py index 54ec2f75de4..1496d57865b 100644 --- a/src/pybind/mgr/dashboard/controllers/rbd.py +++ b/src/pybind/mgr/dashboard/controllers/rbd.py @@ -317,7 +317,7 @@ class RbdSnapshot(RESTController): def clone(self, image_spec, snapshot_name, child_pool_name, child_image_name, child_namespace=None, obj_size=None, features=None, stripe_unit=None, stripe_count=None, data_pool=None, - configuration=None, metadata=None): + configuration=None, metadata=None, clone_by_snap_id=False): """ Clones a snapshot to an image """ @@ -334,10 +334,25 @@ class RbdSnapshot(RESTController): # Set features feature_bitmask = format_features(features) + # When clone_by_snap_id is set, treat snapshot_name as a + # numeric snap ID and clone by ID. This is required for + # non-user namespace snapshots (e.g. group snapshots) which + # cannot be cloned by name. Passing an int to rbd.RBD().clone() + # triggers rbd_clone4 (by snap ID) instead of rbd_clone3. + # Clone format 2 is enforced here because this flag is intended + # for non-user namespace snapshots which cannot be protected + # (a prerequisite for clone format 1). + snap_ref = snapshot_name + clone_format = None + if clone_by_snap_id: + snap_ref = int(snapshot_name) + clone_format = 2 + rbd_inst = rbd.RBD() - rbd_inst.clone(p_ioctx, image_name, snapshot_name, ioctx, + rbd_inst.clone(p_ioctx, image_name, snap_ref, ioctx, child_image_name, feature_bitmask, l_order, - stripe_unit, stripe_count, data_pool) + stripe_unit, stripe_count, data_pool, + clone_format=clone_format) RbdConfiguration(pool_ioctx=ioctx, image_name=child_image_name).set_configuration( configuration) diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index 83f0ca14911..b72f8e2893c 100644 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -1198,6 +1198,9 @@ paths: type: string child_pool_name: type: string + clone_by_snap_id: + default: false + type: boolean configuration: type: string data_pool: -- 2.47.3