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