]> git.proxmox.com Git - ceph.git/blob - ceph/src/librbd/mirror/DisableRequest.cc
import 15.2.4
[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 std::unique_lock locker{m_lock};
223 ceph_assert(m_current_ops.empty());
224
225 if (*result < 0) {
226 lderr(cct) << "failed to get registered clients: " << cpp_strerror(*result)
227 << dendl;
228 return m_on_finish;
229 }
230
231 for (auto client : m_clients) {
232 journal::ClientData client_data;
233 auto bl_it = client.data.cbegin();
234 try {
235 using ceph::decode;
236 decode(client_data, bl_it);
237 } catch (const buffer::error &err) {
238 lderr(cct) << "failed to decode client data" << dendl;
239 m_error_result = -EBADMSG;
240 continue;
241 }
242
243 journal::ClientMetaType type = client_data.get_client_meta_type();
244 if (type != journal::ClientMetaType::MIRROR_PEER_CLIENT_META_TYPE) {
245 continue;
246 }
247
248 if (m_current_ops.find(client.id) != m_current_ops.end()) {
249 // Should not happen.
250 lderr(cct) << "clients with the same id "
251 << client.id << dendl;
252 continue;
253 }
254
255 m_current_ops[client.id] = 0;
256 m_ret[client.id] = 0;
257
258 journal::MirrorPeerClientMeta client_meta =
259 boost::get<journal::MirrorPeerClientMeta>(client_data.client_meta);
260
261 for (const auto& sync : client_meta.sync_points) {
262 send_remove_snap(client.id, sync.snap_namespace, sync.snap_name);
263 }
264
265 if (m_current_ops[client.id] == 0) {
266 // no snaps to remove
267 send_unregister_client(client.id);
268 }
269 }
270
271 if (m_current_ops.empty()) {
272 if (m_error_result < 0) {
273 *result = m_error_result;
274 return m_on_finish;
275 } else if (!m_remove) {
276 return m_on_finish;
277 }
278 locker.unlock();
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::unique_lock 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 locker.unlock();
364
365 send_remove_mirror_image();
366 return nullptr;
367 }
368
369 send_unregister_client(client_id);
370 }
371
372 return nullptr;
373 }
374
375 template <typename I>
376 void DisableRequest<I>::send_unregister_client(
377 const std::string &client_id) {
378 CephContext *cct = m_image_ctx->cct;
379 ldout(cct, 10) << dendl;
380
381 ceph_assert(ceph_mutex_is_locked(m_lock));
382 ceph_assert(m_current_ops[client_id] == 0);
383
384 Context *ctx = create_context_callback(
385 &DisableRequest<I>::handle_unregister_client, client_id);
386
387 if (m_ret[client_id] < 0) {
388 m_image_ctx->op_work_queue->queue(ctx, m_ret[client_id]);
389 return;
390 }
391
392 librados::ObjectWriteOperation op;
393 cls::journal::client::client_unregister(&op, client_id);
394 std::string header_oid = ::journal::Journaler::header_oid(m_image_ctx->id);
395 librados::AioCompletion *comp = create_rados_callback(ctx);
396
397 int r = m_image_ctx->md_ctx.aio_operate(header_oid, comp, &op);
398 ceph_assert(r == 0);
399 comp->release();
400 }
401
402 template <typename I>
403 Context *DisableRequest<I>::handle_unregister_client(
404 int *result, const std::string &client_id) {
405
406 CephContext *cct = m_image_ctx->cct;
407 ldout(cct, 10) << "r=" << *result << dendl;
408
409 std::unique_lock locker{m_lock};
410 ceph_assert(m_current_ops[client_id] == 0);
411 m_current_ops.erase(client_id);
412
413 if (*result < 0 && *result != -ENOENT) {
414 lderr(cct) << "failed to unregister remote journal client: "
415 << cpp_strerror(*result) << dendl;
416 m_error_result = *result;
417 }
418
419 if (!m_current_ops.empty()) {
420 return nullptr;
421 }
422
423 if (m_error_result < 0) {
424 *result = m_error_result;
425 return m_on_finish;
426 }
427 locker.unlock();
428
429 send_get_clients();
430 return nullptr;
431 }
432
433 template <typename I>
434 void DisableRequest<I>::send_remove_mirror_image() {
435 CephContext *cct = m_image_ctx->cct;
436 ldout(cct, 10) << dendl;
437
438 auto ctx = util::create_context_callback<
439 DisableRequest<I>,
440 &DisableRequest<I>::handle_remove_mirror_image>(this);
441 auto req = ImageRemoveRequest<I>::create(
442 m_image_ctx->md_ctx, m_mirror_image.global_image_id, m_image_ctx->id,
443 ctx);
444 req->send();
445 }
446
447 template <typename I>
448 Context *DisableRequest<I>::handle_remove_mirror_image(int *result) {
449 CephContext *cct = m_image_ctx->cct;
450 ldout(cct, 10) << "r=" << *result << dendl;
451
452 if (*result < 0) {
453 lderr(cct) << "failed to remove mirror image: " << cpp_strerror(*result)
454 << dendl;
455 return m_on_finish;
456 }
457
458 ldout(cct, 20) << "removed image state from rbd_mirroring object" << dendl;
459 return m_on_finish;
460 }
461
462 template <typename I>
463 Context *DisableRequest<I>::create_context_callback(
464 Context*(DisableRequest<I>::*handle)(int*, const std::string &client_id),
465 const std::string &client_id) {
466
467 return new LambdaContext([this, handle, client_id](int r) {
468 Context *on_finish = (this->*handle)(&r, client_id);
469 if (on_finish != nullptr) {
470 on_finish->complete(r);
471 delete this;
472 }
473 });
474 }
475
476 } // namespace mirror
477 } // namespace librbd
478
479 template class librbd::mirror::DisableRequest<librbd::ImageCtx>;