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