1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
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"
18 #define dout_subsys ceph_subsys_rbd
20 #define dout_prefix *_dout << "librbd::mirror::DisableRequest: "
25 using util::create_rados_callback;
28 DisableRequest<I>::DisableRequest(I *image_ctx, bool force, bool remove,
30 : m_image_ctx(image_ctx), m_force(force), m_remove(remove),
31 m_on_finish(on_finish), m_lock("mirror::DisableRequest::m_lock") {
35 void DisableRequest<I>::send() {
36 send_get_mirror_image();
40 void DisableRequest<I>::send_get_mirror_image() {
41 CephContext *cct = m_image_ctx->cct;
42 ldout(cct, 10) << this << " " << __func__ << dendl;
44 librados::ObjectReadOperation op;
45 cls_client::mirror_image_get_start(&op, m_image_ctx->id);
47 using klass = DisableRequest<I>;
48 librados::AioCompletion *comp =
49 create_rados_callback<klass, &klass::handle_get_mirror_image>(this);
51 int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl);
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;
62 bufferlist::iterator iter = m_out_bl.begin();
63 *result = cls_client::mirror_image_get_finish(&iter, &m_mirror_image);
67 if (*result == -ENOENT) {
68 ldout(cct, 20) << this << " " << __func__
69 << ": mirroring is not enabled for this image" << dendl;
71 } else if (*result == -EOPNOTSUPP) {
72 ldout(cct, 5) << this << " " << __func__
73 << ": mirroring is not supported by OSD" << dendl;
75 lderr(cct) << "failed to retreive mirror image: " << cpp_strerror(*result)
86 void DisableRequest<I>::send_get_tag_owner() {
87 CephContext *cct = m_image_ctx->cct;
88 ldout(cct, 10) << this << " " << __func__ << dendl;
90 using klass = DisableRequest<I>;
91 Context *ctx = util::create_context_callback<
92 klass, &klass::handle_get_tag_owner>(this);
94 Journal<I>::is_tag_owner(m_image_ctx, &m_is_primary, ctx);
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;
103 lderr(cct) << "failed to check tag ownership: " << cpp_strerror(*result)
108 if (!m_is_primary && !m_force) {
109 lderr(cct) << "mirrored image is not primary, "
110 << "add force option to disable mirroring" << dendl;
115 send_set_mirror_image();
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;
124 m_mirror_image.state = cls::rbd::MIRROR_IMAGE_STATE_DISABLING;
126 librados::ObjectWriteOperation op;
127 cls_client::mirror_image_set(&op, m_image_ctx->id, m_mirror_image);
129 using klass = DisableRequest<I>;
130 librados::AioCompletion *comp =
131 create_rados_callback<klass, &klass::handle_set_mirror_image>(this);
133 int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op);
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;
144 lderr(cct) << "failed to disable mirroring: " << cpp_strerror(*result)
149 send_notify_mirroring_watcher();
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;
158 using klass = DisableRequest<I>;
159 Context *ctx = util::create_context_callback<
160 klass, &klass::handle_notify_mirroring_watcher>(this);
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);
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;
173 lderr(cct) << "failed to send update notification: "
174 << cpp_strerror(*result) << dendl;
178 send_promote_image();
182 template <typename I>
183 void DisableRequest<I>::send_promote_image() {
189 CephContext *cct = m_image_ctx->cct;
190 ldout(cct, 10) << this << " " << __func__ << dendl;
192 // Not primary -- shouldn't have the journal open
193 assert(m_image_ctx->journal == nullptr);
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);
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;
208 lderr(cct) << "failed to promote image: " << cpp_strerror(*result) << dendl;
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;
221 using klass = DisableRequest<I>;
222 Context *ctx = util::create_context_callback<
223 klass, &klass::handle_get_clients>(this);
225 std::string header_oid = ::journal::Journaler::header_oid(m_image_ctx->id);
227 cls::journal::client::client_list(m_image_ctx->md_ctx, header_oid, &m_clients,
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;
237 lderr(cct) << "failed to get registered clients: " << cpp_strerror(*result)
242 Mutex::Locker locker(m_lock);
244 assert(m_current_ops.empty());
246 for (auto client : m_clients) {
247 journal::ClientData client_data;
248 bufferlist::iterator bl_it = client.data.begin();
250 ::decode(client_data, bl_it);
251 } catch (const buffer::error &err) {
252 lderr(cct) << "failed to decode client data" << dendl;
253 m_error_result = -EBADMSG;
257 journal::ClientMetaType type = client_data.get_client_meta_type();
258 if (type != journal::ClientMetaType::MIRROR_PEER_CLIENT_META_TYPE) {
262 if (m_current_ops.find(client.id) != m_current_ops.end()) {
263 // Should not happen.
264 lderr(cct) << this << " " << __func__ << ": clients with the same id "
265 << client.id << dendl;
269 m_current_ops[client.id] = 0;
270 m_ret[client.id] = 0;
272 journal::MirrorPeerClientMeta client_meta =
273 boost::get<journal::MirrorPeerClientMeta>(client_data.client_meta);
275 for (const auto& sync : client_meta.sync_points) {
276 send_remove_snap(client.id, sync.snap_namespace, sync.snap_name);
279 if (m_current_ops[client.id] == 0) {
280 // no snaps to remove
281 send_unregister_client(client.id);
285 if (m_current_ops.empty()) {
286 if (m_error_result < 0) {
287 *result = m_error_result;
289 } else if (!m_remove) {
293 // no mirror clients to unregister
294 send_remove_mirror_image();
300 template <typename I>
301 void DisableRequest<I>::send_remove_snap(const std::string &client_id,
302 const cls::rbd::SnapshotNamespace &snap_namespace,
303 const std::string &snap_name) {
304 CephContext *cct = m_image_ctx->cct;
305 ldout(cct, 10) << this << " " << __func__ << ": client_id=" << client_id
306 << ", snap_name=" << snap_name << dendl;
308 assert(m_lock.is_locked());
310 m_current_ops[client_id]++;
312 Context *ctx = create_context_callback(
313 &DisableRequest<I>::handle_remove_snap, client_id);
315 ctx = new FunctionContext([this, snap_namespace, snap_name, ctx](int r) {
316 RWLock::WLocker owner_locker(m_image_ctx->owner_lock);
317 m_image_ctx->operations->execute_snap_remove(snap_namespace,
322 m_image_ctx->op_work_queue->queue(ctx, 0);
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;
331 Mutex::Locker locker(m_lock);
333 assert(m_current_ops[client_id] > 0);
334 m_current_ops[client_id]--;
336 if (*result < 0 && *result != -ENOENT) {
338 "failed to remove temporary snapshot created by remote peer: "
339 << cpp_strerror(*result) << dendl;
340 m_ret[client_id] = *result;
343 if (m_current_ops[client_id] == 0) {
344 send_unregister_client(client_id);
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;
356 assert(m_lock.is_locked());
357 assert(m_current_ops[client_id] == 0);
359 Context *ctx = create_context_callback(
360 &DisableRequest<I>::handle_unregister_client, client_id);
362 if (m_ret[client_id] < 0) {
363 m_image_ctx->op_work_queue->queue(ctx, m_ret[client_id]);
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);
372 int r = m_image_ctx->md_ctx.aio_operate(header_oid, comp, &op);
377 template <typename I>
378 Context *DisableRequest<I>::handle_unregister_client(
379 int *result, const std::string &client_id) {
381 CephContext *cct = m_image_ctx->cct;
382 ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
384 Mutex::Locker locker(m_lock);
385 assert(m_current_ops[client_id] == 0);
386 m_current_ops.erase(client_id);
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;
394 if (!m_current_ops.empty()) {
398 if (m_error_result < 0) {
399 *result = m_error_result;
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;
412 librados::ObjectWriteOperation op;
413 cls_client::mirror_image_remove(&op, m_image_ctx->id);
415 using klass = DisableRequest<I>;
416 librados::AioCompletion *comp =
417 create_rados_callback<klass, &klass::handle_remove_mirror_image>(this);
419 int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op);
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;
429 if (*result == -ENOENT) {
434 lderr(cct) << "failed to remove mirror image: " << cpp_strerror(*result)
439 ldout(cct, 20) << this << " " << __func__
440 << ": removed image state from rbd_mirroring object" << dendl;
442 send_notify_mirroring_watcher_removed();
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;
451 using klass = DisableRequest<I>;
452 Context *ctx = util::create_context_callback<
453 klass, &klass::handle_notify_mirroring_watcher_removed>(this);
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);
460 template <typename I>
461 Context *DisableRequest<I>::handle_notify_mirroring_watcher_removed(
463 CephContext *cct = m_image_ctx->cct;
464 ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
467 lderr(cct) << "failed to send update notification: "
468 << cpp_strerror(*result) << dendl;
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) {
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);
489 } // namespace mirror
490 } // namespace librbd
492 template class librbd::mirror::DisableRequest<librbd::ImageCtx>;