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/MirrorDaemonServiceInfo.h"
6 #include "tools/rbd/Shell.h"
7 #include "tools/rbd/Utils.h"
8 #include "include/Context.h"
9 #include "include/stringify.h"
10 #include "include/rbd/librbd.hpp"
11 #include "common/ceph_json.h"
12 #include "common/config.h"
13 #include "common/debug.h"
14 #include "common/errno.h"
15 #include "common/Formatter.h"
16 #include "common/TextTable.h"
17 #include "common/Throttle.h"
18 #include "global/global_context.h"
24 #include <boost/program_options.hpp>
25 #include "include/ceph_assert.h"
29 #define dout_context g_ceph_context
30 #define dout_subsys ceph_subsys_rbd
32 #define dout_prefix *_dout << "rbd::action::MirrorPool: "
36 namespace mirror_pool
{
38 namespace at
= argument_types
;
39 namespace po
= boost::program_options
;
41 static const std::string
ALL_NAME("all");
42 static const std::string
SITE_NAME("site-name");
46 void add_site_name_optional(po::options_description
*options
) {
47 options
->add_options()
48 (SITE_NAME
.c_str(), po::value
<std::string
>(), "local site name");
51 int set_site_name(librados::Rados
& rados
, const std::string
& site_name
) {
53 int r
= rbd
.mirror_site_name_set(rados
, site_name
);
54 if (r
== -EOPNOTSUPP
) {
55 std::cerr
<< "rbd: cluster does not support site names" << std::endl
;
58 std::cerr
<< "rbd: failed to set site name" << cpp_strerror(r
)
66 struct MirrorPeerDirection
{};
68 void validate(boost::any
& v
, const std::vector
<std::string
>& values
,
69 MirrorPeerDirection
*target_type
, int) {
70 po::validators::check_first_occurrence(v
);
71 const std::string
&s
= po::validators::get_single_string(values
);
74 v
= boost::any(RBD_MIRROR_PEER_DIRECTION_RX
);
75 } else if (s
== "rx-tx") {
76 v
= boost::any(RBD_MIRROR_PEER_DIRECTION_RX_TX
);
78 throw po::validation_error(po::validation_error::invalid_option_value
);
82 int validate_mirroring_enabled(librados::IoCtx
& io_ctx
) {
84 rbd_mirror_mode_t mirror_mode
;
85 int r
= rbd
.mirror_mode_get(io_ctx
, &mirror_mode
);
87 std::cerr
<< "rbd: failed to retrieve mirror mode: "
88 << cpp_strerror(r
) << std::endl
;
92 if (mirror_mode
== RBD_MIRROR_MODE_DISABLED
) {
93 std::cerr
<< "rbd: mirroring not enabled on the pool" << std::endl
;
99 int validate_uuid(const std::string
&uuid
) {
100 std::regex
pattern("^[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}$",
103 if (!std::regex_match(uuid
, match
, pattern
)) {
104 std::cerr
<< "rbd: invalid uuid '" << uuid
<< "'" << std::endl
;
110 int read_key_file(std::string path
, std::string
* key
) {
111 std::ifstream key_file
;
113 if (key_file
.fail()) {
114 std::cerr
<< "rbd: failed to open " << path
<< std::endl
;
118 std::getline(key_file
, *key
);
119 if (key_file
.bad()) {
120 std::cerr
<< "rbd: failed to read key from " << path
<< std::endl
;
128 void add_uuid_option(po::options_description
*positional
) {
129 positional
->add_options()
130 ("uuid", po::value
<std::string
>(), "peer uuid");
133 int get_uuid(const po::variables_map
&vm
, size_t arg_index
,
135 *uuid
= utils::get_positional_argument(vm
, arg_index
);
137 std::cerr
<< "rbd: must specify peer uuid" << std::endl
;
140 return validate_uuid(*uuid
);
143 int get_remote_cluster_spec(const po::variables_map
&vm
,
144 const std::string
&spec
,
145 std::string
*remote_client_name
,
146 std::string
*remote_cluster
,
147 std::map
<std::string
, std::string
>* attributes
) {
148 if (vm
.count("remote-client-name")) {
149 *remote_client_name
= vm
["remote-client-name"].as
<std::string
>();
151 if (vm
.count("remote-cluster")) {
152 *remote_cluster
= vm
["remote-cluster"].as
<std::string
>();
154 if (vm
.count("remote-mon-host")) {
155 (*attributes
)["mon_host"] = vm
["remote-mon-host"].as
<std::string
>();
157 if (vm
.count("remote-key-file")) {
159 int r
= read_key_file(vm
["remote-key-file"].as
<std::string
>(), &key
);
163 (*attributes
)["key"] = key
;
167 std::regex
pattern("^(?:(client\\.[^@]+)@)?([^/@]+)$");
169 if (!std::regex_match(spec
, match
, pattern
)) {
170 std::cerr
<< "rbd: invalid spec '" << spec
<< "'" << std::endl
;
173 if (match
[1].matched
) {
174 *remote_client_name
= match
[1];
176 *remote_cluster
= match
[2];
179 if (remote_cluster
->empty()) {
180 std::cerr
<< "rbd: remote cluster was not specified" << std::endl
;
186 int set_peer_config_key(librados::IoCtx
& io_ctx
, const std::string
& peer_uuid
,
187 std::map
<std::string
, std::string
>&& attributes
) {
189 int r
= rbd
.mirror_peer_set_attributes(io_ctx
, peer_uuid
, attributes
);
191 std::cerr
<< "rbd: permission denied attempting to set peer "
192 << "config-key secrets in the monitor" << std::endl
;
195 std::cerr
<< "rbd: failed to update mirroring peer config: "
196 << cpp_strerror(r
) << std::endl
;
202 int get_peer_config_key(librados::IoCtx
& io_ctx
, const std::string
& peer_uuid
,
203 std::map
<std::string
, std::string
>* attributes
) {
205 int r
= rbd
.mirror_peer_get_attributes(io_ctx
, peer_uuid
, attributes
);
208 } else if (r
== -EPERM
) {
209 std::cerr
<< "rbd: permission denied attempting to access peer "
210 << "config-key secrets from the monitor" << std::endl
;
212 } else if (r
== -EINVAL
) {
213 std::cerr
<< "rbd: corrupt mirroring peer config" << std::endl
;
216 std::cerr
<< "rbd: error reading mirroring peer config: "
217 << cpp_strerror(r
) << std::endl
;
224 int update_peer_config_key(librados::IoCtx
& io_ctx
,
225 const std::string
& peer_uuid
,
226 const std::string
& key
,
227 const std::string
& value
) {
228 std::map
<std::string
, std::string
> attributes
;
229 int r
= get_peer_config_key(io_ctx
, peer_uuid
, &attributes
);
231 return set_peer_config_key(io_ctx
, peer_uuid
, {{key
, value
}});
237 attributes
.erase(key
);
239 attributes
[key
] = value
;
241 return set_peer_config_key(io_ctx
, peer_uuid
, std::move(attributes
));
244 int format_mirror_peers(librados::IoCtx
& io_ctx
,
245 at::Format::Formatter formatter
,
246 const std::vector
<librbd::mirror_peer_t
> &peers
,
249 if (formatter
!= nullptr) {
250 formatter
->open_array_section("peers");
252 std::cout
<< "Peers: ";
254 std::cout
<< "none" << std::endl
;
256 tbl
.define_column("", TextTable::LEFT
, TextTable::LEFT
);
257 tbl
.define_column("UUID", TextTable::LEFT
, TextTable::LEFT
);
258 tbl
.define_column("NAME", TextTable::LEFT
, TextTable::LEFT
);
259 tbl
.define_column("CLIENT", TextTable::LEFT
, TextTable::LEFT
);
261 tbl
.define_column("MON_HOST", TextTable::LEFT
, TextTable::LEFT
);
262 tbl
.define_column("KEY", TextTable::LEFT
, TextTable::LEFT
);
267 for (auto &peer
: peers
) {
268 std::map
<std::string
, std::string
> attributes
;
270 int r
= get_peer_config_key(io_ctx
, peer
.uuid
, &attributes
);
271 if (r
< 0 && r
!= -ENOENT
) {
276 if (formatter
!= nullptr) {
277 formatter
->open_object_section("peer");
278 formatter
->dump_string("uuid", peer
.uuid
);
279 formatter
->dump_string("cluster_name", peer
.cluster_name
);
280 formatter
->dump_string("client_name", peer
.client_name
);
281 for (auto& pair
: attributes
) {
282 formatter
->dump_string(pair
.first
.c_str(), pair
.second
);
284 formatter
->close_section();
291 tbl
<< attributes
["mon_host"]
292 << attributes
["key"];
294 tbl
<< TextTable::endrow
;
298 if (formatter
!= nullptr) {
299 formatter
->close_section();
301 std::cout
<< std::endl
<< tbl
;
306 class ImageRequestBase
{
309 dout(20) << this << " " << __func__
<< ": image_name=" << m_image_name
312 auto ctx
= new FunctionContext([this](int r
) {
316 // will pause here until slots are available
317 m_finalize_ctx
= m_throttle
.start_op(ctx
);
323 ImageRequestBase(librados::IoCtx
&io_ctx
, OrderedThrottle
&throttle
,
324 const std::string
&image_name
)
325 : m_io_ctx(io_ctx
), m_throttle(throttle
), m_image_name(image_name
) {
327 virtual ~ImageRequestBase() {
330 virtual bool skip_get_info() const {
333 virtual void get_info(librbd::Image
&image
, librbd::mirror_image_info_t
*info
,
334 librbd::RBD::AioCompletion
*aio_comp
) {
335 image
.aio_mirror_image_get_info(info
, sizeof(librbd::mirror_image_info_t
),
339 virtual bool skip_action(const librbd::mirror_image_info_t
&info
) const {
342 virtual void execute_action(librbd::Image
&image
,
343 librbd::RBD::AioCompletion
*aio_comp
) = 0;
344 virtual void handle_execute_action(int r
) {
345 dout(20) << this << " " << __func__
<< ": r=" << r
<< dendl
;
347 if (r
< 0 && r
!= -ENOENT
) {
348 std::cerr
<< "rbd: failed to " << get_action_type() << " image "
349 << m_image_name
<< ": " << cpp_strerror(r
) << std::endl
;
356 virtual void finalize_action() {
358 virtual std::string
get_action_type() const = 0;
387 librados::IoCtx
&m_io_ctx
;
388 OrderedThrottle
&m_throttle
;
389 const std::string m_image_name
;
391 librbd::Image m_image
;
392 Context
*m_finalize_ctx
= nullptr;
394 librbd::mirror_image_info_t m_mirror_image_info
;
399 dout(20) << this << " " << __func__
<< dendl
;
402 auto aio_completion
= utils::create_aio_completion
<
403 ImageRequestBase
, &ImageRequestBase::handle_open_image
>(this);
404 rbd
.aio_open(m_io_ctx
, m_image
, m_image_name
.c_str(), nullptr,
408 void handle_open_image(int r
) {
409 dout(20) << this << " " << __func__
<< ": r=" << r
<< dendl
;
412 std::cerr
<< "rbd: failed to open image "
413 << m_image_name
<< ": " << cpp_strerror(r
) << std::endl
;
414 m_finalize_ctx
->complete(r
);
422 if (skip_get_info()) {
426 dout(20) << this << " " << __func__
<< dendl
;
428 auto aio_completion
= utils::create_aio_completion
<
429 ImageRequestBase
, &ImageRequestBase::handle_get_info
>(this);
430 get_info(m_image
, &m_mirror_image_info
, aio_completion
);
433 void handle_get_info(int r
) {
434 dout(20) << this << " " << __func__
<< ": r=" << r
<< dendl
;
440 std::cerr
<< "rbd: failed to retrieve mirror image info for "
441 << m_image_name
<< ": " << cpp_strerror(r
) << std::endl
;
450 void execute_action() {
451 if (skip_action(m_mirror_image_info
)) {
455 dout(20) << this << " " << __func__
<< dendl
;
457 auto aio_completion
= utils::create_aio_completion
<
458 ImageRequestBase
, &ImageRequestBase::handle_execute_action
>(this);
459 execute_action(m_image
, aio_completion
);
463 dout(20) << this << " " << __func__
<< dendl
;
465 auto aio_completion
= utils::create_aio_completion
<
466 ImageRequestBase
, &ImageRequestBase::handle_close_image
>(this);
467 m_image
.aio_close(aio_completion
);
470 void handle_close_image(int r
) {
471 dout(20) << this << " " << __func__
<< ": r=" << r
<< dendl
;
474 std::cerr
<< "rbd: failed to close image "
475 << m_image_name
<< ": " << cpp_strerror(r
) << std::endl
;
478 m_finalize_ctx
->complete(r
);
481 void handle_finalize(int r
) {
482 dout(20) << this << " " << __func__
<< ": r=" << r
<< dendl
;
484 if (r
== 0 && m_ret_val
< 0) {
490 m_throttle
.end_op(r
);
496 class PromoteImageRequest
: public ImageRequestBase
{
498 PromoteImageRequest(librados::IoCtx
&io_ctx
, OrderedThrottle
&throttle
,
499 const std::string
&image_name
, std::atomic
<unsigned> *counter
,
501 : ImageRequestBase(io_ctx
, throttle
, image_name
), m_counter(counter
),
506 bool skip_action(const librbd::mirror_image_info_t
&info
) const override
{
507 return (info
.state
!= RBD_MIRROR_IMAGE_ENABLED
|| info
.primary
);
510 void execute_action(librbd::Image
&image
,
511 librbd::RBD::AioCompletion
*aio_comp
) override
{
512 image
.aio_mirror_image_promote(m_force
, aio_comp
);
515 void handle_execute_action(int r
) override
{
519 ImageRequestBase::handle_execute_action(r
);
522 std::string
get_action_type() const override
{
527 std::atomic
<unsigned> *m_counter
= nullptr;
531 class DemoteImageRequest
: public ImageRequestBase
{
533 DemoteImageRequest(librados::IoCtx
&io_ctx
, OrderedThrottle
&throttle
,
534 const std::string
&image_name
, std::atomic
<unsigned> *counter
)
535 : ImageRequestBase(io_ctx
, throttle
, image_name
), m_counter(counter
) {
539 bool skip_action(const librbd::mirror_image_info_t
&info
) const override
{
540 return (info
.state
!= RBD_MIRROR_IMAGE_ENABLED
|| !info
.primary
);
543 void execute_action(librbd::Image
&image
,
544 librbd::RBD::AioCompletion
*aio_comp
) override
{
545 image
.aio_mirror_image_demote(aio_comp
);
547 void handle_execute_action(int r
) override
{
551 ImageRequestBase::handle_execute_action(r
);
554 std::string
get_action_type() const override
{
559 std::atomic
<unsigned> *m_counter
= nullptr;
562 class StatusImageRequest
: public ImageRequestBase
{
565 librados::IoCtx
&io_ctx
, OrderedThrottle
&throttle
,
566 const std::string
&image_name
,
567 const std::map
<std::string
, std::string
> &instance_ids
,
568 const MirrorDaemonServiceInfo
&daemon_service_info
,
569 at::Format::Formatter formatter
)
570 : ImageRequestBase(io_ctx
, throttle
, image_name
),
571 m_instance_ids(instance_ids
), m_daemon_service_info(daemon_service_info
),
572 m_formatter(formatter
) {
576 bool skip_get_info() const override
{
580 void execute_action(librbd::Image
&image
,
581 librbd::RBD::AioCompletion
*aio_comp
) override
{
582 image
.get_id(&m_image_id
);
583 image
.aio_mirror_image_get_status(&m_mirror_image_status
,
584 sizeof(m_mirror_image_status
), aio_comp
);
587 void finalize_action() override
{
588 if (m_mirror_image_status
.info
.global_id
.empty()) {
592 std::string state
= utils::mirror_image_status_state(m_mirror_image_status
);
593 std::string instance_id
= (m_mirror_image_status
.up
&&
594 m_instance_ids
.count(m_image_id
)) ?
595 m_instance_ids
.find(m_image_id
)->second
: "";
596 std::string last_update
= (
597 m_mirror_image_status
.last_update
== 0 ?
598 "" : utils::timestr(m_mirror_image_status
.last_update
));
600 if (m_formatter
!= nullptr) {
601 m_formatter
->open_object_section("image");
602 m_formatter
->dump_string("name", m_mirror_image_status
.name
);
603 m_formatter
->dump_string("global_id",
604 m_mirror_image_status
.info
.global_id
);
605 m_formatter
->dump_string("state", state
);
606 m_formatter
->dump_string("description",
607 m_mirror_image_status
.description
);
608 m_daemon_service_info
.dump(instance_id
, m_formatter
);
609 m_formatter
->dump_string("last_update", last_update
);
610 m_formatter
->close_section(); // image
612 std::cout
<< "\n" << m_mirror_image_status
.name
<< ":\n"
614 << m_mirror_image_status
.info
.global_id
<< "\n"
615 << " state: " << state
<< "\n"
617 << m_mirror_image_status
.description
<< "\n";
618 if (!instance_id
.empty()) {
619 std::cout
<< " service: "
620 << m_daemon_service_info
.get_description(instance_id
) << "\n";
622 std::cout
<< " last_update: " << last_update
<< std::endl
;
626 std::string
get_action_type() const override
{
631 const std::map
<std::string
, std::string
> &m_instance_ids
;
632 const MirrorDaemonServiceInfo
&m_daemon_service_info
;
633 at::Format::Formatter m_formatter
;
634 std::string m_image_id
;
635 librbd::mirror_image_status_t m_mirror_image_status
;
638 template <typename RequestT
>
639 class ImageRequestAllocator
{
641 template <class... Args
>
642 RequestT
*operator()(librados::IoCtx
&io_ctx
, OrderedThrottle
&throttle
,
643 const std::string
&image_name
, Args
&&... args
) {
644 return new RequestT(io_ctx
, throttle
, image_name
,
645 std::forward
<Args
>(args
)...);
649 template <typename RequestT
>
650 class ImageRequestGenerator
{
652 template <class... Args
>
653 ImageRequestGenerator(librados::IoCtx
&io_ctx
, Args
&&... args
)
655 m_factory(std::bind(ImageRequestAllocator
<RequestT
>(),
656 std::ref(m_io_ctx
), std::ref(m_throttle
),
657 std::placeholders::_1
, std::forward
<Args
>(args
)...)),
658 m_throttle(g_conf().get_val
<uint64_t>("rbd_concurrent_management_ops"),
663 // use the alphabetical list of image names for pool-level
664 // mirror image operations
666 int r
= rbd
.list2(m_io_ctx
, &m_images
);
667 if (r
< 0 && r
!= -ENOENT
) {
668 std::cerr
<< "rbd: failed to list images within pool" << std::endl
;
672 for (auto &image
: m_images
) {
673 auto request
= m_factory(image
.name
);
677 return m_throttle
.wait_for_ret();
680 typedef std::function
<RequestT
*(const std::string
&)> Factory
;
682 librados::IoCtx
&m_io_ctx
;
685 OrderedThrottle m_throttle
;
687 std::vector
<librbd::image_spec_t
> m_images
;
691 } // anonymous namespace
693 void get_peer_bootstrap_create_arguments(po::options_description
*positional
,
694 po::options_description
*options
) {
695 at::add_pool_options(positional
, options
, false);
696 options
->add_options()
697 (SITE_NAME
.c_str(), po::value
<std::string
>(), "local site name");
700 int execute_peer_bootstrap_create(
701 const po::variables_map
&vm
,
702 const std::vector
<std::string
> &ceph_global_init_args
) {
703 std::string pool_name
;
704 size_t arg_index
= 0;
705 int r
= utils::get_pool_and_namespace_names(vm
, true, true, &pool_name
,
706 nullptr, &arg_index
);
711 librados::Rados rados
;
712 librados::IoCtx io_ctx
;
713 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
718 r
= validate_mirroring_enabled(io_ctx
);
723 if (vm
.count(SITE_NAME
)) {
724 r
= set_site_name(rados
, vm
[SITE_NAME
].as
<std::string
>());
732 r
= rbd
.mirror_peer_bootstrap_create(io_ctx
, &token
);
734 std::cerr
<< "rbd: mismatch with pre-existing RBD mirroring peer user caps"
737 std::cerr
<< "rbd: failed to create mirroring bootstrap token: "
738 << cpp_strerror(r
) << std::endl
;
742 std::cout
<< token
<< std::endl
;
746 void get_peer_bootstrap_import_arguments(po::options_description
*positional
,
747 po::options_description
*options
) {
748 at::add_pool_options(positional
, options
, false);
749 options
->add_options()
750 (SITE_NAME
.c_str(), po::value
<std::string
>(), "local site name");
751 positional
->add_options()
752 ("token-path", po::value
<std::string
>(),
753 "bootstrap token file (or '-' for stdin)");
754 options
->add_options()
755 ("token-path", po::value
<std::string
>(),
756 "bootstrap token file (or '-' for stdin)")
757 ("direction", po::value
<MirrorPeerDirection
>(),
758 "mirroring direction (rx-only, rx-tx)\n"
762 int execute_peer_bootstrap_import(
763 const po::variables_map
&vm
,
764 const std::vector
<std::string
> &ceph_global_init_args
) {
765 std::string pool_name
;
766 size_t arg_index
= 0;
767 int r
= utils::get_pool_and_namespace_names(vm
, true, true, &pool_name
,
768 nullptr, &arg_index
);
773 std::string token_path
;
774 if (vm
.count("token-path")) {
775 token_path
= vm
["token-path"].as
<std::string
>();
777 token_path
= utils::get_positional_argument(vm
, arg_index
++);
780 if (token_path
.empty()) {
781 std::cerr
<< "rbd: token path was not specified" << std::endl
;
785 rbd_mirror_peer_direction_t mirror_peer_direction
=
786 RBD_MIRROR_PEER_DIRECTION_RX_TX
;
787 if (vm
.count("direction")) {
788 mirror_peer_direction
= vm
["direction"].as
<rbd_mirror_peer_direction_t
>();
791 int fd
= STDIN_FILENO
;
792 if (token_path
!= "-") {
793 fd
= open(token_path
.c_str(), O_RDONLY
);
796 std::cerr
<< "rbd: error opening " << token_path
<< std::endl
;
802 memset(token
, 0, sizeof(token
));
803 r
= safe_read(fd
, token
, sizeof(token
) - 1);
804 if (fd
!= STDIN_FILENO
) {
805 VOID_TEMP_FAILURE_RETRY(close(fd
));
809 std::cerr
<< "rbd: error reading token file: " << cpp_strerror(r
)
814 librados::Rados rados
;
815 librados::IoCtx io_ctx
;
816 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
821 if (vm
.count(SITE_NAME
)) {
822 r
= set_site_name(rados
, vm
[SITE_NAME
].as
<std::string
>());
829 r
= rbd
.mirror_peer_bootstrap_import(io_ctx
, mirror_peer_direction
, token
);
831 std::cerr
<< "rbd: mirroring is not enabled on remote peer" << std::endl
;
834 std::cerr
<< "rbd: failed to import peer bootstrap token" << std::endl
;
841 void get_peer_add_arguments(po::options_description
*positional
,
842 po::options_description
*options
) {
843 at::add_pool_options(positional
, options
, false);
844 positional
->add_options()
845 ("remote-cluster-spec", "remote cluster spec\n"
846 "(example: [<client name>@]<cluster name>)");
847 options
->add_options()
848 ("remote-client-name", po::value
<std::string
>(), "remote client name")
849 ("remote-cluster", po::value
<std::string
>(), "remote cluster name")
850 ("remote-mon-host", po::value
<std::string
>(), "remote mon host(s)")
851 ("remote-key-file", po::value
<std::string
>(),
852 "path to file containing remote key");
855 int execute_peer_add(const po::variables_map
&vm
,
856 const std::vector
<std::string
> &ceph_global_init_args
) {
857 std::string pool_name
;
858 size_t arg_index
= 0;
859 int r
= utils::get_pool_and_namespace_names(vm
, true, true, &pool_name
,
860 nullptr, &arg_index
);
865 std::string remote_client_name
= g_ceph_context
->_conf
->name
.to_str();
866 std::string remote_cluster
;
867 std::map
<std::string
, std::string
> attributes
;
868 r
= get_remote_cluster_spec(
869 vm
, utils::get_positional_argument(vm
, arg_index
),
870 &remote_client_name
, &remote_cluster
, &attributes
);
875 // TODO support namespaces
876 librados::Rados rados
;
877 librados::IoCtx io_ctx
;
878 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
883 r
= validate_mirroring_enabled(io_ctx
);
888 // TODO: temporary restriction to prevent adding multiple peers
889 // until rbd-mirror daemon can properly handle the scenario
891 std::vector
<librbd::mirror_peer_t
> mirror_peers
;
892 r
= rbd
.mirror_peer_list(io_ctx
, &mirror_peers
);
894 std::cerr
<< "rbd: failed to list mirror peers" << std::endl
;
897 if (!mirror_peers
.empty()) {
898 std::cerr
<< "rbd: multiple peers are not currently supported" << std::endl
;
903 r
= rbd
.mirror_peer_add(io_ctx
, &uuid
, remote_cluster
, remote_client_name
);
905 std::cerr
<< "rbd: error adding mirror peer" << std::endl
;
909 if (!attributes
.empty()) {
910 r
= set_peer_config_key(io_ctx
, uuid
, std::move(attributes
));
916 std::cout
<< uuid
<< std::endl
;
920 void get_peer_remove_arguments(po::options_description
*positional
,
921 po::options_description
*options
) {
922 at::add_pool_options(positional
, options
, false);
923 add_uuid_option(positional
);
926 int execute_peer_remove(const po::variables_map
&vm
,
927 const std::vector
<std::string
> &ceph_global_init_args
) {
928 std::string pool_name
;
929 size_t arg_index
= 0;
930 int r
= utils::get_pool_and_namespace_names(vm
, true, true, &pool_name
,
931 nullptr, &arg_index
);
937 r
= get_uuid(vm
, arg_index
, &uuid
);
942 // TODO support namespaces
943 librados::Rados rados
;
944 librados::IoCtx io_ctx
;
945 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
950 r
= validate_mirroring_enabled(io_ctx
);
956 r
= rbd
.mirror_peer_remove(io_ctx
, uuid
);
958 std::cerr
<< "rbd: error removing mirror peer" << std::endl
;
964 void get_peer_set_arguments(po::options_description
*positional
,
965 po::options_description
*options
) {
966 at::add_pool_options(positional
, options
, false);
967 add_uuid_option(positional
);
968 positional
->add_options()
969 ("key", "peer parameter [client, cluster, mon-host, key-file]")
970 ("value", "new value for specified key");
973 int execute_peer_set(const po::variables_map
&vm
,
974 const std::vector
<std::string
> &ceph_global_init_args
) {
975 std::string pool_name
;
976 size_t arg_index
= 0;
977 int r
= utils::get_pool_and_namespace_names(vm
, true, true, &pool_name
,
978 nullptr, &arg_index
);
984 r
= get_uuid(vm
, arg_index
++, &uuid
);
989 std::set
<std::string
> valid_keys
{{"client", "cluster", "mon-host",
991 std::string key
= utils::get_positional_argument(vm
, arg_index
++);
992 if (valid_keys
.find(key
) == valid_keys
.end()) {
993 std::cerr
<< "rbd: must specify ";
994 for (auto& valid_key
: valid_keys
) {
995 std::cerr
<< "'" << valid_key
<< "'";
996 if (&valid_key
!= &(*valid_keys
.rbegin())) {
1000 std::cerr
<< " key." << std::endl
;
1004 std::string value
= utils::get_positional_argument(vm
, arg_index
++);
1005 if (value
.empty() && (key
== "client" || key
== "cluster")) {
1006 std::cerr
<< "rbd: must specify new " << key
<< " value." << std::endl
;
1007 } else if (key
== "key-file") {
1009 r
= read_key_file(value
, &value
);
1013 } else if (key
== "mon-host") {
1017 // TODO support namespaces
1018 librados::Rados rados
;
1019 librados::IoCtx io_ctx
;
1020 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
1025 r
= validate_mirroring_enabled(io_ctx
);
1031 if (key
== "client") {
1032 r
= rbd
.mirror_peer_set_client(io_ctx
, uuid
.c_str(), value
.c_str());
1033 } else if (key
== "cluster") {
1034 r
= rbd
.mirror_peer_set_cluster(io_ctx
, uuid
.c_str(), value
.c_str());
1036 r
= update_peer_config_key(io_ctx
, uuid
, key
, value
);
1038 std::cerr
<< "rbd: mirror peer " << uuid
<< " does not exist"
1049 void get_disable_arguments(po::options_description
*positional
,
1050 po::options_description
*options
) {
1051 at::add_pool_options(positional
, options
, false);
1054 void get_enable_arguments(po::options_description
*positional
,
1055 po::options_description
*options
) {
1056 at::add_pool_options(positional
, options
, false);
1057 positional
->add_options()
1058 ("mode", "mirror mode [image or pool]");
1059 add_site_name_optional(options
);
1062 int execute_enable_disable(librados::IoCtx
& io_ctx
,
1063 rbd_mirror_mode_t next_mirror_mode
,
1064 const std::string
&mode
, bool ignore_no_update
) {
1066 rbd_mirror_mode_t current_mirror_mode
;
1067 int r
= rbd
.mirror_mode_get(io_ctx
, ¤t_mirror_mode
);
1069 std::cerr
<< "rbd: failed to retrieve mirror mode: "
1070 << cpp_strerror(r
) << std::endl
;
1074 if (current_mirror_mode
== next_mirror_mode
) {
1075 if (!ignore_no_update
) {
1076 if (mode
== "disabled") {
1077 std::cout
<< "rbd: mirroring is already " << mode
<< std::endl
;
1079 std::cout
<< "rbd: mirroring is already configured for "
1080 << mode
<< " mode" << std::endl
;
1084 } else if (next_mirror_mode
== RBD_MIRROR_MODE_IMAGE
&&
1085 current_mirror_mode
== RBD_MIRROR_MODE_POOL
) {
1086 std::cout
<< "note: changing mirroring mode from pool to image"
1088 } else if (next_mirror_mode
== RBD_MIRROR_MODE_POOL
&&
1089 current_mirror_mode
== RBD_MIRROR_MODE_IMAGE
) {
1090 std::cout
<< "note: changing mirroring mode from image to pool"
1094 r
= rbd
.mirror_mode_set(io_ctx
, next_mirror_mode
);
1101 int execute_disable(const po::variables_map
&vm
,
1102 const std::vector
<std::string
> &ceph_global_init_args
) {
1103 std::string pool_name
;
1104 size_t arg_index
= 0;
1105 int r
= utils::get_pool_and_namespace_names(vm
, true, true, &pool_name
,
1106 nullptr, &arg_index
);
1111 librados::Rados rados
;
1112 librados::IoCtx io_ctx
;
1114 // TODO support namespaces
1115 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
1120 return execute_enable_disable(io_ctx
, RBD_MIRROR_MODE_DISABLED
, "disabled",
1124 int execute_enable(const po::variables_map
&vm
,
1125 const std::vector
<std::string
> &ceph_global_init_args
) {
1126 std::string pool_name
;
1127 size_t arg_index
= 0;
1128 int r
= utils::get_pool_and_namespace_names(vm
, true, true, &pool_name
,
1129 nullptr, &arg_index
);
1134 rbd_mirror_mode_t mirror_mode
;
1135 std::string mode
= utils::get_positional_argument(vm
, arg_index
++);
1136 if (mode
== "image") {
1137 mirror_mode
= RBD_MIRROR_MODE_IMAGE
;
1138 } else if (mode
== "pool") {
1139 mirror_mode
= RBD_MIRROR_MODE_POOL
;
1141 std::cerr
<< "rbd: must specify 'image' or 'pool' mode." << std::endl
;
1145 librados::Rados rados
;
1146 librados::IoCtx io_ctx
;
1148 // TODO support namespaces
1149 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
1154 bool updated
= false;
1155 if (vm
.count(SITE_NAME
)) {
1158 auto site_name
= vm
[SITE_NAME
].as
<std::string
>();
1159 std::string original_site_name
;
1160 r
= rbd
.mirror_site_name_get(rados
, &original_site_name
);
1161 updated
= (r
>= 0 && site_name
!= original_site_name
);
1163 r
= set_site_name(rados
, site_name
);
1169 return execute_enable_disable(io_ctx
, mirror_mode
, mode
, updated
);
1172 void get_info_arguments(po::options_description
*positional
,
1173 po::options_description
*options
) {
1174 at::add_pool_options(positional
, options
, false);
1175 at::add_format_options(options
);
1176 options
->add_options()
1177 (ALL_NAME
.c_str(), po::bool_switch(), "list all attributes");
1180 int execute_info(const po::variables_map
&vm
,
1181 const std::vector
<std::string
> &ceph_global_init_args
) {
1182 std::string pool_name
;
1183 size_t arg_index
= 0;
1184 int r
= utils::get_pool_and_namespace_names(vm
, true, false, &pool_name
,
1185 nullptr, &arg_index
);
1190 at::Format::Formatter formatter
;
1191 r
= utils::get_formatter(vm
, &formatter
);
1196 // TODO support namespaces
1197 librados::Rados rados
;
1198 librados::IoCtx io_ctx
;
1199 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
1205 rbd_mirror_mode_t mirror_mode
;
1206 r
= rbd
.mirror_mode_get(io_ctx
, &mirror_mode
);
1211 std::string site_name
;
1212 r
= rbd
.mirror_site_name_get(rados
, &site_name
);
1213 if (r
< 0 && r
!= -EOPNOTSUPP
) {
1217 std::vector
<librbd::mirror_peer_t
> mirror_peers
;
1218 r
= rbd
.mirror_peer_list(io_ctx
, &mirror_peers
);
1223 std::string mirror_mode_desc
;
1224 switch (mirror_mode
) {
1225 case RBD_MIRROR_MODE_DISABLED
:
1226 mirror_mode_desc
= "disabled";
1228 case RBD_MIRROR_MODE_IMAGE
:
1229 mirror_mode_desc
= "image";
1231 case RBD_MIRROR_MODE_POOL
:
1232 mirror_mode_desc
= "pool";
1235 mirror_mode_desc
= "unknown";
1239 if (formatter
!= nullptr) {
1240 formatter
->open_object_section("mirror");
1241 formatter
->dump_string("mode", mirror_mode_desc
);
1243 std::cout
<< "Mode: " << mirror_mode_desc
<< std::endl
;
1246 if (mirror_mode
!= RBD_MIRROR_MODE_DISABLED
) {
1247 if (formatter
!= nullptr) {
1248 formatter
->dump_string("site_name", site_name
);
1250 std::cout
<< "Site Name: " << site_name
<< std::endl
;
1253 r
= format_mirror_peers(io_ctx
, formatter
, mirror_peers
,
1254 vm
[ALL_NAME
].as
<bool>());
1259 if (formatter
!= nullptr) {
1260 formatter
->close_section();
1261 formatter
->flush(std::cout
);
1266 void get_status_arguments(po::options_description
*positional
,
1267 po::options_description
*options
) {
1268 at::add_pool_options(positional
, options
, false);
1269 at::add_format_options(options
);
1270 at::add_verbose_option(options
);
1273 int execute_status(const po::variables_map
&vm
,
1274 const std::vector
<std::string
> &ceph_global_init_args
) {
1275 std::string pool_name
;
1276 size_t arg_index
= 0;
1277 int r
= utils::get_pool_and_namespace_names(vm
, true, false, &pool_name
,
1278 nullptr, &arg_index
);
1283 at::Format::Formatter formatter
;
1284 r
= utils::get_formatter(vm
, &formatter
);
1289 bool verbose
= vm
[at::VERBOSE
].as
<bool>();
1291 // TODO support namespaces
1292 librados::Rados rados
;
1293 librados::IoCtx io_ctx
;
1294 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
1299 r
= validate_mirroring_enabled(io_ctx
);
1306 std::map
<librbd::mirror_image_status_state_t
, int> states
;
1307 r
= rbd
.mirror_image_status_summary(io_ctx
, &states
);
1309 std::cerr
<< "rbd: failed to get status summary for mirrored images: "
1310 << cpp_strerror(r
) << std::endl
;
1314 if (formatter
!= nullptr) {
1315 formatter
->open_object_section("status");
1318 enum Health
{Ok
= 0, Warning
= 1, Error
= 2} health
= Ok
;
1319 const char *names
[] = {"OK", "WARNING", "ERROR"};
1322 for (auto &it
: states
) {
1323 auto &state
= it
.first
;
1324 if (health
< Warning
&&
1325 (state
!= MIRROR_IMAGE_STATUS_STATE_REPLAYING
&&
1326 state
!= MIRROR_IMAGE_STATUS_STATE_STOPPED
)) {
1329 if (health
< Error
&&
1330 state
== MIRROR_IMAGE_STATUS_STATE_ERROR
) {
1336 if (formatter
!= nullptr) {
1337 formatter
->open_object_section("summary");
1338 formatter
->dump_string("health", names
[health
]);
1339 formatter
->open_object_section("states");
1340 for (auto &it
: states
) {
1341 std::string state_name
= utils::mirror_image_status_state(it
.first
);
1342 formatter
->dump_int(state_name
.c_str(), it
.second
);
1344 formatter
->close_section(); // states
1345 formatter
->close_section(); // summary
1347 std::cout
<< "health: " << names
[health
] << std::endl
;
1348 std::cout
<< "images: " << total
<< " total" << std::endl
;
1349 for (auto &it
: states
) {
1350 std::cout
<< " " << it
.second
<< " "
1351 << utils::mirror_image_status_state(it
.first
) << std::endl
;
1358 if (formatter
!= nullptr) {
1359 formatter
->open_array_section("images");
1362 std::map
<std::string
, std::string
> instance_ids
;
1363 MirrorDaemonServiceInfo
daemon_service_info(io_ctx
);
1365 std::string start_image_id
;
1367 std::map
<std::string
, std::string
> ids
;
1368 r
= rbd
.mirror_image_instance_id_list(io_ctx
, start_image_id
, 1024, &ids
);
1370 if (r
== -EOPNOTSUPP
) {
1371 std::cerr
<< "rbd: newer release of Ceph OSDs required to map image "
1372 << "to rbd-mirror daemon instance" << std::endl
;
1374 std::cerr
<< "rbd: failed to get instance id list: "
1375 << cpp_strerror(r
) << std::endl
;
1383 instance_ids
.insert(ids
.begin(), ids
.end());
1384 start_image_id
= ids
.rbegin()->first
;
1387 if (!instance_ids
.empty()) {
1388 daemon_service_info
.init();
1391 ImageRequestGenerator
<StatusImageRequest
> generator(
1392 io_ctx
, instance_ids
, daemon_service_info
, formatter
);
1393 ret
= generator
.execute();
1395 if (formatter
!= nullptr) {
1396 formatter
->close_section(); // images
1400 if (formatter
!= nullptr) {
1401 formatter
->close_section(); // status
1402 formatter
->flush(std::cout
);
1408 void get_promote_arguments(po::options_description
*positional
,
1409 po::options_description
*options
) {
1410 options
->add_options()
1411 ("force", po::bool_switch(),
1412 "promote even if not cleanly demoted by remote cluster");
1413 at::add_pool_options(positional
, options
, false);
1416 int execute_promote(const po::variables_map
&vm
,
1417 const std::vector
<std::string
> &ceph_global_init_args
) {
1418 std::string pool_name
;
1419 size_t arg_index
= 0;
1420 int r
= utils::get_pool_and_namespace_names(vm
, true, true, &pool_name
,
1421 nullptr, &arg_index
);
1426 // TODO support namespaces
1427 librados::Rados rados
;
1428 librados::IoCtx io_ctx
;
1429 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
1434 r
= validate_mirroring_enabled(io_ctx
);
1439 utils::disable_cache();
1441 std::atomic
<unsigned> counter
= { 0 };
1442 ImageRequestGenerator
<PromoteImageRequest
> generator(io_ctx
, &counter
,
1443 vm
["force"].as
<bool>());
1444 r
= generator
.execute();
1446 std::cout
<< "Promoted " << counter
.load() << " mirrored images" << std::endl
;
1450 void get_demote_arguments(po::options_description
*positional
,
1451 po::options_description
*options
) {
1452 at::add_pool_options(positional
, options
, false);
1455 int execute_demote(const po::variables_map
&vm
,
1456 const std::vector
<std::string
> &ceph_global_init_args
) {
1457 std::string pool_name
;
1458 size_t arg_index
= 0;
1459 int r
= utils::get_pool_and_namespace_names(vm
, true, true, &pool_name
,
1460 nullptr, &arg_index
);
1465 // TODO support namespaces
1466 librados::Rados rados
;
1467 librados::IoCtx io_ctx
;
1468 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
1473 r
= validate_mirroring_enabled(io_ctx
);
1478 utils::disable_cache();
1480 std::atomic
<unsigned> counter
{ 0 };
1481 ImageRequestGenerator
<DemoteImageRequest
> generator(io_ctx
, &counter
);
1482 r
= generator
.execute();
1484 std::cout
<< "Demoted " << counter
.load() << " mirrored images" << std::endl
;
1488 Shell::Action
action_bootstrap_create(
1489 {"mirror", "pool", "peer", "bootstrap", "create"}, {},
1490 "Create a peer bootstrap token to import in a remote cluster", "",
1491 &get_peer_bootstrap_create_arguments
, &execute_peer_bootstrap_create
);
1492 Shell::Action
action_bootstreap_import(
1493 {"mirror", "pool", "peer", "bootstrap", "import"}, {},
1494 "Import a peer bootstrap token created from a remote cluster", "",
1495 &get_peer_bootstrap_import_arguments
, &execute_peer_bootstrap_import
);
1497 Shell::Action
action_add(
1498 {"mirror", "pool", "peer", "add"}, {},
1499 "Add a mirroring peer to a pool.", "",
1500 &get_peer_add_arguments
, &execute_peer_add
);
1501 Shell::Action
action_remove(
1502 {"mirror", "pool", "peer", "remove"}, {},
1503 "Remove a mirroring peer from a pool.", "",
1504 &get_peer_remove_arguments
, &execute_peer_remove
);
1505 Shell::Action
action_set(
1506 {"mirror", "pool", "peer", "set"}, {},
1507 "Update mirroring peer settings.", "",
1508 &get_peer_set_arguments
, &execute_peer_set
);
1510 Shell::Action
action_disable(
1511 {"mirror", "pool", "disable"}, {},
1512 "Disable RBD mirroring by default within a pool.", "",
1513 &get_disable_arguments
, &execute_disable
);
1514 Shell::Action
action_enable(
1515 {"mirror", "pool", "enable"}, {},
1516 "Enable RBD mirroring by default within a pool.", "",
1517 &get_enable_arguments
, &execute_enable
);
1518 Shell::Action
action_info(
1519 {"mirror", "pool", "info"}, {},
1520 "Show information about the pool mirroring configuration.", {},
1521 &get_info_arguments
, &execute_info
);
1522 Shell::Action
action_status(
1523 {"mirror", "pool", "status"}, {},
1524 "Show status for all mirrored images in the pool.", {},
1525 &get_status_arguments
, &execute_status
);
1526 Shell::Action
action_promote(
1527 {"mirror", "pool", "promote"}, {},
1528 "Promote all non-primary images in the pool.", {},
1529 &get_promote_arguments
, &execute_promote
);
1530 Shell::Action
action_demote(
1531 {"mirror", "pool", "demote"}, {},
1532 "Demote all primary images in the pool.", {},
1533 &get_demote_arguments
, &execute_demote
);
1535 } // namespace mirror_pool
1536 } // namespace action