1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "tools/rbd/ArgumentTypes.h"
5 #include "tools/rbd/Shell.h"
6 #include "tools/rbd/Utils.h"
7 #include "common/errno.h"
8 #include "include/stringify.h"
10 #include <boost/program_options.hpp>
18 bool is_auto_delete_snapshot(librbd::Image
* image
,
19 const librbd::snap_info_t
&snap_info
) {
20 librbd::snap_namespace_type_t namespace_type
;
21 int r
= image
->snap_get_namespace_type(snap_info
.id
, &namespace_type
);
26 switch (namespace_type
) {
27 case RBD_SNAP_NAMESPACE_TYPE_TRASH
:
34 } // anonymous namespace
36 namespace at
= argument_types
;
37 namespace po
= boost::program_options
;
39 static int do_delete(librbd::RBD
&rbd
, librados::IoCtx
& io_ctx
,
40 const char *imgname
, bool no_progress
)
42 utils::ProgressContext
pc("Removing image", no_progress
);
43 int r
= rbd
.remove_with_progress(io_ctx
, imgname
, pc
);
52 void get_arguments(po::options_description
*positional
,
53 po::options_description
*options
) {
54 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
55 at::add_no_progress_option(options
);
58 int execute(const po::variables_map
&vm
,
59 const std::vector
<std::string
> &ceph_global_init_args
) {
61 std::string pool_name
;
62 std::string namespace_name
;
63 std::string image_name
;
64 std::string snap_name
;
65 int r
= utils::get_pool_image_snapshot_names(
66 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &namespace_name
,
67 &image_name
, &snap_name
, true, utils::SNAPSHOT_PRESENCE_NONE
,
68 utils::SPEC_VALIDATION_NONE
);
73 librados::Rados rados
;
74 librados::IoCtx io_ctx
;
75 r
= utils::init(pool_name
, namespace_name
, &rados
, &io_ctx
);
80 io_ctx
.set_pool_full_try();
83 r
= do_delete(rbd
, io_ctx
, image_name
.c_str(),
84 vm
[at::NO_PROGRESS
].as
<bool>());
86 if (r
== -ENOTEMPTY
) {
88 std::vector
<librbd::snap_info_t
> snaps
;
89 int image_r
= utils::open_image(io_ctx
, image_name
, true, &image
);
91 image_r
= image
.snap_list(snaps
);
94 snaps
.erase(std::remove_if(snaps
.begin(), snaps
.end(),
95 [&image
](const librbd::snap_info_t
& snap
) {
96 return is_auto_delete_snapshot(&image
,
102 if (!snaps
.empty()) {
103 std::cerr
<< "rbd: image has snapshots - these must be deleted"
104 << " with 'rbd snap purge' before the image can be removed."
107 std::cerr
<< "rbd: image has snapshots with linked clones - these must "
108 << "be deleted or flattened before the image can be removed."
111 } else if (r
== -EBUSY
) {
112 std::cerr
<< "rbd: error: image still has watchers"
114 << "This means the image is still open or the client using "
115 << "it crashed. Try again after closing/unmapping it or "
116 << "waiting 30s for the crashed client to timeout."
118 } else if (r
== -EMLINK
) {
120 int image_r
= utils::open_image(io_ctx
, image_name
, true, &image
);
121 librbd::group_info_t group_info
;
123 image_r
= image
.get_group(&group_info
, sizeof(group_info
));
126 std::string pool_name
= "";
127 librados::Rados
rados(io_ctx
);
128 librados::IoCtx pool_io_ctx
;
129 image_r
= rados
.ioctx_create2(group_info
.pool
, pool_io_ctx
);
131 pool_name
= "<missing group pool " + stringify(group_info
.pool
) + ">";
133 pool_name
= pool_io_ctx
.get_pool_name();
135 std::cerr
<< "rbd: error: image belongs to a group "
137 if (!io_ctx
.get_namespace().empty()) {
138 std::cerr
<< io_ctx
.get_namespace() << "/";
140 std::cerr
<< group_info
.name
;
142 std::cerr
<< "rbd: error: image belongs to a group";
144 std::cerr
<< std::endl
145 << "Remove the image from the group and try again."
149 std::cerr
<< "rbd: delete error: " << cpp_strerror(r
) << std::endl
;
156 Shell::Action
action(
157 {"remove"}, {"rm"}, "Delete an image.", "", &get_arguments
, &execute
);
159 } // namespace remove
160 } // namespace action