// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "tools/rbd/ArgumentTypes.h" #include "tools/rbd/Shell.h" #include "tools/rbd/Utils.h" #include "include/Context.h" #include "include/stringify.h" #include "include/rbd/librbd.hpp" #include "common/config.h" #include "common/debug.h" #include "common/errno.h" #include "common/Formatter.h" #include "common/TextTable.h" #include "common/Throttle.h" #include "global/global_context.h" #include #include #include #include #include "include/assert.h" #include #define dout_context g_ceph_context #define dout_subsys ceph_subsys_rbd #undef dout_prefix #define dout_prefix *_dout << "rbd::action::MirrorPool: " namespace rbd { namespace action { namespace mirror_pool { namespace at = argument_types; namespace po = boost::program_options; namespace { int validate_mirroring_enabled(librados::IoCtx& io_ctx) { librbd::RBD rbd; rbd_mirror_mode_t mirror_mode; int r = rbd.mirror_mode_get(io_ctx, &mirror_mode); if (r < 0) { std::cerr << "rbd: failed to retrieve mirror mode: " << cpp_strerror(r) << std::endl; return r; } if (mirror_mode == RBD_MIRROR_MODE_DISABLED) { std::cerr << "rbd: mirroring not enabled on the pool" << std::endl; return -EINVAL; } return 0; } int validate_uuid(const std::string &uuid) { boost::regex pattern("^[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}$", boost::regex::icase); boost::smatch match; if (!boost::regex_match(uuid, match, pattern)) { std::cerr << "rbd: invalid uuid '" << uuid << "'" << std::endl; return -EINVAL; } return 0; } void add_uuid_option(po::options_description *positional) { positional->add_options() ("uuid", po::value(), "peer uuid"); } int get_uuid(const po::variables_map &vm, size_t arg_index, std::string *uuid) { *uuid = utils::get_positional_argument(vm, arg_index); if (uuid->empty()) { std::cerr << "rbd: must specify peer uuid" << std::endl; return -EINVAL; } return validate_uuid(*uuid); } int get_remote_cluster_spec(const po::variables_map &vm, const std::string &spec, std::string *remote_client_name, std::string *remote_cluster) { if (vm.count("remote-client-name")) { *remote_client_name = vm["remote-client-name"].as(); } if (vm.count("remote-cluster")) { *remote_cluster = vm["remote-cluster"].as(); } if (!spec.empty()) { boost::regex pattern("^(?:(client\\.[^@]+)@)?([^/@]+)$"); boost::smatch match; if (!boost::regex_match(spec, match, pattern)) { std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl; return -EINVAL; } if (match[1].matched) { *remote_client_name = match[1]; } *remote_cluster = match[2]; } if (remote_cluster->empty()) { std::cerr << "rbd: remote cluster was not specified" << std::endl; return -EINVAL; } return 0; } void format_mirror_peers(const std::string &config_path, at::Format::Formatter formatter, const std::vector &peers) { if (formatter != nullptr) { formatter->open_array_section("peers"); for (auto &peer : peers) { formatter->open_object_section("peer"); formatter->dump_string("uuid", peer.uuid); formatter->dump_string("cluster_name", peer.cluster_name); formatter->dump_string("client_name", peer.client_name); formatter->close_section(); } formatter->close_section(); } else { std::cout << "Peers: "; if (peers.empty()) { std::cout << "none" << std::endl; } else { TextTable tbl; tbl.define_column("", TextTable::LEFT, TextTable::LEFT); tbl.define_column("UUID", TextTable::LEFT, TextTable::LEFT); tbl.define_column("NAME", TextTable::LEFT, TextTable::LEFT); tbl.define_column("CLIENT", TextTable::LEFT, TextTable::LEFT); for (auto &peer : peers) { tbl << " " << peer.uuid << peer.cluster_name << peer.client_name << TextTable::endrow; } std::cout << std::endl << tbl; } } } class ImageRequestBase { public: void send() { dout(20) << this << " " << __func__ << ": image_name=" << m_image_name << dendl; auto ctx = new FunctionContext([this](int r) { handle_finalize(r); }); // will pause here until slots are available m_finalize_ctx = m_throttle.start_op(ctx); open_image(); } protected: ImageRequestBase(librados::IoCtx &io_ctx, OrderedThrottle &throttle, const std::string &image_name) : m_io_ctx(io_ctx), m_throttle(throttle), m_image_name(image_name) { } virtual ~ImageRequestBase() { } virtual bool skip_get_info() const { return false; } virtual void get_info(librbd::Image &image, librbd::mirror_image_info_t *info, librbd::RBD::AioCompletion *aio_comp) { image.aio_mirror_image_get_info(info, sizeof(librbd::mirror_image_info_t), aio_comp); } virtual bool skip_action(const librbd::mirror_image_info_t &info) const { return false; } virtual void execute_action(librbd::Image &image, librbd::RBD::AioCompletion *aio_comp) = 0; virtual void handle_execute_action(int r) { dout(20) << this << " " << __func__ << ": r=" << r << dendl; if (r < 0 && r != -ENOENT) { std::cerr << "rbd: failed to " << get_action_type() << " image " << m_image_name << ": " << cpp_strerror(r) << std::endl; m_ret_val = r; } close_image(); } virtual void finalize_action() { } virtual std::string get_action_type() const = 0; private: /** * @verbatim * * * | * v * OPEN_IMAGE * | * v * GET_INFO * | * v * EXECUTE_ACTION * | * v * CLOSE_IMAGE * | * v * FINALIZE_ACTION * | * v * * * @endverbatim */ librados::IoCtx &m_io_ctx; OrderedThrottle &m_throttle; const std::string m_image_name; librbd::Image m_image; Context *m_finalize_ctx; librbd::mirror_image_info_t m_mirror_image_info; int m_ret_val = 0; void open_image() { dout(20) << this << " " << __func__ << dendl; librbd::RBD rbd; auto aio_completion = utils::create_aio_completion< ImageRequestBase, &ImageRequestBase::handle_open_image>(this); rbd.aio_open(m_io_ctx, m_image, m_image_name.c_str(), nullptr, aio_completion); } void handle_open_image(int r) { dout(20) << this << " " << __func__ << ": r=" << r << dendl; if (r < 0) { std::cerr << "rbd: failed to open image " << m_image_name << ": " << cpp_strerror(r) << std::endl; m_finalize_ctx->complete(r); return; } get_info(); } void get_info() { if (skip_get_info()) { execute_action(); return; } dout(20) << this << " " << __func__ << dendl; auto aio_completion = utils::create_aio_completion< ImageRequestBase, &ImageRequestBase::handle_get_info>(this); get_info(m_image, &m_mirror_image_info, aio_completion); } void handle_get_info(int r) { dout(20) << this << " " << __func__ << ": r=" << r << dendl; if (r == -ENOENT) { close_image(); return; } else if (r < 0) { std::cerr << "rbd: failed to retrieve mirror image info for " << m_image_name << ": " << cpp_strerror(r) << std::endl; m_ret_val = r; close_image(); return; } execute_action(); } void execute_action() { if (skip_action(m_mirror_image_info)) { close_image(); return; } dout(20) << this << " " << __func__ << dendl; auto aio_completion = utils::create_aio_completion< ImageRequestBase, &ImageRequestBase::handle_execute_action>(this); execute_action(m_image, aio_completion); } void close_image() { dout(20) << this << " " << __func__ << dendl; auto aio_completion = utils::create_aio_completion< ImageRequestBase, &ImageRequestBase::handle_close_image>(this); m_image.aio_close(aio_completion); } void handle_close_image(int r) { dout(20) << this << " " << __func__ << ": r=" << r << dendl; if (r < 0) { std::cerr << "rbd: failed to close image " << m_image_name << ": " << cpp_strerror(r) << std::endl; } m_finalize_ctx->complete(r); } void handle_finalize(int r) { dout(20) << this << " " << __func__ << ": r=" << r << dendl; if (r == 0 && m_ret_val < 0) { r = m_ret_val; } if (r >= 0) { finalize_action(); } m_throttle.end_op(r); } }; class PromoteImageRequest : public ImageRequestBase { public: PromoteImageRequest(librados::IoCtx &io_ctx, OrderedThrottle &throttle, const std::string &image_name, std::atomic *counter, bool force) : ImageRequestBase(io_ctx, throttle, image_name), m_counter(counter), m_force(force) { } protected: bool skip_action(const librbd::mirror_image_info_t &info) const override { return (info.state != RBD_MIRROR_IMAGE_ENABLED || info.primary); } void execute_action(librbd::Image &image, librbd::RBD::AioCompletion *aio_comp) override { image.aio_mirror_image_promote(m_force, aio_comp); } void handle_execute_action(int r) override { if (r >= 0) { (*m_counter)++; } ImageRequestBase::handle_execute_action(r); } std::string get_action_type() const override { return "promote"; } private: std::atomic *m_counter = nullptr; bool m_force; }; class DemoteImageRequest : public ImageRequestBase { public: DemoteImageRequest(librados::IoCtx &io_ctx, OrderedThrottle &throttle, const std::string &image_name, std::atomic *counter) : ImageRequestBase(io_ctx, throttle, image_name), m_counter(counter) { } protected: bool skip_action(const librbd::mirror_image_info_t &info) const override { return (info.state != RBD_MIRROR_IMAGE_ENABLED || !info.primary); } void execute_action(librbd::Image &image, librbd::RBD::AioCompletion *aio_comp) override { image.aio_mirror_image_demote(aio_comp); } void handle_execute_action(int r) override { if (r >= 0) { (*m_counter)++; } ImageRequestBase::handle_execute_action(r); } std::string get_action_type() const override { return "demote"; } private: std::atomic *m_counter = nullptr; }; class StatusImageRequest : public ImageRequestBase { public: StatusImageRequest(librados::IoCtx &io_ctx, OrderedThrottle &throttle, const std::string &image_name, at::Format::Formatter formatter) : ImageRequestBase(io_ctx, throttle, image_name), m_formatter(formatter) { } protected: bool skip_get_info() const override { return true; } void execute_action(librbd::Image &image, librbd::RBD::AioCompletion *aio_comp) override { image.aio_mirror_image_get_status(&m_mirror_image_status, sizeof(m_mirror_image_status), aio_comp); } void finalize_action() override { if (m_mirror_image_status.info.global_id.empty()) { return; } std::string state = utils::mirror_image_status_state(m_mirror_image_status); std::string last_update = ( m_mirror_image_status.last_update == 0 ? "" : utils::timestr(m_mirror_image_status.last_update)); if (m_formatter != nullptr) { m_formatter->open_object_section("image"); m_formatter->dump_string("name", m_mirror_image_status.name); m_formatter->dump_string("global_id", m_mirror_image_status.info.global_id); m_formatter->dump_string("state", state); m_formatter->dump_string("description", m_mirror_image_status.description); m_formatter->dump_string("last_update", last_update); m_formatter->close_section(); // image } else { std::cout << "\n" << m_mirror_image_status.name << ":\n" << " global_id: " << m_mirror_image_status.info.global_id << "\n" << " state: " << state << "\n" << " description: " << m_mirror_image_status.description << "\n" << " last_update: " << last_update << std::endl; } } std::string get_action_type() const override { return "status"; } private: at::Format::Formatter m_formatter; librbd::mirror_image_status_t m_mirror_image_status; }; template class ImageRequestAllocator { public: template RequestT *operator()(librados::IoCtx &io_ctx, OrderedThrottle &throttle, const std::string &image_name, Args&&... args) { return new RequestT(io_ctx, throttle, image_name, std::forward(args)...); } }; template class ImageRequestGenerator { public: template ImageRequestGenerator(librados::IoCtx &io_ctx, Args&&... args) : m_io_ctx(io_ctx), m_factory(std::bind(ImageRequestAllocator(), std::ref(m_io_ctx), std::ref(m_throttle), std::placeholders::_1, std::forward(args)...)), m_throttle(g_conf->get_val("rbd_concurrent_management_ops"), true) { } int execute() { // use the alphabetical list of image names for pool-level // mirror image operations librbd::RBD rbd; int r = rbd.list(m_io_ctx, m_image_names); if (r < 0 && r != -ENOENT) { std::cerr << "rbd: failed to list images within pool" << std::endl; return r; } for (auto &image_name : m_image_names) { auto request = m_factory(image_name); request->send(); } return m_throttle.wait_for_ret(); } private: typedef std::function Factory; librados::IoCtx &m_io_ctx; Factory m_factory; OrderedThrottle m_throttle; std::vector m_image_names; }; } // anonymous namespace void get_peer_add_arguments(po::options_description *positional, po::options_description *options) { at::add_pool_options(positional, options); positional->add_options() ("remote-cluster-spec", "remote cluster spec\n" "(example: [@]"); options->add_options() ("remote-client-name", po::value(), "remote client name") ("remote-cluster", po::value(), "remote cluster name"); } int execute_peer_add(const po::variables_map &vm) { size_t arg_index = 0; std::string pool_name = utils::get_pool_name(vm, &arg_index); std::string remote_client_name = g_ceph_context->_conf->name.to_str(); std::string remote_cluster; int r = get_remote_cluster_spec( vm, utils::get_positional_argument(vm, arg_index), &remote_client_name, &remote_cluster); if (r < 0) { return r; } std::string config_path; if (vm.count(at::CONFIG_PATH)) { config_path = vm[at::CONFIG_PATH].as(); } librados::Rados rados; librados::IoCtx io_ctx; r = utils::init(pool_name, &rados, &io_ctx); if (r < 0) { return r; } r = validate_mirroring_enabled(io_ctx); if (r < 0) { return r; } // TODO: temporary restriction to prevent adding multiple peers // until rbd-mirror daemon can properly handle the scenario librbd::RBD rbd; std::vector mirror_peers; r = rbd.mirror_peer_list(io_ctx, &mirror_peers); if (r < 0) { std::cerr << "rbd: failed to list mirror peers" << std::endl; return r; } if (!mirror_peers.empty()) { std::cerr << "rbd: multiple peers are not currently supported" << std::endl; return -EINVAL; } std::string uuid; r = rbd.mirror_peer_add(io_ctx, &uuid, remote_cluster, remote_client_name); if (r < 0) { std::cerr << "rbd: error adding mirror peer" << std::endl; return r; } std::cout << uuid << std::endl; return 0; } void get_peer_remove_arguments(po::options_description *positional, po::options_description *options) { at::add_pool_options(positional, options); add_uuid_option(positional); } int execute_peer_remove(const po::variables_map &vm) { size_t arg_index = 0; std::string pool_name = utils::get_pool_name(vm, &arg_index); std::string uuid; int r = get_uuid(vm, arg_index, &uuid); if (r < 0) { return r; } librados::Rados rados; librados::IoCtx io_ctx; r = utils::init(pool_name, &rados, &io_ctx); if (r < 0) { return r; } r = validate_mirroring_enabled(io_ctx); if (r < 0) { return r; } librbd::RBD rbd; r = rbd.mirror_peer_remove(io_ctx, uuid); if (r < 0) { std::cerr << "rbd: error removing mirror peer" << std::endl; return r; } return 0; } void get_peer_set_arguments(po::options_description *positional, po::options_description *options) { at::add_pool_options(positional, options); add_uuid_option(positional); positional->add_options() ("key", "peer parameter [client or cluster]") ("value", "new client or cluster name"); } int execute_peer_set(const po::variables_map &vm) { size_t arg_index = 0; std::string pool_name = utils::get_pool_name(vm, &arg_index); std::string uuid; int r = get_uuid(vm, arg_index++, &uuid); if (r < 0) { return r; } std::string key = utils::get_positional_argument(vm, arg_index++); if (key != "client" && key != "cluster") { std::cerr << "rbd: must specify 'client' or 'cluster' key." << std::endl; return -EINVAL; } std::string value = utils::get_positional_argument(vm, arg_index++); if (value.empty()) { std::cerr << "rbd: must specify new " << key << " value." << std::endl; } librados::Rados rados; librados::IoCtx io_ctx; r = utils::init(pool_name, &rados, &io_ctx); if (r < 0) { return r; } r = validate_mirroring_enabled(io_ctx); if (r < 0) { return r; } librbd::RBD rbd; if (key == "client") { r = rbd.mirror_peer_set_client(io_ctx, uuid.c_str(), value.c_str()); } else { r = rbd.mirror_peer_set_cluster(io_ctx, uuid.c_str(), value.c_str()); } if (r < 0) { return r; } return 0; } void get_disable_arguments(po::options_description *positional, po::options_description *options) { at::add_pool_options(positional, options); } void get_enable_arguments(po::options_description *positional, po::options_description *options) { at::add_pool_options(positional, options); positional->add_options() ("mode", "mirror mode [image or pool]"); } int execute_enable_disable(const std::string &pool_name, rbd_mirror_mode_t next_mirror_mode, const std::string &mode) { librados::Rados rados; librados::IoCtx io_ctx; rbd_mirror_mode_t current_mirror_mode; int r = utils::init(pool_name, &rados, &io_ctx); if (r < 0) { return r; } librbd::RBD rbd; r = rbd.mirror_mode_get(io_ctx, ¤t_mirror_mode); if (r < 0) { std::cerr << "rbd: failed to retrieve mirror mode: " << cpp_strerror(r) << std::endl; return r; } if (current_mirror_mode == next_mirror_mode) { if (mode == "disabled") { std::cout << "mirroring is already " << mode << std::endl; } else { std::cout << "mirroring is already configured for " << mode << " mode" << std::endl; } return 0; } else if (next_mirror_mode == RBD_MIRROR_MODE_IMAGE && current_mirror_mode == RBD_MIRROR_MODE_POOL) { std::cout << "note: changing mirroring mode from pool to image" << std::endl; } else if (next_mirror_mode == RBD_MIRROR_MODE_POOL && current_mirror_mode == RBD_MIRROR_MODE_IMAGE) { std::cout << "note: changing mirroring mode from image to pool" << std::endl; } r = rbd.mirror_mode_set(io_ctx, next_mirror_mode); if (r < 0) { return r; } return 0; } int execute_disable(const po::variables_map &vm) { size_t arg_index = 0; std::string pool_name = utils::get_pool_name(vm, &arg_index); return execute_enable_disable(pool_name, RBD_MIRROR_MODE_DISABLED, "disabled"); } int execute_enable(const po::variables_map &vm) { size_t arg_index = 0; std::string pool_name = utils::get_pool_name(vm, &arg_index); rbd_mirror_mode_t mirror_mode; std::string mode = utils::get_positional_argument(vm, arg_index++); if (mode == "image") { mirror_mode = RBD_MIRROR_MODE_IMAGE; } else if (mode == "pool") { mirror_mode = RBD_MIRROR_MODE_POOL; } else { std::cerr << "rbd: must specify 'image' or 'pool' mode." << std::endl; return -EINVAL; } return execute_enable_disable(pool_name, mirror_mode, mode); } void get_info_arguments(po::options_description *positional, po::options_description *options) { at::add_pool_options(positional, options); at::add_format_options(options); } int execute_info(const po::variables_map &vm) { size_t arg_index = 0; std::string pool_name = utils::get_pool_name(vm, &arg_index); at::Format::Formatter formatter; int r = utils::get_formatter(vm, &formatter); if (r < 0) { return r; } std::string config_path; if (vm.count(at::CONFIG_PATH)) { config_path = vm[at::CONFIG_PATH].as(); } librados::Rados rados; librados::IoCtx io_ctx; r = utils::init(pool_name, &rados, &io_ctx); if (r < 0) { return r; } librbd::RBD rbd; rbd_mirror_mode_t mirror_mode; r = rbd.mirror_mode_get(io_ctx, &mirror_mode); if (r < 0) { return r; } std::vector mirror_peers; r = rbd.mirror_peer_list(io_ctx, &mirror_peers); if (r < 0) { return r; } std::string mirror_mode_desc; switch (mirror_mode) { case RBD_MIRROR_MODE_DISABLED: mirror_mode_desc = "disabled"; break; case RBD_MIRROR_MODE_IMAGE: mirror_mode_desc = "image"; break; case RBD_MIRROR_MODE_POOL: mirror_mode_desc = "pool"; break; default: mirror_mode_desc = "unknown"; break; } if (formatter != nullptr) { formatter->open_object_section("mirror"); formatter->dump_string("mode", mirror_mode_desc); } else { std::cout << "Mode: " << mirror_mode_desc << std::endl; } if (mirror_mode != RBD_MIRROR_MODE_DISABLED) { format_mirror_peers(config_path, formatter, mirror_peers); } if (formatter != nullptr) { formatter->close_section(); formatter->flush(std::cout); } return 0; } void get_status_arguments(po::options_description *positional, po::options_description *options) { at::add_pool_options(positional, options); at::add_format_options(options); at::add_verbose_option(options); } int execute_status(const po::variables_map &vm) { size_t arg_index = 0; std::string pool_name = utils::get_pool_name(vm, &arg_index); at::Format::Formatter formatter; int r = utils::get_formatter(vm, &formatter); if (r < 0) { return r; } bool verbose = vm[at::VERBOSE].as(); std::string config_path; if (vm.count(at::CONFIG_PATH)) { config_path = vm[at::CONFIG_PATH].as(); } librados::Rados rados; librados::IoCtx io_ctx; r = utils::init(pool_name, &rados, &io_ctx); if (r < 0) { return r; } r = validate_mirroring_enabled(io_ctx); if (r < 0) { return r; } librbd::RBD rbd; std::map states; r = rbd.mirror_image_status_summary(io_ctx, &states); if (r < 0) { std::cerr << "rbd: failed to get status summary for mirrored images: " << cpp_strerror(r) << std::endl; return r; } if (formatter != nullptr) { formatter->open_object_section("status"); } enum Health {Ok = 0, Warning = 1, Error = 2} health = Ok; const char *names[] = {"OK", "WARNING", "ERROR"}; int total = 0; for (auto &it : states) { auto &state = it.first; if (health < Warning && (state != MIRROR_IMAGE_STATUS_STATE_REPLAYING && state != MIRROR_IMAGE_STATUS_STATE_STOPPED)) { health = Warning; } if (health < Error && state == MIRROR_IMAGE_STATUS_STATE_ERROR) { health = Error; } total += it.second; } if (formatter != nullptr) { formatter->open_object_section("summary"); formatter->dump_string("health", names[health]); formatter->open_object_section("states"); for (auto &it : states) { std::string state_name = utils::mirror_image_status_state(it.first); formatter->dump_int(state_name.c_str(), it.second); } formatter->close_section(); // states formatter->close_section(); // summary } else { std::cout << "health: " << names[health] << std::endl; std::cout << "images: " << total << " total" << std::endl; for (auto &it : states) { std::cout << " " << it.second << " " << utils::mirror_image_status_state(it.first) << std::endl; } } int ret = 0; if (verbose) { if (formatter != nullptr) { formatter->open_array_section("images"); } ImageRequestGenerator generator(io_ctx, formatter); ret = generator.execute(); if (formatter != nullptr) { formatter->close_section(); // images } } if (formatter != nullptr) { formatter->close_section(); // status formatter->flush(std::cout); } return ret; } void get_promote_arguments(po::options_description *positional, po::options_description *options) { options->add_options() ("force", po::bool_switch(), "promote even if not cleanly demoted by remote cluster"); at::add_pool_options(positional, options); } int execute_promote(const po::variables_map &vm) { size_t arg_index = 0; std::string pool_name = utils::get_pool_name(vm, &arg_index); librados::Rados rados; librados::IoCtx io_ctx; int r = utils::init(pool_name, &rados, &io_ctx); if (r < 0) { return r; } r = validate_mirroring_enabled(io_ctx); if (r < 0) { return r; } std::atomic counter = { 0 }; ImageRequestGenerator generator(io_ctx, &counter, vm["force"].as()); r = generator.execute(); std::cout << "Promoted " << counter.load() << " mirrored images" << std::endl; return r; } void get_demote_arguments(po::options_description *positional, po::options_description *options) { at::add_pool_options(positional, options); } int execute_demote(const po::variables_map &vm) { size_t arg_index = 0; std::string pool_name = utils::get_pool_name(vm, &arg_index); librados::Rados rados; librados::IoCtx io_ctx; int r = utils::init(pool_name, &rados, &io_ctx); if (r < 0) { return r; } r = validate_mirroring_enabled(io_ctx); if (r < 0) { return r; } std::atomic counter { 0 }; ImageRequestGenerator generator(io_ctx, &counter); r = generator.execute(); std::cout << "Demoted " << counter.load() << " mirrored images" << std::endl; return r; } Shell::Action action_add( {"mirror", "pool", "peer", "add"}, {}, "Add a mirroring peer to a pool.", "", &get_peer_add_arguments, &execute_peer_add); Shell::Action action_remove( {"mirror", "pool", "peer", "remove"}, {}, "Remove a mirroring peer from a pool.", "", &get_peer_remove_arguments, &execute_peer_remove); Shell::Action action_set( {"mirror", "pool", "peer", "set"}, {}, "Update mirroring peer settings.", "", &get_peer_set_arguments, &execute_peer_set); Shell::Action action_disable( {"mirror", "pool", "disable"}, {}, "Disable RBD mirroring by default within a pool.", "", &get_disable_arguments, &execute_disable); Shell::Action action_enable( {"mirror", "pool", "enable"}, {}, "Enable RBD mirroring by default within a pool.", "", &get_enable_arguments, &execute_enable); Shell::Action action_info( {"mirror", "pool", "info"}, {}, "Show information about the pool mirroring configuration.", {}, &get_info_arguments, &execute_info); Shell::Action action_status( {"mirror", "pool", "status"}, {}, "Show status for all mirrored images in the pool.", {}, &get_status_arguments, &execute_status); Shell::Action action_promote( {"mirror", "pool", "promote"}, {}, "Promote all non-primary images in the pool.", {}, &get_promote_arguments, &execute_promote); Shell::Action action_demote( {"mirror", "pool", "demote"}, {}, "Demote all primary images in the pool.", {}, &get_demote_arguments, &execute_demote); } // namespace mirror_pool } // namespace action } // namespace rbd