From: Imran Imtiaz Date: Thu, 13 Nov 2025 10:27:28 +0000 (+0000) Subject: mgr/dashboard: add API endpoint to add images to consistency groups X-Git-Tag: testing/wip-pdonnell-testing-20251120.153717-debug~11^2 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=750c25b082c94a97b0a04bf31ea1ded76c6ce7e9;p=ceph-ci.git mgr/dashboard: add API endpoint to add images to consistency groups Signed-off-by: Imran Imtiaz Fixes: https://tracker.ceph.com/issues/73840 Create a consistency group dashboard API endpoint that enables adding RBD images to the group. --- diff --git a/src/pybind/mgr/dashboard/controllers/rbd.py b/src/pybind/mgr/dashboard/controllers/rbd.py index c1d1c01934b..fab2bfb1a04 100644 --- a/src/pybind/mgr/dashboard/controllers/rbd.py +++ b/src/pybind/mgr/dashboard/controllers/rbd.py @@ -471,13 +471,16 @@ class RbdGroup(RESTController): super().__init__() self.rbd_inst = rbd.RBD() + @handle_rbd_error() @EndpointDoc("Display RBD Groups by pool name", parameters={ 'pool_name': (str, 'Name of the pool'), }, responses={200: RBD_GROUP_LIST_SCHEMA}) - def list(self, pool_name): + def list(self, pool_name, namespace=None): with mgr.rados.open_ioctx(pool_name) as ioctx: + RbdService.validate_namespace(ioctx, namespace) + ioctx.set_namespace(namespace) result = [] groups = self.rbd_inst.group_list(ioctx) for group in groups: @@ -487,11 +490,30 @@ class RbdGroup(RESTController): }) return result + @handle_rbd_error() @EndpointDoc("Create an RBD Group", parameters={ 'pool_name': (str, 'Name of the pool'), 'name': (str, 'Name of the group'), }) - def create(self, pool_name, name): + def create(self, pool_name, name, namespace=None): with mgr.rados.open_ioctx(pool_name) as ioctx: + RbdService.validate_namespace(ioctx, namespace) + ioctx.set_namespace(namespace) return self.rbd_inst.group_create(ioctx, name) + + @RESTController.Collection('POST', path='/{group_name}/image') + @handle_rbd_error() + @EndpointDoc("Add an image to an RBD Group", + parameters={ + 'pool_name': (str, 'Name of the pool'), + 'group_name': (str, 'Name of the group'), + 'image_name': (str, 'Name of the image'), + }, + responses={200: None}) + def add_image(self, pool_name, group_name, image_name, namespace=None): + with mgr.rados.open_ioctx(pool_name) as ioctx: + group = rbd.Group(ioctx, group_name) + RbdService.validate_namespace(ioctx, namespace) + ioctx.set_namespace(namespace) + return group.add_image(ioctx, image_name) diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index ca0163d98f6..6ac1c229a9f 100755 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -1599,6 +1599,11 @@ paths: required: true schema: type: string + - allowEmptyValue: true + in: query + name: namespace + schema: + type: string responses: '200': content: @@ -1648,6 +1653,8 @@ paths: name: description: Name of the group type: string + namespace: + type: string required: - name type: object @@ -1676,6 +1683,59 @@ paths: summary: Create an RBD Group tags: - RbdGroup + /api/block/pool/{pool_name}/group/{group_name}/image: + post: + parameters: + - description: Name of the pool + in: path + name: pool_name + required: true + schema: + type: string + - description: Name of the group + in: path + name: group_name + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + properties: + image_name: + description: Name of the image + type: string + namespace: + type: string + required: + - image_name + type: object + responses: + '201': + content: + application/vnd.ceph.api.v1.0+json: + type: object + description: Resource created. + '202': + content: + application/vnd.ceph.api.v1.0+json: + type: object + description: Operation is still executing. Please check the task queue. + '400': + description: Operation exception. Please check the response body for details. + '401': + description: Unauthenticated access. Please login first. + '403': + description: Unauthorized access. Please check your permissions. + '500': + description: Unexpected error. Please check the response body for the stack + trace. + security: + - jwt: [] + summary: Add an image to an RBD Group + tags: + - RbdGroup /api/block/pool/{pool_name}/namespace: get: parameters: diff --git a/src/pybind/mgr/dashboard/services/rbd.py b/src/pybind/mgr/dashboard/services/rbd.py index 07b86988ba3..41e73983bc1 100644 --- a/src/pybind/mgr/dashboard/services/rbd.py +++ b/src/pybind/mgr/dashboard/services/rbd.py @@ -236,6 +236,7 @@ class RbdConfiguration(object): """ Removes an option by name. Will not raise an error, if the option hasn't been found. :type option_name str + """ def _remove(ioctx): try: @@ -267,7 +268,6 @@ class RbdConfiguration(object): class RbdService(object): _rbd_inst = rbd.RBD() - # set of image features that can be enable on existing images ALLOW_ENABLE_FEATURES = {"exclusive-lock", "object-map", "fast-diff", "journaling"} @@ -694,6 +694,15 @@ class RbdService(object): rbd_inst = cls._rbd_inst return rbd_call(pool_name, namespace, rbd_inst.trash_move, image_name, delay) + @classmethod + def validate_namespace(cls, ioctx, namespace): + namespaces = cls._rbd_inst.namespace_list(ioctx) + if namespace and namespace not in namespaces: + raise DashboardException( + msg='Namespace not found', + code='namespace_not_found', + component='rbd') + class RbdSnapshotService(object):