From: VinayBhaskar-V Date: Wed, 20 Aug 2025 18:30:09 +0000 (+0000) Subject: rbd-mirror: integration of the new GroupSnapshotNamespaceMirror::complete field X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=03844c150109f75cd797015eea75f54e4fb166dc;p=ceph-ci.git rbd-mirror: integration of the new GroupSnapshotNamespaceMirror::complete field This commit introduces the new field **complete**, of type **MirrorGroupSnapshotCompleteState** enum, to the GroupSnapshotNamespaceMirror structure. This change is necessary to align behavior of mirror group snapshots with that of mirror image snapshots, allowing for a precise differentiation between a group snapshot that has been created and one that has been fully synced. **1. Handling Old-Style Snapshots** Decoding Old Snapshots: The original GroupSnapshotNamespaceMirror structure lacked the complete field, which implicitly defaulted to a bool value of false upon initialization. When an old snapshot (lacking the complete field) is decoded by an upgraded client, the implicit default value maps to MIRROR_GROUP_SNAPSHOT_COMPLETE_IF_CREATED. Completion Check: A snapshot is determined old by checking it's complete filed i.e complete == MIRROR_GROUP_SNAPSHOT_COMPLETE_IF_CREATED and if it's old the sync completion for these group snapshots is determined by checking the state field i.e state == GROUP_SNAPSHOT_STATE_CREATED. During a upgrade where **OSDs have not yet been updated**, the new client will be forced to create snapshots using the old style. These snapshots will be initialized with MIRROR_GROUP_SNAPSHOT_COMPLETE_IF_CREATED and will stay on that to prevent immediate, incorrect cleanup by the old OSDs and in this case state field is set to **GROUP_SNAPSHOT_STATE_CREATED** only after snapshot completed it's sync. **2. Handling New-Style Snapshots** New snapshots are initialized with complete == **MIRROR_GROUP_SNAPSHOT_INCOMPLETE**, state == GROUP_SNAPSHOT_STATE_CREATING. The group snapshot's state is marked as GROUP_SNAPSHOT_STATE_CREATED as soon as its metadata is fully available and stored. Completion Check: The snapshot's sync is confirmed only when complete == MIRROR_GROUP_SNAPSHOT_COMPLETE along with state check (state == GROUP_SNAPSHOT_STATE_CREATED) is satisfied. This approach ensures seamless transition and compatibility, allowing the system to correctly interpret the synchronization status of both old and newly created group snapshots. Signed-off-by: VinayBhaskar-V Resolves: rhbz#2396583 --- diff --git a/qa/workunits/rbd/rbd_mirror_group_simple.sh b/qa/workunits/rbd/rbd_mirror_group_simple.sh index 9b42f95332f..090690e2a03 100755 --- a/qa/workunits/rbd/rbd_mirror_group_simple.sh +++ b/qa/workunits/rbd/rbd_mirror_group_simple.sh @@ -1670,7 +1670,7 @@ test_create_group_with_regular_snapshots_then_mirror() group_snap_create "${primary_cluster}" "${pool}/${group}" "${snap}" check_group_snap_exists "${primary_cluster}" "${pool}/${group}" "${snap}" local group_snap_id - get_newest_group_snapshot_id "${primary_cluster}" "${pool}/${group}" group_snap_id + get_newest_created_group_snapshot_id "${primary_cluster}" "${pool}/${group}" group_snap_id mirror_group_enable "${primary_cluster}" "${pool}/${group}" wait_for_group_present "${secondary_cluster}" "${pool}" "${group}" "${group_image_count}" @@ -1681,10 +1681,9 @@ test_create_group_with_regular_snapshots_then_mirror() wait_for_group_status_in_pool_dir "${primary_cluster}" "${pool}"/"${group}" 'down+unknown' 0 fi + wait_for_group_synced "${primary_cluster}" "${pool}/${group}" "${secondary_cluster}" "${pool}/${group}" check_group_snap_exists "${secondary_cluster}" "${pool}/${group}" "${snap}" - wait_for_group_snap_sync_complete "${secondary_cluster}" "${pool}/${group}" "${group_snap_id}" - wait_for_group_synced "${primary_cluster}" "${pool}/${group}" "${secondary_cluster}" "${pool}/${group}" mirror_group_snapshot_and_wait_for_sync_complete "${secondary_cluster}" "${primary_cluster}" "${pool}"/"${group}" group_snap_remove "${primary_cluster}" "${pool}/${group}" "${snap}" @@ -1952,9 +1951,9 @@ test_stopped_daemon() wait_for_group_synced "${primary_cluster}" "${pool}"/"${group}" "${secondary_cluster}" "${pool}"/"${group}" local primary_group_snap_id - get_newest_group_snapshot_id "${primary_cluster}" "${pool}"/"${group}" primary_group_snap_id + get_newest_complete_mirror_group_snapshot_id "${primary_cluster}" "${pool}"/"${group}" primary_group_snap_id local secondary_group_snap_id - get_newest_group_snapshot_id "${secondary_cluster}" "${pool}"/"${group}" secondary_group_snap_id + get_newest_complete_mirror_group_snapshot_id "${secondary_cluster}" "${pool}"/"${group}" secondary_group_snap_id test "${primary_group_snap_id}" = "${secondary_group_snap_id}" || { fail "mismatched ids"; return 1; } # Add image to synced group (whilst daemon is stopped) @@ -1979,7 +1978,7 @@ test_stopped_daemon() wait_for_group_present "${secondary_cluster}" "${pool}" "${group}" "${group_image_count}" fi - get_newest_group_snapshot_id "${primary_cluster}" "${pool}"/"${group}" primary_group_snap_id + get_newest_complete_mirror_group_snapshot_id "${primary_cluster}" "${pool}"/"${group}" primary_group_snap_id test "${primary_group_snap_id}" != "${secondary_group_snap_id}" || { fail "matched ids"; return 1; } echo "starting daemon" @@ -1988,7 +1987,7 @@ test_stopped_daemon() wait_for_group_status_in_pool_dir "${secondary_cluster}" "${pool}"/"${group}" 'up+replaying' $(("${group_image_count}"+1)) wait_for_group_synced "${primary_cluster}" "${pool}"/"${group}" "${secondary_cluster}" "${pool}"/"${group}" - get_newest_group_snapshot_id "${secondary_cluster}" "${pool}"/"${group}" secondary_group_snap_id + get_newest_complete_mirror_group_snapshot_id "${secondary_cluster}" "${pool}"/"${group}" secondary_group_snap_id test "${primary_group_snap_id}" = "${secondary_group_snap_id}" || { fail "mismatched ids"; return 1; } # removed image from synced group (whilst daemon is stopped) @@ -2008,7 +2007,7 @@ test_stopped_daemon() mirror_group_enable "${primary_cluster}" "${pool}/${group}" fi - get_newest_group_snapshot_id "${primary_cluster}" "${pool}"/"${group}" primary_group_snap_id + get_newest_complete_mirror_group_snapshot_id "${primary_cluster}" "${pool}"/"${group}" primary_group_snap_id test "${primary_group_snap_id}" != "${secondary_group_snap_id}" || { fail "matched ids"; return 1; } echo "starting daemon" @@ -2018,7 +2017,7 @@ test_stopped_daemon() wait_for_group_status_in_pool_dir "${secondary_cluster}" "${pool}"/"${group}" 'up+replaying' "${group_image_count}" wait_for_group_synced "${primary_cluster}" "${pool}"/"${group}" "${secondary_cluster}" "${pool}"/"${group}" - get_newest_group_snapshot_id "${secondary_cluster}" "${pool}"/"${group}" secondary_group_snap_id + get_newest_complete_mirror_group_snapshot_id "${secondary_cluster}" "${pool}"/"${group}" secondary_group_snap_id test "${primary_group_snap_id}" = "${secondary_group_snap_id}" || { fail "mismatched ids"; return 1; } # TODO When dynamic groups are support this test could be extended with more actions whilst daemon is stopped. @@ -2641,7 +2640,7 @@ test_force_promote() fi local group_snap_id - get_newest_group_snapshot_id "${primary_cluster}" "${pool}/${group0}" group_snap_id + get_newest_complete_mirror_group_snapshot_id "${primary_cluster}" "${pool}/${group0}" group_snap_id echo "id = ${group_snap_id}" wait_for_test_group_snap_present "${secondary_cluster}" "${pool}/${group0}" "${group_snap_id}" 1 @@ -2890,7 +2889,7 @@ test_force_promote_before_initial_sync() mirror_group_enable "${primary_cluster}" "${pool}/${group0}" wait_for_group_present "${secondary_cluster}" "${pool}" "${group0}" "${image_count}" local group_snap_id - get_newest_group_snapshot_id "${primary_cluster}" "${pool}/${group0}" group_snap_id + get_newest_complete_mirror_group_snapshot_id "${primary_cluster}" "${pool}/${group0}" group_snap_id wait_for_test_group_snap_present "${secondary_cluster}" "${pool}/${group0}" "${group_snap_id}" 1 # stop the daemon to prevent further syncing of snapshots @@ -3281,8 +3280,8 @@ test_odf_failover_failback() # failback to original primary (cluster2) local group_snap_id_a group_snap_id_b - get_newest_group_snapshot_id "${secondary_cluster}" "${pool}"/"${group0}" group_snap_id_a - get_newest_group_snapshot_id "${primary_cluster}" "${pool}"/"${group0}" group_snap_id_b + get_newest_complete_mirror_group_snapshot_id "${secondary_cluster}" "${pool}"/"${group0}" group_snap_id_a + get_newest_complete_mirror_group_snapshot_id "${primary_cluster}" "${pool}"/"${group0}" group_snap_id_b test "${group_snap_id_a}" = "${group_snap_id_b}" || fail "group not synced" # demote - neither site is primary @@ -3291,7 +3290,7 @@ test_odf_failover_failback() # confirm that a new snapshot was taken by the demote operation local group_snap_id_c - get_newest_group_snapshot_id "${secondary_cluster}" "${pool}"/"${group0}" group_snap_id_c + get_newest_complete_mirror_group_snapshot_id "${secondary_cluster}" "${pool}"/"${group0}" group_snap_id_c test "${group_snap_id_a}" != "${group_snap_id_c}" || fail "new snap not taken by demote" local group_id_before group_id_after @@ -3314,8 +3313,8 @@ test_odf_failover_failback() wait_for_group_synced "${secondary_cluster}" "${pool}"/"${group0}" "${primary_cluster}" "${pool}/${group0}" local group_snap_id_e group_snap_id_f - get_newest_group_snapshot_id "${secondary_cluster}" "${pool}"/"${group0}" group_snap_id_e - get_newest_group_snapshot_id "${primary_cluster}" "${pool}"/"${group0}" group_snap_id_f + get_newest_complete_mirror_group_snapshot_id "${secondary_cluster}" "${pool}"/"${group0}" group_snap_id_e + get_newest_complete_mirror_group_snapshot_id "${primary_cluster}" "${pool}"/"${group0}" group_snap_id_f test "${group_snap_id_c}" = "${group_snap_id_e}" || fail "new snap on original secondary" test "${group_snap_id_c}" = "${group_snap_id_f}" || fail "group not synced" @@ -3510,7 +3509,7 @@ test_resync() compare_image_with_snapshot "${secondary_cluster}" "${pool}/${image_prefix}0" "${primary_cluster}" "${pool}/${image_prefix}0@${snap0}" local group_snap_id secondary_group_snap_id primary_group_snap_id - get_newest_group_snapshot_id "${primary_cluster}" "${pool}/${group0}" primary_group_snap_id + get_newest_complete_mirror_group_snapshot_id "${primary_cluster}" "${pool}/${group0}" primary_group_snap_id echo "id = ${primary_group_snap_id}" # stop the daemon to prevent further syncing of snapshots @@ -3644,12 +3643,12 @@ test_demote_snap_sync() stop_mirrors "${secondary_cluster}" wait_for_group_status_in_pool_dir "${secondary_cluster}" "${pool}"/"${group0}" 'down+stopped' local group_snap_id - get_newest_group_snapshot_id "${primary_cluster}" "${pool}/${group0}" group_snap_id + get_newest_complete_mirror_group_snapshot_id "${primary_cluster}" "${pool}/${group0}" group_snap_id mirror_group_demote "${primary_cluster}" "${pool}/${group0}" local primary_demote_snap_id - get_newest_group_snapshot_id "${primary_cluster}" "${pool}/${group0}" primary_demote_snap_id + get_newest_complete_mirror_group_snapshot_id "${primary_cluster}" "${pool}/${group0}" primary_demote_snap_id test "${group_snap_id}" != "${primary_demote_snap_id}" || { fail "no new snapshot after demote"; return 1; } @@ -3659,7 +3658,7 @@ test_demote_snap_sync() wait_for_group_status_in_pool_dir "${primary_cluster}" "${pool}/${group0}" 'up+unknown' local secondary_snap_id - get_newest_group_snapshot_id "${secondary_cluster}" "${pool}/${group0}" secondary_snap_id + get_newest_complete_mirror_group_snapshot_id "${secondary_cluster}" "${pool}/${group0}" secondary_snap_id test "${primary_demote_snap_id}" = "${secondary_snap_id}" || { fail "demote snapshot ${primary_demote_snap_id} not synced"; return 1; } @@ -3705,7 +3704,7 @@ test_demote_snap_sync_after_restart() write_image "${primary_cluster}" "${pool}" "${image_prefix}1" 256 4194304 mirror_group_demote "${primary_cluster}" "${pool}/${group0}" local group_snap_id - get_newest_group_snapshot_id "${primary_cluster}" "${pool}/${group0}" group_snap_id + get_newest_complete_mirror_group_snapshot_id "${primary_cluster}" "${pool}/${group0}" group_snap_id wait_for_test_group_snap_present "${secondary_cluster}" "${pool}/${group0}" "${group_snap_id}" 1 stop_mirrors "${secondary_cluster}" '-9' diff --git a/qa/workunits/rbd/rbd_mirror_helpers.sh b/qa/workunits/rbd/rbd_mirror_helpers.sh index 5c8e434593f..86aa135c7db 100755 --- a/qa/workunits/rbd/rbd_mirror_helpers.sh +++ b/qa/workunits/rbd/rbd_mirror_helpers.sh @@ -2299,7 +2299,7 @@ wait_for_group_synced() local secondary_group_spec=$4 local group_snap_id - get_newest_group_snapshot_id "${cluster}" "${group_spec}" group_snap_id + get_newest_complete_mirror_group_snapshot_id "${cluster}" "${group_spec}" group_snap_id wait_for_group_snap_present "${secondary_cluster}" "${secondary_group_spec}" "${group_snap_id}" wait_for_group_snap_sync_complete "${secondary_cluster}" "${secondary_group_spec}" "${group_snap_id}" } @@ -2697,9 +2697,21 @@ test_group_snap_sync_state() local group_snap_id=$3 local expected_state=$4 - run_cmd "rbd --cluster ${cluster} group snap list ${group_spec} --format xml --pretty-format" + run_cmd "rbd --cluster ${cluster} group snap list ${group_spec} --format xml --pretty-format" + + # Get and for the given group snapshot ID + local state snaps_synced + state=$(xmlstarlet sel -t -v "//group_snaps/group_snap[id='${group_snap_id}']/state" < "$CMD_STDOUT") + snaps_synced=$(xmlstarlet sel -t -v "//group_snaps/group_snap[id='${group_snap_id}']/namespace/complete" < "$CMD_STDOUT") - test "${expected_state}" = "$(xmlstarlet sel -t -v "//group_snaps/group_snap[id='${group_snap_id}']/state" < "$CMD_STDOUT")" || { fail; return 1; } + if [ "$expected_state" = "complete" ]; then + # Test if state is created and snaps_synced is 'true' + test "$state" = "created" || { fail; return 1; } + test "$snaps_synced" = "true" || { fail; return 1; } + elif [ "$expected_state" = "incomplete" ]; then + # Test if snaps_synced is 'false' + test "$snaps_synced" = "false" || { fail; return 1; } + fi } test_group_snap_sync_complete() @@ -2878,14 +2890,27 @@ wait_for_group_replay_stopped() wait_for_group_replay_state "${cluster}" "${group_spec}" 'stopped' "${image_count}" 'false' } -get_newest_group_snapshot_id() +get_newest_created_group_snapshot_id() { local cluster=$1 local group_spec=$2 local -n _group_snap_id=$3 - run_cmd "rbd --cluster ${cluster} group snap list ${group_spec} --format xml --pretty-format" - _group_snap_id=$(xmlstarlet sel -t -v "(//group_snaps/group_snap[state='complete']/id)[last()]" "$CMD_STDOUT" ) && return 0 + run_cmd "rbd --cluster ${cluster} group snap list ${group_spec} --format xml --pretty-format" + _group_snap_id=$(xmlstarlet sel -t -v "(//group_snaps/group_snap[state='created']/id)[last()]" "$CMD_STDOUT" ) && return 0 + + fail "Failed to get snapshot id" + return 1 +} + +get_newest_complete_mirror_group_snapshot_id() +{ + local cluster=$1 + local group_spec=$2 + local -n _group_snap_id=$3 + + run_cmd "rbd --cluster ${cluster} group snap list ${group_spec} --format xml --pretty-format" + _group_snap_id=$(xmlstarlet sel -t -v "(//group_snaps/group_snap[namespace/complete='true']/id)[last()]" "$CMD_STDOUT" ) && return 0 fail "Failed to get snapshot id" return 1 @@ -2945,7 +2970,7 @@ test_images_in_latest_synced_group() local expected_synced_image_count=$3 local group_snap_id - get_newest_group_snapshot_id "${cluster}" "${group_spec}" group_snap_id + get_newest_complete_mirror_group_snapshot_id "${cluster}" "${group_spec}" group_snap_id test_group_synced_image_status "${cluster}" "${group_spec}" "${group_snap_id}" "${expected_synced_image_count}" } diff --git a/src/cls/rbd/cls_rbd.cc b/src/cls/rbd/cls_rbd.cc index b8fe251f2b7..f32050c71ca 100644 --- a/src/cls/rbd/cls_rbd.cc +++ b/src/cls/rbd/cls_rbd.cc @@ -8685,7 +8685,7 @@ int group_snap_set(cls_method_context_t hctx, } std::string key = group::snap_key(group_snap.id); - if (group_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE) { + if (group_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_CREATING) { bufferlist snap_bl; r = cls_cxx_map_get_val(hctx, key, &snap_bl); if (r < 0 && r != -ENOENT) { @@ -8818,8 +8818,8 @@ int group_snap_unlink(cls_method_context_t hctx, CLS_ERR("error decoding snapshot: %s", group_snap_id.c_str()); return -EIO; } - if (group_snap.state != cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE) { - CLS_LOG(20, "snap %s not complete", group_snap_id.c_str()); + if (group_snap.state != cls::rbd::GROUP_SNAPSHOT_STATE_CREATED) { + CLS_LOG(20, "snap %s is not created", group_snap_id.c_str()); return -ENOENT; } diff --git a/src/cls/rbd/cls_rbd_types.cc b/src/cls/rbd/cls_rbd_types.cc index 19fe72653ff..cb0a19e88ef 100644 --- a/src/cls/rbd/cls_rbd_types.cc +++ b/src/cls/rbd/cls_rbd_types.cc @@ -1465,6 +1465,24 @@ std::ostream& operator<<(std::ostream& os, MirrorSnapshotState state) { return os; } +std::ostream& operator<<(std::ostream& os, MirrorGroupSnapshotCompleteState state) { + switch (state) { + case MIRROR_GROUP_SNAPSHOT_COMPLETE_IF_CREATED: + os << "complete_if_created"; + break; + case MIRROR_GROUP_SNAPSHOT_INCOMPLETE: + os << "incomplete"; + break; + case MIRROR_GROUP_SNAPSHOT_COMPLETE: + os << "complete"; + break; + default: + os << "unknown (" << static_cast(state) << ")"; + break; + } + return os; +} + void GroupSnapshotNamespaceMirror::encode(bufferlist& bl) const { using ceph::encode; encode(state, bl); @@ -1486,7 +1504,7 @@ void GroupSnapshotNamespaceMirror::decode(uint8_t version, void GroupSnapshotNamespaceMirror::dump(Formatter *f) const { f->dump_stream("state") << state; - f->dump_bool("complete", complete); + f->dump_stream("complete") << complete; f->open_array_section("mirror_peer_uuids"); for (auto &peer : mirror_peer_uuids) { f->dump_string("mirror_peer_uuid", peer); @@ -1599,7 +1617,8 @@ void GroupSnapshotNamespace::generate_test_instances( o.push_back(new GroupSnapshotNamespace(GroupSnapshotNamespaceUser())); o.push_back(new GroupSnapshotNamespace(GroupSnapshotNamespaceMirror( MIRROR_SNAPSHOT_STATE_PRIMARY, - {"peer uuid"}, "", ""))); + {"peer uuid"}, "", "", + MIRROR_GROUP_SNAPSHOT_COMPLETE))); } std::ostream& operator<<(std::ostream& os, const GroupSnapshotNamespaceType& type) { @@ -1644,11 +1663,11 @@ std::ostream& operator<<(std::ostream& os, const GroupSnapshotNamespaceUnknown& std::ostream& operator<<(std::ostream& os, GroupSnapshotState state) { switch (state) { - case GROUP_SNAPSHOT_STATE_INCOMPLETE: - os << "incomplete"; + case GROUP_SNAPSHOT_STATE_CREATING: + os << "creating"; break; - case GROUP_SNAPSHOT_STATE_COMPLETE: - os << "complete"; + case GROUP_SNAPSHOT_STATE_CREATED: + os << "created"; break; default: os << "unknown (" << static_cast(state) << ")"; @@ -1727,13 +1746,13 @@ void GroupSnapshot::dump(Formatter *f) const { void GroupSnapshot::generate_test_instances(std::list &o) { o.push_back(new GroupSnapshot("10152ae8944a", GroupSnapshotNamespaceUser{}, "groupsnapshot1", - GROUP_SNAPSHOT_STATE_INCOMPLETE)); + GROUP_SNAPSHOT_STATE_CREATING)); o.push_back(new GroupSnapshot("1018643c9869", GroupSnapshotNamespaceMirror{ MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, - "uuid", "id"}, + "uuid", "id", MIRROR_GROUP_SNAPSHOT_COMPLETE}, "groupsnapshot2", - GROUP_SNAPSHOT_STATE_COMPLETE)); + GROUP_SNAPSHOT_STATE_CREATED)); } void TrashImageSpec::encode(bufferlist& bl) const { @@ -1992,5 +2011,46 @@ void sanitize_entity_inst(entity_inst_t* entity_inst) { entity_inst->addr.set_type(entity_addr_t::TYPE_ANY); } +bool is_mirror_group_snapshot_complete( + const cls::rbd::GroupSnapshotState &group_snap_state, + const cls::rbd::MirrorGroupSnapshotCompleteState &complete) { + // complete -- mirror_ns->complete is used + if (complete == cls::rbd::MIRROR_GROUP_SNAPSHOT_COMPLETE) { + ceph_assert(group_snap_state == cls::rbd::GROUP_SNAPSHOT_STATE_CREATED); + return true; + } + + // complete -- mirror_ns->complete is not used (backwards compatibility) + if (complete == cls::rbd::MIRROR_GROUP_SNAPSHOT_COMPLETE_IF_CREATED && + group_snap_state == cls::rbd::GROUP_SNAPSHOT_STATE_CREATED) { + return true; + } + + return false; +} + +cls::rbd::MirrorGroupSnapshotCompleteState +get_mirror_group_snapshot_complete_initial(int8_t require_osd_release) { + if (require_osd_release >= CEPH_RELEASE_TENTACLE) { + // a new-style mirror group snapshot (i.e. first + // MIRROR_GROUP_SNAPSHOT_INCOMPLETE, later transitions to + // MIRROR_GROUP_SNAPSHOT_COMPLETE) + return cls::rbd::MIRROR_GROUP_SNAPSHOT_INCOMPLETE; + } else { + // an old-style mirror group snapshot (i.e. stays in + // MIRROR_GROUP_SNAPSHOT_COMPLETE_IF_CREATED) + return cls::rbd::MIRROR_GROUP_SNAPSHOT_COMPLETE_IF_CREATED; + } +} + +void set_mirror_group_snapshot_complete(cls::rbd::GroupSnapshot& group_snap) { + auto& mirror_ns = std::get( + group_snap.snapshot_namespace); + if (mirror_ns.complete != cls::rbd::MIRROR_GROUP_SNAPSHOT_COMPLETE_IF_CREATED) { + ceph_assert(group_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_CREATED); + mirror_ns.complete = cls::rbd::MIRROR_GROUP_SNAPSHOT_COMPLETE; + } +} + } // namespace rbd } // namespace cls diff --git a/src/cls/rbd/cls_rbd_types.h b/src/cls/rbd/cls_rbd_types.h index 570e3c40b63..88e447badf7 100644 --- a/src/cls/rbd/cls_rbd_types.h +++ b/src/cls/rbd/cls_rbd_types.h @@ -977,12 +977,35 @@ struct GroupSnapshotNamespaceUser { } }; +enum MirrorGroupSnapshotCompleteState { + MIRROR_GROUP_SNAPSHOT_COMPLETE_IF_CREATED = 0, + MIRROR_GROUP_SNAPSHOT_INCOMPLETE = 1, + MIRROR_GROUP_SNAPSHOT_COMPLETE = 2, +}; + +inline void encode(const MirrorGroupSnapshotCompleteState& state, ceph::buffer::list& bl) +{ + using ceph::encode; + encode(static_cast(state), bl); +} + +inline void decode(MirrorGroupSnapshotCompleteState& state, + ceph::buffer::list::const_iterator& it) +{ + uint8_t int_state; + using ceph::decode; + decode(int_state, it); + state = static_cast(int_state); +} + +std::ostream& operator<<(std::ostream& os, MirrorGroupSnapshotCompleteState state); + struct GroupSnapshotNamespaceMirror { static const GroupSnapshotNamespaceType GROUP_SNAPSHOT_NAMESPACE_TYPE = GROUP_SNAPSHOT_NAMESPACE_TYPE_MIRROR; MirrorSnapshotState state = MIRROR_SNAPSHOT_STATE_NON_PRIMARY; - bool complete = false; // TODO: modify methods to handle this field + MirrorGroupSnapshotCompleteState complete = MIRROR_GROUP_SNAPSHOT_INCOMPLETE; std::set mirror_peer_uuids; std::string primary_mirror_uuid; @@ -993,8 +1016,9 @@ struct GroupSnapshotNamespaceMirror { GroupSnapshotNamespaceMirror(MirrorSnapshotState state, const std::set &mirror_peer_uuids, const std::string &primary_mirror_uuid, - const std::string &primary_snap_id) - : state(state), mirror_peer_uuids(mirror_peer_uuids), + const std::string &primary_snap_id, + MirrorGroupSnapshotCompleteState complete) + : state(state), complete(complete), mirror_peer_uuids(mirror_peer_uuids), primary_mirror_uuid(primary_mirror_uuid), primary_snap_id(primary_snap_id) { } @@ -1082,8 +1106,8 @@ GroupSnapshotNamespaceType get_group_snap_namespace_type( const GroupSnapshotNamespace& snapshot_namespace); enum GroupSnapshotState { - GROUP_SNAPSHOT_STATE_INCOMPLETE = 0, - GROUP_SNAPSHOT_STATE_COMPLETE = 1, + GROUP_SNAPSHOT_STATE_CREATING = 0, + GROUP_SNAPSHOT_STATE_CREATED = 1, }; inline void encode(const GroupSnapshotState &state, ceph::buffer::list& bl, uint64_t features=0) @@ -1141,7 +1165,7 @@ struct GroupSnapshot { GroupSnapshotNamespace snapshot_namespace = {GroupSnapshotNamespaceUser{}}; std::string name; - GroupSnapshotState state = GROUP_SNAPSHOT_STATE_INCOMPLETE; + GroupSnapshotState state = GROUP_SNAPSHOT_STATE_CREATING; std::vector snaps; GroupSnapshot() {} @@ -1421,6 +1445,15 @@ std::ostream& operator<<(std::ostream& os, const AssertSnapcSeqState& state); void sanitize_entity_inst(entity_inst_t* entity_inst); +bool is_mirror_group_snapshot_complete( + const cls::rbd::GroupSnapshotState &group_snap_state, + const cls::rbd::MirrorGroupSnapshotCompleteState &complete); + +cls::rbd::MirrorGroupSnapshotCompleteState +get_mirror_group_snapshot_complete_initial(int8_t require_osd_release); + +void set_mirror_group_snapshot_complete(cls::rbd::GroupSnapshot& group_snap); + } // namespace rbd } // namespace cls diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h index bac6e5ac86c..39c30feb59b 100644 --- a/src/include/rbd/librbd.h +++ b/src/include/rbd/librbd.h @@ -299,8 +299,11 @@ typedef struct { } rbd_group_spec_t; typedef enum { - RBD_GROUP_SNAP_STATE_INCOMPLETE, - RBD_GROUP_SNAP_STATE_COMPLETE + RBD_GROUP_SNAP_STATE_INCOMPLETE = 0, /* deprecated */ + RBD_GROUP_SNAP_STATE_COMPLETE = 1, /* deprecated */ + + RBD_GROUP_SNAP_STATE_CREATING = 0, + RBD_GROUP_SNAP_STATE_CREATED = 1 } rbd_group_snap_state_t; typedef enum { diff --git a/src/librbd/api/Group.cc b/src/librbd/api/Group.cc index 6c46a81d414..15a374e18e6 100644 --- a/src/librbd/api/Group.cc +++ b/src/librbd/api/Group.cc @@ -87,9 +87,11 @@ std::string calc_ind_image_snap_name(uint64_t pool_id, class GetGroupMirrorVisitor { public: group_snap_mirror_namespace_t *mirror_snap; + cls::rbd::GroupSnapshotState group_snap_state; - explicit GetGroupMirrorVisitor(group_snap_mirror_namespace_t *mirror_snap) - : mirror_snap(mirror_snap) {} + explicit GetGroupMirrorVisitor(group_snap_mirror_namespace_t *mirror_snap, + cls::rbd::GroupSnapshotState group_snap_state) + : mirror_snap(mirror_snap), group_snap_state(group_snap_state) {} template inline int operator()(const T&) const { @@ -99,7 +101,8 @@ public: inline int operator()( const cls::rbd::GroupSnapshotNamespaceMirror& snap_namespace) { mirror_snap->state = static_cast(snap_namespace.state); - mirror_snap->complete = snap_namespace.complete; + mirror_snap->complete = is_mirror_group_snapshot_complete(group_snap_state, + snap_namespace.complete); mirror_snap->mirror_peer_uuids = snap_namespace.mirror_peer_uuids; mirror_snap->primary_mirror_uuid = snap_namespace.primary_mirror_uuid; mirror_snap->primary_snap_id = snap_namespace.primary_snap_id; @@ -918,7 +921,7 @@ int Group::snap_create(librados::IoCtx& group_ioctx, group_snap.id = librbd::util::generate_image_id(group_ioctx); group_snap.name = string(snap_name); - group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE; + group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_CREATING; group_snap.snaps = image_snaps; cls::rbd::ImageSnapshotNamespaceGroup ne{group_ioctx.get_id(), group_id, @@ -1062,7 +1065,7 @@ int Group::snap_create(librados::IoCtx& group_ioctx, } group_snap.snaps = image_snaps; - group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE; + group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_CREATED; r = cls_client::group_snap_set(&group_ioctx, group_header_oid, group_snap); if (r < 0) { @@ -1366,8 +1369,8 @@ int Group::snap_rollback(librados::IoCtx& group_ioctx, return -ENOENT; } - if (group_snap->state != cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE) { - lderr(cct) << "group snapshot is not complete" << dendl; + if (group_snap->state != cls::rbd::GROUP_SNAPSHOT_STATE_CREATED) { + lderr(cct) << "group snapshot is not created" << dendl; return -EINVAL; } @@ -1461,7 +1464,7 @@ int Group::snap_get_mirror_namespace( return r; } - GetGroupMirrorVisitor visitor(mirror_namespace); + GetGroupMirrorVisitor visitor(mirror_namespace, group_snap.state); r = group_snap.snapshot_namespace.visit(visitor); if (r < 0) { return r; diff --git a/src/librbd/api/Mirror.cc b/src/librbd/api/Mirror.cc index 3d728c648ae..a180fa420ff 100644 --- a/src/librbd/api/Mirror.cc +++ b/src/librbd/api/Mirror.cc @@ -563,8 +563,9 @@ std::string prepare_non_primary_mirror_snap_name(CephContext *cct, int get_last_mirror_snapshot_state(librados::IoCtx &group_ioctx, const std::string &group_id, + cls::rbd::GroupSnapshotState *group_snap_state, cls::rbd::MirrorSnapshotState *state, - cls::rbd::GroupSnapshotState *sync) { + cls::rbd::MirrorGroupSnapshotCompleteState *sync) { std::vector snaps; C_SaferCond cond; @@ -582,10 +583,9 @@ int get_last_mirror_snapshot_state(librados::IoCtx &group_ioctx, &it->snapshot_namespace); if (ns != nullptr) { // XXXMG: check primary_mirror_uuid matches? + *group_snap_state = it->state; *state = ns->state; - if (sync != nullptr) { - *sync = it->state; - } + *sync = ns->complete; return 0; } } @@ -2613,9 +2613,19 @@ int prepare_group_images(IoCtx& group_ioctx, } } - group_snap->snapshot_namespace = cls::rbd::GroupSnapshotNamespaceMirror{ - snap_state, *mirror_peer_uuids, {}, {}}; + librados::Rados rados(group_ioctx); + int8_t require_osd_release; + r = rados.get_min_compatible_osd(&require_osd_release); + if (r < 0) { + lderr(cct) << "failed to retrieve min OSD release: " << cpp_strerror(r) + << dendl; + return r; + } + auto complete = cls::rbd::get_mirror_group_snapshot_complete_initial(require_osd_release); + group_snap->snapshot_namespace = cls::rbd::GroupSnapshotNamespaceMirror{ + snap_state, *mirror_peer_uuids, {}, {}, + complete}; for (auto image_ctx: *image_ctxs) { group_snap->snaps.emplace_back(image_ctx->md_ctx.get_id(), image_ctx->id, CEPH_NOSNAP); @@ -2992,23 +3002,37 @@ int create_orphan_group_snapshot(IoCtx& group_ioctx, << ", group_snap_id=" << group_snap_id << dendl; + librados::Rados rados(group_ioctx); + int8_t require_osd_release; + int r = rados.get_min_compatible_osd(&require_osd_release); + if (r < 0) { + lderr(cct) << "failed to retrieve min OSD release: " << cpp_strerror(r) + << dendl; + return r; + } + + auto complete = cls::rbd::get_mirror_group_snapshot_complete_initial(require_osd_release); + auto mirror_namespace = cls::rbd::GroupSnapshotNamespaceMirror( + cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, {}, {}, + complete); + cls::rbd::GroupSnapshot group_snap{ - group_snap_id, - cls::rbd::GroupSnapshotNamespaceMirror{ - cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, {} , {}}, + group_snap_id, mirror_namespace, prepare_non_primary_mirror_snap_name(cct, global_group_id, group_snap_id), - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; std::string group_header_oid = librbd::util::group_header_name(group_id); - int r = cls_client::group_snap_set(&group_ioctx, - group_header_oid, group_snap); + r = cls_client::group_snap_set(&group_ioctx, + group_header_oid, group_snap); if (r < 0) { lderr(cct) << "failed to create group snapshot: " << cpp_strerror(r) << dendl; return r; } - group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE; + group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_CREATED; + cls::rbd::set_mirror_group_snapshot_complete(group_snap); + r = cls_client::group_snap_set(&group_ioctx, group_header_oid, group_snap); if (r < 0) { lderr(cct) << "failed to mark snapshot complete: " << cpp_strerror(r) @@ -3048,10 +3072,12 @@ int Mirror::group_promote(IoCtx& group_ioctx, const char *group_name, } cls::rbd::MirrorSnapshotState state; - cls::rbd::GroupSnapshotState sync; - r = get_last_mirror_snapshot_state(group_ioctx, group_id, &state, &sync); + cls::rbd::MirrorGroupSnapshotCompleteState sync; + cls::rbd::GroupSnapshotState group_snap_state; + r = get_last_mirror_snapshot_state(group_ioctx, group_id, &group_snap_state, &state, &sync); if (r == -ENOENT) { - sync = cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE; + group_snap_state = cls::rbd::GROUP_SNAPSHOT_STATE_CREATING; + sync = cls::rbd::MIRROR_GROUP_SNAPSHOT_INCOMPLETE; state = cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED; // XXXMG? r = 0; } @@ -3065,7 +3091,7 @@ int Mirror::group_promote(IoCtx& group_ioctx, const char *group_name, lderr(cct) << "group " << group_name << " is already primary" << dendl; return -EINVAL; } else if (!force && (state == cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY || - sync != cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE)) { + !is_mirror_group_snapshot_complete(group_snap_state, sync))) { lderr(cct) << "group " << group_name << " is primary within a remote cluster or demotion is not propagated yet" << dendl; @@ -3133,8 +3159,7 @@ int Mirror::group_promote(IoCtx& group_ioctx, const char *group_name, if (mirror_ns == nullptr || mirror_ns->is_orphan()) { continue; } - - if (snap->state != cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE) { + if (!is_mirror_group_snapshot_complete(snap->state, mirror_ns->complete)) { need_rollback = true; continue; } @@ -3190,7 +3215,7 @@ int Mirror::group_promote(IoCtx& group_ioctx, const char *group_name, cls::rbd::GroupSnapshotNamespaceMirror{}, prepare_primary_mirror_snap_name(cct, mirror_group.global_group_id, group_snap_id), - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; std::vector image_ctxs; std::vector quiesce_requests; @@ -3235,7 +3260,9 @@ int Mirror::group_promote(IoCtx& group_ioctx, const char *group_name, ldout(cct, 20) << "undoing group promote: " << ret_code << dendl; remove_group_snap(group_ioctx, group_id, &group_snap, &image_ctxs); } else if (!ret_code) { - group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE; + group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_CREATED; + cls::rbd::set_mirror_group_snapshot_complete(group_snap); + r = cls_client::group_snap_set(&group_ioctx, group_header_oid, group_snap); if (r < 0) { lderr(cct) << "failed to update group snapshot metadata: " @@ -3293,7 +3320,7 @@ int Mirror::group_demote(IoCtx& group_ioctx, cls::rbd::GroupSnapshotNamespaceMirror{}, prepare_primary_mirror_snap_name(cct, mirror_group.global_group_id, group_snap_id), - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; std::vector quiesce_requests; std::vector image_ctxs; @@ -3333,7 +3360,9 @@ int Mirror::group_demote(IoCtx& group_ioctx, std::string group_header_oid = librbd::util::group_header_name(group_id); if (!ret_code) { - group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE; + group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_CREATED; + cls::rbd::set_mirror_group_snapshot_complete(group_snap); + r = cls_client::group_snap_set(&group_ioctx, group_header_oid, group_snap); if (r < 0) { lderr(cct) << "failed to update group snapshot metadata: " diff --git a/src/librbd/mirror/GroupEnableRequest.cc b/src/librbd/mirror/GroupEnableRequest.cc index 21b07a78b43..4deb2e51c0d 100644 --- a/src/librbd/mirror/GroupEnableRequest.cc +++ b/src/librbd/mirror/GroupEnableRequest.cc @@ -431,11 +431,21 @@ void GroupEnableRequest::create_primary_group_snapshot() { + "." + m_group_snap.id; m_group_snap.name = snap_name; - cls::rbd::MirrorSnapshotState state = cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY; + librados::Rados rados(m_group_ioctx); + int8_t require_osd_release; + int r = rados.get_min_compatible_osd(&require_osd_release); + if (r < 0) { + lderr(m_cct) << "failed to retrieve min OSD release: " << cpp_strerror(r) + << dendl; + m_ret_val = r; + disable_mirror_group(); + return; + } - // Create incomplete group snap + auto complete = cls::rbd::get_mirror_group_snapshot_complete_initial(require_osd_release); m_group_snap.snapshot_namespace = cls::rbd::GroupSnapshotNamespaceMirror{ - state, m_mirror_peer_uuids, {}, {}}; + cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, m_mirror_peer_uuids, {}, {}, + complete}; for (auto image_ctx: m_image_ctxs) { m_group_snap.snaps.emplace_back(image_ctx->md_ctx.get_id(), image_ctx->id, @@ -448,8 +458,8 @@ void GroupEnableRequest::create_primary_group_snapshot() { auto aio_comp = create_rados_callback< GroupEnableRequest, &GroupEnableRequest::handle_create_primary_group_snapshot>(this); - int r = m_group_ioctx.aio_operate(librbd::util::group_header_name(m_group_id), - aio_comp, &op); + r = m_group_ioctx.aio_operate(librbd::util::group_header_name(m_group_id), + aio_comp, &op); ceph_assert(r == 0); aio_comp->release(); } @@ -532,7 +542,9 @@ void GroupEnableRequest::update_primary_group_snapshot() { m_group_snap.snaps[i].snap_id = m_snap_ids[i]; } - m_group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE; + m_group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_CREATED; + cls::rbd::set_mirror_group_snapshot_complete(m_group_snap); + librados::ObjectWriteOperation op; cls_client::group_snap_set(&op, m_group_snap); diff --git a/src/librbd/mirror/GroupGetInfoRequest.cc b/src/librbd/mirror/GroupGetInfoRequest.cc index 32fb2f602e1..5376f73a7b6 100644 --- a/src/librbd/mirror/GroupGetInfoRequest.cc +++ b/src/librbd/mirror/GroupGetInfoRequest.cc @@ -172,7 +172,7 @@ void GroupGetInfoRequest::handle_get_last_mirror_snapshot_state(int r) { // XXXMG: check primary_mirror_uuid matches? switch (ns->state) { case cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY: - if (it->state == cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE) { + if (!is_mirror_group_snapshot_complete(it->state, ns->complete)) { continue; } *m_promotion_state = PROMOTION_STATE_PRIMARY; @@ -181,13 +181,13 @@ void GroupGetInfoRequest::handle_get_last_mirror_snapshot_state(int r) { *m_promotion_state = PROMOTION_STATE_NON_PRIMARY; break; case cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED: - if (it->state == cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE) { + if (!is_mirror_group_snapshot_complete(it->state, ns->complete)) { continue; } *m_promotion_state = PROMOTION_STATE_ORPHAN; break; case cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED: - if (it->state == cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE) { + if (is_mirror_group_snapshot_complete(it->state, ns->complete)) { *m_promotion_state = PROMOTION_STATE_ORPHAN; } else { *m_promotion_state = PROMOTION_STATE_NON_PRIMARY; diff --git a/src/librbd/mirror/snapshot/GroupCreatePrimaryRequest.cc b/src/librbd/mirror/snapshot/GroupCreatePrimaryRequest.cc index 8f888ff5758..b0c9ea07a28 100644 --- a/src/librbd/mirror/snapshot/GroupCreatePrimaryRequest.cc +++ b/src/librbd/mirror/snapshot/GroupCreatePrimaryRequest.cc @@ -448,9 +448,20 @@ void GroupCreatePrimaryRequest::generate_group_snap() { // TODO: Fix this to handle primary demoted snaps cls::rbd::MirrorSnapshotState state = cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY; - // Create incomplete group snap + librados::Rados rados(m_group_ioctx); + int8_t require_osd_release; + int r = rados.get_min_compatible_osd(&require_osd_release); + if (r < 0) { + lderr(m_cct) << "failed to retrieve min OSD release: " << cpp_strerror(r) + << dendl; + m_ret_code = r; + close_images(); + return; + } + + auto complete = cls::rbd::get_mirror_group_snapshot_complete_initial(require_osd_release); m_group_snap.snapshot_namespace = cls::rbd::GroupSnapshotNamespaceMirror{ - state, m_mirror_peer_uuids, {}, {}}; + state, m_mirror_peer_uuids, {}, {}, complete}; for (auto image_ctx: m_image_ctxs) { m_group_snap.snaps.emplace_back(image_ctx->md_ctx.get_id(), image_ctx->id, @@ -483,7 +494,7 @@ void GroupCreatePrimaryRequest::handle_set_snap_metadata(int r) { lderr(m_cct) << "failed to set group snapshot metadata: " << cpp_strerror(r) << dendl; m_ret_code = r; - if (m_group_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE) { + if (m_group_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_CREATED) { remove_incomplete_group_snap(); } else { close_images(); @@ -491,7 +502,7 @@ void GroupCreatePrimaryRequest::handle_set_snap_metadata(int r) { return; } - if (m_group_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE) { + if (m_group_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_CREATED) { release_image_exclusive_locks(); } else { notify_quiesce(); @@ -646,7 +657,9 @@ void GroupCreatePrimaryRequest::handle_create_image_snaps(int r) { remove_incomplete_group_snap(); return; } else { - m_group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE; + m_group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_CREATED; + cls::rbd::set_mirror_group_snapshot_complete(m_group_snap); + *m_snap_id = m_group_snap.id; set_snap_metadata(); diff --git a/src/librbd/mirror/snapshot/GroupUnlinkPeerRequest.cc b/src/librbd/mirror/snapshot/GroupUnlinkPeerRequest.cc index 47778ecefd0..43c27add12f 100644 --- a/src/librbd/mirror/snapshot/GroupUnlinkPeerRequest.cc +++ b/src/librbd/mirror/snapshot/GroupUnlinkPeerRequest.cc @@ -53,7 +53,7 @@ void GroupUnlinkPeerRequest::unlink_peer() { if (ns->mirror_peer_uuids.empty() || (ns->mirror_peer_uuids.count(peer) != 0 && ns->is_primary() && - it->state == cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE)){ + !is_mirror_group_snapshot_complete(it->state, ns->complete))) { process_snapshot(*it, peer); return; } @@ -158,10 +158,10 @@ void GroupUnlinkPeerRequest::process_snapshot(cls::rbd::GroupSnapshot group_s const auto& ns = std::get( group_snap.snapshot_namespace); if (ns.mirror_peer_uuids.empty() || - group_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE) { + !is_mirror_group_snapshot_complete(group_snap.state, ns.complete)) { remove_group_snapshot(group_snap); } else { - // Note: avoid calling remove_peer_uuid() for INCOMPLETE snapshots as + // Note: avoid calling remove_peer_uuid() for CREATING snapshots as // group_snap_set() returns EEXIST error remove_peer_uuid(group_snap, mirror_peer_uuid); } @@ -224,7 +224,7 @@ void GroupUnlinkPeerRequest::remove_group_snapshot( m_group_snap_id = group_snap.id; C_Gather *gather_ctx = new C_Gather(m_cct, ctx); - if (group_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE) { + if (group_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_CREATING) { for (size_t i = 0; i < m_image_ctxs->size(); ++i) { ImageCtx *ictx = (*m_image_ctxs)[i]; for (auto it = ictx->snap_info.rbegin(); diff --git a/src/pybind/rbd/c_rbd.pxd b/src/pybind/rbd/c_rbd.pxd index 0df93d44e77..ad9f5424909 100644 --- a/src/pybind/rbd/c_rbd.pxd +++ b/src/pybind/rbd/c_rbd.pxd @@ -243,8 +243,8 @@ cdef extern from "rbd/librbd.h" nogil: rbd_group_image_state_t state ctypedef enum rbd_group_snap_state_t: - _RBD_GROUP_SNAP_STATE_INCOMPLETE "RBD_GROUP_SNAP_STATE_INCOMPLETE" - _RBD_GROUP_SNAP_STATE_COMPLETE "RBD_GROUP_SNAP_STATE_COMPLETE" + _RBD_GROUP_SNAP_STATE_CREATING "RBD_GROUP_SNAP_STATE_CREATING" + _RBD_GROUP_SNAP_STATE_CREATED "RBD_GROUP_SNAP_STATE_CREATED" ctypedef enum rbd_group_snap_namespace_type_t: _RBD_GROUP_SNAP_NAMESPACE_TYPE_USER "RBD_GROUP_SNAP_NAMESPACE_TYPE_USER" @@ -268,7 +268,7 @@ cdef extern from "rbd/librbd.h" nogil: rbd_snap_mirror_state_t state; size_t mirror_peer_uuids_count; char* mirror_peer_uuids; - bint complete + bint complete; char* primary_mirror_uuid; char* primary_snap_id; diff --git a/src/pybind/rbd/mock_rbd.pxi b/src/pybind/rbd/mock_rbd.pxi index 5ffe24009db..bed3fa53ddb 100644 --- a/src/pybind/rbd/mock_rbd.pxi +++ b/src/pybind/rbd/mock_rbd.pxi @@ -247,8 +247,8 @@ cdef nogil: rbd_group_image_state_t state ctypedef enum rbd_group_snap_state_t: - _RBD_GROUP_SNAP_STATE_INCOMPLETE "RBD_GROUP_SNAP_STATE_INCOMPLETE" - _RBD_GROUP_SNAP_STATE_COMPLETE "RBD_GROUP_SNAP_STATE_COMPLETE" + _RBD_GROUP_SNAP_STATE_CREATING "RBD_GROUP_SNAP_STATE_CREATING" + _RBD_GROUP_SNAP_STATE_CREATED "RBD_GROUP_SNAP_STATE_CREATED" ctypedef enum rbd_group_snap_namespace_type_t: _RBD_GROUP_SNAP_NAMESPACE_TYPE_USER "RBD_GROUP_SNAP_NAMESPACE_TYPE_USER" @@ -272,7 +272,7 @@ cdef nogil: rbd_snap_mirror_state_t state; size_t mirror_peer_uuids_count; char* mirror_peer_uuids; - bint complete + bint complete; char* primary_mirror_uuid; char* primary_snap_id; diff --git a/src/pybind/rbd/rbd.pyx b/src/pybind/rbd/rbd.pyx index 69ca123b71a..6ea3bae65c8 100644 --- a/src/pybind/rbd/rbd.pyx +++ b/src/pybind/rbd/rbd.pyx @@ -139,8 +139,8 @@ RBD_SNAP_MIRROR_STATE_NON_PRIMARY_DEMOTED = _RBD_SNAP_MIRROR_STATE_NON_PRIMARY_D RBD_GROUP_IMAGE_STATE_ATTACHED = _RBD_GROUP_IMAGE_STATE_ATTACHED RBD_GROUP_IMAGE_STATE_INCOMPLETE = _RBD_GROUP_IMAGE_STATE_INCOMPLETE -RBD_GROUP_SNAP_STATE_INCOMPLETE = _RBD_GROUP_SNAP_STATE_INCOMPLETE -RBD_GROUP_SNAP_STATE_COMPLETE = _RBD_GROUP_SNAP_STATE_COMPLETE +RBD_GROUP_SNAP_STATE_CREATING = _RBD_GROUP_SNAP_STATE_CREATING +RBD_GROUP_SNAP_STATE_CREATED = _RBD_GROUP_SNAP_STATE_CREATED RBD_GROUP_SNAP_NAMESPACE_TYPE_USER = _RBD_GROUP_SNAP_NAMESPACE_TYPE_USER diff --git a/src/test/cls_rbd/test_cls_rbd.cc b/src/test/cls_rbd/test_cls_rbd.cc index fdd9d427892..af8dd762107 100644 --- a/src/test/cls_rbd/test_cls_rbd.cc +++ b/src/test/cls_rbd/test_cls_rbd.cc @@ -3065,7 +3065,7 @@ TEST_F(TestClsRbd, group_snap_set_empty_name) { string snap_id = "snap_id"; cls::rbd::GroupSnapshot snap = {snap_id, cls::rbd::GroupSnapshotNamespaceUser{}, "", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(-EINVAL, group_snap_set(&ioctx, group_id, snap)); } @@ -3079,7 +3079,7 @@ TEST_F(TestClsRbd, group_snap_set_empty_id) { string snap_id = "snap_id"; cls::rbd::GroupSnapshot snap = {"", cls::rbd::GroupSnapshotNamespaceUser{}, "snap_name", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(-EINVAL, group_snap_set(&ioctx, group_id, snap)); } @@ -3094,13 +3094,13 @@ TEST_F(TestClsRbd, group_snap_set_duplicate_id) { cls::rbd::GroupSnapshot snap = {snap_id, cls::rbd::GroupSnapshotNamespaceUser{}, "snap_name", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap)); cls::rbd::GroupSnapshot snap1 = {snap_id, cls::rbd::GroupSnapshotNamespaceUser{}, "snap_name1", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(-EEXIST, group_snap_set(&ioctx, group_id, snap1)); } @@ -3115,14 +3115,14 @@ TEST_F(TestClsRbd, group_snap_set_duplicate_name) { cls::rbd::GroupSnapshot snap = {snap_id1, cls::rbd::GroupSnapshotNamespaceUser{}, "snap_name", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap)); string snap_id2 = "snap_id2"; cls::rbd::GroupSnapshot snap1 = {snap_id2, cls::rbd::GroupSnapshotNamespaceUser{}, "snap_name", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(-EEXIST, group_snap_set(&ioctx, group_id, snap1)); } @@ -3137,7 +3137,7 @@ TEST_F(TestClsRbd, group_snap_set) { cls::rbd::GroupSnapshot snap = {snap_id, cls::rbd::GroupSnapshotNamespaceUser{}, "test_snapshot", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap)); set keys; @@ -3161,21 +3161,21 @@ TEST_F(TestClsRbd, group_snap_list) { cls::rbd::GroupSnapshot snap1 = {snap_id1, cls::rbd::GroupSnapshotNamespaceUser{}, "test_snapshot1", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap1)); string snap_id0 = "snap_id0"; cls::rbd::GroupSnapshot snap0 = {snap_id0, cls::rbd::GroupSnapshotNamespaceUser{}, "test_snapshot0", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap0)); string snap_id2 = "snap_id2"; cls::rbd::GroupSnapshot snap2 = {snap_id2, cls::rbd::GroupSnapshotNamespaceUser{}, "test_snapshot2", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap2)); std::vector snapshots; @@ -3207,7 +3207,7 @@ TEST_F(TestClsRbd, group_snap_list) { cls::rbd::GroupSnapshot snap4 = {snap_id4, cls::rbd::GroupSnapshotNamespaceUser{}, "test_snapshot4", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap4)); ASSERT_EQ(0, group_snap_list_order(&ioctx, group_id, "", 10, &snap_orders)); @@ -3237,7 +3237,7 @@ TEST_F(TestClsRbd, group_snap_list_max_return) { cls::rbd::GroupSnapshot snap = {snap_id, cls::rbd::GroupSnapshotNamespaceUser{}, "test_snapshot" + hexify(i), - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap)); } @@ -3272,7 +3272,7 @@ TEST_F(TestClsRbd, group_snap_list_max_read) { cls::rbd::GroupSnapshot snap = {snap_id, cls::rbd::GroupSnapshotNamespaceUser{}, "test_snapshot" + hexify(i), - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap)); } @@ -3297,7 +3297,7 @@ TEST_F(TestClsRbd, group_snap_remove) { cls::rbd::GroupSnapshot snap = {snap_id, cls::rbd::GroupSnapshotNamespaceUser{}, "test_snapshot", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap)); set keys; @@ -3330,7 +3330,7 @@ TEST_F(TestClsRbd, group_snap_remove_without_order) { cls::rbd::GroupSnapshot snap = {snap_id, cls::rbd::GroupSnapshotNamespaceUser{}, "test_snapshot", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap)); // Simulate an older snapshot by removing the order key @@ -3366,7 +3366,7 @@ TEST_F(TestClsRbd, group_snap_unlink) { cls::rbd::GroupSnapshot snap = {snap_id, cls::rbd::GroupSnapshotNamespaceUser{}, "test_snapshot", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap)); ASSERT_EQ(-ENOENT, group_snap_unlink(&ioctx, group_id, snap_id, {})); @@ -3374,7 +3374,7 @@ TEST_F(TestClsRbd, group_snap_unlink) { ASSERT_EQ(0, group_snap_get_by_id(&ioctx, group_id, snap_id, &read_snap)); ASSERT_EQ(read_snap, snap); - snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE; + snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_CREATED; ASSERT_EQ(0, group_snap_set(&ioctx, group_id, snap)); ASSERT_EQ(0, group_snap_unlink(&ioctx, group_id, snap_id, {})); ASSERT_EQ(-ENOENT, group_snap_get_by_id(&ioctx, group_id, snap_id, &read_snap)); @@ -3412,7 +3412,7 @@ TEST_F(TestClsRbd, group_snap_get_by_id) { cls::rbd::GroupSnapshot snap = {snap_id, cls::rbd::GroupSnapshotNamespaceUser{}, "test_snapshot", - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; cls::rbd::GroupSnapshot received_snap; ASSERT_EQ(-ENOENT, group_snap_get_by_id(&ioctx, group_id, snap_id, &received_snap)); diff --git a/src/test/librbd/test_Groups.cc b/src/test/librbd/test_Groups.cc index 87f50769381..ad08310c5df 100644 --- a/src/test/librbd/test_Groups.cc +++ b/src/test/librbd/test_Groups.cc @@ -520,7 +520,7 @@ TEST_F(TestGroup, snap_get_info) ASSERT_EQ(0, rbd_group_snap_get_info(ioctx2, gp_name, gp_snap_name, &gp_snap_info)); ASSERT_STREQ(gp_snap_name, gp_snap_info.name); - ASSERT_EQ(RBD_GROUP_SNAP_STATE_COMPLETE, gp_snap_info.state); + ASSERT_EQ(RBD_GROUP_SNAP_STATE_CREATED, gp_snap_info.state); ASSERT_EQ(RBD_GROUP_SNAP_NAMESPACE_TYPE_USER, gp_snap_info.namespace_type); ASSERT_STREQ("", gp_snap_info.image_snap_name); ASSERT_EQ(0U, gp_snap_info.image_snaps_count); @@ -535,7 +535,7 @@ TEST_F(TestGroup, snap_get_info) ASSERT_EQ(0, rbd_group_snap_get_info(ioctx2, gp_name, gp_snap_name, &gp_snap_info)); ASSERT_STREQ(gp_snap_name, gp_snap_info.name); - ASSERT_EQ(RBD_GROUP_SNAP_STATE_COMPLETE, gp_snap_info.state); + ASSERT_EQ(RBD_GROUP_SNAP_STATE_CREATED, gp_snap_info.state); ASSERT_EQ(RBD_GROUP_SNAP_NAMESPACE_TYPE_USER, gp_snap_info.namespace_type); ASSERT_EQ(1U, gp_snap_info.image_snaps_count); ASSERT_EQ(m_image_name, gp_snap_info.image_snaps[0].image_name); @@ -573,7 +573,7 @@ TEST_F(TestGroup, snap_get_infoPP) ASSERT_EQ(0, m_rbd.group_snap_get_info(ioctx2, gp_name, gp_snap_name, &gp_snap_info)); ASSERT_EQ(gp_snap_name, gp_snap_info.name); - ASSERT_EQ(RBD_GROUP_SNAP_STATE_COMPLETE, gp_snap_info.state); + ASSERT_EQ(RBD_GROUP_SNAP_STATE_CREATED, gp_snap_info.state); ASSERT_EQ(RBD_GROUP_SNAP_NAMESPACE_TYPE_USER, gp_snap_info.namespace_type); ASSERT_EQ("", gp_snap_info.image_snap_name); ASSERT_EQ(0U, gp_snap_info.image_snaps.size()); @@ -587,7 +587,7 @@ TEST_F(TestGroup, snap_get_infoPP) ASSERT_EQ(0, m_rbd.group_snap_get_info(ioctx2, gp_name, gp_snap_name, &gp_snap_info)); ASSERT_EQ(gp_snap_name, gp_snap_info.name); - ASSERT_EQ(RBD_GROUP_SNAP_STATE_COMPLETE, gp_snap_info.state); + ASSERT_EQ(RBD_GROUP_SNAP_STATE_CREATED, gp_snap_info.state); ASSERT_EQ(RBD_GROUP_SNAP_NAMESPACE_TYPE_USER, gp_snap_info.namespace_type); ASSERT_EQ(1U, gp_snap_info.image_snaps.size()); ASSERT_EQ(m_image_name, gp_snap_info.image_snaps[0].image_name); @@ -653,7 +653,7 @@ TEST_F(TestGroup, snap_list2) ASSERT_EQ(4U, num_snaps); for (int i = 0; i < 4; i++) { - ASSERT_EQ(RBD_GROUP_SNAP_STATE_COMPLETE, gp_snaps[i].state); + ASSERT_EQ(RBD_GROUP_SNAP_STATE_CREATED, gp_snaps[i].state); ASSERT_EQ(RBD_GROUP_SNAP_NAMESPACE_TYPE_USER, gp_snaps[i].namespace_type); if (!strcmp(gp_snaps[i].name, gp_snap_names[0])) { ASSERT_EQ(0U, gp_snaps[i].image_snaps_count); @@ -741,7 +741,7 @@ TEST_F(TestGroup, snap_list2PP) ASSERT_EQ(4U, gp_snaps.size()); for (const auto& gp_snap : gp_snaps) { - ASSERT_EQ(RBD_GROUP_SNAP_STATE_COMPLETE, gp_snap.state); + ASSERT_EQ(RBD_GROUP_SNAP_STATE_CREATED, gp_snap.state); ASSERT_EQ(RBD_GROUP_SNAP_NAMESPACE_TYPE_USER, gp_snap.namespace_type); if (gp_snap.name == gp_snap_names[0]) { ASSERT_EQ(0U, gp_snap.image_snaps.size()); diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py index 9e3995687fb..7a3f12493e3 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -50,7 +50,7 @@ from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists, RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR, RBD_WRITE_ZEROES_FLAG_THICK_PROVISION, RBD_ENCRYPTION_FORMAT_LUKS1, RBD_ENCRYPTION_FORMAT_LUKS2, - RBD_ENCRYPTION_FORMAT_LUKS, RBD_GROUP_SNAP_STATE_COMPLETE, + RBD_ENCRYPTION_FORMAT_LUKS, RBD_GROUP_SNAP_STATE_CREATED, RBD_GROUP_SNAP_NAMESPACE_TYPE_USER) rados = None @@ -3158,7 +3158,7 @@ class TestGroups(object): image_names = [] assert sorted(snap_info_dict.keys()) == self.gp_snap_keys assert snap_info_dict['name'] == snap_name - assert snap_info_dict['state'] == RBD_GROUP_SNAP_STATE_COMPLETE + assert snap_info_dict['state'] == RBD_GROUP_SNAP_STATE_CREATED assert snap_info_dict['namespace_type'] == RBD_GROUP_SNAP_NAMESPACE_TYPE_USER for image_snap in snap_info_dict['image_snaps']: assert sorted(image_snap.keys()) == self.img_snap_keys @@ -3180,7 +3180,7 @@ class TestGroups(object): snap_info_dict = self.group.get_snap_info(snap_name) assert sorted(snap_info_dict.keys()) == self.gp_snap_keys assert snap_info_dict['name'] == snap_name - assert snap_info_dict['state'] == RBD_GROUP_SNAP_STATE_COMPLETE + assert snap_info_dict['state'] == RBD_GROUP_SNAP_STATE_CREATED assert snap_info_dict['namespace_type'] == RBD_GROUP_SNAP_NAMESPACE_TYPE_USER assert snap_info_dict['image_snap_name'] == "" assert snap_info_dict['image_snaps'] == [] diff --git a/src/tools/rbd/Utils.cc b/src/tools/rbd/Utils.cc index a87ecc6fa72..cb4b583d266 100644 --- a/src/tools/rbd/Utils.cc +++ b/src/tools/rbd/Utils.cc @@ -1004,6 +1004,17 @@ std::string image_id(librbd::Image& image) { return id; } +std::string group_snap_state(librbd::group_snap_state_t state) { + switch (state) { + case RBD_GROUP_SNAP_STATE_CREATING: + return "creating"; + case RBD_GROUP_SNAP_STATE_CREATED: + return "created"; + default: + return "unknown (" + stringify(state) + ")"; + } +} + std::string mirror_image_mode(librbd::mirror_image_mode_t mode) { switch (mode) { case RBD_MIRROR_IMAGE_MODE_JOURNAL: diff --git a/src/tools/rbd/Utils.h b/src/tools/rbd/Utils.h index aefc6b74cfb..ae0fd588f26 100644 --- a/src/tools/rbd/Utils.h +++ b/src/tools/rbd/Utils.h @@ -244,6 +244,8 @@ bool is_not_user_snap_namespace(librbd::Image* image, std::string image_id(librbd::Image& image); +std::string group_snap_state( + librbd::group_snap_state_t state); std::string mirror_image_mode( librbd::mirror_image_mode_t mirror_image_mode); std::string mirror_image_state( diff --git a/src/tools/rbd/action/Group.cc b/src/tools/rbd/action/Group.cc index 99fb24a6487..30068ae20fe 100644 --- a/src/tools/rbd/action/Group.cc +++ b/src/tools/rbd/action/Group.cc @@ -70,18 +70,6 @@ void add_group_spec_options(po::options_description *pos, } } -std::string get_group_snap_state_name(rbd_group_snap_state_t state) -{ - switch (state) { - case RBD_GROUP_SNAP_STATE_INCOMPLETE: - return "incomplete"; - case RBD_GROUP_SNAP_STATE_COMPLETE: - return "complete"; - default: - return "unknown (" + stringify(state) + ")"; - } -} - std::string get_group_snap_namespace_name( librbd::group_snap_namespace_type_t type) { @@ -777,9 +765,8 @@ int execute_group_snap_list(const po::variables_map &vm, } for (const auto& snap : snaps) { - auto state_string = get_group_snap_state_name(snap.state); + auto state_string = utils::group_snap_state(snap.state); auto type_string = get_group_snap_namespace_name(snap.namespace_type); - int get_mirror_res = -ENOENT; librbd::group_snap_mirror_namespace_t mirror_snap; std::string mirror_snap_state = "unknown"; @@ -816,6 +803,7 @@ int execute_group_snap_list(const po::variables_map &vm, f->dump_string("peer_uuid", uuid); } f->close_section(); + f->dump_bool("complete", mirror_snap.complete); if (mirror_snap.state == RBD_SNAP_MIRROR_STATE_NON_PRIMARY || mirror_snap.state == RBD_SNAP_MIRROR_STATE_NON_PRIMARY_DEMOTED) { f->dump_string("primary_mirror_uuid", @@ -835,7 +823,11 @@ int execute_group_snap_list(const po::variables_map &vm, if (mirror_snap.state == RBD_SNAP_MIRROR_STATE_NON_PRIMARY || mirror_snap.state == RBD_SNAP_MIRROR_STATE_NON_PRIMARY_DEMOTED) { oss << " " << mirror_snap.primary_mirror_uuid << ":" - << mirror_snap.primary_snap_id; + << mirror_snap.primary_snap_id << " "; + if (!mirror_snap.complete) { + oss << "not "; + } + oss << "copied"; } oss << ")"; } @@ -894,7 +886,7 @@ int execute_group_snap_info(const po::variables_map &vm, return r; } - auto state_string = get_group_snap_state_name(group_snap.state); + auto state_string = utils::group_snap_state(group_snap.state); if (f) { f->open_object_section("group_snapshot"); f->dump_string("id", group_snap.id); diff --git a/src/tools/rbd/action/MirrorGroup.cc b/src/tools/rbd/action/MirrorGroup.cc index fdb22f1ece2..a460ace3f8e 100644 --- a/src/tools/rbd/action/MirrorGroup.cc +++ b/src/tools/rbd/action/MirrorGroup.cc @@ -459,12 +459,7 @@ int execute_status(const po::variables_map &vm, if (!snaps.empty()) { formatter->open_array_section("snapshots"); for (auto &snap : snaps) { - std::string state_string; - if (snap.state == RBD_GROUP_SNAP_STATE_INCOMPLETE) { - state_string = "incomplete"; - } else { - state_string = "ok"; - } + auto state_string = utils::group_snap_state(snap.state); formatter->open_object_section("snapshot"); formatter->dump_string("name", snap.name); formatter->dump_string("state", state_string); diff --git a/src/tools/rbd_mirror/group_replayer/Replayer.cc b/src/tools/rbd_mirror/group_replayer/Replayer.cc index a2933f9ac3d..5e2c82d7a33 100644 --- a/src/tools/rbd_mirror/group_replayer/Replayer.cc +++ b/src/tools/rbd_mirror/group_replayer/Replayer.cc @@ -61,10 +61,12 @@ const cls::rbd::GroupSnapshot* get_latest_mirror_group_snapshot( const cls::rbd::GroupSnapshot* get_latest_complete_mirror_group_snapshot( const std::vector& gp_snaps) { for (auto it = gp_snaps.rbegin(); it != gp_snaps.rend(); ++it) { - if (it->state == cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE && - (cls::rbd::get_group_snap_namespace_type(it->snapshot_namespace) == - cls::rbd::GROUP_SNAPSHOT_NAMESPACE_TYPE_MIRROR)) { - return &*it; + auto gp_snap_ns = std::get_if( + &it->snapshot_namespace); + if (gp_snap_ns != nullptr) { + if (is_mirror_group_snapshot_complete(it->state, gp_snap_ns->complete)) { + return &*it; + } } } return nullptr; @@ -408,13 +410,19 @@ void Replayer::validate_local_group_snapshots() { // process snapshots requiring validation for (auto &local_snap : m_local_group_snaps) { // skip validation for already complete snapshots - if (local_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE) { - continue; + auto ns = std::get_if( + &local_snap.snapshot_namespace); + if (local_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_CREATED) { + if (ns == nullptr) { + // user group snapshot + continue; + } + if (is_mirror_group_snapshot_complete(local_snap.state, ns->complete)) { + continue; + } } // skip validation for primary snapshots - auto ns = std::get_if( - &local_snap.snapshot_namespace); if (ns != nullptr && ns->is_primary()) { continue; } @@ -487,11 +495,11 @@ void Replayer::handle_load_local_group_snapshots(int r) { handle_replay_complete(&locker, 0, "orphan (force promoting)"); return; } - if (last_local_snap->state == cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE) { + if (!is_mirror_group_snapshot_complete(last_local_snap->state, ns.complete)) { m_retry_validate_snap = true; } } else { // primary snapshot - if (last_local_snap->state == cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE) { + if (is_mirror_group_snapshot_complete(last_local_snap->state, ns.complete)) { handle_replay_complete(&locker, 0, "local group is primary"); return; } @@ -745,20 +753,24 @@ void Replayer::try_create_group_snapshot( if (!prev_snap_id.empty()) { snap = std::next(remote_snap); // attempt to sync next remote snapshot } - if (snap != m_remote_group_snaps.end() && - snap->state == cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE) { + if (snap != m_remote_group_snaps.end()) { auto next_remote_snap_ns = std::get_if( &snap->snapshot_namespace); if (next_remote_snap_ns == nullptr) { - dout(10) << "found remote user group snapshot: " - << snap->id << dendl; - create_group_snapshot(*snap, locker); - continue; + if (snap->state == cls::rbd::GROUP_SNAPSHOT_STATE_CREATED) { + dout(10) << "found remote user group snapshot: " + << snap->id << dendl; + create_group_snapshot(*snap, locker); + continue; + } } else if (next_remote_snap_ns->is_primary()) { - dout(10) << "found primary remote mirror group snapshot: " - << snap->id << dendl; - create_group_snapshot(*snap, locker); - return; + if (is_mirror_group_snapshot_complete(snap->state, + next_remote_snap_ns->complete)) { + dout(10) << "found primary remote mirror group snapshot: " + << snap->id << dendl; + create_group_snapshot(*snap, locker); + return; + } } else { dout(10) << "skipping non-primary remote group snapshot: " << snap->id << dendl; @@ -845,12 +857,13 @@ void Replayer::create_group_snapshot(cls::rbd::GroupSnapshot snap, // check if we have a valid mirror snapshot to proceed bool can_proceed = false; for (; next_remote_snap != m_remote_group_snaps.end(); ++next_remote_snap) { - auto next_snap_type = cls::rbd::get_group_snap_namespace_type( - next_remote_snap->snapshot_namespace); + auto next_remote_snap_ns = std::get_if( + &next_remote_snap->snapshot_namespace); - if (next_snap_type == cls::rbd::GROUP_SNAPSHOT_NAMESPACE_TYPE_USER) { + if (next_remote_snap_ns == nullptr) { continue; // skip user snapshots - } else if (next_remote_snap->state == cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE) { + } else if (!is_mirror_group_snapshot_complete(next_remote_snap->state, + next_remote_snap_ns->complete)) { dout(10) << "next mirror snapshot is incomplete, waiting: " << next_remote_snap->id << dendl; return; // wait and try later @@ -929,8 +942,7 @@ void Replayer::create_mirror_snapshot( return s.id == group_snap_id; }); - if (itl != m_local_group_snaps.end() && - itl->state == cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE) { + if (itl != m_local_group_snaps.end()) { dout(20) << "group snapshot: " << group_snap_id << " already exists" << dendl; on_finish->complete(0); @@ -960,11 +972,25 @@ void Replayer::create_mirror_snapshot( } } + librados::Rados rados(m_local_io_ctx); + int8_t require_osd_release; + r = rados.get_min_compatible_osd(&require_osd_release); + if (r < 0) { + derr << "failed to retrieve min OSD release: " << cpp_strerror(r) + << dendl; + on_finish->complete(r); + return; + } + + auto complete = cls::rbd::get_mirror_group_snapshot_complete_initial(require_osd_release); + auto mirror_namespace = cls::rbd::GroupSnapshotNamespaceMirror(snap_state, + mirror_peer_uuids, m_remote_mirror_uuid, group_snap_id, + complete); + cls::rbd::GroupSnapshot local_snap = - {group_snap_id, - cls::rbd::GroupSnapshotNamespaceMirror{ - snap_state, mirror_peer_uuids, m_remote_mirror_uuid, group_snap_id}, - {}, cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + {group_snap_id, mirror_namespace, + {}, cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; + local_snap.name = prepare_non_primary_mirror_snap_name(m_global_group_id, group_snap_id); @@ -1032,6 +1058,11 @@ void Replayer::mirror_snapshot_complete( cls::rbd::GroupSnapshot remote_snap = *itr; locker.unlock(); + if (local_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_CREATED) { + check_mirror_snapshot_sync_complete(group_snap_id, local_snap, on_finish); + return; + } + bufferlist* out_bl = new bufferlist(); std::vector* local_images = new std::vector(); @@ -1073,12 +1104,12 @@ void Replayer::handle_mirror_snapshot_image_list( const std::vector& local_images, Context *on_finish) { dout(10) << group_snap_id << dendl; - post_mirror_snapshot_complete(group_snap_id, local_snap, remote_snap, - local_images, on_finish); + post_mirror_snapshot_created(group_snap_id, local_snap, remote_snap, + local_images, on_finish); } template -void Replayer::post_mirror_snapshot_complete( +void Replayer::post_mirror_snapshot_created( const std::string &group_snap_id, const cls::rbd::GroupSnapshot &local_snap, const cls::rbd::GroupSnapshot &remote_snap, @@ -1090,7 +1121,7 @@ void Replayer::post_mirror_snapshot_complete( local_image_snap_specs.reserve(remote_snap.snaps.size()); for (auto& image : local_images) { - bool image_snap_complete = false; + bool image_snap_created = false; std::string image_header_oid = librbd::util::header_name(image.spec.image_id); ::SnapContext snapc; int r = librbd::cls_client::get_snapcontext(&m_local_io_ctx, image_header_oid, &snapc); @@ -1120,23 +1151,13 @@ void Replayer::post_mirror_snapshot_complete( continue; } - // make sure the image snapshot is COMPLETE - if (mirror_ns->group_snap_id == group_snap_id && mirror_ns->complete) { - image_snap_complete = true; + if (mirror_ns->group_snap_id == group_snap_id) { + image_snap_created = true; cls::rbd::ImageSnapshotSpec snap_spec; snap_spec.pool = image.spec.pool_id; snap_spec.image_id = image.spec.image_id; snap_spec.snap_id = snap_info.id; - - // check if this spec already exists in local snaps - auto it = std::find_if(local_snap.snaps.begin(), local_snap.snaps.end(), - [&snap_spec](const cls::rbd::ImageSnapshotSpec &s) { - return snap_spec.pool == s.pool && - snap_spec.image_id == s.image_id; - }); - if (it == local_snap.snaps.end()) { - local_image_snap_specs.push_back(snap_spec); - } + local_image_snap_specs.push_back(snap_spec); break; } else { dout(20) << "expecting remote group snap id: " << group_snap_id @@ -1146,7 +1167,7 @@ void Replayer::post_mirror_snapshot_complete( } } - if (!image_snap_complete) { + if (!image_snap_created) { set_image_replayer_limits(image.spec.image_id, &remote_snap, &locker); } } @@ -1176,14 +1197,14 @@ void Replayer::post_mirror_snapshot_complete( } } - // User snap is added and not yet turned complete, wait for it. + // User snap is added and not yet turned created, wait for it. for (auto local_snap = m_local_group_snaps.begin(); local_snap != m_local_group_snaps.end(); ++local_snap) { if (local_snap->id == group_snap_id) { break; - } else if (local_snap->state == cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE) { + } else if (local_snap->state == cls::rbd::GROUP_SNAPSHOT_STATE_CREATING) { locker.unlock(); - // there is a user group snap waiting sync, so lets wait for it to moved to complete + // there is a user group snap waiting sync, so lets wait for it to moved to created on_finish->complete(-EAGAIN); return; } @@ -1192,14 +1213,29 @@ void Replayer::post_mirror_snapshot_complete( if (remote_snap.snaps.size() == local_image_snap_specs.size()) { cls::rbd::GroupSnapshot local_snap_copy = local_snap; local_snap_copy.snaps = local_image_snap_specs; - local_snap_copy.state = cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE; + const auto &mirror_namespace = std::get( + local_snap_copy.snapshot_namespace); + if (mirror_namespace.complete == + cls::rbd::MIRROR_GROUP_SNAPSHOT_COMPLETE_IF_CREATED) { + dout(10) << "local group snap info: " + << "id: " << local_snap_copy.id + << ", name: " << local_snap_copy.name + << ", state: " << local_snap_copy.state + << ", snaps.size: " << local_snap_copy.snaps.size() + << dendl; + locker.unlock(); + check_mirror_snapshot_sync_complete(group_snap_id, local_snap_copy, on_finish); + return; + } + local_snap_copy.state = cls::rbd::GROUP_SNAPSHOT_STATE_CREATED; librados::ObjectWriteOperation op; librbd::cls_client::group_snap_set(&op, local_snap_copy); auto comp = create_rados_callback( - new LambdaContext([this, group_snap_id, on_finish](int r) { - handle_post_mirror_snapshot_complete(r, group_snap_id, on_finish); + new LambdaContext([this, group_snap_id, local_snap_copy, on_finish](int r) { + handle_post_mirror_snapshot_created(r, group_snap_id, local_snap_copy, + on_finish); })); int r = m_local_io_ctx.aio_operate( librbd::util::group_header_name(m_local_group_id), comp, &op); @@ -1219,11 +1255,98 @@ void Replayer::post_mirror_snapshot_complete( } template -void Replayer::handle_post_mirror_snapshot_complete( +void Replayer::handle_post_mirror_snapshot_created( + int r, const std::string &group_snap_id, + const cls::rbd::GroupSnapshot &local_snap, + Context *on_finish) { + dout(10) << group_snap_id << ", r=" << r << dendl; + + if (r < 0) { + derr << "failed to set snap state to created for group snap id: " + << group_snap_id << ", : " << cpp_strerror(r) << dendl; + on_finish->complete(r); + return; + } + + check_mirror_snapshot_sync_complete(group_snap_id, local_snap, on_finish); +} + +template +void Replayer::check_mirror_snapshot_sync_complete( + const std::string &group_snap_id, + const cls::rbd::GroupSnapshot &local_snap, + Context *on_finish) { + std::unique_lock locker{m_lock}; + dout(10) << group_snap_id << dendl; + + uint64_t num_images_sync_pending = 0; + for (const auto& snap_spec : local_snap.snaps) { + std::string image_header_oid = librbd::util::header_name(snap_spec.image_id); + cls::rbd::SnapshotInfo snap_info; + int r = librbd::cls_client::snapshot_get(&m_local_io_ctx, image_header_oid, + snap_spec.snap_id, &snap_info); + if (r < 0) { + derr << "failed getting snap info for snap id: " << snap_spec.snap_id + << ", : " << cpp_strerror(r) << dendl; + locker.unlock(); + on_finish->complete(r); + return; + } + const auto& mirror_ns = std::get( + snap_info.snapshot_namespace); + if (!mirror_ns.complete) { + num_images_sync_pending++; + } + } + + if (num_images_sync_pending == 0) { + // snapshot has been fully synced + set_mirror_snapshot_complete(group_snap_id, local_snap, on_finish); + } else { + locker.unlock(); + on_finish->complete(0); + return; + } +} + +template +void Replayer::set_mirror_snapshot_complete( + const std::string &group_snap_id, + const cls::rbd::GroupSnapshot &local_snap, + Context *on_finish) { + dout(10) << group_snap_id << dendl; + ceph_assert(ceph_mutex_is_locked_by_me(m_lock)); + + cls::rbd::GroupSnapshot local_snap_copy = local_snap; + auto &mirror_namespace = std::get( + local_snap_copy.snapshot_namespace); + if (mirror_namespace.complete == cls::rbd::MIRROR_GROUP_SNAPSHOT_COMPLETE_IF_CREATED) { + local_snap_copy.state = cls::rbd::GROUP_SNAPSHOT_STATE_CREATED; + } else { + ceph_assert(local_snap_copy.state == cls::rbd::GROUP_SNAPSHOT_STATE_CREATED); + mirror_namespace.complete = cls::rbd::MIRROR_GROUP_SNAPSHOT_COMPLETE; + } + librados::ObjectWriteOperation op; + librbd::cls_client::group_snap_set(&op, local_snap_copy); + + auto comp = create_rados_callback( + new LambdaContext([this, group_snap_id, on_finish](int r) { + handle_set_mirror_snapshot_complete(r, group_snap_id, on_finish); + })); + int r = m_local_io_ctx.aio_operate( + librbd::util::group_header_name(m_local_group_id), comp, &op); + ceph_assert(r == 0); + comp->release(); +} + +template +void Replayer::handle_set_mirror_snapshot_complete( int r, const std::string &group_snap_id, Context *on_finish) { dout(10) << group_snap_id << ", r=" << r << dendl; if (r < 0) { + derr << "failed to set mirror snapshot complete field for group snap id: " + << group_snap_id << ", : " << cpp_strerror(r) << dendl; on_finish->complete(r); return; } @@ -1265,8 +1388,7 @@ void Replayer::create_user_snapshot( return s.id == group_snap_id; }); - if (itl != m_local_group_snaps.end() && - itl->state == cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE) { + if (itl != m_local_group_snaps.end()) { dout(20) << "group snapshot: " << group_snap_id << " already exists" << dendl; on_finish->complete(0); @@ -1278,7 +1400,7 @@ void Replayer::create_user_snapshot( group_snap_id, // keeping it same as remote group snap id cls::rbd::GroupSnapshotNamespaceUser{}, snap->name, - cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE}; + cls::rbd::GROUP_SNAPSHOT_STATE_CREATING}; librbd::cls_client::group_snap_set(&op, group_snap); @@ -1385,12 +1507,12 @@ void Replayer::handle_user_snapshot_image_list( const std::vector& local_images, Context *on_finish) { dout(10) << group_snap_id << dendl; - post_user_snapshot_complete(group_snap_id, local_snap, remote_snap, - local_images, on_finish); + post_user_snapshot_created(group_snap_id, local_snap, remote_snap, + local_images, on_finish); } template -void Replayer::post_user_snapshot_complete( +void Replayer::post_user_snapshot_created( const std::string &group_snap_id, const cls::rbd::GroupSnapshot &local_snap, const cls::rbd::GroupSnapshot &remote_snap, @@ -1454,14 +1576,14 @@ void Replayer::post_user_snapshot_complete( if (remote_snap.snaps.size() == local_image_snap_specs.size()) { cls::rbd::GroupSnapshot local_snap_copy = local_snap; local_snap_copy.snaps = local_image_snap_specs; - local_snap_copy.state = cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE; + local_snap_copy.state = cls::rbd::GROUP_SNAPSHOT_STATE_CREATED; librados::ObjectWriteOperation op; librbd::cls_client::group_snap_set(&op, local_snap_copy); auto comp = create_rados_callback( new LambdaContext([this, group_snap_id, on_finish](int r) { - handle_post_user_snapshot_complete(r, group_snap_id, on_finish); + handle_post_user_snapshot_created(r, group_snap_id, on_finish); })); int r = m_local_io_ctx.aio_operate( librbd::util::group_header_name(m_local_group_id), comp, &op); @@ -1481,7 +1603,7 @@ void Replayer::post_user_snapshot_complete( } template -void Replayer::handle_post_user_snapshot_complete( +void Replayer::handle_post_user_snapshot_created( int r, const std::string &group_snap_id, Context *on_finish) { dout(10) << group_snap_id << ", r=" << r << dendl; @@ -1638,10 +1760,16 @@ void Replayer::prune_mirror_group_snapshots( auto last_local_snap_id = m_local_group_snaps.rbegin()->id; for (auto local_snap = m_local_group_snaps.begin(); local_snap != m_local_group_snaps.end(); ++local_snap) { - if (local_snap->state != cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE) { - break; + auto local_snap_ns = std::get_if( + &local_snap->snapshot_namespace); + if (local_snap_ns == nullptr) { + if (local_snap->state != cls::rbd::GROUP_SNAPSHOT_STATE_CREATED) { + break; + } + } else if (!is_mirror_group_snapshot_complete(local_snap->state, + local_snap_ns->complete)) { + break; } - auto snap_type = cls::rbd::get_group_snap_namespace_type( local_snap->snapshot_namespace); if (snap_type != cls::rbd::GROUP_SNAPSHOT_NAMESPACE_TYPE_MIRROR) { @@ -1652,11 +1780,13 @@ void Replayer::prune_mirror_group_snapshots( if (next_local_snap == m_local_group_snaps.end()) { break; // no next snap. } - auto next_snap_type = cls::rbd::get_group_snap_namespace_type( - next_local_snap->snapshot_namespace); - if (next_snap_type != cls::rbd::GROUP_SNAPSHOT_NAMESPACE_TYPE_MIRROR) { + auto next_local_snap_ns = std::get_if( + &next_local_snap->snapshot_namespace); + if (next_local_snap_ns == nullptr) { continue; // next snap is user snap again. - } else if (next_local_snap->state != cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE) { + } else if ( + !is_mirror_group_snapshot_complete(next_local_snap->state, + next_local_snap_ns->complete)) { break; // next snap is not complete yet. } } @@ -1676,10 +1806,19 @@ void Replayer::prune_mirror_group_snapshots( auto next_local_snap = std::next(local_snap); // If next local snap is end, or if it is the syncing in-progress snap, // then we still need this group snapshot. - if (next_local_snap == m_local_group_snaps.end() || - (next_local_snap->id == last_local_snap_id && - next_local_snap->state != cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE)) { + if (next_local_snap == m_local_group_snaps.end()) { break; + } else if (next_local_snap->id == last_local_snap_id) { + auto next_local_snap_ns = std::get_if( + &next_local_snap->snapshot_namespace); + if (next_local_snap_ns == nullptr) { + if (next_local_snap->state != cls::rbd::GROUP_SNAPSHOT_STATE_CREATED) { + break; + } + } else if (!is_mirror_group_snapshot_complete(next_local_snap->state, + next_local_snap_ns->complete)) { + break; + } } else { auto next_snap_type = cls::rbd::get_group_snap_namespace_type( next_local_snap->snapshot_namespace); diff --git a/src/tools/rbd_mirror/group_replayer/Replayer.h b/src/tools/rbd_mirror/group_replayer/Replayer.h index 45bd78735ec..5594bdb1e54 100644 --- a/src/tools/rbd_mirror/group_replayer/Replayer.h +++ b/src/tools/rbd_mirror/group_replayer/Replayer.h @@ -186,13 +186,25 @@ private: const cls::rbd::GroupSnapshot &remote_snap, const std::vector& local_images, Context *on_finish); - void post_mirror_snapshot_complete( + void post_mirror_snapshot_created( const std::string &group_snap_id, const cls::rbd::GroupSnapshot &local_snap, const cls::rbd::GroupSnapshot &remote_snap, const std::vector& local_images, Context *on_finish); - void handle_post_mirror_snapshot_complete( + void handle_post_mirror_snapshot_created( + int r, const std::string &group_snap_id, + const cls::rbd::GroupSnapshot &local_snap, + Context *on_finish); + void check_mirror_snapshot_sync_complete( + const std::string &group_snap_id, + const cls::rbd::GroupSnapshot &local_snap, + Context *on_finish); + void set_mirror_snapshot_complete( + const std::string &group_snap_id, + const cls::rbd::GroupSnapshot &local_snap, + Context *on_finish); + void handle_set_mirror_snapshot_complete( int r, const std::string &group_snap_id, Context *on_finish); void create_user_snapshot( @@ -210,13 +222,13 @@ private: const cls::rbd::GroupSnapshot &remote_snap, const std::vector& local_images, Context *on_finish); - void post_user_snapshot_complete( + void post_user_snapshot_created( const std::string &group_snap_id, const cls::rbd::GroupSnapshot &local_snap, const cls::rbd::GroupSnapshot &remote_snap, const std::vector& local_images, Context *on_finish); - void handle_post_user_snapshot_complete( + void handle_post_user_snapshot_created( int r, const std::string &group_snap_id, Context *on_finish); void mirror_group_snapshot_unlink_peer(const std::string &snap_id);