]> git.proxmox.com Git - ceph.git/blob - ceph/src/librbd/mirror/snapshot/CreatePrimaryRequest.cc
6c5cb5d9d50c3e975dd686dc1308e80d8040cfb4
[ceph.git] / ceph / src / librbd / mirror / snapshot / CreatePrimaryRequest.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "librbd/mirror/snapshot/CreatePrimaryRequest.h"
5 #include "common/dout.h"
6 #include "common/errno.h"
7 #include "cls/rbd/cls_rbd_client.h"
8 #include "librbd/ImageCtx.h"
9 #include "librbd/ImageState.h"
10 #include "librbd/Operations.h"
11 #include "librbd/Utils.h"
12 #include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
13 #include "librbd/mirror/snapshot/Utils.h"
14
15 #define dout_subsys ceph_subsys_rbd
16
17 #undef dout_prefix
18 #define dout_prefix *_dout << "librbd::mirror::snapshot::CreatePrimaryRequest: " \
19 << this << " " << __func__ << ": "
20
21 namespace librbd {
22 namespace mirror {
23 namespace snapshot {
24
25 using librbd::util::create_context_callback;
26 using librbd::util::create_rados_callback;
27
28 template <typename I>
29 CreatePrimaryRequest<I>::CreatePrimaryRequest(
30 I *image_ctx, const std::string& global_image_id, uint32_t flags,
31 uint64_t *snap_id, Context *on_finish)
32 : m_image_ctx(image_ctx), m_global_image_id(global_image_id),
33 m_flags(flags), m_snap_id(snap_id), m_on_finish(on_finish) {
34 m_default_ns_ctx.dup(m_image_ctx->md_ctx);
35 m_default_ns_ctx.set_namespace("");
36 }
37
38 template <typename I>
39 void CreatePrimaryRequest<I>::send() {
40 if (!util::can_create_primary_snapshot(
41 m_image_ctx,
42 ((m_flags & CREATE_PRIMARY_FLAG_DEMOTED) != 0),
43 ((m_flags & CREATE_PRIMARY_FLAG_FORCE) != 0), nullptr, nullptr)) {
44 finish(-EINVAL);
45 return;
46 }
47
48 uuid_d uuid_gen;
49 uuid_gen.generate_random();
50 m_snap_name = ".mirror.primary." + m_global_image_id + "." +
51 uuid_gen.to_string();
52
53 get_mirror_peers();
54 }
55
56 template <typename I>
57 void CreatePrimaryRequest<I>::get_mirror_peers() {
58 CephContext *cct = m_image_ctx->cct;
59 ldout(cct, 20) << dendl;
60
61 librados::ObjectReadOperation op;
62 cls_client::mirror_peer_list_start(&op);
63
64 librados::AioCompletion *comp = create_rados_callback<
65 CreatePrimaryRequest<I>,
66 &CreatePrimaryRequest<I>::handle_get_mirror_peers>(this);
67 m_out_bl.clear();
68 int r = m_default_ns_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl);
69 ceph_assert(r == 0);
70 comp->release();
71 }
72
73 template <typename I>
74 void CreatePrimaryRequest<I>::handle_get_mirror_peers(int r) {
75 CephContext *cct = m_image_ctx->cct;
76 ldout(cct, 20) << "r=" << r << dendl;
77
78 std::vector<cls::rbd::MirrorPeer> peers;
79 if (r == 0) {
80 auto iter = m_out_bl.cbegin();
81 r = cls_client::mirror_peer_list_finish(&iter, &peers);
82 }
83
84 if (r < 0) {
85 lderr(cct) << "failed to retrieve mirror peers: " << cpp_strerror(r)
86 << dendl;
87 finish(r);
88 return;
89 }
90
91 for (auto &peer : peers) {
92 if (peer.mirror_peer_direction == cls::rbd::MIRROR_PEER_DIRECTION_RX) {
93 continue;
94 }
95 m_mirror_peer_uuids.insert(peer.uuid);
96 }
97
98 if (m_mirror_peer_uuids.empty() &&
99 ((m_flags & CREATE_PRIMARY_FLAG_IGNORE_EMPTY_PEERS) == 0)) {
100 lderr(cct) << "no mirror tx peers configured for the pool" << dendl;
101 finish(-EINVAL);
102 return;
103 }
104
105 create_snapshot();
106 }
107
108 template <typename I>
109 void CreatePrimaryRequest<I>::create_snapshot() {
110 cls::rbd::MirrorSnapshotNamespace ns{
111 ((m_flags & CREATE_PRIMARY_FLAG_DEMOTED) != 0 ?
112 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED :
113 cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY),
114 m_mirror_peer_uuids, "", CEPH_NOSNAP};
115
116 CephContext *cct = m_image_ctx->cct;
117 ldout(cct, 20) << "name=" << m_snap_name << ", "
118 << "ns=" << ns << dendl;
119 auto ctx = create_context_callback<
120 CreatePrimaryRequest<I>,
121 &CreatePrimaryRequest<I>::handle_create_snapshot>(this);
122 m_image_ctx->operations->snap_create(ns, m_snap_name, ctx);
123 }
124
125 template <typename I>
126 void CreatePrimaryRequest<I>::handle_create_snapshot(int r) {
127 CephContext *cct = m_image_ctx->cct;
128 ldout(cct, 20) << "r=" << r << dendl;
129
130 if (r < 0) {
131 lderr(cct) << "failed to create mirror snapshot: " << cpp_strerror(r)
132 << dendl;
133 finish(r);
134 return;
135 }
136
137 refresh_image();
138 }
139
140 template <typename I>
141 void CreatePrimaryRequest<I>::refresh_image() {
142 // if snapshot created via remote RPC, refresh is required to retrieve
143 // the snapshot id
144 if (m_snap_id == nullptr) {
145 unlink_peer();
146 return;
147 }
148
149 CephContext *cct = m_image_ctx->cct;
150 ldout(cct, 20) << dendl;
151
152 auto ctx = create_context_callback<
153 CreatePrimaryRequest<I>,
154 &CreatePrimaryRequest<I>::handle_refresh_image>(this);
155 m_image_ctx->state->refresh(ctx);
156 }
157
158 template <typename I>
159 void CreatePrimaryRequest<I>::handle_refresh_image(int r) {
160 CephContext *cct = m_image_ctx->cct;
161 ldout(cct, 20) << "r=" << r << dendl;
162
163 if (r < 0) {
164 lderr(cct) << "failed to refresh image: " << cpp_strerror(r) << dendl;
165 finish(r);
166 return;
167 }
168
169 {
170 std::shared_lock image_locker{m_image_ctx->image_lock};
171 *m_snap_id = m_image_ctx->get_snap_id(
172 cls::rbd::MirrorSnapshotNamespace{}, m_snap_name);
173 ldout(cct, 20) << "snap_id=" << *m_snap_id << dendl;
174 }
175
176 unlink_peer();
177 }
178
179 template <typename I>
180 void CreatePrimaryRequest<I>::unlink_peer() {
181 uint64_t max_snapshots = m_image_ctx->config.template get_val<uint64_t>(
182 "rbd_mirroring_max_mirroring_snapshots");
183 ceph_assert(max_snapshots >= 3);
184
185 std::string peer_uuid;
186 uint64_t snap_id = CEPH_NOSNAP;
187
188 for (auto &peer : m_mirror_peer_uuids) {
189 std::shared_lock image_locker{m_image_ctx->image_lock};
190 size_t count = 0;
191 uint64_t prev_snap_id = 0;
192 for (auto &snap_it : m_image_ctx->snap_info) {
193 auto info = boost::get<cls::rbd::MirrorSnapshotNamespace>(
194 &snap_it.second.snap_namespace);
195 if (info == nullptr) {
196 continue;
197 }
198 if (info->state != cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY) {
199 // reset counters -- we count primary snapshots after the last promotion
200 count = 0;
201 prev_snap_id = 0;
202 continue;
203 }
204 count++;
205 if (count == max_snapshots - 1) {
206 prev_snap_id = snap_it.first;
207 } else if (count > max_snapshots) {
208 peer_uuid = peer;
209 snap_id = prev_snap_id;
210 break;
211 }
212 }
213 if (snap_id != CEPH_NOSNAP) {
214 break;
215 }
216 }
217
218 if (snap_id == CEPH_NOSNAP) {
219 finish(0);
220 return;
221 }
222
223 CephContext *cct = m_image_ctx->cct;
224 ldout(cct, 20) << "peer=" << peer_uuid << ", snap_id=" << snap_id << dendl;
225
226 auto ctx = create_context_callback<
227 CreatePrimaryRequest<I>,
228 &CreatePrimaryRequest<I>::handle_unlink_peer>(this);
229 auto req = UnlinkPeerRequest<I>::create(m_image_ctx, snap_id, peer_uuid, ctx);
230 req->send();
231 }
232
233 template <typename I>
234 void CreatePrimaryRequest<I>::handle_unlink_peer(int r) {
235 CephContext *cct = m_image_ctx->cct;
236 ldout(cct, 20) << "r=" << r << dendl;
237
238 if (r < 0) {
239 lderr(cct) << "failed to unlink peer: " << cpp_strerror(r) << dendl;
240 finish(0); // not fatal
241 return;
242 }
243
244 unlink_peer();
245 }
246
247 template <typename I>
248 void CreatePrimaryRequest<I>::finish(int r) {
249 CephContext *cct = m_image_ctx->cct;
250 ldout(cct, 20) << "r=" << r << dendl;
251
252 m_on_finish->complete(r);
253 delete this;
254 }
255
256 } // namespace snapshot
257 } // namespace mirror
258 } // namespace librbd
259
260 template class librbd::mirror::snapshot::CreatePrimaryRequest<librbd::ImageCtx>;