]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
pybind/rbd: add Python bindings for mirror group control
authorImran Imtiaz <imran.imtiaz@uk.ibm.com>
Fri, 13 Feb 2026 11:06:00 +0000 (11:06 +0000)
committerImran Imtiaz <imran.imtiaz@uk.ibm.com>
Fri, 13 Feb 2026 11:06:00 +0000 (11:06 +0000)
Add methods to the rbd.Group class for mirror group operations:
- mirror_group_enable(mode) / mirror_group_disable(force)
- mirror_group_promote(force) / mirror_group_demote()
- mirror_group_resync()
- mirror_group_get_global_status()
- mirror_group_get_instance_id()

Also adds the required Cython declarations in c_rbd.pxd for
rbd_mirror_group_status_state_t, rbd_mirror_group_site_status_t,
and rbd_mirror_group_global_status_t structs.

Fixes: https://tracker.ceph.com/issues/74707
Signed-off-by: Imran Imtiaz <imran.imtiaz@uk.ibm.com>
src/include/rbd/librbd.h
src/librbd/librbd.cc
src/pybind/rbd/c_rbd.pxd
src/pybind/rbd/mock_rbd.pxi
src/pybind/rbd/rbd.pyx

index d32e267c73cd76948d9dc760fca0ff3ff7255aa0..cd828c6ec7e7223b36373c7fcf88684c02c6cc25 100644 (file)
@@ -1699,6 +1699,9 @@ CEPH_RBD_API int rbd_aio_mirror_group_get_info(
     rados_ioctx_t p, const char *name,
     rbd_mirror_group_info_t *mirror_group_info, size_t info_size,
     rbd_completion_t c);
+CEPH_RBD_API int rbd_mirror_group_get_instance_id(
+    rados_ioctx_t p, const char *name, char *instance_id,
+    size_t *instance_id_max_length);
 
 CEPH_RBD_API int rbd_namespace_create(rados_ioctx_t io,
                                       const char *namespace_name);
index 3b609b26266fcff11c8deb8fae17f05152213922..45c32d4c65598f91855010891706c0103b12d8e9 100644 (file)
@@ -4208,7 +4208,7 @@ extern "C" int rbd_mirror_group_global_status_list(rados_ioctx_t p,
   return 0;
 }
 
-extern "C" void rbd_mirror_group_status_cleanup(
+extern "C" void rbd_mirror_group_global_status_cleanup(
     rbd_mirror_group_global_status_t *status) {
   free(status->name);
   rbd_mirror_group_get_info_cleanup(&status->info);
@@ -4222,7 +4222,7 @@ extern "C" void rbd_mirror_group_global_status_list_cleanup(
     char **group_ids, rbd_mirror_group_global_status_t *groups, size_t len) {
   for (size_t i = 0; i < len; i++) {
     free(group_ids[i]);
-    rbd_mirror_group_status_cleanup(&groups[i]);
+    rbd_mirror_group_global_status_cleanup(&groups[i]);
   }
 }
 
index ad9f54249093a2d50deff0c123b541c4ea178d02..25744dc7843695da96b35465e27f81bb54b387b3 100644 (file)
@@ -211,6 +211,31 @@ cdef extern from "rbd/librbd.h" nogil:
         rbd_mirror_group_state_t state
         bint primary
 
+    ctypedef enum rbd_mirror_group_status_state_t:
+        _MIRROR_GROUP_STATUS_STATE_UNKNOWN "MIRROR_GROUP_STATUS_STATE_UNKNOWN"
+        _MIRROR_GROUP_STATUS_STATE_ERROR "MIRROR_GROUP_STATUS_STATE_ERROR"
+        _MIRROR_GROUP_STATUS_STATE_STARTING_REPLAY "MIRROR_GROUP_STATUS_STATE_STARTING_REPLAY"
+        _MIRROR_GROUP_STATUS_STATE_REPLAYING "MIRROR_GROUP_STATUS_STATE_REPLAYING"
+        _MIRROR_GROUP_STATUS_STATE_STOPPING_REPLAY "MIRROR_GROUP_STATUS_STATE_STOPPING_REPLAY"
+        _MIRROR_GROUP_STATUS_STATE_STOPPED "MIRROR_GROUP_STATUS_STATE_STOPPED"
+
+    ctypedef struct rbd_mirror_group_site_status_t:
+        char *mirror_uuid
+        rbd_mirror_group_status_state_t state
+        char *description
+        uint32_t mirror_image_count
+        int64_t *mirror_image_pool_ids
+        char **mirror_image_global_ids
+        rbd_mirror_image_site_status_t *mirror_images
+        time_t last_update
+        bint up
+
+    ctypedef struct rbd_mirror_group_global_status_t:
+        char *name
+        rbd_mirror_group_info_t info
+        uint32_t site_statuses_count
+        rbd_mirror_group_site_status_t *site_statuses
+
     ctypedef enum rbd_lock_mode_t:
         _RBD_LOCK_MODE_EXCLUSIVE "RBD_LOCK_MODE_EXCLUSIVE"
         _RBD_LOCK_MODE_SHARED "RBD_LOCK_MODE_SHARED"
@@ -519,6 +544,23 @@ cdef extern from "rbd/librbd.h" nogil:
     void rbd_mirror_group_info_list_cleanup(
         char **group_ids, rbd_mirror_group_info_t *info_entries,
         size_t num_entries);
+    int rbd_mirror_group_enable(rados_ioctx_t gp_ioctx, const char *gp_name,
+                                rbd_mirror_image_mode_t mirror_image_mode)
+    int rbd_mirror_group_disable(rados_ioctx_t gp_ioctx, const char *gp_name,
+                                 bint force)
+    int rbd_mirror_group_promote(rados_ioctx_t gp_ioctx, const char *gp_name,
+                                 bint force)
+    int rbd_mirror_group_demote(rados_ioctx_t gp_ioctx, const char *gp_name)
+    int rbd_mirror_group_resync(rados_ioctx_t gp_ioctx, const char *gp_name)
+    int rbd_mirror_group_get_global_status(rados_ioctx_t gp_ioctx, const char *gp_name,
+                                           rbd_mirror_group_global_status_t *status,
+                                           size_t status_size)
+    void rbd_mirror_group_global_status_cleanup(
+        rbd_mirror_group_global_status_t *status)
+    int rbd_mirror_group_get_instance_id(rados_ioctx_t gp_ioctx,
+                                         const char *gp_name,
+                                         char *instance_id,
+                                         size_t *max_instance_id_len)
 
     int rbd_pool_metadata_get(rados_ioctx_t io_ctx, const char *key,
                               char *value, size_t *val_len)
index bed3fa53ddb7272f8b1691d6e48a21ab7e0131fd..22f1c0d81238d88533c7bb9a20ec035d063d46f2 100644 (file)
@@ -215,6 +215,31 @@ cdef nogil:
         rbd_mirror_group_state_t state
         bint primary
 
+    ctypedef enum rbd_mirror_group_status_state_t:
+        _MIRROR_GROUP_STATUS_STATE_UNKNOWN "MIRROR_GROUP_STATUS_STATE_UNKNOWN"
+        _MIRROR_GROUP_STATUS_STATE_ERROR "MIRROR_GROUP_STATUS_STATE_ERROR"
+        _MIRROR_GROUP_STATUS_STATE_STARTING_REPLAY "MIRROR_GROUP_STATUS_STATE_STARTING_REPLAY"
+        _MIRROR_GROUP_STATUS_STATE_REPLAYING "MIRROR_GROUP_STATUS_STATE_REPLAYING"
+        _MIRROR_GROUP_STATUS_STATE_STOPPING_REPLAY "MIRROR_GROUP_STATUS_STATE_STOPPING_REPLAY"
+        _MIRROR_GROUP_STATUS_STATE_STOPPED "MIRROR_GROUP_STATUS_STATE_STOPPED"
+
+    ctypedef struct rbd_mirror_group_site_status_t:
+        char *mirror_uuid
+        rbd_mirror_group_status_state_t state
+        char *description
+        uint32_t mirror_image_count
+        int64_t *mirror_image_pool_ids
+        char **mirror_image_global_ids
+        rbd_mirror_image_site_status_t *mirror_images
+        time_t last_update
+        bint up
+
+    ctypedef struct rbd_mirror_group_global_status_t:
+        char *name
+        rbd_mirror_group_info_t info
+        uint32_t site_statuses_count
+        rbd_mirror_group_site_status_t *site_statuses
+
     ctypedef enum rbd_lock_mode_t:
         _RBD_LOCK_MODE_EXCLUSIVE "RBD_LOCK_MODE_EXCLUSIVE"
         _RBD_LOCK_MODE_SHARED "RBD_LOCK_MODE_SHARED"
@@ -590,6 +615,32 @@ cdef nogil:
         size_t num_entries):
         pass
 
+    int rbd_mirror_group_enable(rados_ioctx_t gp_ioctx, const char *gp_name,
+                                rbd_mirror_image_mode_t mirror_image_mode):
+        pass
+    int rbd_mirror_group_disable(rados_ioctx_t gp_ioctx, const char *gp_name,
+                                 bint force):
+        pass
+    int rbd_mirror_group_promote(rados_ioctx_t gp_ioctx, const char *gp_name,
+                                 bint force):
+        pass
+    int rbd_mirror_group_demote(rados_ioctx_t gp_ioctx, const char *gp_name):
+        pass
+    int rbd_mirror_group_resync(rados_ioctx_t gp_ioctx, const char *gp_name):
+        pass
+    int rbd_mirror_group_get_global_status(rados_ioctx_t gp_ioctx, const char *gp_name,
+                                           rbd_mirror_group_global_status_t *status,
+                                           size_t status_size):
+        pass
+    void rbd_mirror_group_global_status_cleanup(
+        rbd_mirror_group_global_status_t *status):
+        pass
+    int rbd_mirror_group_get_instance_id(rados_ioctx_t gp_ioctx,
+                                         const char *gp_name,
+                                         char *instance_id,
+                                         size_t *instance_id_max_length):
+        pass
+
     int rbd_pool_metadata_get(rados_ioctx_t io_ctx, const char *key,
                               char *value, size_t *val_len):
         pass
index 6ea3bae65c87b6ff66bca5ac44a351e59ba48aa5..bac678faf1746c372e6c5b32be43ad8ea0d0630b 100644 (file)
@@ -3249,6 +3249,167 @@ cdef class Group(object):
 
         return completion
 
+    def mirror_group_enable(self, mode):
+        """
+        Enable mirroring for the group.
+
+        :param mode: mirror mode (RBD_MIRROR_IMAGE_MODE_SNAPSHOT)
+        """
+        cdef:
+            rbd_mirror_image_mode_t _mode = mode
+        with nogil:
+            ret = rbd_mirror_group_enable(self._ioctx, self._name, _mode)
+        if ret != 0:
+            raise make_ex(ret, 'error enabling mirroring for group %s' % self._name,
+                          group_errno_to_exception)
+
+    def mirror_group_disable(self, force):
+        """
+        Disable mirroring for the group.
+
+        :param force: force disable even if not primary
+        """
+        cdef:
+            bint _force = force
+        with nogil:
+            ret = rbd_mirror_group_disable(self._ioctx, self._name, _force)
+        if ret != 0:
+            raise make_ex(ret, 'error disabling mirroring for group %s' % self._name,
+                          group_errno_to_exception)
+
+    def mirror_group_promote(self, force):
+        """
+        Promote group to primary for mirroring.
+
+        :param force: force promotion even if not cleanly demoted
+        """
+        cdef:
+            bint _force = force
+        with nogil:
+            ret = rbd_mirror_group_promote(self._ioctx, self._name, _force)
+        if ret != 0:
+            raise make_ex(ret, 'error promoting group %s to primary' % self._name,
+                          group_errno_to_exception)
+
+    def mirror_group_demote(self):
+        """
+        Demote group to non-primary for mirroring.
+        """
+        with nogil:
+            ret = rbd_mirror_group_demote(self._ioctx, self._name)
+        if ret != 0:
+            raise make_ex(ret, 'error demoting group %s to non-primary' % self._name,
+                          group_errno_to_exception)
+
+    def mirror_group_resync(self):
+        """
+        Flag the group to resync.
+        """
+        with nogil:
+            ret = rbd_mirror_group_resync(self._ioctx, self._name)
+        if ret != 0:
+            raise make_ex(ret, 'error flagging group for resync %s' % self._name,
+                          group_errno_to_exception)
+
+    def mirror_group_get_global_status(self):
+        """
+        Get global mirroring status for the group.
+
+        :returns: dict - contains the following keys:
+
+            * ``name`` (str) - group name
+            * ``info`` (dict) - mirror group info with keys:
+
+                * ``global_id`` (str) - group global id
+                * ``image_mode`` (int) - mirror mode
+                * ``state`` (int) - mirror state
+                * ``primary`` (bool) - is group primary
+
+            * ``site_statuses`` (list) - list of site status dicts with keys:
+
+                * ``mirror_uuid`` (str) - mirror UUID
+                * ``state`` (int) - group status mirror state
+                * ``description`` (str) - status description
+                * ``last_update`` (datetime) - last status update time
+                * ``up`` (bool) - is mirroring agent responsible for group up
+                * ``mirror_images`` (list) - list of mirror image dicts with keys:
+
+                    * ``pool_id`` (int) - pool ID
+                    * ``global_id`` (str) - image global ID
+                    * ``state`` (int) - image status mirror state
+                    * ``description`` (str) - status description
+                    * ``last_update`` (datetime) - last status update time
+                    * ``up`` (bool) - is mirroring agent responsible for image up
+        """
+        cdef rbd_mirror_group_global_status_t c_status
+        with nogil:
+            ret = rbd_mirror_group_get_global_status(self._ioctx, self._name,
+                                                     &c_status, sizeof(c_status))
+        if ret != 0:
+            raise make_ex(ret, 'error getting global mirror status for group %s' % self._name,
+                          group_errno_to_exception)
+
+        try:
+            status = {
+                'name': decode_cstr(c_status.name),
+                'info': {
+                    'global_id': decode_cstr(c_status.info.global_id),
+                    'image_mode': int(c_status.info.mirror_image_mode),
+                    'state': int(c_status.info.state),
+                    'primary': c_status.info.primary,
+                },
+                'site_statuses': []
+            }
+
+            for i in range(c_status.site_statuses_count):
+                site = {
+                    'mirror_uuid': decode_cstr(c_status.site_statuses[i].mirror_uuid),
+                    'state': int(c_status.site_statuses[i].state),
+                    'description': decode_cstr(c_status.site_statuses[i].description),
+                    'last_update': datetime.fromtimestamp(c_status.site_statuses[i].last_update,
+                                                          tz=timezone.utc),
+                    'up': c_status.site_statuses[i].up,
+                    'mirror_images': []
+                }
+
+                for j in range(c_status.site_statuses[i].mirror_image_count):
+                    image = {
+                        'pool_id': c_status.site_statuses[i].mirror_image_pool_ids[j],
+                        'global_id': decode_cstr(c_status.site_statuses[i].mirror_image_global_ids[j]),
+                        'state': int(c_status.site_statuses[i].mirror_images[j].state),
+                        'description': decode_cstr(c_status.site_statuses[i].mirror_images[j].description),
+                        'last_update': datetime.fromtimestamp(c_status.site_statuses[i].mirror_images[j].last_update,
+                                                              tz=timezone.utc),
+                        'up': c_status.site_statuses[i].mirror_images[j].up,
+                    }
+                    site['mirror_images'].append(image)
+
+                status['site_statuses'].append(site)
+
+            return status
+        finally:
+            rbd_mirror_group_global_status_cleanup(&c_status)
+
+    def mirror_group_get_instance_id(self):
+        """
+        Get the instance ID of the mirror daemon managing this group.
+
+        :returns: str - instance ID
+        """
+        cdef:
+            char instance_id[4096]
+            size_t max_len = sizeof(instance_id)
+
+        with nogil:
+            ret = rbd_mirror_group_get_instance_id(self._ioctx, self._name,
+                                                   instance_id, &max_len)
+        if ret != 0:
+            raise make_ex(ret, 'error getting mirror instance ID for group %s' % self._name,
+                          group_errno_to_exception)
+
+        return decode_cstr(instance_id)
+
+
 def requires_not_closed(f):
     def wrapper(self, *args, **kwargs):
         self.require_not_closed()