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 "include/types.h"
8 #include "include/stringify.h"
9 #include "common/errno.h"
10 #include "common/Formatter.h"
11 #include "common/TextTable.h"
13 #include <boost/program_options.hpp>
19 namespace at
= argument_types
;
20 namespace po
= boost::program_options
;
22 int do_list_snaps(librbd::Image
& image
, Formatter
*f
)
24 std::vector
<librbd::snap_info_t
> snaps
;
28 r
= image
.snap_list(snaps
);
33 f
->open_array_section("snapshots");
35 t
.define_column("SNAPID", TextTable::RIGHT
, TextTable::RIGHT
);
36 t
.define_column("NAME", TextTable::LEFT
, TextTable::LEFT
);
37 t
.define_column("SIZE", TextTable::RIGHT
, TextTable::RIGHT
);
38 t
.define_column("TIMESTAMP", TextTable::LEFT
, TextTable::LEFT
);
41 for (std::vector
<librbd::snap_info_t
>::iterator s
= snaps
.begin();
42 s
!= snaps
.end(); ++s
) {
43 struct timespec timestamp
;
44 image
.snap_get_timestamp(s
->id
, ×tamp
);
46 if(timestamp
.tv_sec
!= 0) {
47 time_t tt
= timestamp
.tv_sec
;
49 tt_str
= tt_str
.substr(0, tt_str
.length() - 1);
53 f
->open_object_section("snapshot");
54 f
->dump_unsigned("id", s
->id
);
55 f
->dump_string("name", s
->name
);
56 f
->dump_unsigned("size", s
->size
);
57 f
->dump_string("timestamp", tt_str
);
60 t
<< s
->id
<< s
->name
<< stringify(prettybyte_t(s
->size
)) << tt_str
68 } else if (snaps
.size()) {
75 int do_add_snap(librbd::Image
& image
, const char *snapname
)
77 int r
= image
.snap_create(snapname
);
84 int do_remove_snap(librbd::Image
& image
, const char *snapname
, bool force
,
87 uint32_t flags
= force
? RBD_SNAP_REMOVE_FORCE
: 0;
89 utils::ProgressContext
pc("Removing snap", no_progress
);
91 r
= image
.snap_remove2(snapname
, flags
, pc
);
101 int do_rollback_snap(librbd::Image
& image
, const char *snapname
,
104 utils::ProgressContext
pc("Rolling back to snapshot", no_progress
);
105 int r
= image
.snap_rollback_with_progress(snapname
, pc
);
114 int do_purge_snaps(librbd::Image
& image
, bool no_progress
)
116 utils::ProgressContext
pc("Removing all snapshots", no_progress
);
117 std::vector
<librbd::snap_info_t
> snaps
;
118 bool is_protected
= false;
119 int r
= image
.snap_list(snaps
);
123 } else if (0 == snaps
.size()) {
126 for (size_t i
= 0; i
< snaps
.size(); ++i
) {
127 r
= image
.snap_is_protected(snaps
[i
].name
.c_str(), &is_protected
);
131 } else if (is_protected
== true) {
133 std::cerr
<< "\r" << "rbd: snapshot '" << snaps
[i
].name
.c_str()
134 << "' is protected from removal." << std::endl
;
138 for (size_t i
= 0; i
< snaps
.size(); ++i
) {
139 r
= image
.snap_remove(snaps
[i
].name
.c_str());
144 pc
.update_progress(i
+ 1, snaps
.size());
152 int do_protect_snap(librbd::Image
& image
, const char *snapname
)
154 int r
= image
.snap_protect(snapname
);
161 int do_unprotect_snap(librbd::Image
& image
, const char *snapname
)
163 int r
= image
.snap_unprotect(snapname
);
170 int do_set_limit(librbd::Image
& image
, uint64_t limit
)
172 return image
.snap_set_limit(limit
);
175 int do_clear_limit(librbd::Image
& image
)
177 return image
.snap_set_limit(UINT64_MAX
);
180 void get_list_arguments(po::options_description
*positional
,
181 po::options_description
*options
) {
182 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
183 at::add_image_id_option(options
);
184 at::add_format_options(options
);
187 int execute_list(const po::variables_map
&vm
) {
188 size_t arg_index
= 0;
189 std::string pool_name
;
190 std::string image_name
;
191 std::string snap_name
;
192 std::string image_id
;
194 if (vm
.count(at::IMAGE_ID
)) {
195 image_id
= vm
[at::IMAGE_ID
].as
<std::string
>();
198 bool has_image_spec
= utils::check_if_image_spec_present(
199 vm
, at::ARGUMENT_MODIFIER_NONE
, arg_index
);
201 if (!image_id
.empty() && has_image_spec
) {
202 std::cerr
<< "rbd: trying to access image using both name and id. "
208 if (image_id
.empty()) {
209 r
= utils::get_pool_image_snapshot_names(vm
, at::ARGUMENT_MODIFIER_NONE
,
210 &arg_index
, &pool_name
,
211 &image_name
, &snap_name
,
212 utils::SNAPSHOT_PRESENCE_NONE
,
213 utils::SPEC_VALIDATION_NONE
);
215 r
= utils::get_pool_snapshot_names(vm
, at::ARGUMENT_MODIFIER_NONE
,
216 &arg_index
, &pool_name
, &snap_name
,
217 utils::SNAPSHOT_PRESENCE_NONE
,
218 utils::SPEC_VALIDATION_NONE
);
224 at::Format::Formatter formatter
;
225 r
= utils::get_formatter(vm
, &formatter
);
230 librados::Rados rados
;
231 librados::IoCtx io_ctx
;
233 r
= utils::init_and_open_image(pool_name
, image_name
, image_id
, "", true,
234 &rados
, &io_ctx
, &image
);
239 r
= do_list_snaps(image
, formatter
.get());
241 cerr
<< "rbd: failed to list snapshots: " << cpp_strerror(r
)
248 void get_create_arguments(po::options_description
*positional
,
249 po::options_description
*options
) {
250 at::add_snap_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
253 int execute_create(const po::variables_map
&vm
) {
254 size_t arg_index
= 0;
255 std::string pool_name
;
256 std::string image_name
;
257 std::string snap_name
;
258 int r
= utils::get_pool_image_snapshot_names(
259 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &image_name
,
260 &snap_name
, utils::SNAPSHOT_PRESENCE_REQUIRED
, utils::SPEC_VALIDATION_SNAP
);
265 librados::Rados rados
;
266 librados::IoCtx io_ctx
;
268 r
= utils::init_and_open_image(pool_name
, image_name
, "", "", false, &rados
,
274 r
= do_add_snap(image
, snap_name
.c_str());
276 cerr
<< "rbd: failed to create snapshot: " << cpp_strerror(r
)
283 void get_remove_arguments(po::options_description
*positional
,
284 po::options_description
*options
) {
285 at::add_snap_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
286 at::add_no_progress_option(options
);
287 at::add_image_id_option(options
);
289 options
->add_options()
290 ("force", po::bool_switch(), "flatten children and unprotect snapshot if needed.");
293 int execute_remove(const po::variables_map
&vm
) {
294 size_t arg_index
= 0;
295 std::string pool_name
;
296 std::string image_name
;
297 std::string snap_name
;
298 std::string image_id
;
299 bool force
= vm
["force"].as
<bool>();
301 if (vm
.count(at::IMAGE_ID
)) {
302 image_id
= vm
[at::IMAGE_ID
].as
<std::string
>();
305 bool has_image_spec
= utils::check_if_image_spec_present(
306 vm
, at::ARGUMENT_MODIFIER_NONE
, arg_index
);
308 if (!image_id
.empty() && has_image_spec
) {
309 std::cerr
<< "rbd: trying to access image using both name and id. "
315 if (image_id
.empty()) {
316 r
= utils::get_pool_image_snapshot_names(vm
, at::ARGUMENT_MODIFIER_NONE
,
317 &arg_index
, &pool_name
,
318 &image_name
, &snap_name
,
319 utils::SNAPSHOT_PRESENCE_REQUIRED
,
320 utils::SPEC_VALIDATION_NONE
);
322 r
= utils::get_pool_snapshot_names(vm
, at::ARGUMENT_MODIFIER_NONE
,
323 &arg_index
, &pool_name
, &snap_name
,
324 utils::SNAPSHOT_PRESENCE_REQUIRED
,
325 utils::SPEC_VALIDATION_NONE
);
331 librados::Rados rados
;
332 librados::IoCtx io_ctx
;
334 r
= utils::init(pool_name
, &rados
, &io_ctx
);
339 io_ctx
.set_osdmap_full_try();
340 if (image_id
.empty()) {
341 r
= utils::open_image(io_ctx
, image_name
, false, &image
);
343 r
= utils::open_image_by_id(io_ctx
, image_id
, false, &image
);
349 r
= do_remove_snap(image
, snap_name
.c_str(), force
, vm
[at::NO_PROGRESS
].as
<bool>());
352 std::cerr
<< "rbd: snapshot '" << snap_name
<< "' "
353 << "is protected from removal." << std::endl
;
355 std::cerr
<< "rbd: failed to remove snapshot: " << cpp_strerror(r
)
363 void get_purge_arguments(po::options_description
*positional
,
364 po::options_description
*options
) {
365 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
366 at::add_image_id_option(options
);
367 at::add_no_progress_option(options
);
370 int execute_purge(const po::variables_map
&vm
) {
371 size_t arg_index
= 0;
372 std::string pool_name
;
373 std::string image_name
;
374 std::string snap_name
;
375 std::string image_id
;
377 if (vm
.count(at::IMAGE_ID
)) {
378 image_id
= vm
[at::IMAGE_ID
].as
<std::string
>();
381 bool has_image_spec
= utils::check_if_image_spec_present(
382 vm
, at::ARGUMENT_MODIFIER_NONE
, arg_index
);
384 if (!image_id
.empty() && has_image_spec
) {
385 std::cerr
<< "rbd: trying to access image using both name and id. "
391 if (image_id
.empty()) {
392 r
= utils::get_pool_image_snapshot_names(vm
, at::ARGUMENT_MODIFIER_NONE
,
393 &arg_index
, &pool_name
,
394 &image_name
, &snap_name
,
395 utils::SNAPSHOT_PRESENCE_NONE
,
396 utils::SPEC_VALIDATION_NONE
);
398 r
= utils::get_pool_snapshot_names(vm
, at::ARGUMENT_MODIFIER_NONE
,
399 &arg_index
, &pool_name
, &snap_name
,
400 utils::SNAPSHOT_PRESENCE_NONE
,
401 utils::SPEC_VALIDATION_NONE
);
407 librados::Rados rados
;
408 librados::IoCtx io_ctx
;
410 r
= utils::init(pool_name
, &rados
, &io_ctx
);
415 io_ctx
.set_osdmap_full_try();
416 if (image_id
.empty()) {
417 r
= utils::open_image(io_ctx
, image_name
, false, &image
);
419 r
= utils::open_image_by_id(io_ctx
, image_id
, false, &image
);
425 r
= do_purge_snaps(image
, vm
[at::NO_PROGRESS
].as
<bool>());
428 std::cerr
<< "rbd: removing snaps failed: " << cpp_strerror(r
)
436 void get_rollback_arguments(po::options_description
*positional
,
437 po::options_description
*options
) {
438 at::add_snap_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
439 at::add_no_progress_option(options
);
442 int execute_rollback(const po::variables_map
&vm
) {
443 size_t arg_index
= 0;
444 std::string pool_name
;
445 std::string image_name
;
446 std::string snap_name
;
447 int r
= utils::get_pool_image_snapshot_names(
448 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &image_name
,
449 &snap_name
, utils::SNAPSHOT_PRESENCE_REQUIRED
, utils::SPEC_VALIDATION_NONE
);
454 librados::Rados rados
;
455 librados::IoCtx io_ctx
;
457 r
= utils::init_and_open_image(pool_name
, image_name
, "", "", false, &rados
,
463 r
= do_rollback_snap(image
, snap_name
.c_str(),
464 vm
[at::NO_PROGRESS
].as
<bool>());
466 std::cerr
<< "rbd: rollback failed: " << cpp_strerror(r
) << std::endl
;
472 void get_protect_arguments(po::options_description
*positional
,
473 po::options_description
*options
) {
474 at::add_snap_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
477 int execute_protect(const po::variables_map
&vm
) {
478 size_t arg_index
= 0;
479 std::string pool_name
;
480 std::string image_name
;
481 std::string snap_name
;
482 int r
= utils::get_pool_image_snapshot_names(
483 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &image_name
,
484 &snap_name
, utils::SNAPSHOT_PRESENCE_REQUIRED
, utils::SPEC_VALIDATION_NONE
);
489 librados::Rados rados
;
490 librados::IoCtx io_ctx
;
492 r
= utils::init_and_open_image(pool_name
, image_name
, "", "", false, &rados
,
498 bool is_protected
= false;
499 r
= image
.snap_is_protected(snap_name
.c_str(), &is_protected
);
501 std::cerr
<< "rbd: protecting snap failed: " << cpp_strerror(r
)
504 } else if (is_protected
) {
505 std::cerr
<< "rbd: snap is already protected" << std::endl
;
509 r
= do_protect_snap(image
, snap_name
.c_str());
511 std::cerr
<< "rbd: protecting snap failed: " << cpp_strerror(r
)
518 void get_unprotect_arguments(po::options_description
*positional
,
519 po::options_description
*options
) {
520 at::add_snap_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
521 at::add_image_id_option(options
);
524 int execute_unprotect(const po::variables_map
&vm
) {
525 size_t arg_index
= 0;
526 std::string pool_name
;
527 std::string image_name
;
528 std::string snap_name
;
529 std::string image_id
;
531 if (vm
.count(at::IMAGE_ID
)) {
532 image_id
= vm
[at::IMAGE_ID
].as
<std::string
>();
535 bool has_image_spec
= utils::check_if_image_spec_present(
536 vm
, at::ARGUMENT_MODIFIER_NONE
, arg_index
);
538 if (!image_id
.empty() && has_image_spec
) {
539 std::cerr
<< "rbd: trying to access image using both name and id. "
545 if (image_id
.empty()) {
546 r
= utils::get_pool_image_snapshot_names(vm
, at::ARGUMENT_MODIFIER_NONE
,
547 &arg_index
, &pool_name
,
548 &image_name
, &snap_name
,
549 utils::SNAPSHOT_PRESENCE_REQUIRED
,
550 utils::SPEC_VALIDATION_NONE
);
552 r
= utils::get_pool_snapshot_names(vm
, at::ARGUMENT_MODIFIER_NONE
,
553 &arg_index
, &pool_name
, &snap_name
,
554 utils::SNAPSHOT_PRESENCE_REQUIRED
,
555 utils::SPEC_VALIDATION_NONE
);
561 librados::Rados rados
;
562 librados::IoCtx io_ctx
;
564 r
= utils::init(pool_name
, &rados
, &io_ctx
);
569 io_ctx
.set_osdmap_full_try();
570 if (image_id
.empty()) {
571 r
= utils::open_image(io_ctx
, image_name
, false, &image
);
573 r
= utils::open_image_by_id(io_ctx
, image_id
, false, &image
);
579 bool is_protected
= false;
580 r
= image
.snap_is_protected(snap_name
.c_str(), &is_protected
);
582 std::cerr
<< "rbd: unprotecting snap failed: " << cpp_strerror(r
)
585 } else if (!is_protected
) {
586 std::cerr
<< "rbd: snap is already unprotected" << std::endl
;
590 r
= do_unprotect_snap(image
, snap_name
.c_str());
592 std::cerr
<< "rbd: unprotecting snap failed: " << cpp_strerror(r
)
599 void get_set_limit_arguments(po::options_description
*pos
,
600 po::options_description
*opt
) {
601 at::add_image_spec_options(pos
, opt
, at::ARGUMENT_MODIFIER_NONE
);
602 at::add_limit_option(opt
);
605 int execute_set_limit(const po::variables_map
&vm
) {
606 size_t arg_index
= 0;
607 std::string pool_name
;
608 std::string image_name
;
609 std::string snap_name
;
612 int r
= utils::get_pool_image_snapshot_names(
613 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &image_name
,
614 &snap_name
, utils::SNAPSHOT_PRESENCE_NONE
, utils::SPEC_VALIDATION_NONE
);
619 if (vm
.count(at::LIMIT
)) {
620 limit
= vm
[at::LIMIT
].as
<uint64_t>();
622 std::cerr
<< "rbd: must specify --limit <num>" << std::endl
;
626 librados::Rados rados
;
627 librados::IoCtx io_ctx
;
629 r
= utils::init_and_open_image(pool_name
, image_name
, "", "", false, &rados
,
635 r
= do_set_limit(image
, limit
);
637 std::cerr
<< "rbd: setting snapshot limit failed: " << cpp_strerror(r
)
644 void get_clear_limit_arguments(po::options_description
*pos
,
645 po::options_description
*opt
) {
646 at::add_image_spec_options(pos
, opt
, at::ARGUMENT_MODIFIER_NONE
);
649 int execute_clear_limit(const po::variables_map
&vm
) {
650 size_t arg_index
= 0;
651 std::string pool_name
;
652 std::string image_name
;
653 std::string snap_name
;
655 int r
= utils::get_pool_image_snapshot_names(
656 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &image_name
,
657 &snap_name
, utils::SNAPSHOT_PRESENCE_NONE
, utils::SPEC_VALIDATION_NONE
);
662 librados::Rados rados
;
663 librados::IoCtx io_ctx
;
665 r
= utils::init_and_open_image(pool_name
, image_name
, "", "", false, &rados
,
671 r
= do_clear_limit(image
);
673 std::cerr
<< "rbd: clearing snapshot limit failed: " << cpp_strerror(r
)
680 void get_rename_arguments(po::options_description
*positional
,
681 po::options_description
*options
) {
682 at::add_snap_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_SOURCE
);
683 at::add_snap_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_DEST
);
686 int execute_rename(const po::variables_map
&vm
) {
687 size_t arg_index
= 0;
688 std::string pool_name
;
689 std::string image_name
;
690 std::string src_snap_name
;
691 int r
= utils::get_pool_image_snapshot_names(
692 vm
, at::ARGUMENT_MODIFIER_SOURCE
, &arg_index
, &pool_name
, &image_name
,
693 &src_snap_name
, utils::SNAPSHOT_PRESENCE_REQUIRED
,
694 utils::SPEC_VALIDATION_NONE
);
699 std::string
dest_pool_name(pool_name
);
700 std::string dest_image_name
;
701 std::string dest_snap_name
;
702 r
= utils::get_pool_image_snapshot_names(
703 vm
, at::ARGUMENT_MODIFIER_DEST
, &arg_index
, &dest_pool_name
,
704 &dest_image_name
, &dest_snap_name
, utils::SNAPSHOT_PRESENCE_REQUIRED
,
705 utils::SPEC_VALIDATION_SNAP
);
710 if (pool_name
!= dest_pool_name
) {
711 std::cerr
<< "rbd: source and destination pool must be the same"
714 } else if (image_name
!= dest_image_name
) {
715 std::cerr
<< "rbd: source and destination image name must be the same"
720 librados::Rados rados
;
721 librados::IoCtx io_ctx
;
723 r
= utils::init_and_open_image(pool_name
, image_name
, "", "", false, &rados
,
729 r
= image
.snap_rename(src_snap_name
.c_str(), dest_snap_name
.c_str());
731 std::cerr
<< "rbd: renaming snap failed: " << cpp_strerror(r
)
738 Shell::Action
action_list(
739 {"snap", "list"}, {"snap", "ls"}, "Dump list of image snapshots.", "",
740 &get_list_arguments
, &execute_list
);
741 Shell::Action
action_create(
742 {"snap", "create"}, {"snap", "add"}, "Create a snapshot.", "",
743 &get_create_arguments
, &execute_create
);
744 Shell::Action
action_remove(
745 {"snap", "remove"}, {"snap", "rm"}, "Deletes a snapshot.", "",
746 &get_remove_arguments
, &execute_remove
);
747 Shell::Action
action_purge(
748 {"snap", "purge"}, {}, "Deletes all snapshots.", "",
749 &get_purge_arguments
, &execute_purge
);
750 Shell::Action
action_rollback(
751 {"snap", "rollback"}, {"snap", "revert"}, "Rollback image to snapshot.", "",
752 &get_rollback_arguments
, &execute_rollback
);
753 Shell::Action
action_protect(
754 {"snap", "protect"}, {}, "Prevent a snapshot from being deleted.", "",
755 &get_protect_arguments
, &execute_protect
);
756 Shell::Action
action_unprotect(
757 {"snap", "unprotect"}, {}, "Allow a snapshot to be deleted.", "",
758 &get_unprotect_arguments
, &execute_unprotect
);
759 Shell::Action
action_set_limit(
760 {"snap", "limit", "set"}, {}, "Limit the number of snapshots.", "",
761 &get_set_limit_arguments
, &execute_set_limit
);
762 Shell::Action
action_clear_limit(
763 {"snap", "limit", "clear"}, {}, "Remove snapshot limit.", "",
764 &get_clear_limit_arguments
, &execute_clear_limit
);
765 Shell::Action
action_rename(
766 {"snap", "rename"}, {}, "Rename a snapshot.", "",
767 &get_rename_arguments
, &execute_rename
);
770 } // namespace action