]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/rbd_mirror/ImageDeleter.cc
update source to Ceph Pacific 16.2.2
[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 "global/global_context.h"
22#include "librbd/internal.h"
23#include "librbd/ImageCtx.h"
24#include "librbd/ImageState.h"
7c673cae 25#include "librbd/Operations.h"
f67539c2 26#include "librbd/asio/ContextWQ.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,
f67539c2
TL
144 librbd::asio::ContextWQ* work_queue,
145 Context* on_finish) {
11fdf7f2
TL
146 dout(10) << "global_image_id=" << global_image_id << ", "
147 << "resync=" << resync << dendl;
148
149 auto req = rbd::mirror::image_deleter::TrashMoveRequest<>::create(
150 local_io_ctx, global_image_id, resync, work_queue, on_finish);
151 req->send();
152}
7c673cae 153
11fdf7f2
TL
154#undef dout_prefix
155#define dout_prefix *_dout << "rbd::mirror::ImageDeleter: " << this << " " \
156 << __func__ << ": "
7c673cae 157
11fdf7f2
TL
158template <typename I>
159void ImageDeleter<I>::init(Context* on_finish) {
160 dout(10) << dendl;
161
162 m_asok_hook = new ImageDeleterAdminSocketHook<I>(
163 g_ceph_context, m_local_io_ctx.get_pool_name(), this);
164
165 m_trash_watcher = image_deleter::TrashWatcher<I>::create(m_local_io_ctx,
166 m_threads,
167 m_trash_listener);
168 m_trash_watcher->init(on_finish);
7c673cae
FG
169}
170
c07f9fc5 171template <typename I>
11fdf7f2
TL
172void ImageDeleter<I>::shut_down(Context* on_finish) {
173 dout(10) << dendl;
7c673cae 174
11fdf7f2
TL
175 delete m_asok_hook;
176 m_asok_hook = nullptr;
7c673cae 177
9f95a23c
TL
178 m_image_deletion_throttler->drain(m_local_io_ctx.get_namespace(),
179 -ESTALE);
180
11fdf7f2
TL
181 shut_down_trash_watcher(on_finish);
182}
7c673cae 183
11fdf7f2
TL
184template <typename I>
185void ImageDeleter<I>::shut_down_trash_watcher(Context* on_finish) {
186 dout(10) << dendl;
187 ceph_assert(m_trash_watcher);
9f95a23c 188 auto ctx = new LambdaContext([this, on_finish](int r) {
11fdf7f2
TL
189 delete m_trash_watcher;
190 m_trash_watcher = nullptr;
191
192 wait_for_ops(on_finish);
193 });
194 m_trash_watcher->shut_down(ctx);
7c673cae
FG
195}
196
c07f9fc5 197template <typename I>
11fdf7f2
TL
198void ImageDeleter<I>::wait_for_ops(Context* on_finish) {
199 {
9f95a23c 200 std::scoped_lock locker{m_threads->timer_lock, m_lock};
11fdf7f2
TL
201 m_running = false;
202 cancel_retry_timer();
203 }
7c673cae 204
9f95a23c 205 auto ctx = new LambdaContext([this, on_finish](int) {
11fdf7f2
TL
206 cancel_all_deletions(on_finish);
207 });
208 m_async_op_tracker.wait_for_ops(ctx);
209}
7c673cae 210
11fdf7f2
TL
211template <typename I>
212void ImageDeleter<I>::cancel_all_deletions(Context* on_finish) {
9f95a23c
TL
213 m_image_deletion_throttler->drain(m_local_io_ctx.get_namespace(),
214 -ECANCELED);
11fdf7f2 215 {
9f95a23c 216 std::lock_guard locker{m_lock};
11fdf7f2
TL
217 // wake up any external state machines waiting on deletions
218 ceph_assert(m_in_flight_delete_queue.empty());
219 for (auto& queue : {&m_delete_queue, &m_retry_delete_queue}) {
220 for (auto& info : *queue) {
221 notify_on_delete(info->image_id, -ECANCELED);
222 }
223 queue->clear();
c07f9fc5 224 }
7c673cae 225 }
11fdf7f2 226 on_finish->complete(0);
7c673cae
FG
227}
228
c07f9fc5 229template <typename I>
11fdf7f2
TL
230void ImageDeleter<I>::wait_for_deletion(const std::string& image_id,
231 bool scheduled_only,
232 Context* on_finish) {
233 dout(5) << "image_id=" << image_id << dendl;
7c673cae 234
9f95a23c 235 on_finish = new LambdaContext([this, on_finish](int r) {
11fdf7f2 236 m_threads->work_queue->queue(on_finish, r);
7c673cae
FG
237 });
238
9f95a23c 239 std::lock_guard locker{m_lock};
11fdf7f2
TL
240 auto del_info = find_delete_info(image_id);
241 if (!del_info && scheduled_only) {
7c673cae 242 // image not scheduled for deletion
11fdf7f2 243 on_finish->complete(0);
7c673cae
FG
244 return;
245 }
246
11fdf7f2
TL
247 notify_on_delete(image_id, -ESTALE);
248 m_on_delete_contexts[image_id] = on_finish;
249}
7c673cae 250
11fdf7f2
TL
251template <typename I>
252void ImageDeleter<I>::complete_active_delete(DeleteInfoRef* delete_info,
253 int r) {
254 dout(20) << "info=" << *delete_info << ", r=" << r << dendl;
9f95a23c 255 std::lock_guard locker{m_lock};
11fdf7f2
TL
256 notify_on_delete((*delete_info)->image_id, r);
257 delete_info->reset();
7c673cae
FG
258}
259
c07f9fc5 260template <typename I>
11fdf7f2
TL
261void ImageDeleter<I>::enqueue_failed_delete(DeleteInfoRef* delete_info,
262 int error_code,
263 double retry_delay) {
264 dout(20) << "info=" << *delete_info << ", r=" << error_code << dendl;
f67539c2 265 if (error_code == -EBLOCKLISTED) {
9f95a23c 266 std::lock_guard locker{m_lock};
f67539c2 267 derr << "blocklisted while deleting local image" << dendl;
11fdf7f2 268 complete_active_delete(delete_info, error_code);
7c673cae
FG
269 return;
270 }
271
9f95a23c 272 std::scoped_lock locker{m_threads->timer_lock, m_lock};
11fdf7f2
TL
273 auto& delete_info_ref = *delete_info;
274 notify_on_delete(delete_info_ref->image_id, error_code);
275 delete_info_ref->error_code = error_code;
276 ++delete_info_ref->retries;
9f95a23c
TL
277 delete_info_ref->retry_time = (clock_t::now() +
278 ceph::make_timespan(retry_delay));
11fdf7f2
TL
279 m_retry_delete_queue.push_back(delete_info_ref);
280
281 schedule_retry_timer();
7c673cae
FG
282}
283
c07f9fc5 284template <typename I>
11fdf7f2
TL
285typename ImageDeleter<I>::DeleteInfoRef
286ImageDeleter<I>::find_delete_info(const std::string &image_id) {
9f95a23c 287 ceph_assert(ceph_mutex_is_locked(m_lock));
11fdf7f2
TL
288 DeleteQueue delete_queues[] = {m_in_flight_delete_queue,
289 m_retry_delete_queue,
290 m_delete_queue};
291
292 DeleteInfo delete_info{image_id};
293 for (auto& queue : delete_queues) {
294 auto it = std::find_if(queue.begin(), queue.end(),
295 [&delete_info](const DeleteInfoRef& ref) {
296 return delete_info == *ref;
297 });
298 if (it != queue.end()) {
299 return *it;
300 }
7c673cae 301 }
11fdf7f2
TL
302 return {};
303}
7c673cae 304
11fdf7f2 305template <typename I>
9f95a23c 306void ImageDeleter<I>::print_status(Formatter *f) {
11fdf7f2 307 dout(20) << dendl;
7c673cae 308
9f95a23c
TL
309 f->open_object_section("image_deleter_status");
310 f->open_array_section("delete_images_queue");
7c673cae 311
9f95a23c 312 std::lock_guard l{m_lock};
11fdf7f2 313 for (const auto& image : m_delete_queue) {
9f95a23c 314 image->print_status(f);
7c673cae
FG
315 }
316
9f95a23c
TL
317 f->close_section();
318 f->open_array_section("failed_deletes_queue");
11fdf7f2 319 for (const auto& image : m_retry_delete_queue) {
9f95a23c 320 image->print_status(f, true);
7c673cae
FG
321 }
322
9f95a23c
TL
323 f->close_section();
324 f->close_section();
11fdf7f2 325}
7c673cae 326
11fdf7f2
TL
327template <typename I>
328vector<string> ImageDeleter<I>::get_delete_queue_items() {
329 vector<string> items;
7c673cae 330
9f95a23c 331 std::lock_guard l{m_lock};
11fdf7f2
TL
332 for (const auto& del_info : m_delete_queue) {
333 items.push_back(del_info->image_id);
334 }
7c673cae 335
11fdf7f2
TL
336 return items;
337}
7c673cae 338
11fdf7f2
TL
339template <typename I>
340vector<pair<string, int> > ImageDeleter<I>::get_failed_queue_items() {
341 vector<pair<string, int> > items;
7c673cae 342
9f95a23c 343 std::lock_guard l{m_lock};
11fdf7f2
TL
344 for (const auto& del_info : m_retry_delete_queue) {
345 items.push_back(make_pair(del_info->image_id,
346 del_info->error_code));
347 }
7c673cae 348
11fdf7f2
TL
349 return items;
350}
7c673cae 351
11fdf7f2
TL
352template <typename I>
353void ImageDeleter<I>::remove_images() {
354 dout(10) << dendl;
355
9f95a23c
TL
356 std::lock_guard locker{m_lock};
357 while (m_running && !m_delete_queue.empty()) {
7c673cae 358
11fdf7f2
TL
359 DeleteInfoRef delete_info = m_delete_queue.front();
360 m_delete_queue.pop_front();
7c673cae 361
11fdf7f2 362 ceph_assert(delete_info);
9f95a23c
TL
363
364 auto on_start = create_async_context_callback(
365 m_threads->work_queue, new LambdaContext(
366 [this, delete_info](int r) {
367 if (r < 0) {
368 notify_on_delete(delete_info->image_id, r);
369 return;
370 }
371 remove_image(delete_info);
372 }));
373
374 m_image_deletion_throttler->start_op(m_local_io_ctx.get_namespace(),
375 delete_info->image_id, on_start);
7c673cae 376 }
11fdf7f2 377}
7c673cae 378
11fdf7f2
TL
379template <typename I>
380void ImageDeleter<I>::remove_image(DeleteInfoRef delete_info) {
381 dout(10) << "info=" << *delete_info << dendl;
9f95a23c
TL
382
383 std::lock_guard locker{m_lock};
7c673cae 384
11fdf7f2
TL
385 m_in_flight_delete_queue.push_back(delete_info);
386 m_async_op_tracker.start_op();
7c673cae 387
9f95a23c 388 auto ctx = new LambdaContext([this, delete_info](int r) {
11fdf7f2
TL
389 handle_remove_image(delete_info, r);
390 m_async_op_tracker.finish_op();
391 });
7c673cae 392
eafe8130 393 auto req = image_deleter::TrashRemoveRequest<I>::create(
11fdf7f2
TL
394 m_local_io_ctx, delete_info->image_id, &delete_info->error_result,
395 m_threads->work_queue, ctx);
396 req->send();
7c673cae
FG
397}
398
c07f9fc5 399template <typename I>
11fdf7f2
TL
400void ImageDeleter<I>::handle_remove_image(DeleteInfoRef delete_info,
401 int r) {
402 dout(10) << "info=" << *delete_info << ", r=" << r << dendl;
7c673cae 403
9f95a23c
TL
404 m_image_deletion_throttler->finish_op(m_local_io_ctx.get_namespace(),
405 delete_info->image_id);
11fdf7f2 406 {
9f95a23c
TL
407 std::lock_guard locker{m_lock};
408 ceph_assert(ceph_mutex_is_locked(m_lock));
11fdf7f2
TL
409 auto it = std::find(m_in_flight_delete_queue.begin(),
410 m_in_flight_delete_queue.end(), delete_info);
411 ceph_assert(it != m_in_flight_delete_queue.end());
412 m_in_flight_delete_queue.erase(it);
413 }
7c673cae 414
11fdf7f2
TL
415 if (r < 0) {
416 if (delete_info->error_result == image_deleter::ERROR_RESULT_COMPLETE) {
417 complete_active_delete(&delete_info, r);
418 } else if (delete_info->error_result ==
419 image_deleter::ERROR_RESULT_RETRY_IMMEDIATELY) {
420 enqueue_failed_delete(&delete_info, r, m_busy_interval);
421 } else {
422 auto cct = reinterpret_cast<CephContext *>(m_local_io_ctx.cct());
423 double failed_interval = cct->_conf.get_val<double>(
424 "rbd_mirror_delete_retry_interval");
425 enqueue_failed_delete(&delete_info, r, failed_interval);
426 }
427 } else {
428 complete_active_delete(&delete_info, 0);
429 }
7c673cae 430
11fdf7f2
TL
431 // process the next queued image to delete
432 remove_images();
7c673cae
FG
433}
434
c07f9fc5 435template <typename I>
11fdf7f2 436void ImageDeleter<I>::schedule_retry_timer() {
9f95a23c
TL
437 ceph_assert(ceph_mutex_is_locked(m_threads->timer_lock));
438 ceph_assert(ceph_mutex_is_locked(m_lock));
11fdf7f2 439 if (!m_running || m_timer_ctx != nullptr || m_retry_delete_queue.empty()) {
7c673cae
FG
440 return;
441 }
442
11fdf7f2
TL
443 dout(10) << dendl;
444 auto &delete_info = m_retry_delete_queue.front();
9f95a23c 445 m_timer_ctx = new LambdaContext([this](int r) {
11fdf7f2
TL
446 handle_retry_timer();
447 });
448 m_threads->timer->add_event_at(delete_info->retry_time, m_timer_ctx);
7c673cae
FG
449}
450
c07f9fc5 451template <typename I>
11fdf7f2
TL
452void ImageDeleter<I>::cancel_retry_timer() {
453 dout(10) << dendl;
9f95a23c 454 ceph_assert(ceph_mutex_is_locked(m_threads->timer_lock));
11fdf7f2
TL
455 if (m_timer_ctx != nullptr) {
456 bool canceled = m_threads->timer->cancel_event(m_timer_ctx);
457 m_timer_ctx = nullptr;
458 ceph_assert(canceled);
7c673cae
FG
459 }
460}
461
c07f9fc5 462template <typename I>
11fdf7f2
TL
463void ImageDeleter<I>::handle_retry_timer() {
464 dout(10) << dendl;
9f95a23c
TL
465 ceph_assert(ceph_mutex_is_locked(m_threads->timer_lock));
466 std::lock_guard locker{m_lock};
11fdf7f2
TL
467
468 ceph_assert(m_timer_ctx != nullptr);
469 m_timer_ctx = nullptr;
470
471 ceph_assert(m_running);
472 ceph_assert(!m_retry_delete_queue.empty());
473
474 // move all ready-to-ready items back to main queue
9f95a23c 475 auto now = clock_t::now();
11fdf7f2
TL
476 while (!m_retry_delete_queue.empty()) {
477 auto &delete_info = m_retry_delete_queue.front();
478 if (delete_info->retry_time > now) {
479 break;
7c673cae 480 }
7c673cae 481
11fdf7f2
TL
482 m_delete_queue.push_back(delete_info);
483 m_retry_delete_queue.pop_front();
7c673cae
FG
484 }
485
11fdf7f2
TL
486 // schedule wake up for any future retries
487 schedule_retry_timer();
488
489 // start (concurrent) removal of images
490 m_async_op_tracker.start_op();
9f95a23c 491 auto ctx = new LambdaContext([this](int r) {
11fdf7f2
TL
492 remove_images();
493 m_async_op_tracker.finish_op();
494 });
495 m_threads->work_queue->queue(ctx, 0);
7c673cae
FG
496}
497
c07f9fc5 498template <typename I>
11fdf7f2 499void ImageDeleter<I>::handle_trash_image(const std::string& image_id,
9f95a23c
TL
500 const ImageDeleter<I>::clock_t::time_point& deferment_end_time) {
501 std::scoped_lock locker{m_threads->timer_lock, m_lock};
7c673cae 502
11fdf7f2
TL
503 auto del_info = find_delete_info(image_id);
504 if (del_info != nullptr) {
505 dout(20) << "image " << image_id << " "
506 << "was already scheduled for deletion" << dendl;
507 return;
7c673cae
FG
508 }
509
11fdf7f2 510 dout(10) << "image_id=" << image_id << ", "
9f95a23c 511 << "deferment_end_time=" << utime_t{deferment_end_time} << dendl;
7c673cae 512
11fdf7f2
TL
513 del_info.reset(new DeleteInfo(image_id));
514 del_info->retry_time = deferment_end_time;
515 m_retry_delete_queue.push_back(del_info);
7c673cae 516
11fdf7f2 517 schedule_retry_timer();
7c673cae
FG
518}
519
c07f9fc5 520template <typename I>
11fdf7f2
TL
521void ImageDeleter<I>::notify_on_delete(const std::string& image_id,
522 int r) {
523 dout(10) << "image_id=" << image_id << ", r=" << r << dendl;
524 auto it = m_on_delete_contexts.find(image_id);
525 if (it == m_on_delete_contexts.end()) {
526 return;
7c673cae 527 }
7c673cae 528
11fdf7f2
TL
529 it->second->complete(r);
530 m_on_delete_contexts.erase(it);
7c673cae
FG
531}
532
c07f9fc5 533template <typename I>
9f95a23c 534void ImageDeleter<I>::DeleteInfo::print_status(Formatter *f,
c07f9fc5 535 bool print_failure_info) {
9f95a23c
TL
536 f->open_object_section("delete_info");
537 f->dump_string("image_id", image_id);
538 if (print_failure_info) {
539 f->dump_string("error_code", cpp_strerror(error_code));
540 f->dump_int("retries", retries);
7c673cae 541 }
9f95a23c 542 f->close_section();
7c673cae
FG
543}
544
545} // namespace mirror
546} // namespace rbd
c07f9fc5
FG
547
548template class rbd::mirror::ImageDeleter<librbd::ImageCtx>;