]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rbd-mirror: integration of the new GroupSnapshotNamespaceMirror::complete field
authorVinayBhaskar-V <vvarada@redhat.com>
Wed, 20 Aug 2025 18:30:09 +0000 (18:30 +0000)
committerIlya Dryomov <idryomov@redhat.com>
Sat, 22 Nov 2025 16:46:48 +0000 (17:46 +0100)
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 <vvarada@redhat.com>
Resolves: rhbz#2396583

24 files changed:
qa/workunits/rbd/rbd_mirror_group_simple.sh
qa/workunits/rbd/rbd_mirror_helpers.sh
src/cls/rbd/cls_rbd.cc
src/cls/rbd/cls_rbd_types.cc
src/cls/rbd/cls_rbd_types.h
src/include/rbd/librbd.h
src/librbd/api/Group.cc
src/librbd/api/Mirror.cc
src/librbd/mirror/GroupEnableRequest.cc
src/librbd/mirror/GroupGetInfoRequest.cc
src/librbd/mirror/snapshot/GroupCreatePrimaryRequest.cc
src/librbd/mirror/snapshot/GroupUnlinkPeerRequest.cc
src/pybind/rbd/c_rbd.pxd
src/pybind/rbd/mock_rbd.pxi
src/pybind/rbd/rbd.pyx
src/test/cls_rbd/test_cls_rbd.cc
src/test/librbd/test_Groups.cc
src/test/pybind/test_rbd.py
src/tools/rbd/Utils.cc
src/tools/rbd/Utils.h
src/tools/rbd/action/Group.cc
src/tools/rbd/action/MirrorGroup.cc
src/tools/rbd_mirror/group_replayer/Replayer.cc
src/tools/rbd_mirror/group_replayer/Replayer.h

index 9b42f95332fc3d6f8bc555a09b4949e7d32144ff..090690e2a03fb10b67c1832c263bcb2cf28bffe6 100755 (executable)
@@ -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'
index 5c8e434593f7dd78983f6cea96f2f5d8d36e1326..86aa135c7db5a2b8495ed9e1c54d223d6b12c0c6 100755 (executable)
@@ -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 <state> and <snaps_synced> 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}"
 }
 
index b8fe251f2b7302920ba797a75d3dbeb510f1ad6c..f32050c71cab5adac2dad86a1a7033631c01ff11 100644 (file)
@@ -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;
   }
 
index 19fe72653ff3b9509a4a88ef5905f58433520eab..cb0a19e88efbbaa0fb5579e41c4cfb5200d32f8b 100644 (file)
@@ -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<uint32_t>(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<uint32_t>(state) << ")";
@@ -1727,13 +1746,13 @@ void GroupSnapshot::dump(Formatter *f) const {
 void GroupSnapshot::generate_test_instances(std::list<GroupSnapshot *> &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<cls::rbd::GroupSnapshotNamespaceMirror>(
+      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
index 570e3c40b633152d43506ee36b79d898a53a1f00..88e447badf7768c9be8be45ec52d94b8362ddc85 100644 (file)
@@ -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<uint8_t>(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<MirrorGroupSnapshotCompleteState>(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<std::string> mirror_peer_uuids;
 
   std::string primary_mirror_uuid;
@@ -993,8 +1016,9 @@ struct GroupSnapshotNamespaceMirror {
   GroupSnapshotNamespaceMirror(MirrorSnapshotState state,
                                const std::set<std::string> &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<ImageSnapshotSpec> 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
 
index bac6e5ac86cffda6c741ab03839c1d5f5bd8c659..39c30feb59b44b0cf9a7c9a5c8b1e478155854f4 100644 (file)
@@ -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 {
index 6c46a81d414a0ef25fbdb557f327237bc15cf0f4..15a374e18e6460e2b976855c70be5933c008e0bc 100644 (file)
@@ -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 <typename T>
   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_mirror_state_t>(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<I>::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<I>::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<I>::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<I>::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;
index 3d728c648aebaab7faac059bdb5d25abe526ea73..a180fa420ff0e45570b8d5a053a2c36d2c42d352 100644 (file)
@@ -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<cls::rbd::GroupSnapshot> 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<I>::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<I>::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<I>::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<I>::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<I *> image_ctxs;
   std::vector<uint64_t> quiesce_requests;
@@ -3235,7 +3260,9 @@ int Mirror<I>::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<I>::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<uint64_t> quiesce_requests;
   std::vector<I *> image_ctxs;
@@ -3333,7 +3360,9 @@ int Mirror<I>::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: "
index 21b07a78b43a55c426d63925f02ed5e8eeb4fcf8..4deb2e51c0d237f5094ce5552bfc4a62986b089f 100644 (file)
@@ -431,11 +431,21 @@ void GroupEnableRequest<I>::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<I>::create_primary_group_snapshot() {
   auto aio_comp = create_rados_callback<
     GroupEnableRequest<I>,
     &GroupEnableRequest<I>::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<I>::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);
 
index 32fb2f602e18814d965067451dc7f9342c649bfe..5376f73a7b65136af51f67ec2ef740f366f5bcf9 100644 (file)
@@ -172,7 +172,7 @@ void GroupGetInfoRequest<I>::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<I>::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;
index 8f888ff5758241ee4170774aca9ddefedee84c91..b0c9ea07a28c70c24045d08fd46e619125360385 100644 (file)
@@ -448,9 +448,20 @@ void GroupCreatePrimaryRequest<I>::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<I>::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<I>::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<I>::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();
index 47778ecefd0848976b58284afe5841a01f4b23da..43c27add12f590c0598fb53c765b573d8574ac5a 100644 (file)
@@ -53,7 +53,7 @@ void GroupUnlinkPeerRequest<I>::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<I>::process_snapshot(cls::rbd::GroupSnapshot group_s
   const auto& ns = std::get<cls::rbd::GroupSnapshotNamespaceMirror>(
       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<I>::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();
index 0df93d44e776b312624ca360515101e2035843ab..ad9f54249093a2d50deff0c123b541c4ea178d02 100644 (file)
@@ -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;
 
index 5ffe24009db16e7d76a4427faa4fd3be19366b7f..bed3fa53ddb7272f8b1691d6e48a21ab7e0131fd 100644 (file)
@@ -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;
 
index 69ca123b71a773050c510e688aa70aeb477a8d5c..6ea3bae65c87b6ff66bca5ac44a351e59ba48aa5 100644 (file)
@@ -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
 
index fdd9d427892e64da17bdc590ccfec06c3885a109..af8dd762107bbbe7f498210a0b233e2d510d0c55 100644 (file)
@@ -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<string> 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<cls::rbd::GroupSnapshot> 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<string> 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));
index 87f50769381e582ccb52323e761d7b4035353a27..ad08310c5df7c0089e89cc9fc225ecf24dea9bc5 100644 (file)
@@ -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());
index 9e3995687fbad8bae18559e44372abf73a8802e9..7a3f12493e39ef7ad744380ff6963c935d9d5f02 100644 (file)
@@ -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'] == []
index a87ecc6fa72faa286b182a7149ef3bfa6696d1df..cb4b583d266aa60e8d91c7c88c40b2e0a79ce817 100644 (file)
@@ -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:
index aefc6b74cfbbc5e2eb1cbae2727ee60b9ed98eae..ae0fd588f266508b9efafec2a2318a3fd4b9c53e 100644 (file)
@@ -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(
index 99fb24a6487076a6471c6ec669d291d4d8a0e2ae..30068ae20fed87475c9d5bc9b47841dad5741bb8 100644 (file)
@@ -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);
index fdb22f1ece2c707c2af836a2eb521ae8bf7d7e36..a460ace3f8eb92bdabb73f3a2fa05a9143d9b0eb 100644 (file)
@@ -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);
index a2933f9ac3df31117853d99d2eb352993efcb179..5e2c82d7a33364ab7aac088289b8483fea428201 100644 (file)
@@ -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<cls::rbd::GroupSnapshot>& 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<cls::rbd::GroupSnapshotNamespaceMirror>(
+      &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<I>::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<cls::rbd::GroupSnapshotNamespaceMirror>(
+        &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<cls::rbd::GroupSnapshotNamespaceMirror>(
-        &local_snap.snapshot_namespace);
     if (ns != nullptr && ns->is_primary()) {
       continue;
     }
@@ -487,11 +495,11 @@ void Replayer<I>::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<I>::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<cls::rbd::GroupSnapshotNamespaceMirror>(
             &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<I>::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<cls::rbd::GroupSnapshotNamespaceMirror>(
+            &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<I>::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<I>::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<I>::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<cls::rbd::GroupImageStatus>* local_images =
     new std::vector<cls::rbd::GroupImageStatus>();
@@ -1073,12 +1104,12 @@ void Replayer<I>::handle_mirror_snapshot_image_list(
     const std::vector<cls::rbd::GroupImageStatus>& 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 <typename I>
-void Replayer<I>::post_mirror_snapshot_complete(
+void Replayer<I>::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<I>::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<I>::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<I>::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<I>::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<I>::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<cls::rbd::GroupSnapshotNamespaceMirror>(
+            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<I>::post_mirror_snapshot_complete(
 }
 
 template <typename I>
-void Replayer<I>::handle_post_mirror_snapshot_complete(
+void Replayer<I>::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 <typename I>
+void Replayer<I>::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<cls::rbd::MirrorSnapshotNamespace>(
+          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 <typename I>
+void Replayer<I>::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<cls::rbd::GroupSnapshotNamespaceMirror>(
+          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 <typename I>
+void Replayer<I>::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<I>::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<I>::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<I>::handle_user_snapshot_image_list(
     const std::vector<cls::rbd::GroupImageStatus>& 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 <typename I>
-void Replayer<I>::post_user_snapshot_complete(
+void Replayer<I>::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<I>::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<I>::post_user_snapshot_complete(
 }
 
 template <typename I>
-void Replayer<I>::handle_post_user_snapshot_complete(
+void Replayer<I>::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<I>::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<cls::rbd::GroupSnapshotNamespaceMirror>(
+            &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<I>::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<cls::rbd::GroupSnapshotNamespaceMirror>(
+            &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<I>::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<cls::rbd::GroupSnapshotNamespaceMirror>(
+            &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);
index 45bd78735ec9b78b5c1d2cc515484782daeec999..5594bdb1e542326eeaf32b8d973bd964c3dda33e 100644 (file)
@@ -186,13 +186,25 @@ private:
     const cls::rbd::GroupSnapshot &remote_snap,
     const std::vector<cls::rbd::GroupImageStatus>& 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<cls::rbd::GroupImageStatus>& 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<cls::rbd::GroupImageStatus>& 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<cls::rbd::GroupImageStatus>& 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);