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"}, "Delete a snapshot.", "",
746 &get_remove_arguments, &execute_remove);
747 Shell::Action action_purge(
748 {"snap", "purge"}, {}, "Delete 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