]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/rbd_mirror/ImageDeleter.cc
import 15.2.0 Octopus source
[ceph.git] / ceph / src / tools / rbd_mirror / ImageDeleter.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2016 SUSE LINUX GmbH
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
c07f9fc5 14
7c673cae
FG
15#include "include/rados/librados.hpp"
16#include "common/Formatter.h"
17#include "common/admin_socket.h"
18#include "common/debug.h"
19#include "common/errno.h"
11fdf7f2 20#include "common/Timer.h"
7c673cae
FG
21#include "common/WorkQueue.h"
22#include "global/global_context.h"
23#include "librbd/internal.h"
24#include "librbd/ImageCtx.h"
25#include "librbd/ImageState.h"
7c673cae 26#include "librbd/Operations.h"
7c673cae
FG
27#include "cls/rbd/cls_rbd_client.h"
28#include "cls/rbd/cls_rbd_types.h"
29#include "librbd/Utils.h"
30#include "ImageDeleter.h"
11fdf7f2 31#include "tools/rbd_mirror/Threads.h"
9f95a23c 32#include "tools/rbd_mirror/Throttler.h"
11fdf7f2 33#include "tools/rbd_mirror/image_deleter/TrashMoveRequest.h"
eafe8130 34#include "tools/rbd_mirror/image_deleter/TrashRemoveRequest.h"
11fdf7f2
TL
35#include "tools/rbd_mirror/image_deleter/TrashWatcher.h"
36#include <map>
37#include <sstream>
7c673cae
FG
38
39#define dout_context g_ceph_context
40#define dout_subsys ceph_subsys_rbd_mirror
7c673cae
FG
41
42using std::string;
7c673cae
FG
43using std::stringstream;
44using std::vector;
45using std::pair;
46using std::make_pair;
47
48using librados::IoCtx;
49using namespace librbd;
50
51namespace rbd {
52namespace mirror {
53
9f95a23c
TL
54using librbd::util::create_async_context_callback;
55
7c673cae
FG
56namespace {
57
58class ImageDeleterAdminSocketCommand {
59public:
60 virtual ~ImageDeleterAdminSocketCommand() {}
9f95a23c 61 virtual int call(Formatter *f) = 0;
7c673cae
FG
62};
63
c07f9fc5 64template <typename I>
7c673cae
FG
65class StatusCommand : public ImageDeleterAdminSocketCommand {
66public:
c07f9fc5 67 explicit StatusCommand(ImageDeleter<I> *image_del) : image_del(image_del) {}
7c673cae 68
9f95a23c
TL
69 int call(Formatter *f) override {
70 image_del->print_status(f);
71 return 0;
7c673cae
FG
72 }
73
74private:
c07f9fc5 75 ImageDeleter<I> *image_del;
7c673cae
FG
76};
77
7c673cae
FG
78} // anonymous namespace
79
c07f9fc5 80template <typename I>
7c673cae
FG
81class ImageDeleterAdminSocketHook : public AdminSocketHook {
82public:
11fdf7f2
TL
83 ImageDeleterAdminSocketHook(CephContext *cct, const std::string& pool_name,
84 ImageDeleter<I> *image_del) :
7c673cae
FG
85 admin_socket(cct->get_admin_socket()) {
86
87 std::string command;
88 int r;
89
11fdf7f2 90 command = "rbd mirror deletion status " + pool_name;
9f95a23c 91 r = admin_socket->register_command(command, this,
7c673cae
FG
92 "get status for image deleter");
93 if (r == 0) {
c07f9fc5 94 commands[command] = new StatusCommand<I>(image_del);
7c673cae
FG
95 }
96
97 }
98
99 ~ImageDeleterAdminSocketHook() override {
9f95a23c 100 (void)admin_socket->unregister_commands(this);
7c673cae
FG
101 for (Commands::const_iterator i = commands.begin(); i != commands.end();
102 ++i) {
7c673cae
FG
103 delete i->second;
104 }
105 }
106
9f95a23c
TL
107 int call(std::string_view command, const cmdmap_t& cmdmap,
108 Formatter *f,
109 std::ostream& errss,
110 bufferlist& out) override {
7c673cae 111 Commands::const_iterator i = commands.find(command);
11fdf7f2 112 ceph_assert(i != commands.end());
9f95a23c 113 return i->second->call(f);
7c673cae
FG
114 }
115
116private:
11fdf7f2
TL
117 typedef std::map<std::string, ImageDeleterAdminSocketCommand*,
118 std::less<>> Commands;
7c673cae
FG
119 AdminSocket *admin_socket;
120 Commands commands;
121};
122
c07f9fc5 123template <typename I>
9f95a23c
TL
124ImageDeleter<I>::ImageDeleter(
125 librados::IoCtx& local_io_ctx, Threads<librbd::ImageCtx>* threads,
126 Throttler<librbd::ImageCtx>* image_deletion_throttler,
127 ServiceDaemon<librbd::ImageCtx>* service_daemon)
11fdf7f2 128 : m_local_io_ctx(local_io_ctx), m_threads(threads),
9f95a23c 129 m_image_deletion_throttler(image_deletion_throttler),
11fdf7f2 130 m_service_daemon(service_daemon), m_trash_listener(this),
9f95a23c
TL
131 m_lock(ceph::make_mutex(
132 librbd::util::unique_lock_name("rbd::mirror::ImageDeleter::m_lock",
133 this))) {
7c673cae
FG
134}
135
11fdf7f2
TL
136#undef dout_prefix
137#define dout_prefix *_dout << "rbd::mirror::ImageDeleter: " << " " \
138 << __func__ << ": "
139
c07f9fc5 140template <typename I>
11fdf7f2
TL
141void ImageDeleter<I>::trash_move(librados::IoCtx& local_io_ctx,
142 const std::string& global_image_id,
143 bool resync,
144 ContextWQ* work_queue, Context* on_finish) {
145 dout(10) << "global_image_id=" << global_image_id << ", "
146 << "resync=" << resync << dendl;
147
148 auto req = rbd::mirror::image_deleter::TrashMoveRequest<>::create(
149 local_io_ctx, global_image_id, resync, work_queue, on_finish);
150 req->send();
151}
7c673cae 152
11fdf7f2
TL
153#undef dout_prefix
154#define dout_prefix *_dout << "rbd::mirror::ImageDeleter: " << this << " " \
155 << __func__ << ": "
7c673cae 156
11fdf7f2
TL
157template <typename I>
158void ImageDeleter<I>::init(Context* on_finish) {
159 dout(10) << dendl;
160
161 m_asok_hook = new ImageDeleterAdminSocketHook<I>(
162 g_ceph_context, m_local_io_ctx.get_pool_name(), this);
163
164 m_trash_watcher = image_deleter::TrashWatcher<I>::create(m_local_io_ctx,
165 m_threads,
166 m_trash_listener);
167 m_trash_watcher->init(on_finish);
7c673cae
FG
168}
169
c07f9fc5 170template <typename I>
11fdf7f2
TL
171void ImageDeleter<I>::shut_down(Context* on_finish) {
172 dout(10) << dendl;
7c673cae 173
11fdf7f2
TL
174 delete m_asok_hook;
175 m_asok_hook = nullptr;
7c673cae 176
9f95a23c
TL
177 m_image_deletion_throttler->drain(m_local_io_ctx.get_namespace(),
178 -ESTALE);
179
11fdf7f2
TL
180 shut_down_trash_watcher(on_finish);
181}
7c673cae 182
11fdf7f2
TL
183template <typename I>
184void ImageDeleter<I>::shut_down_trash_watcher(Context* on_finish) {
185 dout(10) << dendl;
186 ceph_assert(m_trash_watcher);
9f95a23c 187 auto ctx = new LambdaContext([this, on_finish](int r) {
11fdf7f2
TL
188 delete m_trash_watcher;
189 m_trash_watcher = nullptr;
190
191 wait_for_ops(on_finish);
192 });
193 m_trash_watcher->shut_down(ctx);
7c673cae
FG
194}
195
c07f9fc5 196template <typename I>
11fdf7f2
TL
197void ImageDeleter<I>::wait_for_ops(Context* on_finish) {
198 {
9f95a23c 199 std::scoped_lock locker{m_threads->timer_lock, m_lock};
11fdf7f2
TL
200 m_running = false;
201 cancel_retry_timer();
202 }
7c673cae 203
9f95a23c 204 auto ctx = new LambdaContext([this, on_finish](int) {
11fdf7f2
TL
205 cancel_all_deletions(on_finish);
206 });
207 m_async_op_tracker.wait_for_ops(ctx);
208}
7c673cae 209
11fdf7f2
TL
210template <typename I>
211void ImageDeleter<I>::cancel_all_deletions(Context* on_finish) {
9f95a23c
TL
212 m_image_deletion_throttler->drain(m_local_io_ctx.get_namespace(),
213 -ECANCELED);
11fdf7f2 214 {
9f95a23c 215 std::lock_guard locker{m_lock};
11fdf7f2
TL
216 // wake up any external state machines waiting on deletions
217 ceph_assert(m_in_flight_delete_queue.empty());
218 for (auto& queue : {&m_delete_queue, &m_retry_delete_queue}) {
219 for (auto& info : *queue) {
220 notify_on_delete(info->image_id, -ECANCELED);
221 }
222 queue->clear();
c07f9fc5 223 }
7c673cae 224 }
11fdf7f2 225 on_finish->complete(0);
7c673cae
FG
226}
227
c07f9fc5 228template <typename I>
11fdf7f2
TL
229void ImageDeleter<I>::wait_for_deletion(const std::string& image_id,
230 bool scheduled_only,
231 Context* on_finish) {
232 dout(5) << "image_id=" << image_id << dendl;
7c673cae 233
9f95a23c 234 on_finish = new LambdaContext([this, on_finish](int r) {
11fdf7f2 235 m_threads->work_queue->queue(on_finish, r);
7c673cae
FG
236 });
237
9f95a23c 238 std::lock_guard locker{m_lock};
11fdf7f2
TL
239 auto del_info = find_delete_info(image_id);
240 if (!del_info && scheduled_only) {
7c673cae 241 // image not scheduled for deletion
11fdf7f2 242 on_finish->complete(0);
7c673cae
FG
243 return;
244 }
245
11fdf7f2
TL
246 notify_on_delete(image_id, -ESTALE);
247 m_on_delete_contexts[image_id] = on_finish;
248}
7c673cae 249
11fdf7f2
TL
250template <typename I>
251void ImageDeleter<I>::complete_active_delete(DeleteInfoRef* delete_info,
252 int r) {
253 dout(20) << "info=" << *delete_info << ", r=" << r << dendl;
9f95a23c 254 std::lock_guard locker{m_lock};
11fdf7f2
TL
255 notify_on_delete((*delete_info)->image_id, r);
256 delete_info->reset();
7c673cae
FG
257}
258
c07f9fc5 259template <typename I>
11fdf7f2
TL
260void ImageDeleter<I>::enqueue_failed_delete(DeleteInfoRef* delete_info,
261 int error_code,
262 double retry_delay) {
263 dout(20) << "info=" << *delete_info << ", r=" << error_code << dendl;
264 if (error_code == -EBLACKLISTED) {
9f95a23c 265 std::lock_guard locker{m_lock};
11fdf7f2
TL
266 derr << "blacklisted while deleting local image" << dendl;
267 complete_active_delete(delete_info, error_code);
7c673cae
FG
268 return;
269 }
270
9f95a23c 271 std::scoped_lock locker{m_threads->timer_lock, m_lock};
11fdf7f2
TL
272 auto& delete_info_ref = *delete_info;
273 notify_on_delete(delete_info_ref->image_id, error_code);
274 delete_info_ref->error_code = error_code;
275 ++delete_info_ref->retries;
9f95a23c
TL
276 delete_info_ref->retry_time = (clock_t::now() +
277 ceph::make_timespan(retry_delay));
11fdf7f2
TL
278 m_retry_delete_queue.push_back(delete_info_ref);
279
280 schedule_retry_timer();
7c673cae
FG
281}
282
c07f9fc5 283template <typename I>
11fdf7f2
TL
284typename ImageDeleter<I>::DeleteInfoRef
285ImageDeleter<I>::find_delete_info(const std::string &image_id) {
9f95a23c 286 ceph_assert(ceph_mutex_is_locked(m_lock));
11fdf7f2
TL
287 DeleteQueue delete_queues[] = {m_in_flight_delete_queue,
288 m_retry_delete_queue,
289 m_delete_queue};
290
291 DeleteInfo delete_info{image_id};
292 for (auto& queue : delete_queues) {
293 auto it = std::find_if(queue.begin(), queue.end(),
294 [&delete_info](const DeleteInfoRef& ref) {
295 return delete_info == *ref;
296 });
297 if (it != queue.end()) {
298 return *it;
299 }
7c673cae 300 }
11fdf7f2
TL
301 return {};
302}
7c673cae 303
11fdf7f2 304template <typename I>
9f95a23c 305void ImageDeleter<I>::print_status(Formatter *f) {
11fdf7f2 306 dout(20) << dendl;
7c673cae 307
9f95a23c
TL
308 f->open_object_section("image_deleter_status");
309 f->open_array_section("delete_images_queue");
7c673cae 310
9f95a23c 311 std::lock_guard l{m_lock};
11fdf7f2 312 for (const auto& image : m_delete_queue) {
9f95a23c 313 image->print_status(f);
7c673cae
FG
314 }
315
9f95a23c
TL
316 f->close_section();
317 f->open_array_section("failed_deletes_queue");
11fdf7f2 318 for (const auto& image : m_retry_delete_queue) {
9f95a23c 319 image->print_status(f, true);
7c673cae
FG
320 }
321
9f95a23c
TL
322 f->close_section();
323 f->close_section();
11fdf7f2 324}
7c673cae 325
11fdf7f2
TL
326template <typename I>
327vector<string> ImageDeleter<I>::get_delete_queue_items() {
328 vector<string> items;
7c673cae 329
9f95a23c 330 std::lock_guard l{m_lock};
11fdf7f2
TL
331 for (const auto& del_info : m_delete_queue) {
332 items.push_back(del_info->image_id);
333 }
7c673cae 334
11fdf7f2
TL
335 return items;
336}
7c673cae 337
11fdf7f2
TL
338template <typename I>
339vector<pair<string, int> > ImageDeleter<I>::get_failed_queue_items() {
340 vector<pair<string, int> > items;
7c673cae 341
9f95a23c 342 std::lock_guard l{m_lock};
11fdf7f2
TL
343 for (const auto& del_info : m_retry_delete_queue) {
344 items.push_back(make_pair(del_info->image_id,
345 del_info->error_code));
346 }
7c673cae 347
11fdf7f2
TL
348 return items;
349}
7c673cae 350
11fdf7f2
TL
351template <typename I>
352void ImageDeleter<I>::remove_images() {
353 dout(10) << dendl;
354
9f95a23c
TL
355 std::lock_guard locker{m_lock};
356 while (m_running && !m_delete_queue.empty()) {
7c673cae 357
11fdf7f2
TL
358 DeleteInfoRef delete_info = m_delete_queue.front();
359 m_delete_queue.pop_front();
7c673cae 360
11fdf7f2 361 ceph_assert(delete_info);
9f95a23c
TL
362
363 auto on_start = create_async_context_callback(
364 m_threads->work_queue, new LambdaContext(
365 [this, delete_info](int r) {
366 if (r < 0) {
367 notify_on_delete(delete_info->image_id, r);
368 return;
369 }
370 remove_image(delete_info);
371 }));
372
373 m_image_deletion_throttler->start_op(m_local_io_ctx.get_namespace(),
374 delete_info->image_id, on_start);
7c673cae 375 }
11fdf7f2 376}
7c673cae 377
11fdf7f2
TL
378template <typename I>
379void ImageDeleter<I>::remove_image(DeleteInfoRef delete_info) {
380 dout(10) << "info=" << *delete_info << dendl;
9f95a23c
TL
381
382 std::lock_guard locker{m_lock};
7c673cae 383
11fdf7f2
TL
384 m_in_flight_delete_queue.push_back(delete_info);
385 m_async_op_tracker.start_op();
7c673cae 386
9f95a23c 387 auto ctx = new LambdaContext([this, delete_info](int r) {
11fdf7f2
TL
388 handle_remove_image(delete_info, r);
389 m_async_op_tracker.finish_op();
390 });
7c673cae 391
eafe8130 392 auto req = image_deleter::TrashRemoveRequest<I>::create(
11fdf7f2
TL
393 m_local_io_ctx, delete_info->image_id, &delete_info->error_result,
394 m_threads->work_queue, ctx);
395 req->send();
7c673cae
FG
396}
397
c07f9fc5 398template <typename I>
11fdf7f2
TL
399void ImageDeleter<I>::handle_remove_image(DeleteInfoRef delete_info,
400 int r) {
401 dout(10) << "info=" << *delete_info << ", r=" << r << dendl;
7c673cae 402
9f95a23c
TL
403 m_image_deletion_throttler->finish_op(m_local_io_ctx.get_namespace(),
404 delete_info->image_id);
11fdf7f2 405 {
9f95a23c
TL
406 std::lock_guard locker{m_lock};
407 ceph_assert(ceph_mutex_is_locked(m_lock));
11fdf7f2
TL
408 auto it = std::find(m_in_flight_delete_queue.begin(),
409 m_in_flight_delete_queue.end(), delete_info);
410 ceph_assert(it != m_in_flight_delete_queue.end());
411 m_in_flight_delete_queue.erase(it);
412 }
7c673cae 413
11fdf7f2
TL
414 if (r < 0) {
415 if (delete_info->error_result == image_deleter::ERROR_RESULT_COMPLETE) {
416 complete_active_delete(&delete_info, r);
417 } else if (delete_info->error_result ==
418 image_deleter::ERROR_RESULT_RETRY_IMMEDIATELY) {
419 enqueue_failed_delete(&delete_info, r, m_busy_interval);
420 } else {
421 auto cct = reinterpret_cast<CephContext *>(m_local_io_ctx.cct());
422 double failed_interval = cct->_conf.get_val<double>(
423 "rbd_mirror_delete_retry_interval");
424 enqueue_failed_delete(&delete_info, r, failed_interval);
425 }
426 } else {
427 complete_active_delete(&delete_info, 0);
428 }
7c673cae 429
11fdf7f2
TL
430 // process the next queued image to delete
431 remove_images();
7c673cae
FG
432}
433
c07f9fc5 434template <typename I>
11fdf7f2 435void ImageDeleter<I>::schedule_retry_timer() {
9f95a23c
TL
436 ceph_assert(ceph_mutex_is_locked(m_threads->timer_lock));
437 ceph_assert(ceph_mutex_is_locked(m_lock));
11fdf7f2 438 if (!m_running || m_timer_ctx != nullptr || m_retry_delete_queue.empty()) {
7c673cae
FG
439 return;
440 }
441
11fdf7f2
TL
442 dout(10) << dendl;
443 auto &delete_info = m_retry_delete_queue.front();
9f95a23c 444 m_timer_ctx = new LambdaContext([this](int r) {
11fdf7f2
TL
445 handle_retry_timer();
446 });
447 m_threads->timer->add_event_at(delete_info->retry_time, m_timer_ctx);
7c673cae
FG
448}
449
c07f9fc5 450template <typename I>
11fdf7f2
TL
451void ImageDeleter<I>::cancel_retry_timer() {
452 dout(10) << dendl;
9f95a23c 453 ceph_assert(ceph_mutex_is_locked(m_threads->timer_lock));
11fdf7f2
TL
454 if (m_timer_ctx != nullptr) {
455 bool canceled = m_threads->timer->cancel_event(m_timer_ctx);
456 m_timer_ctx = nullptr;
457 ceph_assert(canceled);
7c673cae
FG
458 }
459}
460
c07f9fc5 461template <typename I>
11fdf7f2
TL
462void ImageDeleter<I>::handle_retry_timer() {
463 dout(10) << dendl;
9f95a23c
TL
464 ceph_assert(ceph_mutex_is_locked(m_threads->timer_lock));
465 std::lock_guard locker{m_lock};
11fdf7f2
TL
466
467 ceph_assert(m_timer_ctx != nullptr);
468 m_timer_ctx = nullptr;
469
470 ceph_assert(m_running);
471 ceph_assert(!m_retry_delete_queue.empty());
472
473 // move all ready-to-ready items back to main queue
9f95a23c 474 auto now = clock_t::now();
11fdf7f2
TL
475 while (!m_retry_delete_queue.empty()) {
476 auto &delete_info = m_retry_delete_queue.front();
477 if (delete_info->retry_time > now) {
478 break;
7c673cae 479 }
7c673cae 480
11fdf7f2
TL
481 m_delete_queue.push_back(delete_info);
482 m_retry_delete_queue.pop_front();
7c673cae
FG
483 }
484
11fdf7f2
TL
485 // schedule wake up for any future retries
486 schedule_retry_timer();
487
488 // start (concurrent) removal of images
489 m_async_op_tracker.start_op();
9f95a23c 490 auto ctx = new LambdaContext([this](int r) {
11fdf7f2
TL
491 remove_images();
492 m_async_op_tracker.finish_op();
493 });
494 m_threads->work_queue->queue(ctx, 0);
7c673cae
FG
495}
496
c07f9fc5 497template <typename I>
11fdf7f2 498void ImageDeleter<I>::handle_trash_image(const std::string& image_id,
9f95a23c
TL
499 const ImageDeleter<I>::clock_t::time_point& deferment_end_time) {
500 std::scoped_lock locker{m_threads->timer_lock, m_lock};
7c673cae 501
11fdf7f2
TL
502 auto del_info = find_delete_info(image_id);
503 if (del_info != nullptr) {
504 dout(20) << "image " << image_id << " "
505 << "was already scheduled for deletion" << dendl;
506 return;
7c673cae
FG
507 }
508
11fdf7f2 509 dout(10) << "image_id=" << image_id << ", "
9f95a23c 510 << "deferment_end_time=" << utime_t{deferment_end_time} << dendl;
7c673cae 511
11fdf7f2
TL
512 del_info.reset(new DeleteInfo(image_id));
513 del_info->retry_time = deferment_end_time;
514 m_retry_delete_queue.push_back(del_info);
7c673cae 515
11fdf7f2 516 schedule_retry_timer();
7c673cae
FG
517}
518
c07f9fc5 519template <typename I>
11fdf7f2
TL
520void ImageDeleter<I>::notify_on_delete(const std::string& image_id,
521 int r) {
522 dout(10) << "image_id=" << image_id << ", r=" << r << dendl;
523 auto it = m_on_delete_contexts.find(image_id);
524 if (it == m_on_delete_contexts.end()) {
525 return;
7c673cae 526 }
7c673cae 527
11fdf7f2
TL
528 it->second->complete(r);
529 m_on_delete_contexts.erase(it);
7c673cae
FG
530}
531
c07f9fc5 532template <typename I>
9f95a23c 533void ImageDeleter<I>::DeleteInfo::print_status(Formatter *f,
c07f9fc5 534 bool print_failure_info) {
9f95a23c
TL
535 f->open_object_section("delete_info");
536 f->dump_string("image_id", image_id);
537 if (print_failure_info) {
538 f->dump_string("error_code", cpp_strerror(error_code));
539 f->dump_int("retries", retries);
7c673cae 540 }
9f95a23c 541 f->close_section();
7c673cae
FG
542}
543
544} // namespace mirror
545} // namespace rbd
c07f9fc5
FG
546
547template class rbd::mirror::ImageDeleter<librbd::ImageCtx>;