Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / librbd / image / RemoveRequest.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 "common/dout.h"
5 #include "common/errno.h"
6 #include "librbd/internal.h"
7 #include "librbd/ImageState.h"
8 #include "librbd/Journal.h"
9 #include "librbd/ObjectMap.h"
10 #include "librbd/ExclusiveLock.h"
11 #include "librbd/MirroringWatcher.h"
12 #include "librbd/journal/RemoveRequest.h"
13 #include "librbd/image/RemoveRequest.h"
14 #include "librbd/operation/TrimRequest.h"
15 #include "librbd/mirror/DisableRequest.h"
16
17 #define dout_subsys ceph_subsys_rbd
18 #undef dout_prefix
19 #define dout_prefix *_dout << "librbd::image::RemoveRequest: " << this << " " \
20                            << __func__ << ": "
21
22 namespace librbd {
23 namespace image {
24
25 using librados::IoCtx;
26 using util::create_context_callback;
27 using util::create_async_context_callback;
28 using util::create_rados_callback;
29
30 template<typename I>
31 RemoveRequest<I>::RemoveRequest(IoCtx &ioctx, const std::string &image_name,
32                                 const std::string &image_id, bool force,
33                                 bool from_trash_remove,
34                                 ProgressContext &prog_ctx,
35                                 ContextWQ *op_work_queue, Context *on_finish)
36   : m_ioctx(ioctx), m_image_name(image_name), m_image_id(image_id),
37     m_force(force), m_from_trash_remove(from_trash_remove),
38     m_prog_ctx(prog_ctx), m_op_work_queue(op_work_queue),
39     m_on_finish(on_finish) {
40   m_cct = reinterpret_cast<CephContext *>(m_ioctx.cct());
41
42   m_image_ctx = I::create((m_image_id.empty() ? m_image_name : std::string()),
43                           m_image_id, nullptr, m_ioctx, false);
44 }
45
46 template<typename I>
47 void RemoveRequest<I>::send() {
48   ldout(m_cct, 20) << dendl;
49
50   open_image();
51 }
52
53 template<typename I>
54 void RemoveRequest<I>::open_image() {
55   ldout(m_cct, 20) << dendl;
56
57   using klass = RemoveRequest<I>;
58   Context *ctx = create_context_callback<klass, &klass::handle_open_image>(
59     this);
60
61   m_image_ctx->state->open(true, ctx);
62 }
63
64 template<typename I>
65 void RemoveRequest<I>::handle_open_image(int r) {
66   ldout(m_cct, 20) << "r=" << r << dendl;
67
68   if (r < 0) {
69     m_image_ctx->destroy();
70     m_image_ctx = nullptr;
71
72     if (r != -ENOENT) {
73       lderr(m_cct) << "error opening image: " << cpp_strerror(r) << dendl;
74       finish(r);
75       return;
76     }
77
78     remove_image();
79     return;
80   }
81
82   m_image_id = m_image_ctx->id;
83   m_image_name = m_image_ctx->name;
84   m_header_oid = m_image_ctx->header_oid;
85   m_old_format = m_image_ctx->old_format;
86   m_unknown_format = false;
87
88   check_exclusive_lock();
89 }
90
91 template<typename I>
92 void RemoveRequest<I>::check_exclusive_lock() {
93   ldout(m_cct, 20) << dendl;
94
95   if (m_image_ctx->exclusive_lock == nullptr) {
96     validate_image_removal();
97   } else {
98     acquire_exclusive_lock();
99   }
100 }
101
102 template<typename I>
103 void RemoveRequest<I>::acquire_exclusive_lock() {
104   ldout(m_cct, 20) << dendl;
105
106   using klass = RemoveRequest<I>;
107   if (m_force) {
108     Context *ctx = create_context_callback<
109       klass, &klass::handle_exclusive_lock_force>(this);
110     m_exclusive_lock = m_image_ctx->exclusive_lock;
111     m_exclusive_lock->shut_down(ctx);
112   } else {
113     Context *ctx = create_context_callback<
114       klass, &klass::handle_exclusive_lock>(this);
115     RWLock::WLocker owner_lock(m_image_ctx->owner_lock);
116     m_image_ctx->exclusive_lock->try_acquire_lock(ctx);
117   }
118 }
119
120 template<typename I>
121 void RemoveRequest<I>::handle_exclusive_lock_force(int r) {
122   ldout(m_cct, 20) << "r=" << r << dendl;
123
124   delete m_exclusive_lock;
125   m_exclusive_lock = nullptr;
126
127   if (r < 0) {
128     lderr(m_cct) << "error shutting down exclusive lock: "
129                  << cpp_strerror(r) << dendl;
130     send_close_image(r);
131     return;
132   }
133
134   assert(m_image_ctx->exclusive_lock == nullptr);
135   validate_image_removal();
136 }
137
138 template<typename I>
139 void RemoveRequest<I>::handle_exclusive_lock(int r) {
140   ldout(m_cct, 20) << "r=" << r << dendl;
141
142   if (r < 0 || !m_image_ctx->exclusive_lock->is_lock_owner()) {
143     lderr(m_cct) << "cannot obtain exclusive lock - not removing" << dendl;
144     send_close_image(-EBUSY);
145     return;
146   }
147
148   validate_image_removal();
149 }
150
151 template<typename I>
152 void RemoveRequest<I>::validate_image_removal() {
153   ldout(m_cct, 20) << dendl;
154
155   check_image_snaps();
156 }
157
158 template<typename I>
159 void RemoveRequest<I>::check_image_snaps() {
160   ldout(m_cct, 20) << dendl;
161
162   if (m_image_ctx->snaps.size()) {
163     lderr(m_cct) << "image has snapshots - not removing" << dendl;
164     send_close_image(-ENOTEMPTY);
165     return;
166   }
167
168   list_image_watchers();
169 }
170
171 template<typename I>
172 void RemoveRequest<I>::list_image_watchers() {
173   ldout(m_cct, 20) << dendl;
174
175   librados::ObjectReadOperation op;
176   op.list_watchers(&m_watchers, &m_ret_val);
177
178   using klass = RemoveRequest<I>;
179   librados::AioCompletion *rados_completion =
180     create_rados_callback<klass, &klass::handle_list_image_watchers>(this);
181
182   int r = m_image_ctx->md_ctx.aio_operate(m_header_oid, rados_completion,
183                                           &op, &m_out_bl);
184   assert(r == 0);
185   rados_completion->release();
186 }
187
188 template<typename I>
189 void RemoveRequest<I>::handle_list_image_watchers(int r) {
190   ldout(m_cct, 20) << "r=" << r << dendl;
191
192   if (r == 0 && m_ret_val < 0) {
193     r = m_ret_val;
194   }
195   if (r < 0) {
196     lderr(m_cct) << "error listing image watchers: " << cpp_strerror(r)
197                  << dendl;
198     send_close_image(r);
199     return;
200   }
201
202   get_mirror_image();
203 }
204
205 template<typename I>
206 void RemoveRequest<I>::get_mirror_image() {
207   ldout(m_cct, 20) << dendl;
208   if ((m_watchers.empty()) ||
209       ((m_image_ctx->features & RBD_FEATURE_JOURNALING) == 0)) {
210     check_image_watchers();
211     return;
212   }
213
214   librados::ObjectReadOperation op;
215   cls_client::mirror_image_get_start(&op, m_image_id);
216
217   using klass = RemoveRequest<I>;
218   librados::AioCompletion *comp =
219     create_rados_callback<klass, &klass::handle_get_mirror_image>(this);
220   m_out_bl.clear();
221   int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl);
222   assert(r == 0);
223   comp->release();
224 }
225
226 template<typename I>
227 void RemoveRequest<I>::handle_get_mirror_image(int r) {
228   ldout(m_cct, 20) << "r=" << r << dendl;
229
230   if (r == -ENOENT || r == -EOPNOTSUPP) {
231     check_image_watchers();
232     return;
233   } else if (r < 0) {
234     ldout(m_cct, 5) << "error retrieving mirror image: " << cpp_strerror(r)
235                     << dendl;
236   }
237
238   list_mirror_watchers();
239 }
240
241 template<typename I>
242 void RemoveRequest<I>::list_mirror_watchers() {
243   ldout(m_cct, 20) << dendl;
244
245   librados::ObjectReadOperation op;
246   op.list_watchers(&m_mirror_watchers, &m_ret_val);
247
248   using klass = RemoveRequest<I>;
249   librados::AioCompletion *rados_completion =
250     create_rados_callback<klass, &klass::handle_list_mirror_watchers>(this);
251   m_out_bl.clear();
252   int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, rados_completion,
253                                           &op, &m_out_bl);
254   assert(r == 0);
255   rados_completion->release();
256 }
257
258 template<typename I>
259 void RemoveRequest<I>::handle_list_mirror_watchers(int r) {
260   ldout(m_cct, 20) << "r=" << r << dendl;
261
262   if (r == 0 && m_ret_val < 0) {
263     r = m_ret_val;
264   }
265   if (r < 0 && r != -ENOENT) {
266     ldout(m_cct, 5) << "error listing mirror watchers: " << cpp_strerror(r)
267                     << dendl;
268   }
269
270   for (auto &watcher : m_mirror_watchers) {
271     m_watchers.remove_if([watcher] (obj_watch_t &w) {
272         return (strncmp(w.addr, watcher.addr, sizeof(w.addr)) == 0);
273       });
274   }
275
276   check_image_watchers();
277 }
278
279 template<typename I>
280 void RemoveRequest<I>::check_image_watchers() {
281   if (m_watchers.size() > 1) {
282     lderr(m_cct) << "image has watchers - not removing" << dendl;
283     send_close_image(-EBUSY);
284     return;
285   }
286
287   check_group();
288 }
289
290 template<typename I>
291 void RemoveRequest<I>::check_group() {
292   ldout(m_cct, 20) << dendl;
293
294   librados::ObjectReadOperation op;
295   librbd::cls_client::image_get_group_start(&op);
296
297   using klass = RemoveRequest<I>;
298   librados::AioCompletion *rados_completion = create_rados_callback<
299     klass, &klass::handle_check_group>(this);
300   m_out_bl.clear();
301   int r = m_image_ctx->md_ctx.aio_operate(m_header_oid, rados_completion, &op,
302                                           &m_out_bl);
303   assert(r == 0);
304   rados_completion->release();
305 }
306
307 template<typename I>
308 void RemoveRequest<I>::handle_check_group(int r) {
309   ldout(m_cct, 20) << "r=" << r << dendl;
310
311   cls::rbd::GroupSpec s;
312   if (r == 0) {
313     bufferlist::iterator it = m_out_bl.begin();
314     r = librbd::cls_client::image_get_group_finish(&it, &s);
315   }
316   if (r < 0 && r != -EOPNOTSUPP) {
317     lderr(m_cct) << "error fetching group for image: "
318                  << cpp_strerror(r) << dendl;
319     send_close_image(r);
320     return;
321   }
322
323   if (s.is_valid()) {
324     lderr(m_cct) << "image is in a group - not removing" << dendl;
325     send_close_image(-EMLINK);
326     return;
327   }
328
329   trim_image();
330 }
331
332 template<typename I>
333 void RemoveRequest<I>::trim_image() {
334   ldout(m_cct, 20) << dendl;
335
336   using klass = RemoveRequest<I>;
337   Context *ctx = create_async_context_callback(
338     *m_image_ctx, create_context_callback<
339       klass, &klass::handle_trim_image>(this));
340
341   RWLock::RLocker owner_lock(m_image_ctx->owner_lock);
342   auto req = librbd::operation::TrimRequest<I>::create(
343     *m_image_ctx, ctx, m_image_ctx->size, 0, m_prog_ctx);
344   req->send();
345 }
346
347 template<typename I>
348 void RemoveRequest<I>::handle_trim_image(int r) {
349   ldout(m_cct, 20) << "r=" << r << dendl;
350
351   if (r < 0) {
352     lderr(m_cct) << "warning: failed to remove some object(s): "
353                  << cpp_strerror(r) << dendl;
354   }
355
356   if (m_old_format) {
357     send_close_image(r);
358     return;
359   }
360
361   remove_child();
362 }
363
364 template<typename I>
365 void RemoveRequest<I>::remove_child() {
366   ldout(m_cct, 20) << dendl;
367
368   m_image_ctx->parent_lock.get_read();
369   ParentInfo parent_info = m_image_ctx->parent_md;
370   m_image_ctx->parent_lock.put_read();
371
372   librados::ObjectWriteOperation op;
373   librbd::cls_client::remove_child(&op, parent_info.spec, m_image_id);
374
375   using klass = RemoveRequest<I>;
376   librados::AioCompletion *rados_completion =
377     create_rados_callback<klass, &klass::handle_remove_child>(this);
378   int r = m_image_ctx->md_ctx.aio_operate(RBD_CHILDREN, rados_completion, &op);
379   assert(r == 0);
380   rados_completion->release();
381 }
382
383 template<typename I>
384 void RemoveRequest<I>::handle_remove_child(int r) {
385   ldout(m_cct, 20) << "r=" << r << dendl;
386
387   if (r == -ENOENT) {
388     r = 0;
389   } else if (r < 0) {
390     lderr(m_cct) << "error removing child from children list: "
391                  << cpp_strerror(r) << dendl;
392     send_close_image(r);
393     return;
394   }
395
396
397   send_disable_mirror();
398 }
399
400 template<typename I>
401 void RemoveRequest<I>::send_disable_mirror() {
402   ldout(m_cct, 20) << dendl;
403
404   using klass = RemoveRequest<I>;
405   Context *ctx = create_context_callback<
406     klass, &klass::handle_disable_mirror>(this);
407
408   mirror::DisableRequest<I> *req =
409     mirror::DisableRequest<I>::create(m_image_ctx, m_force, !m_force, ctx);
410   req->send();
411 }
412
413 template<typename I>
414 void RemoveRequest<I>::handle_disable_mirror(int r) {
415   ldout(m_cct, 20) << "r=" << r << dendl;
416
417   if (r == -EOPNOTSUPP) {
418     r = 0;
419   } else if (r < 0) {
420     lderr(m_cct) << "error disabling image mirroring: "
421                  << cpp_strerror(r) << dendl;
422   }
423
424   send_close_image(r);
425 }
426
427 template<typename I>
428 void RemoveRequest<I>::send_close_image(int r) {
429   ldout(m_cct, 20) << dendl;
430
431   m_ret_val = r;
432   using klass = RemoveRequest<I>;
433   Context *ctx = create_context_callback<
434     klass, &klass::handle_send_close_image>(this);
435
436   m_image_ctx->state->close(ctx);
437 }
438
439 template<typename I>
440 void RemoveRequest<I>::handle_send_close_image(int r) {
441   ldout(m_cct, 20) << "r=" << r << dendl;
442
443   if (r < 0) {
444     lderr(m_cct) << "error encountered while closing image: "
445                  << cpp_strerror(r) << dendl;
446   }
447
448   m_image_ctx->destroy();
449   m_image_ctx = nullptr;
450   if (m_ret_val < 0) {
451     r = m_ret_val;
452     finish(r);
453     return;
454   }
455
456   remove_header();
457 }
458
459 template<typename I>
460 void RemoveRequest<I>::remove_header() {
461   ldout(m_cct, 20) << dendl;
462
463   using klass = RemoveRequest<I>;
464   librados::AioCompletion *rados_completion =
465     create_rados_callback<klass, &klass::handle_remove_header>(this);
466   int r = m_ioctx.aio_remove(m_header_oid, rados_completion);
467   assert(r == 0);
468   rados_completion->release();
469 }
470
471 template<typename I>
472 void RemoveRequest<I>::handle_remove_header(int r) {
473   ldout(m_cct, 20) << "r=" << r << dendl;
474
475   if (r < 0 && r != -ENOENT) {
476     lderr(m_cct) << "error removing header: " << cpp_strerror(r) << dendl;
477     m_ret_val = r;
478   }
479
480   remove_image();
481 }
482
483 template<typename I>
484 void RemoveRequest<I>::remove_header_v2() {
485   ldout(m_cct, 20) << dendl;
486
487   if (m_header_oid.empty()) {
488     m_header_oid = util::header_name(m_image_id);
489   }
490
491   using klass = RemoveRequest<I>;
492   librados::AioCompletion *rados_completion =
493     create_rados_callback<klass, &klass::handle_remove_header_v2>(this);
494   int r = m_ioctx.aio_remove(m_header_oid, rados_completion);
495   assert(r == 0);
496   rados_completion->release();
497 }
498
499 template<typename I>
500 void RemoveRequest<I>::handle_remove_header_v2(int r) {
501   ldout(m_cct, 20) << "r=" << r << dendl;
502
503   if (r < 0 && r != -ENOENT) {
504     lderr(m_cct) << "error removing header: " << cpp_strerror(r) << dendl;
505     finish(r);
506     return;
507   }
508
509   send_journal_remove();
510 }
511
512 template<typename I>
513 void RemoveRequest<I>::send_journal_remove() {
514   ldout(m_cct, 20) << dendl;
515
516   using klass = RemoveRequest<I>;
517   Context *ctx = create_context_callback<
518     klass, &klass::handle_journal_remove>(this);
519
520   journal::RemoveRequest<I> *req = journal::RemoveRequest<I>::create(
521     m_ioctx, m_image_id, Journal<>::IMAGE_CLIENT_ID, m_op_work_queue, ctx);
522   req->send();
523 }
524
525 template<typename I>
526 void RemoveRequest<I>::handle_journal_remove(int r) {
527   ldout(m_cct, 20) << "r=" << r << dendl;
528
529   if (r < 0 && r != -ENOENT) {
530     lderr(m_cct) << "failed to remove image journal: " << cpp_strerror(r)
531                  << dendl;
532     finish(r);
533     return;
534   } else {
535     r = 0;
536   }
537
538   send_object_map_remove();
539 }
540
541 template<typename I>
542 void RemoveRequest<I>::send_object_map_remove() {
543   ldout(m_cct, 20) << dendl;
544
545   using klass = RemoveRequest<I>;
546   librados::AioCompletion *rados_completion =
547     create_rados_callback<klass, &klass::handle_object_map_remove>(this);
548
549   int r = ObjectMap<>::aio_remove(m_ioctx,
550                                   m_image_id,
551                                   rados_completion);
552   assert(r == 0);
553   rados_completion->release();
554 }
555
556 template<typename I>
557 void RemoveRequest<I>::handle_object_map_remove(int r) {
558   ldout(m_cct, 20) << "r=" << r << dendl;
559
560   if (r < 0 && r != -ENOENT) {
561     lderr(m_cct) << "failed to remove image journal: " << cpp_strerror(r)
562                  << dendl;
563     finish(r);
564     return;
565   } else {
566     r = 0;
567   }
568
569   mirror_image_remove();
570 }
571
572 template<typename I>
573 void RemoveRequest<I>::mirror_image_remove() {
574   ldout(m_cct, 20) << dendl;
575
576   librados::ObjectWriteOperation op;
577   cls_client::mirror_image_remove(&op, m_image_id);
578
579   using klass = RemoveRequest<I>;
580   librados::AioCompletion *rados_completion =
581     create_rados_callback<klass, &klass::handle_mirror_image_remove>(this);
582   int r = m_ioctx.aio_operate(RBD_MIRRORING, rados_completion, &op);
583   assert(r == 0);
584   rados_completion->release();
585 }
586
587 template<typename I>
588 void RemoveRequest<I>::handle_mirror_image_remove(int r) {
589   ldout(m_cct, 20) << "r=" << r << dendl;
590
591   if (r < 0 && r != -ENOENT && r != -EOPNOTSUPP) {
592     lderr(m_cct) << "failed to remove mirror image state: "
593                  << cpp_strerror(r) << dendl;
594     finish(r);
595     return;
596   }
597
598   if (m_from_trash_remove) {
599     // both the id object and the directory entry have been removed in
600     // a previous call to trash_move.
601     finish(0);
602     return;
603   }
604
605   remove_id_object();
606 }
607
608 template<typename I>
609 void RemoveRequest<I>::remove_image() {
610   ldout(m_cct, 20) << dendl;
611
612   if (m_old_format || m_unknown_format) {
613     remove_v1_image();
614   } else {
615     remove_v2_image();
616   }
617 }
618
619 template<typename I>
620 void RemoveRequest<I>::remove_v1_image() {
621   ldout(m_cct, 20) << dendl;
622
623   Context *ctx = new FunctionContext([this] (int r) {
624       r = tmap_rm(m_ioctx, m_image_name);
625       handle_remove_v1_image(r);
626     });
627
628   m_op_work_queue->queue(ctx, 0);
629 }
630
631 template<typename I>
632 void RemoveRequest<I>::handle_remove_v1_image(int r) {
633   ldout(m_cct, 20) << "r=" << r << dendl;
634
635   m_old_format = (r == 0);
636   if (r == 0 || (r < 0 && !m_unknown_format)) {
637     if (r < 0 && r != -ENOENT) {
638       lderr(m_cct) << "error removing image from v1 directory: "
639                    << cpp_strerror(r) << dendl;
640     }
641
642     m_on_finish->complete(r);
643     delete this;
644     return;
645   }
646
647   if (!m_old_format) {
648     remove_v2_image();
649   }
650 }
651
652 template<typename I>
653 void RemoveRequest<I>::remove_v2_image() {
654   ldout(m_cct, 20) << dendl;
655
656   if (m_image_id.empty()) {
657     dir_get_image_id();
658     return;
659   } else if (m_image_name.empty()) {
660     dir_get_image_name();
661     return;
662   }
663
664   remove_header_v2();
665   return;
666 }
667
668 template<typename I>
669 void RemoveRequest<I>::dir_get_image_id() {
670   ldout(m_cct, 20) << dendl;
671
672   librados::ObjectReadOperation op;
673   librbd::cls_client::dir_get_id_start(&op, m_image_name);
674
675   using klass = RemoveRequest<I>;
676   librados::AioCompletion *rados_completion =
677     create_rados_callback<klass, &klass::handle_dir_get_image_id>(this);
678   m_out_bl.clear();
679   int r = m_ioctx.aio_operate(RBD_DIRECTORY, rados_completion, &op, &m_out_bl);
680   assert(r == 0);
681   rados_completion->release();
682 }
683
684 template<typename I>
685 void RemoveRequest<I>::handle_dir_get_image_id(int r) {
686   ldout(m_cct, 20) << "r=" << r << dendl;
687
688   if (r < 0 && r != -ENOENT) {
689     lderr(m_cct) << "error fetching image id: " << cpp_strerror(r)
690                  << dendl;
691     finish(r);
692     return;
693   }
694
695   if (r == 0) {
696     bufferlist::iterator iter = m_out_bl.begin();
697     r = librbd::cls_client::dir_get_id_finish(&iter, &m_image_id);
698     if (r < 0) {
699       finish(r);
700       return;
701     }
702   }
703
704   remove_header_v2();
705 }
706
707 template<typename I>
708 void RemoveRequest<I>::dir_get_image_name() {
709   ldout(m_cct, 20) << dendl;
710
711   librados::ObjectReadOperation op;
712   librbd::cls_client::dir_get_name_start(&op, m_image_id);
713
714   using klass = RemoveRequest<I>;
715   librados::AioCompletion *rados_completion =
716     create_rados_callback<klass, &klass::handle_dir_get_image_name>(this);
717   m_out_bl.clear();
718   int r = m_ioctx.aio_operate(RBD_DIRECTORY, rados_completion, &op, &m_out_bl);
719   assert(r == 0);
720   rados_completion->release();
721 }
722
723 template<typename I>
724 void RemoveRequest<I>::handle_dir_get_image_name(int r) {
725   ldout(m_cct, 20) << "r=" << r << dendl;
726
727   if (r < 0 && r != -ENOENT) {
728     lderr(m_cct) << "error fetching image name: " << cpp_strerror(r)
729                  << dendl;
730     finish(r);
731     return;
732   }
733
734   if (r == 0) {
735     bufferlist::iterator iter = m_out_bl.begin();
736     r = librbd::cls_client::dir_get_name_finish(&iter, &m_image_name);
737     if (r < 0) {
738       finish(r);
739       return;
740     }
741   }
742
743   remove_header_v2();
744 }
745
746 template<typename I>
747 void RemoveRequest<I>::remove_id_object() {
748   ldout(m_cct, 20) << dendl;
749
750   using klass = RemoveRequest<I>;
751   librados::AioCompletion *rados_completion =
752     create_rados_callback<klass, &klass::handle_remove_id_object>(this);
753   int r = m_ioctx.aio_remove(util::id_obj_name(m_image_name), rados_completion);
754   assert(r == 0);
755   rados_completion->release();
756 }
757
758 template<typename I>
759 void RemoveRequest<I>::handle_remove_id_object(int r) {
760   ldout(m_cct, 20) << "r=" << r << dendl;
761
762   if (r < 0 && r != -ENOENT) {
763     lderr(m_cct) << "error removing id object: " << cpp_strerror(r)
764                  << dendl;
765     finish(r);
766     return;
767   }
768
769   dir_remove_image();
770 }
771
772 template<typename I>
773 void RemoveRequest<I>::dir_remove_image() {
774   ldout(m_cct, 20) << dendl;
775
776   librados::ObjectWriteOperation op;
777   librbd::cls_client::dir_remove_image(&op, m_image_name, m_image_id);
778
779   using klass = RemoveRequest<I>;
780   librados::AioCompletion *rados_completion =
781     create_rados_callback<klass, &klass::handle_dir_remove_image>(this);
782   int r = m_ioctx.aio_operate(RBD_DIRECTORY, rados_completion, &op);
783   assert(r == 0);
784   rados_completion->release();
785 }
786
787 template<typename I>
788 void RemoveRequest<I>::handle_dir_remove_image(int r) {
789   ldout(m_cct, 20) << "r=" << r << dendl;
790
791   if (r < 0 && r != -ENOENT) {
792     lderr(m_cct) << "error removing image from v2 directory: "
793                  << cpp_strerror(r) << dendl;
794   }
795
796   finish(r);
797 }
798
799 template<typename I>
800 void RemoveRequest<I>::finish(int r) {
801   ldout(m_cct, 20) << "r=" << r << dendl;
802
803   m_on_finish->complete(r);
804   delete this;
805 }
806
807 } // namespace image
808 } // namespace librbd
809
810 template class librbd::image::RemoveRequest<librbd::ImageCtx>;