]> git.proxmox.com Git - ceph.git/blob - ceph/src/librbd/mirror/DisableRequest.cc
2d2dd874a80a4093b659c9f7a457aee46c6673a2
[ceph.git] / ceph / src / librbd / mirror / DisableRequest.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/DisableRequest.h"
5 #include "common/WorkQueue.h"
6 #include "common/dout.h"
7 #include "common/errno.h"
8 #include "cls/journal/cls_journal_client.h"
9 #include "journal/Journaler.h"
10 #include "librbd/ImageCtx.h"
11 #include "librbd/ImageState.h"
12 #include "librbd/Journal.h"
13 #include "librbd/Operations.h"
14 #include "librbd/Utils.h"
15 #include "librbd/journal/PromoteRequest.h"
16 #include "librbd/mirror/GetInfoRequest.h"
17 #include "librbd/mirror/ImageRemoveRequest.h"
18 #include "librbd/mirror/ImageStateUpdateRequest.h"
19 #include "librbd/mirror/snapshot/PromoteRequest.h"
20
21 #define dout_subsys ceph_subsys_rbd
22 #undef dout_prefix
23 #define dout_prefix *_dout << "librbd::mirror::DisableRequest: " \
24 << this << " " << __func__ << ": "
25
26 namespace librbd {
27 namespace mirror {
28
29 using util::create_rados_callback;
30
31 template <typename I>
32 DisableRequest<I>::DisableRequest(I *image_ctx, bool force, bool remove,
33 Context *on_finish)
34 : m_image_ctx(image_ctx), m_force(force), m_remove(remove),
35 m_on_finish(on_finish) {
36 }
37
38 template <typename I>
39 void DisableRequest<I>::send() {
40 send_get_mirror_info();
41 }
42
43 template <typename I>
44 void DisableRequest<I>::send_get_mirror_info() {
45 CephContext *cct = m_image_ctx->cct;
46 ldout(cct, 10) << dendl;
47
48
49 using klass = DisableRequest<I>;
50 Context *ctx = util::create_context_callback<
51 klass, &klass::handle_get_mirror_info>(this);
52
53 auto req = GetInfoRequest<I>::create(*m_image_ctx, &m_mirror_image,
54 &m_promotion_state,
55 &m_primary_mirror_uuid, ctx);
56 req->send();
57 }
58
59 template <typename I>
60 Context *DisableRequest<I>::handle_get_mirror_info(int *result) {
61 CephContext *cct = m_image_ctx->cct;
62 ldout(cct, 10) << "r=" << *result << dendl;
63
64 if (*result < 0) {
65 if (*result == -ENOENT) {
66 ldout(cct, 20) << "mirroring is not enabled for this image" << dendl;
67 *result = 0;
68 } else {
69 lderr(cct) << "failed to get mirroring info: " << cpp_strerror(*result)
70 << dendl;
71 }
72 return m_on_finish;
73 }
74
75 m_is_primary = (m_promotion_state == PROMOTION_STATE_PRIMARY ||
76 m_promotion_state == PROMOTION_STATE_UNKNOWN);
77
78 if (!m_is_primary && !m_force) {
79 lderr(cct) << "mirrored image is not primary, "
80 << "add force option to disable mirroring" << dendl;
81 *result = -EINVAL;
82 return m_on_finish;
83 }
84
85 send_image_state_update();
86 return nullptr;
87 }
88
89 template <typename I>
90 void DisableRequest<I>::send_image_state_update() {
91 CephContext *cct = m_image_ctx->cct;
92 ldout(cct, 10) << dendl;
93
94 auto ctx = util::create_context_callback<
95 DisableRequest<I>,
96 &DisableRequest<I>::handle_image_state_update>(this);
97 auto req = ImageStateUpdateRequest<I>::create(
98 m_image_ctx->md_ctx, m_image_ctx->id,
99 cls::rbd::MIRROR_IMAGE_STATE_DISABLING, m_mirror_image, ctx);
100 req->send();
101 }
102
103 template <typename I>
104 Context *DisableRequest<I>::handle_image_state_update(int *result) {
105 CephContext *cct = m_image_ctx->cct;
106 ldout(cct, 10) << "r=" << *result << dendl;
107
108 if (*result < 0) {
109 lderr(cct) << "failed to disable mirroring: " << cpp_strerror(*result)
110 << dendl;
111 return m_on_finish;
112 }
113
114 send_promote_image();
115 return nullptr;
116 }
117
118 template <typename I>
119 void DisableRequest<I>::send_promote_image() {
120 if (m_is_primary) {
121 clean_mirror_state();
122 return;
123 }
124
125 CephContext *cct = m_image_ctx->cct;
126 ldout(cct, 10) << dendl;
127
128 auto ctx = util::create_context_callback<
129 DisableRequest<I>, &DisableRequest<I>::handle_promote_image>(this);
130 if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
131 // Not primary -- shouldn't have the journal open
132 ceph_assert(m_image_ctx->journal == nullptr);
133
134 auto req = journal::PromoteRequest<I>::create(m_image_ctx, true, ctx);
135 req->send();
136 } else if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
137 auto req = mirror::snapshot::PromoteRequest<I>::create(
138 m_image_ctx, m_mirror_image.global_image_id, ctx);
139 req->send();
140 } else {
141 lderr(cct) << "unknown image mirror mode: " << m_mirror_image.mode << dendl;
142 ctx->complete(-EOPNOTSUPP);
143 }
144 }
145
146 template <typename I>
147 Context *DisableRequest<I>::handle_promote_image(int *result) {
148 CephContext *cct = m_image_ctx->cct;
149 ldout(cct, 10) << "r=" << *result << dendl;
150
151 if (*result < 0) {
152 lderr(cct) << "failed to promote image: " << cpp_strerror(*result) << dendl;
153 return m_on_finish;
154 }
155
156 send_refresh_image();
157 return nullptr;
158 }
159
160 template <typename I>
161 void DisableRequest<I>::send_refresh_image() {
162 if (!m_image_ctx->state->is_refresh_required()) {
163 clean_mirror_state();
164 return;
165 }
166
167 CephContext *cct = m_image_ctx->cct;
168 ldout(cct, 10) << dendl;
169
170 auto ctx = util::create_context_callback<
171 DisableRequest<I>,
172 &DisableRequest<I>::handle_refresh_image>(this);
173 m_image_ctx->state->refresh(ctx);
174 }
175
176 template <typename I>
177 Context *DisableRequest<I>::handle_refresh_image(int* result) {
178 CephContext *cct = m_image_ctx->cct;
179 ldout(cct, 10) << "r=" << *result << dendl;
180
181 if (*result < 0) {
182 lderr(cct) << "failed to refresh image: " << cpp_strerror(*result) << dendl;
183 return m_on_finish;
184 }
185
186 clean_mirror_state();
187 return nullptr;
188 }
189
190 template <typename I>
191 void DisableRequest<I>::clean_mirror_state() {
192 CephContext *cct = m_image_ctx->cct;
193 ldout(cct, 10) << dendl;
194
195 if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
196 remove_mirror_snapshots();
197 } else {
198 send_get_clients();
199 }
200 }
201
202 template <typename I>
203 void DisableRequest<I>::send_get_clients() {
204 CephContext *cct = m_image_ctx->cct;
205 ldout(cct, 10) << dendl;
206
207 using klass = DisableRequest<I>;
208 Context *ctx = util::create_context_callback<
209 klass, &klass::handle_get_clients>(this);
210
211 std::string header_oid = ::journal::Journaler::header_oid(m_image_ctx->id);
212 m_clients.clear();
213 cls::journal::client::client_list(m_image_ctx->md_ctx, header_oid, &m_clients,
214 ctx);
215 }
216
217 template <typename I>
218 Context *DisableRequest<I>::handle_get_clients(int *result) {
219 CephContext *cct = m_image_ctx->cct;
220 ldout(cct, 10) << "r=" << *result << dendl;
221
222 if (*result < 0) {
223 lderr(cct) << "failed to get registered clients: " << cpp_strerror(*result)
224 << dendl;
225 return m_on_finish;
226 }
227
228 std::lock_guard locker{m_lock};
229
230 ceph_assert(m_current_ops.empty());
231
232 for (auto client : m_clients) {
233 journal::ClientData client_data;
234 auto bl_it = client.data.cbegin();
235 try {
236 using ceph::decode;
237 decode(client_data, bl_it);
238 } catch (const buffer::error &err) {
239 lderr(cct) << "failed to decode client data" << dendl;
240 m_error_result = -EBADMSG;
241 continue;
242 }
243
244 journal::ClientMetaType type = client_data.get_client_meta_type();
245 if (type != journal::ClientMetaType::MIRROR_PEER_CLIENT_META_TYPE) {
246 continue;
247 }
248
249 if (m_current_ops.find(client.id) != m_current_ops.end()) {
250 // Should not happen.
251 lderr(cct) << "clients with the same id "
252 << client.id << dendl;
253 continue;
254 }
255
256 m_current_ops[client.id] = 0;
257 m_ret[client.id] = 0;
258
259 journal::MirrorPeerClientMeta client_meta =
260 boost::get<journal::MirrorPeerClientMeta>(client_data.client_meta);
261
262 for (const auto& sync : client_meta.sync_points) {
263 send_remove_snap(client.id, sync.snap_namespace, sync.snap_name);
264 }
265
266 if (m_current_ops[client.id] == 0) {
267 // no snaps to remove
268 send_unregister_client(client.id);
269 }
270 }
271
272 if (m_current_ops.empty()) {
273 if (m_error_result < 0) {
274 *result = m_error_result;
275 return m_on_finish;
276 } else if (!m_remove) {
277 return m_on_finish;
278 }
279
280 // no mirror clients to unregister
281 send_remove_mirror_image();
282 }
283
284 return nullptr;
285 }
286
287 template <typename I>
288 void DisableRequest<I>::remove_mirror_snapshots() {
289 CephContext *cct = m_image_ctx->cct;
290 ldout(cct, 10) << dendl;
291
292 // remove snapshot-based mirroring snapshots
293 bool removing_snapshots = false;
294 {
295 std::lock_guard locker{m_lock};
296 std::shared_lock image_locker{m_image_ctx->image_lock};
297
298 for (auto &it : m_image_ctx->snap_info) {
299 auto &snap_info = it.second;
300 auto type = cls::rbd::get_snap_namespace_type(
301 snap_info.snap_namespace);
302 if (type == cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR) {
303 send_remove_snap("", snap_info.snap_namespace, snap_info.name);
304 removing_snapshots = true;
305 }
306 }
307 }
308
309 if (!removing_snapshots) {
310 send_remove_mirror_image();
311 }
312 }
313
314 template <typename I>
315 void DisableRequest<I>::send_remove_snap(
316 const std::string &client_id,
317 const cls::rbd::SnapshotNamespace &snap_namespace,
318 const std::string &snap_name) {
319 CephContext *cct = m_image_ctx->cct;
320 ldout(cct, 10) << "client_id=" << client_id
321 << ", snap_name=" << snap_name << dendl;
322
323 ceph_assert(ceph_mutex_is_locked(m_lock));
324
325 m_current_ops[client_id]++;
326
327 Context *ctx = create_context_callback(
328 &DisableRequest<I>::handle_remove_snap, client_id);
329
330 ctx = new LambdaContext([this, snap_namespace, snap_name, ctx](int r) {
331 m_image_ctx->operations->snap_remove(snap_namespace,
332 snap_name.c_str(),
333 ctx);
334 });
335
336 m_image_ctx->op_work_queue->queue(ctx, 0);
337 }
338
339 template <typename I>
340 Context *DisableRequest<I>::handle_remove_snap(int *result,
341 const std::string &client_id) {
342 CephContext *cct = m_image_ctx->cct;
343 ldout(cct, 10) << "r=" << *result << dendl;
344
345 std::lock_guard locker{m_lock};
346
347 ceph_assert(m_current_ops[client_id] > 0);
348 m_current_ops[client_id]--;
349
350 if (*result < 0 && *result != -ENOENT) {
351 lderr(cct) << "failed to remove mirroring snapshot: "
352 << cpp_strerror(*result) << dendl;
353 m_ret[client_id] = *result;
354 }
355
356 if (m_current_ops[client_id] == 0) {
357 if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
358 ceph_assert(client_id.empty());
359 m_current_ops.erase(client_id);
360 if (m_ret[client_id] < 0) {
361 return m_on_finish;
362 }
363 send_remove_mirror_image();
364 return nullptr;
365 }
366
367 send_unregister_client(client_id);
368 }
369
370 return nullptr;
371 }
372
373 template <typename I>
374 void DisableRequest<I>::send_unregister_client(
375 const std::string &client_id) {
376 CephContext *cct = m_image_ctx->cct;
377 ldout(cct, 10) << dendl;
378
379 ceph_assert(ceph_mutex_is_locked(m_lock));
380 ceph_assert(m_current_ops[client_id] == 0);
381
382 Context *ctx = create_context_callback(
383 &DisableRequest<I>::handle_unregister_client, client_id);
384
385 if (m_ret[client_id] < 0) {
386 m_image_ctx->op_work_queue->queue(ctx, m_ret[client_id]);
387 return;
388 }
389
390 librados::ObjectWriteOperation op;
391 cls::journal::client::client_unregister(&op, client_id);
392 std::string header_oid = ::journal::Journaler::header_oid(m_image_ctx->id);
393 librados::AioCompletion *comp = create_rados_callback(ctx);
394
395 int r = m_image_ctx->md_ctx.aio_operate(header_oid, comp, &op);
396 ceph_assert(r == 0);
397 comp->release();
398 }
399
400 template <typename I>
401 Context *DisableRequest<I>::handle_unregister_client(
402 int *result, const std::string &client_id) {
403
404 CephContext *cct = m_image_ctx->cct;
405 ldout(cct, 10) << "r=" << *result << dendl;
406
407 std::lock_guard locker{m_lock};
408 ceph_assert(m_current_ops[client_id] == 0);
409 m_current_ops.erase(client_id);
410
411 if (*result < 0 && *result != -ENOENT) {
412 lderr(cct) << "failed to unregister remote journal client: "
413 << cpp_strerror(*result) << dendl;
414 m_error_result = *result;
415 }
416
417 if (!m_current_ops.empty()) {
418 return nullptr;
419 }
420
421 if (m_error_result < 0) {
422 *result = m_error_result;
423 return m_on_finish;
424 }
425
426 send_get_clients();
427 return nullptr;
428 }
429
430 template <typename I>
431 void DisableRequest<I>::send_remove_mirror_image() {
432 CephContext *cct = m_image_ctx->cct;
433 ldout(cct, 10) << dendl;
434
435 auto ctx = util::create_context_callback<
436 DisableRequest<I>,
437 &DisableRequest<I>::handle_remove_mirror_image>(this);
438 auto req = ImageRemoveRequest<I>::create(
439 m_image_ctx->md_ctx, m_mirror_image.global_image_id, m_image_ctx->id,
440 ctx);
441 req->send();
442 }
443
444 template <typename I>
445 Context *DisableRequest<I>::handle_remove_mirror_image(int *result) {
446 CephContext *cct = m_image_ctx->cct;
447 ldout(cct, 10) << "r=" << *result << dendl;
448
449 if (*result < 0) {
450 lderr(cct) << "failed to remove mirror image: " << cpp_strerror(*result)
451 << dendl;
452 return m_on_finish;
453 }
454
455 ldout(cct, 20) << "removed image state from rbd_mirroring object" << dendl;
456 return m_on_finish;
457 }
458
459 template <typename I>
460 Context *DisableRequest<I>::create_context_callback(
461 Context*(DisableRequest<I>::*handle)(int*, const std::string &client_id),
462 const std::string &client_id) {
463
464 return new LambdaContext([this, handle, client_id](int r) {
465 Context *on_finish = (this->*handle)(&r, client_id);
466 if (on_finish != nullptr) {
467 on_finish->complete(r);
468 delete this;
469 }
470 });
471 }
472
473 } // namespace mirror
474 } // namespace librbd
475
476 template class librbd::mirror::DisableRequest<librbd::ImageCtx>;