]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/rbd_mirror/ImageDeleter.cc
update sources to v12.1.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 <boost/bind.hpp>
16#include <map>
17#include <set>
18#include <sstream>
19
20#include "include/rados/librados.hpp"
21#include "common/Formatter.h"
22#include "common/admin_socket.h"
23#include "common/debug.h"
24#include "common/errno.h"
25#include "common/WorkQueue.h"
26#include "global/global_context.h"
27#include "librbd/internal.h"
28#include "librbd/ImageCtx.h"
29#include "librbd/ImageState.h"
30#include "librbd/Journal.h"
31#include "librbd/Operations.h"
32#include "librbd/journal/Policy.h"
33#include "cls/rbd/cls_rbd_client.h"
34#include "cls/rbd/cls_rbd_types.h"
35#include "librbd/Utils.h"
36#include "ImageDeleter.h"
37
38#define dout_context g_ceph_context
39#define dout_subsys ceph_subsys_rbd_mirror
40#undef dout_prefix
41#define dout_prefix *_dout << "rbd::mirror::ImageDeleter: " << this << " " \
42 << __func__ << ": "
43
44using std::string;
45using std::map;
46using std::stringstream;
47using std::vector;
48using std::pair;
49using std::make_pair;
50
51using librados::IoCtx;
52using namespace librbd;
53
54namespace rbd {
55namespace mirror {
56
57namespace {
58
59class ImageDeleterAdminSocketCommand {
60public:
61 virtual ~ImageDeleterAdminSocketCommand() {}
62 virtual bool call(Formatter *f, stringstream *ss) = 0;
63};
64
c07f9fc5 65template <typename I>
7c673cae
FG
66class StatusCommand : public ImageDeleterAdminSocketCommand {
67public:
c07f9fc5 68 explicit StatusCommand(ImageDeleter<I> *image_del) : image_del(image_del) {}
7c673cae
FG
69
70 bool call(Formatter *f, stringstream *ss) override {
71 image_del->print_status(f, ss);
72 return true;
73 }
74
75private:
c07f9fc5 76 ImageDeleter<I> *image_del;
7c673cae
FG
77};
78
79struct DeleteJournalPolicy : public librbd::journal::Policy {
80 bool append_disabled() const override {
81 return true;
82 }
83 bool journal_disabled() const override {
84 return false;
85 }
86
87 void allocate_tag_on_lock(Context *on_finish) override {
88 on_finish->complete(0);
89 }
90};
91
92} // anonymous namespace
93
c07f9fc5 94template <typename I>
7c673cae
FG
95class ImageDeleterAdminSocketHook : public AdminSocketHook {
96public:
c07f9fc5 97 ImageDeleterAdminSocketHook(CephContext *cct, ImageDeleter<I> *image_del) :
7c673cae
FG
98 admin_socket(cct->get_admin_socket()) {
99
100 std::string command;
101 int r;
102
103 command = "rbd mirror deletion status";
104 r = admin_socket->register_command(command, command, this,
105 "get status for image deleter");
106 if (r == 0) {
c07f9fc5 107 commands[command] = new StatusCommand<I>(image_del);
7c673cae
FG
108 }
109
110 }
111
112 ~ImageDeleterAdminSocketHook() override {
113 for (Commands::const_iterator i = commands.begin(); i != commands.end();
114 ++i) {
115 (void)admin_socket->unregister_command(i->first);
116 delete i->second;
117 }
118 }
119
120 bool call(std::string command, cmdmap_t& cmdmap, std::string format,
121 bufferlist& out) override {
122 Commands::const_iterator i = commands.find(command);
123 assert(i != commands.end());
124 Formatter *f = Formatter::create(format);
125 stringstream ss;
126 bool r = i->second->call(f, &ss);
127 delete f;
128 out.append(ss);
129 return r;
130 }
131
132private:
133 typedef std::map<std::string, ImageDeleterAdminSocketCommand*> Commands;
134 AdminSocket *admin_socket;
135 Commands commands;
136};
137
c07f9fc5
FG
138template <typename I>
139ImageDeleter<I>::ImageDeleter(ContextWQ *work_queue, SafeTimer *timer,
140 Mutex *timer_lock,
141 ServiceDaemon<librbd::ImageCtx>* service_daemon)
142 : m_work_queue(work_queue),
143 m_service_daemon(service_daemon),
7c673cae
FG
144 m_delete_lock("rbd::mirror::ImageDeleter::Delete"),
145 m_image_deleter_thread(this),
146 m_failed_timer(timer),
147 m_failed_timer_lock(timer_lock),
c07f9fc5 148 m_asok_hook(new ImageDeleterAdminSocketHook<I>(g_ceph_context, this))
7c673cae
FG
149{
150 set_failed_timer_interval(g_ceph_context->_conf->rbd_mirror_delete_retry_interval);
151 m_image_deleter_thread.create("image_deleter");
152}
153
c07f9fc5
FG
154template <typename I>
155ImageDeleter<I>::~ImageDeleter() {
7c673cae
FG
156 dout(20) << "enter" << dendl;
157
158 m_running = false;
159 {
160 Mutex::Locker l (m_delete_lock);
161 m_delete_queue_cond.Signal();
162 }
163 if (m_image_deleter_thread.is_started()) {
164 m_image_deleter_thread.join();
165 }
166
167 delete m_asok_hook;
168 dout(20) << "return" << dendl;
169}
170
c07f9fc5
FG
171template <typename I>
172void ImageDeleter<I>::run() {
7c673cae
FG
173 dout(20) << "enter" << dendl;
174 while(m_running) {
175 m_delete_lock.Lock();
176 while (m_delete_queue.empty()) {
177 dout(20) << "waiting for delete requests" << dendl;
178 m_delete_queue_cond.Wait(m_delete_lock);
179
180 if (!m_running) {
181 m_delete_lock.Unlock();
182 dout(20) << "return" << dendl;
183 return;
184 }
185 }
186
187 m_active_delete = std::move(m_delete_queue.back());
188 m_delete_queue.pop_back();
189 m_delete_lock.Unlock();
190
191 bool move_to_next = process_image_delete();
192 if (!move_to_next) {
193 if (!m_running) {
194 dout(20) << "return" << dendl;
195 return;
196 }
197
198 Mutex::Locker l(m_delete_lock);
199 if (m_delete_queue.size() == 1) {
200 m_delete_queue_cond.Wait(m_delete_lock);
201 }
202 }
203 }
204}
205
c07f9fc5
FG
206template <typename I>
207void ImageDeleter<I>::schedule_image_delete(RadosRef local_rados,
208 int64_t local_pool_id,
209 const std::string& global_image_id,
210 bool ignore_orphaned) {
7c673cae
FG
211 dout(20) << "enter" << dendl;
212
213 Mutex::Locker locker(m_delete_lock);
214
215 auto del_info = find_delete_info(local_pool_id, global_image_id);
216 if (del_info != nullptr) {
217 dout(20) << "image " << global_image_id << " "
218 << "was already scheduled for deletion" << dendl;
c07f9fc5
FG
219 if (ignore_orphaned) {
220 (*del_info)->ignore_orphaned = true;
221 }
7c673cae
FG
222 return;
223 }
224
225 m_delete_queue.push_front(
226 unique_ptr<DeleteInfo>(new DeleteInfo(local_rados, local_pool_id,
c07f9fc5 227 global_image_id, ignore_orphaned)));
7c673cae
FG
228 m_delete_queue_cond.Signal();
229}
230
c07f9fc5
FG
231template <typename I>
232void ImageDeleter<I>::wait_for_scheduled_deletion(int64_t local_pool_id,
233 const std::string &global_image_id,
234 Context *ctx,
235 bool notify_on_failed_retry) {
7c673cae
FG
236
237 ctx = new FunctionContext([this, ctx](int r) {
238 m_work_queue->queue(ctx, r);
239 });
240
241 Mutex::Locker locker(m_delete_lock);
242 auto del_info = find_delete_info(local_pool_id, global_image_id);
243 if (!del_info) {
244 // image not scheduled for deletion
245 ctx->complete(0);
246 return;
247 }
248
249 dout(20) << "local_pool_id=" << local_pool_id << ", "
250 << "global_image_id=" << global_image_id << dendl;
251
252 if ((*del_info)->on_delete != nullptr) {
253 (*del_info)->on_delete->complete(-ESTALE);
254 }
255 (*del_info)->on_delete = ctx;
256 (*del_info)->notify_on_failed_retry = notify_on_failed_retry;
257}
258
c07f9fc5
FG
259template <typename I>
260void ImageDeleter<I>::cancel_waiter(int64_t local_pool_id,
261 const std::string &global_image_id) {
7c673cae
FG
262 Mutex::Locker locker(m_delete_lock);
263 auto del_info = find_delete_info(local_pool_id, global_image_id);
264 if (!del_info) {
265 return;
266 }
267
268 if ((*del_info)->on_delete != nullptr) {
269 (*del_info)->on_delete->complete(-ECANCELED);
270 (*del_info)->on_delete = nullptr;
271 }
272}
273
c07f9fc5
FG
274template <typename I>
275bool ImageDeleter<I>::process_image_delete() {
7c673cae
FG
276 stringstream ss;
277 m_active_delete->to_string(ss);
278 std::string del_info_str = ss.str();
279 dout(10) << "start processing delete request: " << del_info_str << dendl;
280 int r;
281 cls::rbd::MirrorImage mirror_image;
282
283 // remote image was disabled, now we need to delete local image
284 IoCtx ioctx;
285 r = m_active_delete->local_rados->ioctx_create2(
286 m_active_delete->local_pool_id, ioctx);
287 if (r < 0) {
288 derr << "error accessing local pool " << m_active_delete->local_pool_id
289 << ": " << cpp_strerror(r) << dendl;
290 enqueue_failed_delete(r);
291 return true;
292 }
293
294 dout(20) << "connected to local pool: " << ioctx.get_pool_name() << dendl;
295
296 auto &global_image_id = m_active_delete->global_image_id;
297 std::string local_image_id;
298 r = librbd::cls_client::mirror_image_get_image_id(
299 &ioctx, global_image_id, &local_image_id);
300 if (r == -ENOENT) {
301 dout(10) << "image " << global_image_id << " is not mirrored" << dendl;
302 complete_active_delete(r);
303 return true;
304 } else if (r < 0) {
305 derr << "error retrieving local id for image " << global_image_id
306 << ": " << cpp_strerror(r) << dendl;
307 enqueue_failed_delete(r);
308 return true;
309 }
310
c07f9fc5 311 std::string mirror_uuid;
7c673cae 312 C_SaferCond tag_owner_ctx;
c07f9fc5
FG
313 Journal<>::get_tag_owner(ioctx, local_image_id, &mirror_uuid, m_work_queue,
314 &tag_owner_ctx);
7c673cae
FG
315 r = tag_owner_ctx.wait();
316 if (r < 0 && r != -ENOENT) {
317 derr << "error retrieving image primary info for image " << global_image_id
318 << ": " << cpp_strerror(r) << dendl;
319 enqueue_failed_delete(r);
320 return true;
c07f9fc5
FG
321 } else if (r != -ENOENT) {
322 if (mirror_uuid == Journal<>::LOCAL_MIRROR_UUID) {
323 dout(10) << "image " << global_image_id << " is local primary" << dendl;
324 complete_active_delete(-EISPRM);
325 return true;
326 } else if (mirror_uuid == Journal<>::ORPHAN_MIRROR_UUID &&
327 !m_active_delete->ignore_orphaned) {
328 dout(10) << "image " << global_image_id << " is orphaned" << dendl;
329 complete_active_delete(-EISPRM);
330 return true;
331 }
7c673cae
FG
332 }
333
334 dout(20) << "local image is not the primary" << dendl;
7c673cae
FG
335 bool has_snapshots;
336 r = image_has_snapshots_and_children(&ioctx, local_image_id, &has_snapshots);
337 if (r < 0) {
338 enqueue_failed_delete(r);
339 return true;
340 }
341
342 mirror_image.global_image_id = global_image_id;
343 mirror_image.state = cls::rbd::MIRROR_IMAGE_STATE_DISABLING;
344 r = cls_client::mirror_image_set(&ioctx, local_image_id, mirror_image);
345 if (r == -ENOENT) {
346 dout(10) << "local image is not mirrored, aborting deletion..." << dendl;
347 complete_active_delete(r);
348 return true;
349 } else if (r == -EEXIST || r == -EINVAL) {
350 derr << "cannot disable mirroring for image " << global_image_id
351 << ": global_image_id has changed/reused: "
352 << cpp_strerror(r) << dendl;
353 complete_active_delete(r);
354 return true;
355 } else if (r < 0) {
356 derr << "cannot disable mirroring for image " << global_image_id
357 << ": " << cpp_strerror(r) << dendl;
358 enqueue_failed_delete(r);
359 return true;
360 }
361
362 dout(20) << "set local image mirroring to disable" << dendl;
363
364 if (has_snapshots) {
365 dout(20) << "local image has snapshots" << dendl;
366
367 ImageCtx *imgctx = new ImageCtx("", local_image_id, nullptr, ioctx, false);
368 r = imgctx->state->open(false);
369 if (r < 0) {
370 derr << "error opening image " << global_image_id << " ("
371 << local_image_id << "): " << cpp_strerror(r) << dendl;
372 enqueue_failed_delete(r);
373 return true;
374 }
375
376 {
377 RWLock::WLocker snap_locker(imgctx->snap_lock);
378 imgctx->set_journal_policy(new DeleteJournalPolicy());
379 }
380
381 std::vector<librbd::snap_info_t> snaps;
382 r = librbd::snap_list(imgctx, snaps);
383 if (r < 0) {
384 derr << "error listing snapshot of image " << imgctx->name
385 << cpp_strerror(r) << dendl;
386 imgctx->state->close();
387 enqueue_failed_delete(r);
388 return true;
389 }
390
391 for (const auto& snap : snaps) {
392 dout(20) << "processing deletion of snapshot " << imgctx->name << "@"
393 << snap.name << dendl;
394
395 bool is_protected;
396 r = librbd::snap_is_protected(imgctx, snap.name.c_str(), &is_protected);
397 if (r < 0) {
398 derr << "error checking snapshot protection of snapshot "
399 << imgctx->name << "@" << snap.name << ": " << cpp_strerror(r)
400 << dendl;
401 imgctx->state->close();
402 enqueue_failed_delete(r);
403 return true;
404 }
405 if (is_protected) {
406 dout(20) << "snapshot " << imgctx->name << "@" << snap.name
407 << " is protected, issuing unprotect command" << dendl;
408
409 r = imgctx->operations->snap_unprotect(
410 cls::rbd::UserSnapshotNamespace(), snap.name.c_str());
411 if (r == -EBUSY) {
412 // there are still clones of snapshots of this image, therefore send
413 // the delete request to the end of the queue
414 dout(10) << "local image id " << local_image_id << " has "
415 << "snapshots with cloned children, postponing deletion..."
416 << dendl;
417 imgctx->state->close();
418 Mutex::Locker l(m_delete_lock);
419 m_active_delete->notify(r);
420 m_delete_queue.push_front(std::move(m_active_delete));
421 return false;
422 } else if (r < 0) {
423 derr << "error unprotecting snapshot " << imgctx->name << "@"
424 << snap.name << ": " << cpp_strerror(r) << dendl;
425 imgctx->state->close();
426 enqueue_failed_delete(r);
427 return true;
428 }
429 }
430
431 r = imgctx->operations->snap_remove(cls::rbd::UserSnapshotNamespace(),
432 snap.name.c_str());
433 if (r < 0) {
434 derr << "error removing snapshot " << imgctx->name << "@"
435 << snap.name << ": " << cpp_strerror(r) << dendl;
436 imgctx->state->close();
437 enqueue_failed_delete(r);
438 return true;
439 }
440
441 dout(10) << "snapshot " << imgctx->name << "@" << snap.name
442 << " was deleted" << dendl;
443 }
444
445 imgctx->state->close();
446 }
447
448 librbd::NoOpProgressContext ctx;
449 r = librbd::remove(ioctx, "", local_image_id, ctx, true);
450 if (r < 0 && r != -ENOENT) {
451 derr << "error removing image " << global_image_id << " "
452 << "(" << local_image_id << ") from local pool: "
453 << cpp_strerror(r) << dendl;
454 enqueue_failed_delete(r);
455 return true;
456 }
457
458 // image was already deleted from rbd_directory, now we will make sure
459 // that will be also removed from rbd_mirroring
460 if (r == -ENOENT) {
461 dout(20) << "local image does not exist, removing image from rbd_mirroring"
462 << dendl;
463 }
464
465 r = cls_client::mirror_image_remove(&ioctx, local_image_id);
466 if (r < 0 && r != -ENOENT) {
467 derr << "error removing image from mirroring directory: "
468 << cpp_strerror(r) << dendl;
469 enqueue_failed_delete(r);
470 return true;
471 }
472
473 dout(10) << "Successfully deleted image "
474 << global_image_id << " " << "(" << local_image_id << ")" << dendl;
475
476 complete_active_delete(0);
477 return true;
478}
479
c07f9fc5
FG
480template <typename I>
481int ImageDeleter<I>::image_has_snapshots_and_children(IoCtx *ioctx,
482 string& image_id,
483 bool *has_snapshots) {
7c673cae
FG
484 string header_oid = librbd::util::header_name(image_id);
485 ::SnapContext snapc;
486 int r = cls_client::get_snapcontext(ioctx, header_oid, &snapc);
487 if (r < 0 && r != -ENOENT) {
488 derr << "error retrieving snapshot context for image id " << image_id
489 << ": " << cpp_strerror(r) << dendl;
490 return r;
491 }
492
493 *has_snapshots = !snapc.snaps.empty();
494
495 return 0;
496}
497
c07f9fc5
FG
498template <typename I>
499void ImageDeleter<I>::complete_active_delete(int r) {
7c673cae
FG
500 dout(20) << dendl;
501
31f18b77
FG
502 Mutex::Locker delete_locker(m_delete_lock);
503 m_active_delete->notify(r);
504 m_active_delete.reset();
7c673cae
FG
505}
506
c07f9fc5
FG
507template <typename I>
508void ImageDeleter<I>::enqueue_failed_delete(int error_code) {
7c673cae
FG
509 dout(20) << "enter" << dendl;
510
511 if (error_code == -EBLACKLISTED) {
512 derr << "blacklisted while deleting local image" << dendl;
513 complete_active_delete(error_code);
514 return;
515 }
516
517 m_delete_lock.Lock();
518 if (m_active_delete->notify_on_failed_retry) {
519 m_active_delete->notify(error_code);
520 }
521 m_active_delete->error_code = error_code;
522 bool was_empty = m_failed_queue.empty();
523 m_failed_queue.push_front(std::move(m_active_delete));
524 m_delete_lock.Unlock();
525 if (was_empty) {
526 FunctionContext *ctx = new FunctionContext(
c07f9fc5 527 boost::bind(&ImageDeleter<I>::retry_failed_deletions, this));
7c673cae
FG
528 Mutex::Locker l(*m_failed_timer_lock);
529 m_failed_timer->add_event_after(m_failed_interval, ctx);
530 }
531}
532
c07f9fc5
FG
533template <typename I>
534void ImageDeleter<I>::retry_failed_deletions() {
7c673cae
FG
535 dout(20) << "enter" << dendl;
536
537 Mutex::Locker l(m_delete_lock);
538
539 bool empty = m_failed_queue.empty();
540 while (!m_failed_queue.empty()) {
541 m_delete_queue.push_back(std::move(m_failed_queue.back()));
542 m_delete_queue.back()->retries++;
543 m_failed_queue.pop_back();
544 }
545 if (!empty) {
546 m_delete_queue_cond.Signal();
547 }
548}
549
c07f9fc5
FG
550template <typename I>
551unique_ptr<typename ImageDeleter<I>::DeleteInfo> const*
552ImageDeleter<I>::find_delete_info(int64_t local_pool_id,
553 const std::string &global_image_id) {
7c673cae
FG
554 assert(m_delete_lock.is_locked());
555
556 if (m_active_delete && m_active_delete->match(local_pool_id,
557 global_image_id)) {
558 return &m_active_delete;
559 }
560
561 for (const auto& del_info : m_delete_queue) {
562 if (del_info->match(local_pool_id, global_image_id)) {
563 return &del_info;
564 }
565 }
566
567 for (const auto& del_info : m_failed_queue) {
568 if (del_info->match(local_pool_id, global_image_id)) {
569 return &del_info;
570 }
571 }
572
573 return nullptr;
574}
575
c07f9fc5
FG
576template <typename I>
577void ImageDeleter<I>::print_status(Formatter *f, stringstream *ss) {
7c673cae
FG
578 dout(20) << "enter" << dendl;
579
580 if (f) {
581 f->open_object_section("image_deleter_status");
582 f->open_array_section("delete_images_queue");
583 }
584
585 Mutex::Locker l(m_delete_lock);
586 for (const auto& image : m_delete_queue) {
587 image->print_status(f, ss);
588 }
589
590 if (f) {
591 f->close_section();
592 f->open_array_section("failed_deletes_queue");
593 }
594
595 for (const auto& image : m_failed_queue) {
596 image->print_status(f, ss, true);
597 }
598
599 if (f) {
600 f->close_section();
601 f->close_section();
602 f->flush(*ss);
603 }
604}
605
c07f9fc5
FG
606template <typename I>
607void ImageDeleter<I>::DeleteInfo::notify(int r) {
7c673cae
FG
608 if (on_delete) {
609 dout(20) << "executing image deletion handler r=" << r << dendl;
610
611 Context *ctx = on_delete;
612 on_delete = nullptr;
613 ctx->complete(r);
614 }
615}
616
c07f9fc5
FG
617template <typename I>
618void ImageDeleter<I>::DeleteInfo::to_string(stringstream& ss) {
7c673cae
FG
619 ss << "[" << "local_pool_id=" << local_pool_id << ", ";
620 ss << "global_image_id=" << global_image_id << "]";
621}
622
c07f9fc5
FG
623template <typename I>
624void ImageDeleter<I>::DeleteInfo::print_status(Formatter *f, stringstream *ss,
625 bool print_failure_info) {
7c673cae
FG
626 if (f) {
627 f->open_object_section("delete_info");
628 f->dump_int("local_pool_id", local_pool_id);
629 f->dump_string("global_image_id", global_image_id);
630 if (print_failure_info) {
631 f->dump_string("error_code", cpp_strerror(error_code));
632 f->dump_int("retries", retries);
633 }
634 f->close_section();
635 f->flush(*ss);
636 } else {
637 this->to_string(*ss);
638 }
639}
640
c07f9fc5
FG
641template <typename I>
642vector<string> ImageDeleter<I>::get_delete_queue_items() {
7c673cae
FG
643 vector<string> items;
644
645 Mutex::Locker l(m_delete_lock);
646 for (const auto& del_info : m_delete_queue) {
647 items.push_back(del_info->global_image_id);
648 }
649
650 return items;
651}
652
c07f9fc5
FG
653template <typename I>
654vector<pair<string, int> > ImageDeleter<I>::get_failed_queue_items() {
7c673cae
FG
655 vector<pair<string, int> > items;
656
657 Mutex::Locker l(m_delete_lock);
658 for (const auto& del_info : m_failed_queue) {
659 items.push_back(make_pair(del_info->global_image_id,
660 del_info->error_code));
661 }
662
663 return items;
664}
665
c07f9fc5
FG
666template <typename I>
667void ImageDeleter<I>::set_failed_timer_interval(double interval) {
7c673cae
FG
668 this->m_failed_interval = interval;
669}
670
671} // namespace mirror
672} // namespace rbd
c07f9fc5
FG
673
674template class rbd::mirror::ImageDeleter<librbd::ImageCtx>;