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 auto iter
= m_out_bl
.cbegin();
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 retrieve 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 ceph_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 ceph_assert(m_current_ops
.empty());
246 for (auto client
: m_clients
) {
247 journal::ClientData client_data
;
248 auto bl_it
= client
.data
.cbegin();
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
;
258 journal::ClientMetaType type
= client_data
.get_client_meta_type();
259 if (type
!= journal::ClientMetaType::MIRROR_PEER_CLIENT_META_TYPE
) {
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
;
270 m_current_ops
[client
.id
] = 0;
271 m_ret
[client
.id
] = 0;
273 journal::MirrorPeerClientMeta client_meta
=
274 boost::get
<journal::MirrorPeerClientMeta
>(client_data
.client_meta
);
276 for (const auto& sync
: client_meta
.sync_points
) {
277 send_remove_snap(client
.id
, sync
.snap_namespace
, sync
.snap_name
);
280 if (m_current_ops
[client
.id
] == 0) {
281 // no snaps to remove
282 send_unregister_client(client
.id
);
286 if (m_current_ops
.empty()) {
287 if (m_error_result
< 0) {
288 *result
= m_error_result
;
290 } else if (!m_remove
) {
294 // no mirror clients to unregister
295 send_remove_mirror_image();
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
;
309 ceph_assert(m_lock
.is_locked());
311 m_current_ops
[client_id
]++;
313 Context
*ctx
= create_context_callback(
314 &DisableRequest
<I
>::handle_remove_snap
, client_id
);
316 ctx
= new FunctionContext([this, snap_namespace
, snap_name
, ctx
](int r
) {
317 m_image_ctx
->operations
->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 ceph_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 ceph_assert(m_lock
.is_locked());
357 ceph_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 ceph_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
>;