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 "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"
21 #define dout_subsys ceph_subsys_rbd
23 #define dout_prefix *_dout << "librbd::mirror::DisableRequest: " \
24 << this << " " << __func__ << ": "
29 using util::create_rados_callback
;
32 DisableRequest
<I
>::DisableRequest(I
*image_ctx
, bool force
, bool remove
,
34 : m_image_ctx(image_ctx
), m_force(force
), m_remove(remove
),
35 m_on_finish(on_finish
) {
39 void DisableRequest
<I
>::send() {
40 send_get_mirror_info();
44 void DisableRequest
<I
>::send_get_mirror_info() {
45 CephContext
*cct
= m_image_ctx
->cct
;
46 ldout(cct
, 10) << dendl
;
49 using klass
= DisableRequest
<I
>;
50 Context
*ctx
= util::create_context_callback
<
51 klass
, &klass::handle_get_mirror_info
>(this);
53 auto req
= GetInfoRequest
<I
>::create(*m_image_ctx
, &m_mirror_image
,
55 &m_primary_mirror_uuid
, ctx
);
60 Context
*DisableRequest
<I
>::handle_get_mirror_info(int *result
) {
61 CephContext
*cct
= m_image_ctx
->cct
;
62 ldout(cct
, 10) << "r=" << *result
<< dendl
;
65 if (*result
== -ENOENT
) {
66 ldout(cct
, 20) << "mirroring is not enabled for this image" << dendl
;
69 lderr(cct
) << "failed to get mirroring info: " << cpp_strerror(*result
)
75 m_is_primary
= (m_promotion_state
== PROMOTION_STATE_PRIMARY
||
76 m_promotion_state
== PROMOTION_STATE_UNKNOWN
);
78 if (!m_is_primary
&& !m_force
) {
79 lderr(cct
) << "mirrored image is not primary, "
80 << "add force option to disable mirroring" << dendl
;
85 send_image_state_update();
90 void DisableRequest
<I
>::send_image_state_update() {
91 CephContext
*cct
= m_image_ctx
->cct
;
92 ldout(cct
, 10) << dendl
;
94 auto ctx
= util::create_context_callback
<
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
);
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
;
109 lderr(cct
) << "failed to disable mirroring: " << cpp_strerror(*result
)
114 send_promote_image();
118 template <typename I
>
119 void DisableRequest
<I
>::send_promote_image() {
121 clean_mirror_state();
125 CephContext
*cct
= m_image_ctx
->cct
;
126 ldout(cct
, 10) << dendl
;
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);
134 auto req
= journal::PromoteRequest
<I
>::create(m_image_ctx
, true, ctx
);
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
);
141 lderr(cct
) << "unknown image mirror mode: " << m_mirror_image
.mode
<< dendl
;
142 ctx
->complete(-EOPNOTSUPP
);
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
;
152 lderr(cct
) << "failed to promote image: " << cpp_strerror(*result
) << dendl
;
156 send_refresh_image();
160 template <typename I
>
161 void DisableRequest
<I
>::send_refresh_image() {
162 if (!m_image_ctx
->state
->is_refresh_required()) {
163 clean_mirror_state();
167 CephContext
*cct
= m_image_ctx
->cct
;
168 ldout(cct
, 10) << dendl
;
170 auto ctx
= util::create_context_callback
<
172 &DisableRequest
<I
>::handle_refresh_image
>(this);
173 m_image_ctx
->state
->refresh(ctx
);
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
;
182 lderr(cct
) << "failed to refresh image: " << cpp_strerror(*result
) << dendl
;
186 clean_mirror_state();
190 template <typename I
>
191 void DisableRequest
<I
>::clean_mirror_state() {
192 CephContext
*cct
= m_image_ctx
->cct
;
193 ldout(cct
, 10) << dendl
;
195 if (m_mirror_image
.mode
== cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT
) {
196 remove_mirror_snapshots();
202 template <typename I
>
203 void DisableRequest
<I
>::send_get_clients() {
204 CephContext
*cct
= m_image_ctx
->cct
;
205 ldout(cct
, 10) << dendl
;
207 using klass
= DisableRequest
<I
>;
208 Context
*ctx
= util::create_context_callback
<
209 klass
, &klass::handle_get_clients
>(this);
211 std::string header_oid
= ::journal::Journaler::header_oid(m_image_ctx
->id
);
213 cls::journal::client::client_list(m_image_ctx
->md_ctx
, header_oid
, &m_clients
,
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
;
223 lderr(cct
) << "failed to get registered clients: " << cpp_strerror(*result
)
228 std::lock_guard locker
{m_lock
};
230 ceph_assert(m_current_ops
.empty());
232 for (auto client
: m_clients
) {
233 journal::ClientData client_data
;
234 auto bl_it
= client
.data
.cbegin();
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
;
244 journal::ClientMetaType type
= client_data
.get_client_meta_type();
245 if (type
!= journal::ClientMetaType::MIRROR_PEER_CLIENT_META_TYPE
) {
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
;
256 m_current_ops
[client
.id
] = 0;
257 m_ret
[client
.id
] = 0;
259 journal::MirrorPeerClientMeta client_meta
=
260 boost::get
<journal::MirrorPeerClientMeta
>(client_data
.client_meta
);
262 for (const auto& sync
: client_meta
.sync_points
) {
263 send_remove_snap(client
.id
, sync
.snap_namespace
, sync
.snap_name
);
266 if (m_current_ops
[client
.id
] == 0) {
267 // no snaps to remove
268 send_unregister_client(client
.id
);
272 if (m_current_ops
.empty()) {
273 if (m_error_result
< 0) {
274 *result
= m_error_result
;
276 } else if (!m_remove
) {
280 // no mirror clients to unregister
281 send_remove_mirror_image();
287 template <typename I
>
288 void DisableRequest
<I
>::remove_mirror_snapshots() {
289 CephContext
*cct
= m_image_ctx
->cct
;
290 ldout(cct
, 10) << dendl
;
292 // remove snapshot-based mirroring snapshots
293 bool removing_snapshots
= false;
295 std::lock_guard locker
{m_lock
};
296 std::shared_lock image_locker
{m_image_ctx
->image_lock
};
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;
309 if (!removing_snapshots
) {
310 send_remove_mirror_image();
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
;
323 ceph_assert(ceph_mutex_is_locked(m_lock
));
325 m_current_ops
[client_id
]++;
327 Context
*ctx
= create_context_callback(
328 &DisableRequest
<I
>::handle_remove_snap
, client_id
);
330 ctx
= new LambdaContext([this, snap_namespace
, snap_name
, ctx
](int r
) {
331 m_image_ctx
->operations
->snap_remove(snap_namespace
,
336 m_image_ctx
->op_work_queue
->queue(ctx
, 0);
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
;
345 std::lock_guard locker
{m_lock
};
347 ceph_assert(m_current_ops
[client_id
] > 0);
348 m_current_ops
[client_id
]--;
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
;
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) {
363 send_remove_mirror_image();
367 send_unregister_client(client_id
);
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
;
379 ceph_assert(ceph_mutex_is_locked(m_lock
));
380 ceph_assert(m_current_ops
[client_id
] == 0);
382 Context
*ctx
= create_context_callback(
383 &DisableRequest
<I
>::handle_unregister_client
, client_id
);
385 if (m_ret
[client_id
] < 0) {
386 m_image_ctx
->op_work_queue
->queue(ctx
, m_ret
[client_id
]);
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
);
395 int r
= m_image_ctx
->md_ctx
.aio_operate(header_oid
, comp
, &op
);
400 template <typename I
>
401 Context
*DisableRequest
<I
>::handle_unregister_client(
402 int *result
, const std::string
&client_id
) {
404 CephContext
*cct
= m_image_ctx
->cct
;
405 ldout(cct
, 10) << "r=" << *result
<< dendl
;
407 std::lock_guard locker
{m_lock
};
408 ceph_assert(m_current_ops
[client_id
] == 0);
409 m_current_ops
.erase(client_id
);
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
;
417 if (!m_current_ops
.empty()) {
421 if (m_error_result
< 0) {
422 *result
= m_error_result
;
430 template <typename I
>
431 void DisableRequest
<I
>::send_remove_mirror_image() {
432 CephContext
*cct
= m_image_ctx
->cct
;
433 ldout(cct
, 10) << dendl
;
435 auto ctx
= util::create_context_callback
<
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
,
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
;
450 lderr(cct
) << "failed to remove mirror image: " << cpp_strerror(*result
)
455 ldout(cct
, 20) << "removed image state from rbd_mirroring object" << dendl
;
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
) {
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
);
473 } // namespace mirror
474 } // namespace librbd
476 template class librbd::mirror::DisableRequest
<librbd::ImageCtx
>;