]> git.proxmox.com Git - ceph.git/blame - ceph/src/librbd/api/Trash.cc
bump version to 18.2.2-pve1
[ceph.git] / ceph / src / librbd / api / Trash.cc
CommitLineData
11fdf7f2
TL
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/api/Trash.h"
5#include "include/rados/librados.hpp"
6#include "common/dout.h"
7#include "common/errno.h"
8#include "common/Cond.h"
9#include "cls/rbd/cls_rbd_client.h"
f67539c2 10#include "librbd/AsioEngine.h"
11fdf7f2
TL
11#include "librbd/ExclusiveLock.h"
12#include "librbd/ImageCtx.h"
13#include "librbd/ImageState.h"
14#include "librbd/internal.h"
15#include "librbd/Operations.h"
16#include "librbd/TrashWatcher.h"
17#include "librbd/Utils.h"
18#include "librbd/api/DiffIterate.h"
92f5a8d4 19#include "librbd/exclusive_lock/Policy.h"
11fdf7f2
TL
20#include "librbd/image/RemoveRequest.h"
21#include "librbd/mirror/DisableRequest.h"
22#include "librbd/mirror/EnableRequest.h"
23#include "librbd/trash/MoveRequest.h"
eafe8130 24#include "librbd/trash/RemoveRequest.h"
11fdf7f2
TL
25#include <json_spirit/json_spirit.h>
26#include "librbd/journal/DisabledPolicy.h"
27#include "librbd/image/ListWatchersRequest.h"
28
29#define dout_subsys ceph_subsys_rbd
30#undef dout_prefix
31#define dout_prefix *_dout << "librbd::api::Trash: " << __func__ << ": "
32
33namespace librbd {
34namespace api {
35
eafe8130 36template <typename I>
f67539c2 37const typename Trash<I>::TrashImageSources Trash<I>::ALLOWED_RESTORE_SOURCES {
eafe8130 38 cls::rbd::TRASH_IMAGE_SOURCE_USER,
9f95a23c
TL
39 cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING,
40 cls::rbd::TRASH_IMAGE_SOURCE_USER_PARENT
eafe8130
TL
41 };
42
11fdf7f2
TL
43namespace {
44
45template <typename I>
46int disable_mirroring(I *ictx) {
11fdf7f2
TL
47 ldout(ictx->cct, 10) << dendl;
48
49 C_SaferCond ctx;
50 auto req = mirror::DisableRequest<I>::create(ictx, false, true, &ctx);
51 req->send();
9f95a23c 52 int r = ctx.wait();
11fdf7f2
TL
53 if (r < 0) {
54 lderr(ictx->cct) << "failed to disable mirroring: " << cpp_strerror(r)
55 << dendl;
56 return r;
57 }
58
59 return 0;
60}
61
62template <typename I>
63int enable_mirroring(IoCtx &io_ctx, const std::string &image_id) {
64 auto cct = reinterpret_cast<CephContext*>(io_ctx.cct());
65
66 uint64_t features;
67 uint64_t incompatible_features;
68 int r = cls_client::get_features(&io_ctx, util::header_name(image_id), true,
69 &features, &incompatible_features);
70 if (r < 0) {
71 lderr(cct) << "failed to retrieve features: " << cpp_strerror(r) << dendl;
72 return r;
73 }
74
75 if ((features & RBD_FEATURE_JOURNALING) == 0) {
76 return 0;
77 }
78
79 cls::rbd::MirrorMode mirror_mode;
80 r = cls_client::mirror_mode_get(&io_ctx, &mirror_mode);
81 if (r < 0 && r != -ENOENT) {
82 lderr(cct) << "failed to retrieve mirror mode: " << cpp_strerror(r)
83 << dendl;
84 return r;
85 }
86
87 if (mirror_mode != cls::rbd::MIRROR_MODE_POOL) {
88 ldout(cct, 10) << "not pool mirroring mode" << dendl;
89 return 0;
90 }
91
92 ldout(cct, 10) << dendl;
93
f67539c2
TL
94 AsioEngine asio_engine(io_ctx);
95
11fdf7f2 96 C_SaferCond ctx;
9f95a23c 97 auto req = mirror::EnableRequest<I>::create(
1911f103 98 io_ctx, image_id, cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "", false,
f67539c2 99 asio_engine.get_work_queue(), &ctx);
11fdf7f2
TL
100 req->send();
101 r = ctx.wait();
102 if (r < 0) {
103 lderr(cct) << "failed to enable mirroring: " << cpp_strerror(r)
104 << dendl;
105 return r;
106 }
107
108 return 0;
109}
110
f67539c2
TL
111int list_trash_image_specs(
112 librados::IoCtx &io_ctx,
113 std::map<std::string, cls::rbd::TrashImageSpec>* trash_image_specs,
114 bool exclude_user_remove_source) {
115 CephContext *cct((CephContext *)io_ctx.cct());
116 ldout(cct, 20) << "list_trash_image_specs " << &io_ctx << dendl;
117
118 bool more_entries;
119 uint32_t max_read = 1024;
120 std::string last_read;
121 do {
20effc67 122 std::map<std::string, cls::rbd::TrashImageSpec> trash_entries;
f67539c2
TL
123 int r = cls_client::trash_list(&io_ctx, last_read, max_read,
124 &trash_entries);
125 if (r < 0 && r != -ENOENT) {
126 lderr(cct) << "error listing rbd trash entries: " << cpp_strerror(r)
127 << dendl;
128 return r;
129 } else if (r == -ENOENT) {
130 break;
131 }
132
133 if (trash_entries.empty()) {
134 break;
135 }
136
137 for (const auto &entry : trash_entries) {
138 if (exclude_user_remove_source &&
139 entry.second.source == cls::rbd::TRASH_IMAGE_SOURCE_REMOVING) {
140 continue;
141 }
142
143 trash_image_specs->insert({entry.first, entry.second});
144 }
145
146 last_read = trash_entries.rbegin()->first;
147 more_entries = (trash_entries.size() >= max_read);
148 } while (more_entries);
149
150 return 0;
151}
152
11fdf7f2
TL
153} // anonymous namespace
154
155template <typename I>
156int Trash<I>::move(librados::IoCtx &io_ctx, rbd_trash_image_source_t source,
157 const std::string &image_name, const std::string &image_id,
158 uint64_t delay) {
159 ceph_assert(!image_name.empty() && !image_id.empty());
160 CephContext *cct((CephContext *)io_ctx.cct());
161 ldout(cct, 20) << &io_ctx << " name=" << image_name << ", id=" << image_id
162 << dendl;
163
164 auto ictx = new I("", image_id, nullptr, io_ctx, false);
165 int r = ictx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT);
166
167 if (r < 0 && r != -ENOENT) {
168 lderr(cct) << "failed to open image: " << cpp_strerror(r) << dendl;
169 return r;
170 }
171
172 if (r == 0) {
9f95a23c
TL
173 cls::rbd::MirrorImage mirror_image;
174 int mirror_r = cls_client::mirror_image_get(&ictx->md_ctx, ictx->id,
175 &mirror_image);
176 if (mirror_r == -ENOENT) {
177 ldout(ictx->cct, 10) << "mirroring is not enabled for this image"
178 << dendl;
179 } else if (mirror_r < 0) {
180 lderr(ictx->cct) << "failed to retrieve mirror image: "
181 << cpp_strerror(mirror_r) << dendl;
182 return mirror_r;
183 } else if (mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
184 // a remote rbd-mirror might own the exclusive-lock on this image
185 // and therefore we need to disable mirroring so that it closes the image
186 r = disable_mirroring<I>(ictx);
187 if (r < 0) {
188 ictx->state->close();
189 return r;
190 }
191 }
192
11fdf7f2 193 if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
9f95a23c 194 std::unique_lock image_locker{ictx->image_lock};
11fdf7f2
TL
195 ictx->set_journal_policy(new journal::DisabledPolicy());
196 }
197
9f95a23c 198 ictx->owner_lock.lock_shared();
11fdf7f2
TL
199 if (ictx->exclusive_lock != nullptr) {
200 ictx->exclusive_lock->block_requests(0);
201
92f5a8d4 202 r = ictx->operations->prepare_image_update(
9f95a23c 203 exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL, true);
11fdf7f2
TL
204 if (r < 0) {
205 lderr(cct) << "cannot obtain exclusive lock - not removing" << dendl;
9f95a23c 206 ictx->owner_lock.unlock_shared();
11fdf7f2
TL
207 ictx->state->close();
208 return -EBUSY;
209 }
210 }
9f95a23c 211 ictx->owner_lock.unlock_shared();
11fdf7f2 212
9f95a23c 213 ictx->image_lock.lock_shared();
11fdf7f2
TL
214 if (!ictx->migration_info.empty()) {
215 lderr(cct) << "cannot move migrating image to trash" << dendl;
9f95a23c 216 ictx->image_lock.unlock_shared();
11fdf7f2
TL
217 ictx->state->close();
218 return -EBUSY;
219 }
9f95a23c 220 ictx->image_lock.unlock_shared();
11fdf7f2 221
9f95a23c
TL
222 if (mirror_r >= 0 &&
223 mirror_image.mode != cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
224 r = disable_mirroring<I>(ictx);
225 if (r < 0) {
226 ictx->state->close();
227 return r;
228 }
11fdf7f2
TL
229 }
230
231 ictx->state->close();
232 }
233
234 utime_t delete_time{ceph_clock_now()};
235 utime_t deferment_end_time{delete_time};
236 deferment_end_time += delay;
237 cls::rbd::TrashImageSpec trash_image_spec{
238 static_cast<cls::rbd::TrashImageSource>(source), image_name,
239 delete_time, deferment_end_time};
240
241 trash_image_spec.state = cls::rbd::TRASH_IMAGE_STATE_MOVING;
242 C_SaferCond ctx;
243 auto req = trash::MoveRequest<I>::create(io_ctx, image_id, trash_image_spec,
244 &ctx);
245 req->send();
246
247 r = ctx.wait();
248 trash_image_spec.state = cls::rbd::TRASH_IMAGE_STATE_NORMAL;
249 int ret = cls_client::trash_state_set(&io_ctx, image_id,
250 trash_image_spec.state,
251 cls::rbd::TRASH_IMAGE_STATE_MOVING);
252 if (ret < 0 && ret != -EOPNOTSUPP) {
253 lderr(cct) << "error setting trash image state: "
254 << cpp_strerror(ret) << dendl;
255 return ret;
256 }
257 if (r < 0) {
258 return r;
259 }
260
261 C_SaferCond notify_ctx;
262 TrashWatcher<I>::notify_image_added(io_ctx, image_id, trash_image_spec,
263 &notify_ctx);
264 r = notify_ctx.wait();
265 if (r < 0) {
266 lderr(cct) << "failed to send update notification: " << cpp_strerror(r)
267 << dendl;
268 }
269
270 return 0;
271}
272
273template <typename I>
274int Trash<I>::move(librados::IoCtx &io_ctx, rbd_trash_image_source_t source,
275 const std::string &image_name, uint64_t delay) {
276 CephContext *cct((CephContext *)io_ctx.cct());
277 ldout(cct, 20) << &io_ctx << " name=" << image_name << dendl;
278
279 // try to get image id from the directory
280 std::string image_id;
281 int r = cls_client::dir_get_id(&io_ctx, RBD_DIRECTORY, image_name,
282 &image_id);
283 if (r == -ENOENT) {
284 r = io_ctx.stat(util::old_header_name(image_name), nullptr, nullptr);
285 if (r == 0) {
286 // cannot move V1 image to trash
287 ldout(cct, 10) << "cannot move v1 image to trash" << dendl;
288 return -EOPNOTSUPP;
289 }
290
f67539c2
TL
291 // search for an interrupted trash move request
292 std::map<std::string, cls::rbd::TrashImageSpec> trash_image_specs;
293 int r = list_trash_image_specs(io_ctx, &trash_image_specs, true);
294 if (r < 0) {
295 return r;
296 }
20effc67
TL
297 if (auto found_image =
298 std::find_if(
299 trash_image_specs.begin(), trash_image_specs.end(),
300 [&](const auto& pair) {
301 const auto& spec = pair.second;
302 return (spec.source == cls::rbd::TRASH_IMAGE_SOURCE_USER &&
303 spec.state == cls::rbd::TRASH_IMAGE_STATE_MOVING &&
304 spec.name == image_name);
305 });
306 found_image != trash_image_specs.end()) {
307 image_id = found_image->first;
308 } else {
f67539c2
TL
309 return -ENOENT;
310 }
f67539c2
TL
311 ldout(cct, 15) << "derived image id " << image_id << " from existing "
312 << "trash entry" << dendl;
11fdf7f2
TL
313 } else if (r < 0) {
314 lderr(cct) << "failed to retrieve image id: " << cpp_strerror(r) << dendl;
315 return r;
316 }
317
f67539c2
TL
318 if (image_name.empty() || image_id.empty()) {
319 lderr(cct) << "invalid image name/id" << dendl;
320 return -EINVAL;
321 }
322
11fdf7f2
TL
323 return Trash<I>::move(io_ctx, source, image_name, image_id, delay);
324}
325
326template <typename I>
327int Trash<I>::get(IoCtx &io_ctx, const std::string &id,
328 trash_image_info_t *info) {
329 CephContext *cct((CephContext *)io_ctx.cct());
330 ldout(cct, 20) << __func__ << " " << &io_ctx << dendl;
331
332 cls::rbd::TrashImageSpec spec;
333 int r = cls_client::trash_get(&io_ctx, id, &spec);
334 if (r == -ENOENT) {
335 return r;
336 } else if (r < 0) {
337 lderr(cct) << "error retrieving trash entry: " << cpp_strerror(r)
338 << dendl;
339 return r;
340 }
341
342 rbd_trash_image_source_t source = static_cast<rbd_trash_image_source_t>(
343 spec.source);
344 *info = trash_image_info_t{id, spec.name, source, spec.deletion_time.sec(),
345 spec.deferment_end_time.sec()};
346 return 0;
347}
348
349template <typename I>
20effc67 350int Trash<I>::list(IoCtx &io_ctx, std::vector<trash_image_info_t> &entries,
11fdf7f2
TL
351 bool exclude_user_remove_source) {
352 CephContext *cct((CephContext *)io_ctx.cct());
f67539c2 353 ldout(cct, 20) << __func__ << " " << &io_ctx << dendl;
11fdf7f2 354
f67539c2
TL
355 std::map<std::string, cls::rbd::TrashImageSpec> trash_image_specs;
356 int r = list_trash_image_specs(io_ctx, &trash_image_specs,
357 exclude_user_remove_source);
358 if (r < 0) {
359 return r;
360 }
11fdf7f2 361
f67539c2
TL
362 entries.reserve(trash_image_specs.size());
363 for (const auto& [image_id, spec] : trash_image_specs) {
364 rbd_trash_image_source_t source =
365 static_cast<rbd_trash_image_source_t>(spec.source);
366 entries.push_back({image_id, spec.name, source,
367 spec.deletion_time.sec(),
368 spec.deferment_end_time.sec()});
369 }
11fdf7f2
TL
370
371 return 0;
372}
373
374template <typename I>
375int Trash<I>::purge(IoCtx& io_ctx, time_t expire_ts,
376 float threshold, ProgressContext& pctx) {
377 auto *cct((CephContext *) io_ctx.cct());
378 ldout(cct, 20) << &io_ctx << dendl;
379
380 std::vector<librbd::trash_image_info_t> trash_entries;
381 int r = librbd::api::Trash<I>::list(io_ctx, trash_entries, true);
382 if (r < 0) {
383 return r;
384 }
385
386 trash_entries.erase(
387 std::remove_if(trash_entries.begin(), trash_entries.end(),
388 [](librbd::trash_image_info_t info) {
9f95a23c
TL
389 return info.source != RBD_TRASH_IMAGE_SOURCE_USER &&
390 info.source != RBD_TRASH_IMAGE_SOURCE_USER_PARENT;
11fdf7f2
TL
391 }),
392 trash_entries.end());
393
394 std::set<std::string> to_be_removed;
395 if (threshold != -1) {
396 if (threshold < 0 || threshold > 1) {
397 lderr(cct) << "argument 'threshold' is out of valid range"
398 << dendl;
399 return -EINVAL;
400 }
401
402 librados::bufferlist inbl;
403 librados::bufferlist outbl;
404 std::string pool_name = io_ctx.get_pool_name();
405
406 librados::Rados rados(io_ctx);
407 rados.mon_command(R"({"prefix": "df", "format": "json"})", inbl,
408 &outbl, nullptr);
409
410 json_spirit::mValue json;
411 if (!json_spirit::read(outbl.to_str(), json)) {
412 lderr(cct) << "ceph df json output could not be parsed"
413 << dendl;
414 return -EBADMSG;
415 }
416
417 json_spirit::mArray arr = json.get_obj()["pools"].get_array();
418
419 double pool_percent_used = 0;
420 uint64_t pool_total_bytes = 0;
421
422 std::map<std::string, std::vector<std::string>> datapools;
423
424 std::sort(trash_entries.begin(), trash_entries.end(),
425 [](librbd::trash_image_info_t a, librbd::trash_image_info_t b) {
426 return a.deferment_end_time < b.deferment_end_time;
427 }
428 );
429
430 for (const auto &entry : trash_entries) {
431 int64_t data_pool_id = -1;
432 r = cls_client::get_data_pool(&io_ctx, util::header_name(entry.id),
433 &data_pool_id);
434 if (r < 0 && r != -ENOENT && r != -EOPNOTSUPP) {
435 lderr(cct) << "failed to query data pool: " << cpp_strerror(r) << dendl;
436 return r;
437 } else if (data_pool_id == -1) {
438 data_pool_id = io_ctx.get_id();
439 }
440
441 if (data_pool_id != io_ctx.get_id()) {
442 librados::IoCtx data_io_ctx;
443 r = util::create_ioctx(io_ctx, "image", data_pool_id,
444 {}, &data_io_ctx);
445 if (r < 0) {
446 lderr(cct) << "error accessing data pool" << dendl;
447 continue;
448 }
449 auto data_pool = data_io_ctx.get_pool_name();
450 datapools[data_pool].push_back(entry.id);
451 } else {
452 datapools[pool_name].push_back(entry.id);
453 }
454 }
455
456 uint64_t bytes_to_free = 0;
457
458 for (uint8_t i = 0; i < arr.size(); ++i) {
459 json_spirit::mObject obj = arr[i].get_obj();
460 std::string name = obj.find("name")->second.get_str();
461 auto img = datapools.find(name);
462 if (img != datapools.end()) {
463 json_spirit::mObject stats = arr[i].get_obj()["stats"].get_obj();
464 pool_percent_used = stats["percent_used"].get_real();
465 if (pool_percent_used <= threshold) continue;
466
467 bytes_to_free = 0;
468
469 pool_total_bytes = stats["max_avail"].get_uint64() +
470 stats["bytes_used"].get_uint64();
471
472 auto bytes_threshold = (uint64_t) (pool_total_bytes *
473 (pool_percent_used - threshold));
474
475 for (const auto &it : img->second) {
476 auto ictx = new I("", it, nullptr, io_ctx, false);
477 r = ictx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT);
478 if (r == -ENOENT) {
479 continue;
480 } else if (r < 0) {
481 lderr(cct) << "failed to open image " << it << ": "
482 << cpp_strerror(r) << dendl;
483 }
484
485 r = librbd::api::DiffIterate<I>::diff_iterate(
486 ictx, cls::rbd::UserSnapshotNamespace(), nullptr, 0, ictx->size,
487 false, true,
488 [](uint64_t offset, size_t len, int exists, void *arg) {
489 auto *to_free = reinterpret_cast<uint64_t *>(arg);
490 if (exists)
491 (*to_free) += len;
492 return 0;
493 }, &bytes_to_free);
494
495 ictx->state->close();
496 if (r < 0) {
497 lderr(cct) << "failed to calculate disk usage for image " << it
498 << ": " << cpp_strerror(r) << dendl;
499 continue;
500 }
501
502 to_be_removed.insert(it);
503 if (bytes_to_free >= bytes_threshold) {
504 break;
505 }
506 }
507 }
508 }
509
510 if (bytes_to_free == 0) {
511 ldout(cct, 10) << "pool usage is lower than or equal to "
512 << (threshold * 100)
513 << "%" << dendl;
514 return 0;
515 }
516 }
517
518 if (expire_ts == 0) {
519 struct timespec now;
520 clock_gettime(CLOCK_REALTIME, &now);
521 expire_ts = now.tv_sec;
522 }
523
524 for (const auto &entry : trash_entries) {
525 if (expire_ts >= entry.deferment_end_time) {
526 to_be_removed.insert(entry.id);
527 }
528 }
529
530 NoOpProgressContext remove_pctx;
531 uint64_t list_size = to_be_removed.size(), i = 0;
b3b6e05e
TL
532 int remove_err = 1;
533 while (!to_be_removed.empty() && remove_err == 1) {
534 remove_err = 0;
535 for (auto it = to_be_removed.begin(); it != to_be_removed.end(); ) {
536 trash_image_info_t trash_info;
537 r = Trash<I>::get(io_ctx, *it, &trash_info);
538 if (r == -ENOENT) {
539 // likely RBD_TRASH_IMAGE_SOURCE_USER_PARENT image removed as a side
540 // effect of a preceeding remove (last child detach)
541 pctx.update_progress(++i, list_size);
542 it = to_be_removed.erase(it);
543 continue;
544 } else if (r < 0) {
545 lderr(cct) << "error getting image id " << *it
546 << " info: " << cpp_strerror(r) << dendl;
547 return r;
11fdf7f2 548 }
b3b6e05e
TL
549
550 r = Trash<I>::remove(io_ctx, *it, true, remove_pctx);
551 if (r == -ENOTEMPTY || r == -EBUSY || r == -EMLINK || r == -EUCLEAN) {
552 if (!remove_err) {
553 remove_err = r;
554 }
555 ++it;
556 continue;
557 } else if (r < 0) {
558 lderr(cct) << "error removing image id " << *it
559 << ": " << cpp_strerror(r) << dendl;
560 return r;
561 }
562 pctx.update_progress(++i, list_size);
563 it = to_be_removed.erase(it);
564 remove_err = 1;
11fdf7f2 565 }
b3b6e05e
TL
566 ldout(cct, 20) << "remove_err=" << remove_err << dendl;
567 }
568
569 if (!to_be_removed.empty()) {
570 ceph_assert(remove_err < 0);
571 ldout(cct, 10) << "couldn't remove " << to_be_removed.size()
572 << " expired images" << dendl;
573 return remove_err;
11fdf7f2
TL
574 }
575
576 return 0;
577}
578
579template <typename I>
580int Trash<I>::remove(IoCtx &io_ctx, const std::string &image_id, bool force,
581 ProgressContext& prog_ctx) {
582 CephContext *cct((CephContext *)io_ctx.cct());
583 ldout(cct, 20) << "trash_remove " << &io_ctx << " " << image_id
584 << " " << force << dendl;
585
586 cls::rbd::TrashImageSpec trash_spec;
587 int r = cls_client::trash_get(&io_ctx, image_id, &trash_spec);
588 if (r < 0) {
589 lderr(cct) << "error getting image id " << image_id
590 << " info from trash: " << cpp_strerror(r) << dendl;
591 return r;
592 }
593
594 utime_t now = ceph_clock_now();
595 if (now < trash_spec.deferment_end_time && !force) {
596 lderr(cct) << "error: deferment time has not expired." << dendl;
597 return -EPERM;
598 }
f67539c2
TL
599 if (trash_spec.state == cls::rbd::TRASH_IMAGE_STATE_MOVING) {
600 lderr(cct) << "error: image is pending moving to the trash."
601 << dendl;
602 return -EUCLEAN;
603 } else if (trash_spec.state != cls::rbd::TRASH_IMAGE_STATE_NORMAL &&
604 trash_spec.state != cls::rbd::TRASH_IMAGE_STATE_REMOVING) {
11fdf7f2
TL
605 lderr(cct) << "error: image is pending restoration." << dendl;
606 return -EBUSY;
607 }
608
f67539c2 609 AsioEngine asio_engine(io_ctx);
11fdf7f2
TL
610
611 C_SaferCond cond;
eafe8130 612 auto req = librbd::trash::RemoveRequest<I>::create(
f67539c2 613 io_ctx, image_id, asio_engine.get_work_queue(), force, prog_ctx, &cond);
11fdf7f2
TL
614 req->send();
615
616 r = cond.wait();
617 if (r < 0) {
11fdf7f2
TL
618 return r;
619 }
620
621 C_SaferCond notify_ctx;
622 TrashWatcher<I>::notify_image_removed(io_ctx, image_id, &notify_ctx);
623 r = notify_ctx.wait();
624 if (r < 0) {
625 lderr(cct) << "failed to send update notification: " << cpp_strerror(r)
626 << dendl;
627 }
628
629 return 0;
630}
631
632template <typename I>
eafe8130
TL
633int Trash<I>::restore(librados::IoCtx &io_ctx,
634 const TrashImageSources& trash_image_sources,
11fdf7f2
TL
635 const std::string &image_id,
636 const std::string &image_new_name) {
637 CephContext *cct((CephContext *)io_ctx.cct());
638 ldout(cct, 20) << "trash_restore " << &io_ctx << " " << image_id << " "
639 << image_new_name << dendl;
640
641 cls::rbd::TrashImageSpec trash_spec;
642 int r = cls_client::trash_get(&io_ctx, image_id, &trash_spec);
643 if (r < 0) {
644 lderr(cct) << "error getting image id " << image_id
645 << " info from trash: " << cpp_strerror(r) << dendl;
646 return r;
647 }
648
eafe8130
TL
649 if (trash_image_sources.count(trash_spec.source) == 0) {
650 lderr(cct) << "Current trash source '" << trash_spec.source << "' "
651 << "does not match expected: "
652 << trash_image_sources << dendl;
11fdf7f2
TL
653 return -EINVAL;
654 }
655
656 std::string image_name = image_new_name;
657 if (trash_spec.state != cls::rbd::TRASH_IMAGE_STATE_NORMAL &&
658 trash_spec.state != cls::rbd::TRASH_IMAGE_STATE_RESTORING) {
659 lderr(cct) << "error restoring image id " << image_id
660 << ", which is pending deletion" << dendl;
661 return -EBUSY;
662 }
663 r = cls_client::trash_state_set(&io_ctx, image_id,
664 cls::rbd::TRASH_IMAGE_STATE_RESTORING,
665 cls::rbd::TRASH_IMAGE_STATE_NORMAL);
666 if (r < 0 && r != -EOPNOTSUPP) {
667 lderr(cct) << "error setting trash image state: "
668 << cpp_strerror(r) << dendl;
669 return r;
670 }
671
672 if (image_name.empty()) {
673 // if user didn't specify a new name, let's try using the old name
674 image_name = trash_spec.name;
675 ldout(cct, 20) << "restoring image id " << image_id << " with name "
676 << image_name << dendl;
677 }
678
679 // check if no image exists with the same name
680 bool create_id_obj = true;
681 std::string existing_id;
682 r = cls_client::get_id(&io_ctx, util::id_obj_name(image_name), &existing_id);
683 if (r < 0 && r != -ENOENT) {
684 lderr(cct) << "error checking if image " << image_name << " exists: "
685 << cpp_strerror(r) << dendl;
686 int ret = cls_client::trash_state_set(&io_ctx, image_id,
687 cls::rbd::TRASH_IMAGE_STATE_NORMAL,
688 cls::rbd::TRASH_IMAGE_STATE_RESTORING);
689 if (ret < 0 && ret != -EOPNOTSUPP) {
690 lderr(cct) << "error setting trash image state: "
691 << cpp_strerror(ret) << dendl;
692 }
693 return r;
694 } else if (r != -ENOENT){
695 // checking if we are recovering from an incomplete restore
696 if (existing_id != image_id) {
697 ldout(cct, 2) << "an image with the same name already exists" << dendl;
698 int r2 = cls_client::trash_state_set(&io_ctx, image_id,
699 cls::rbd::TRASH_IMAGE_STATE_NORMAL,
700 cls::rbd::TRASH_IMAGE_STATE_RESTORING);
701 if (r2 < 0 && r2 != -EOPNOTSUPP) {
702 lderr(cct) << "error setting trash image state: "
703 << cpp_strerror(r2) << dendl;
704 }
705 return -EEXIST;
706 }
707 create_id_obj = false;
708 }
709
710 if (create_id_obj) {
711 ldout(cct, 2) << "adding id object" << dendl;
712 librados::ObjectWriteOperation op;
713 op.create(true);
714 cls_client::set_id(&op, image_id);
715 r = io_ctx.operate(util::id_obj_name(image_name), &op);
716 if (r < 0) {
717 lderr(cct) << "error adding id object for image " << image_name
718 << ": " << cpp_strerror(r) << dendl;
719 return r;
720 }
721 }
722
723 ldout(cct, 2) << "adding rbd image to v2 directory..." << dendl;
724 r = cls_client::dir_add_image(&io_ctx, RBD_DIRECTORY, image_name,
725 image_id);
726 if (r < 0 && r != -EEXIST) {
727 lderr(cct) << "error adding image to v2 directory: "
728 << cpp_strerror(r) << dendl;
729 return r;
730 }
731
732 r = enable_mirroring<I>(io_ctx, image_id);
733 if (r < 0) {
734 // not fatal -- ignore
735 }
736
737 ldout(cct, 2) << "removing image from trash..." << dendl;
738 r = cls_client::trash_remove(&io_ctx, image_id);
739 if (r < 0 && r != -ENOENT) {
740 lderr(cct) << "error removing image id " << image_id << " from trash: "
741 << cpp_strerror(r) << dendl;
742 return r;
743 }
744
745 C_SaferCond notify_ctx;
746 TrashWatcher<I>::notify_image_removed(io_ctx, image_id, &notify_ctx);
747 r = notify_ctx.wait();
748 if (r < 0) {
749 lderr(cct) << "failed to send update notification: " << cpp_strerror(r)
750 << dendl;
751 }
752
753 return 0;
754}
755
756} // namespace api
757} // namespace librbd
758
759template class librbd::api::Trash<librbd::ImageCtx>;