1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2017 SUSE LINUX GmbH
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.
15 #include "tools/rbd/ArgumentTypes.h"
16 #include "tools/rbd/Shell.h"
17 #include "tools/rbd/Utils.h"
18 #include "common/errno.h"
19 #include "include/stringify.h"
20 #include "common/Formatter.h"
21 #include "common/TextTable.h"
22 #include "common/Clock.h"
25 #include <boost/program_options.hpp>
31 namespace at
= argument_types
;
32 namespace po
= boost::program_options
;
35 void get_move_arguments(po::options_description
*positional
,
36 po::options_description
*options
) {
37 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
38 options
->add_options()
39 (at::DELAY
.c_str(), po::value
<uint64_t>(),
40 "time delay in seconds until effectively remove the image");
43 int execute_move(const po::variables_map
&vm
) {
45 std::string pool_name
;
46 std::string image_name
;
47 std::string snap_name
;
49 int r
= utils::get_pool_image_snapshot_names(
50 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &image_name
,
51 &snap_name
, utils::SNAPSHOT_PRESENCE_NONE
, utils::SPEC_VALIDATION_NONE
);
56 librados::Rados rados
;
57 librados::IoCtx io_ctx
;
58 r
= utils::init(pool_name
, &rados
, &io_ctx
);
64 if (vm
.find(at::DELAY
) != vm
.end()) {
65 delay
= vm
[at::DELAY
].as
<uint64_t>();
69 r
= rbd
.trash_move(io_ctx
, image_name
.c_str(), delay
);
71 std::cerr
<< "rbd: deferred delete error: " << cpp_strerror(r
)
79 void get_remove_arguments(po::options_description
*positional
,
80 po::options_description
*options
) {
81 positional
->add_options()
82 (at::IMAGE_ID
.c_str(), "image id\n(example: [<pool-name>/]<image-id>)");
83 at::add_pool_option(options
, at::ARGUMENT_MODIFIER_NONE
);
84 at::add_image_id_option(options
);
86 at::add_no_progress_option(options
);
87 options
->add_options()
88 ("force", po::bool_switch(), "force remove of non-expired delayed images");
91 int execute_remove(const po::variables_map
&vm
) {
93 std::string pool_name
;
95 int r
= utils::get_pool_image_id(vm
, &arg_index
, &pool_name
, &image_id
);
100 librados::Rados rados
;
101 librados::IoCtx io_ctx
;
102 r
= utils::init(pool_name
, &rados
, &io_ctx
);
109 utils::ProgressContext
pc("Removing image", vm
[at::NO_PROGRESS
].as
<bool>());
110 r
= rbd
.trash_remove_with_progress(io_ctx
, image_id
.c_str(),
111 vm
["force"].as
<bool>(), pc
);
113 if (r
== -ENOTEMPTY
) {
114 std::cerr
<< "rbd: image has snapshots - these must be deleted"
115 << " with 'rbd snap purge' before the image can be removed."
117 } else if (r
== -EBUSY
) {
118 std::cerr
<< "rbd: error: image still has watchers"
120 << "This means the image is still open or the client using "
121 << "it crashed. Try again after closing/unmapping it or "
122 << "waiting 30s for the crashed client to timeout."
124 } else if (r
== -EMLINK
) {
125 std::cerr
<< std::endl
126 << "Remove the image from the consistency group and try again."
128 } else if (r
== -EPERM
) {
129 std::cerr
<< std::endl
130 << "Deferment time has not expired, please use --force if you "
131 << "really want to remove the image"
134 std::cerr
<< "rbd: remove error: " << cpp_strerror(r
) << std::endl
;
145 std::string
delete_status(time_t deferment_end_time
) {
146 time_t now
= ceph_clock_gettime();
148 std::string time_str
= ctime(&deferment_end_time
);
149 time_str
= time_str
.substr(0, time_str
.length() - 1);
151 std::stringstream ss
;
152 if (now
< deferment_end_time
) {
153 ss
<< "protected until " << time_str
;
159 int do_list(librbd::RBD
&rbd
, librados::IoCtx
& io_ctx
, bool long_flag
,
160 bool all_flag
, Formatter
*f
) {
161 std::vector
<librbd::trash_image_info_t
> trash_entries
;
162 int r
= rbd
.trash_list(io_ctx
, trash_entries
);
169 f
->open_array_section("trash");
171 for (const auto& entry
: trash_entries
) {
173 entry
.source
== RBD_TRASH_IMAGE_SOURCE_MIRRORING
) {
177 f
->dump_string("id", entry
.id
);
178 f
->dump_string("name", entry
.name
);
180 std::cout
<< entry
.id
<< " " << entry
.name
<< std::endl
;
193 f
->open_array_section("trash");
195 tbl
.define_column("ID", TextTable::LEFT
, TextTable::LEFT
);
196 tbl
.define_column("NAME", TextTable::LEFT
, TextTable::LEFT
);
197 tbl
.define_column("SOURCE", TextTable::LEFT
, TextTable::LEFT
);
198 tbl
.define_column("DELETED_AT", TextTable::LEFT
, TextTable::LEFT
);
199 tbl
.define_column("STATUS", TextTable::LEFT
, TextTable::LEFT
);
202 for (const auto& entry
: trash_entries
) {
204 entry
.source
== RBD_TRASH_IMAGE_SOURCE_MIRRORING
) {
209 r
= rbd
.open_by_id_read_only(io_ctx
, im
, entry
.id
.c_str(), NULL
);
210 // image might disappear between rbd.list() and rbd.open(); ignore
211 // that, warn about other possible errors (EPERM, say, for opening
212 // an old-format image, because you need execute permission for the
216 std::cerr
<< "rbd: error opening " << entry
.id
<< ": "
217 << cpp_strerror(r
) << std::endl
;
219 // in any event, continue to next image
223 std::string del_source
;
224 switch (entry
.source
) {
225 case RBD_TRASH_IMAGE_SOURCE_USER
:
228 case RBD_TRASH_IMAGE_SOURCE_MIRRORING
:
229 del_source
= "MIRRORING";
233 std::string time_str
= ctime(&entry
.deletion_time
);
234 time_str
= time_str
.substr(0, time_str
.length() - 1);
237 f
->open_object_section("image");
238 f
->dump_string("id", entry
.id
);
239 f
->dump_string("name", entry
.name
);
240 f
->dump_string("source", del_source
);
241 f
->dump_string("deleted_at", time_str
);
242 f
->dump_string("status",
243 delete_status(entry
.deferment_end_time
));
250 << delete_status(entry
.deferment_end_time
)
251 << TextTable::endrow
;
258 } else if (!trash_entries
.empty()) {
262 return r
< 0 ? r
: 0;
265 void get_list_arguments(po::options_description
*positional
,
266 po::options_description
*options
) {
267 at::add_pool_options(positional
, options
);
268 options
->add_options()
269 ("all,a", po::bool_switch(), "list images from all sources");
270 options
->add_options()
271 ("long,l", po::bool_switch(), "long listing format");
272 at::add_format_options(options
);
275 int execute_list(const po::variables_map
&vm
) {
276 size_t arg_index
= 0;
277 std::string pool_name
= utils::get_pool_name(vm
, &arg_index
);
279 at::Format::Formatter formatter
;
280 int r
= utils::get_formatter(vm
, &formatter
);
285 librados::Rados rados
;
286 librados::IoCtx io_ctx
;
287 r
= utils::init(pool_name
, &rados
, &io_ctx
);
293 r
= do_list(rbd
, io_ctx
, vm
["long"].as
<bool>(), vm
["all"].as
<bool>(),
296 std::cerr
<< "rbd: trash list: " << cpp_strerror(r
) << std::endl
;
303 void get_restore_arguments(po::options_description
*positional
,
304 po::options_description
*options
) {
305 positional
->add_options()
306 (at::IMAGE_ID
.c_str(), "image id\n(example: [<pool-name>/]<image-id>)");
307 at::add_pool_option(options
, at::ARGUMENT_MODIFIER_NONE
);
308 at::add_image_id_option(options
);
309 at::add_image_option(options
, at::ARGUMENT_MODIFIER_NONE
, "");
312 int execute_restore(const po::variables_map
&vm
) {
313 size_t arg_index
= 0;
314 std::string pool_name
;
315 std::string image_id
;
316 int r
= utils::get_pool_image_id(vm
, &arg_index
, &pool_name
, &image_id
);
321 librados::Rados rados
;
322 librados::IoCtx io_ctx
;
323 r
= utils::init(pool_name
, &rados
, &io_ctx
);
329 if (vm
.find(at::IMAGE_NAME
) != vm
.end()) {
330 name
= vm
[at::IMAGE_NAME
].as
<std::string
>();
334 r
= rbd
.trash_restore(io_ctx
, image_id
.c_str(), name
.c_str());
337 std::cerr
<< "rbd: error: image does not exist in trash"
339 } else if (r
== -EEXIST
) {
340 std::cerr
<< "rbd: error: an image with the same name already exists, "
341 << "try again with with a different name"
344 std::cerr
<< "rbd: restore error: " << cpp_strerror(r
) << std::endl
;
353 Shell::Action
action_move(
354 {"trash", "move"}, {"trash", "mv"}, "Move an image to the trash.", "",
355 &get_move_arguments
, &execute_move
);
357 Shell::Action
action_remove(
358 {"trash", "remove"}, {"trash", "rm"}, "Remove an image from trash.", "",
359 &get_remove_arguments
, &execute_remove
);
361 Shell::SwitchArguments
switched_arguments({"long", "l"});
362 Shell::Action
action_list(
363 {"trash", "list"}, {"trash", "ls"}, "List trash images.", "",
364 &get_list_arguments
, &execute_list
);
366 Shell::Action
action_restore(
367 {"trash", "restore"}, {}, "Restore an image from trash.", "",
368 &get_restore_arguments
, &execute_restore
);
371 } // namespace action