]> git.proxmox.com Git - ceph.git/blob - ceph/src/tools/rbd/action/MirrorPool.cc
0090a42060b9716886e73eb57b482ab2ba64e4fc
[ceph.git] / ceph / src / tools / rbd / action / MirrorPool.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "tools/rbd/ArgumentTypes.h"
5 #include "tools/rbd/Shell.h"
6 #include "tools/rbd/Utils.h"
7 #include "include/Context.h"
8 #include "include/stringify.h"
9 #include "include/rbd/librbd.hpp"
10 #include "common/config.h"
11 #include "common/debug.h"
12 #include "common/errno.h"
13 #include "common/Formatter.h"
14 #include "common/TextTable.h"
15 #include "common/Throttle.h"
16 #include "global/global_context.h"
17 #include <functional>
18 #include <iostream>
19 #include <boost/program_options.hpp>
20 #include <boost/regex.hpp>
21 #include "include/assert.h"
22
23 #include <atomic>
24
25 #define dout_context g_ceph_context
26 #define dout_subsys ceph_subsys_rbd
27 #undef dout_prefix
28 #define dout_prefix *_dout << "rbd::action::MirrorPool: "
29
30 namespace rbd {
31 namespace action {
32 namespace mirror_pool {
33
34 namespace at = argument_types;
35 namespace po = boost::program_options;
36
37 namespace {
38
39 int validate_uuid(const std::string &uuid) {
40 boost::regex pattern("^[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}$",
41 boost::regex::icase);
42 boost::smatch match;
43 if (!boost::regex_match(uuid, match, pattern)) {
44 std::cerr << "rbd: invalid uuid '" << uuid << "'" << std::endl;
45 return -EINVAL;
46 }
47 return 0;
48 }
49
50 void add_uuid_option(po::options_description *positional) {
51 positional->add_options()
52 ("uuid", po::value<std::string>(), "peer uuid");
53 }
54
55 int get_uuid(const po::variables_map &vm, size_t arg_index,
56 std::string *uuid) {
57 *uuid = utils::get_positional_argument(vm, arg_index);
58 if (uuid->empty()) {
59 std::cerr << "rbd: must specify peer uuid" << std::endl;
60 return -EINVAL;
61 }
62 return validate_uuid(*uuid);
63 }
64
65 int get_remote_cluster_spec(const po::variables_map &vm,
66 const std::string &spec,
67 std::string *remote_client_name,
68 std::string *remote_cluster) {
69 if (vm.count("remote-client-name")) {
70 *remote_client_name = vm["remote-client-name"].as<std::string>();
71 }
72 if (vm.count("remote-cluster")) {
73 *remote_cluster = vm["remote-cluster"].as<std::string>();
74 }
75
76 if (!spec.empty()) {
77 boost::regex pattern("^(?:(client\\.[^@]+)@)?([^/@]+)$");
78 boost::smatch match;
79 if (!boost::regex_match(spec, match, pattern)) {
80 std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl;
81 return -EINVAL;
82 }
83 if (match[1].matched) {
84 *remote_client_name = match[1];
85 }
86 *remote_cluster = match[2];
87 }
88
89 if (remote_cluster->empty()) {
90 std::cerr << "rbd: remote cluster was not specified" << std::endl;
91 return -EINVAL;
92 }
93 return 0;
94 }
95
96 void format_mirror_peers(const std::string &config_path,
97 at::Format::Formatter formatter,
98 const std::vector<librbd::mirror_peer_t> &peers) {
99 if (formatter != nullptr) {
100 formatter->open_array_section("peers");
101 for (auto &peer : peers) {
102 formatter->open_object_section("peer");
103 formatter->dump_string("uuid", peer.uuid);
104 formatter->dump_string("cluster_name", peer.cluster_name);
105 formatter->dump_string("client_name", peer.client_name);
106 formatter->close_section();
107 }
108 formatter->close_section();
109 } else {
110 std::cout << "Peers: ";
111 if (peers.empty()) {
112 std::cout << "none" << std::endl;
113 } else {
114 TextTable tbl;
115 tbl.define_column("", TextTable::LEFT, TextTable::LEFT);
116 tbl.define_column("UUID", TextTable::LEFT, TextTable::LEFT);
117 tbl.define_column("NAME", TextTable::LEFT, TextTable::LEFT);
118 tbl.define_column("CLIENT", TextTable::LEFT, TextTable::LEFT);
119 for (auto &peer : peers) {
120 tbl << " "
121 << peer.uuid
122 << peer.cluster_name
123 << peer.client_name
124 << TextTable::endrow;
125 }
126 std::cout << std::endl << tbl;
127 }
128 }
129 }
130
131 class ImageRequestBase {
132 public:
133 void send() {
134 dout(20) << this << " " << __func__ << ": image_name=" << m_image_name
135 << dendl;
136
137 auto ctx = new FunctionContext([this](int r) {
138 handle_finalize(r);
139 });
140
141 // will pause here until slots are available
142 m_finalize_ctx = m_throttle.start_op(ctx);
143
144 open_image();
145 }
146
147 protected:
148 ImageRequestBase(librados::IoCtx &io_ctx, OrderedThrottle &throttle,
149 const std::string &image_name)
150 : m_io_ctx(io_ctx), m_throttle(throttle), m_image_name(image_name) {
151 }
152 virtual ~ImageRequestBase() {
153 }
154
155 virtual bool skip_get_info() const {
156 return false;
157 }
158 virtual void get_info(librbd::Image &image, librbd::mirror_image_info_t *info,
159 librbd::RBD::AioCompletion *aio_comp) {
160 image.aio_mirror_image_get_info(info, sizeof(librbd::mirror_image_info_t),
161 aio_comp);
162 }
163
164 virtual bool skip_action(const librbd::mirror_image_info_t &info) const {
165 return false;
166 }
167 virtual void execute_action(librbd::Image &image,
168 librbd::RBD::AioCompletion *aio_comp) = 0;
169 virtual void handle_execute_action(int r) {
170 dout(20) << this << " " << __func__ << ": r=" << r << dendl;
171
172 if (r < 0 && r != -ENOENT) {
173 std::cerr << "rbd: failed to " << get_action_type() << " image "
174 << m_image_name << ": " << cpp_strerror(r) << std::endl;
175 m_ret_val = r;
176 }
177
178 close_image();
179 }
180
181 virtual void finalize_action() {
182 }
183 virtual std::string get_action_type() const = 0;
184
185 private:
186 /**
187 * @verbatim
188 *
189 * <start>
190 * |
191 * v
192 * OPEN_IMAGE
193 * |
194 * v
195 * GET_INFO
196 * |
197 * v
198 * EXECUTE_ACTION
199 * |
200 * v
201 * CLOSE_IMAGE
202 * |
203 * v
204 * FINALIZE_ACTION
205 * |
206 * v
207 * <finish>
208 *
209 * @endverbatim
210 */
211
212 librados::IoCtx &m_io_ctx;
213 OrderedThrottle &m_throttle;
214 const std::string m_image_name;
215
216 librbd::Image m_image;
217 Context *m_finalize_ctx;
218
219 librbd::mirror_image_info_t m_mirror_image_info;
220
221 int m_ret_val = 0;
222
223 void open_image() {
224 dout(20) << this << " " << __func__ << dendl;
225
226 librbd::RBD rbd;
227 auto aio_completion = utils::create_aio_completion<
228 ImageRequestBase, &ImageRequestBase::handle_open_image>(this);
229 rbd.aio_open(m_io_ctx, m_image, m_image_name.c_str(), nullptr,
230 aio_completion);
231 }
232
233 void handle_open_image(int r) {
234 dout(20) << this << " " << __func__ << ": r=" << r << dendl;
235
236 if (r < 0) {
237 std::cerr << "rbd: failed to open image "
238 << m_image_name << ": " << cpp_strerror(r) << std::endl;
239 m_finalize_ctx->complete(r);
240 return;
241 }
242
243 get_info();
244 }
245
246 void get_info() {
247 if (skip_get_info()) {
248 execute_action();
249 return;
250 }
251 dout(20) << this << " " << __func__ << dendl;
252
253 auto aio_completion = utils::create_aio_completion<
254 ImageRequestBase, &ImageRequestBase::handle_get_info>(this);
255 get_info(m_image, &m_mirror_image_info, aio_completion);
256 }
257
258 void handle_get_info(int r) {
259 dout(20) << this << " " << __func__ << ": r=" << r << dendl;
260
261 if (r == -ENOENT) {
262 close_image();
263 return;
264 } else if (r < 0) {
265 std::cerr << "rbd: failed to retrieve mirror image info for "
266 << m_image_name << ": " << cpp_strerror(r) << std::endl;
267 m_ret_val = r;
268 close_image();
269 return;
270 }
271
272 execute_action();
273 }
274
275 void execute_action() {
276 if (skip_action(m_mirror_image_info)) {
277 close_image();
278 return;
279 }
280 dout(20) << this << " " << __func__ << dendl;
281
282 auto aio_completion = utils::create_aio_completion<
283 ImageRequestBase, &ImageRequestBase::handle_execute_action>(this);
284 execute_action(m_image, aio_completion);
285 }
286
287 void close_image() {
288 dout(20) << this << " " << __func__ << dendl;
289
290 auto aio_completion = utils::create_aio_completion<
291 ImageRequestBase, &ImageRequestBase::handle_close_image>(this);
292 m_image.aio_close(aio_completion);
293 }
294
295 void handle_close_image(int r) {
296 dout(20) << this << " " << __func__ << ": r=" << r << dendl;
297
298 if (r < 0) {
299 std::cerr << "rbd: failed to close image "
300 << m_image_name << ": " << cpp_strerror(r) << std::endl;
301 }
302
303 m_finalize_ctx->complete(r);
304 }
305
306 void handle_finalize(int r) {
307 dout(20) << this << " " << __func__ << ": r=" << r << dendl;
308
309 if (r == 0 && m_ret_val < 0) {
310 r = m_ret_val;
311 }
312 if (r >= 0) {
313 finalize_action();
314 }
315 m_throttle.end_op(r);
316 }
317
318 };
319
320 class PromoteImageRequest : public ImageRequestBase {
321 public:
322 PromoteImageRequest(librados::IoCtx &io_ctx, OrderedThrottle &throttle,
323 const std::string &image_name, std::atomic<unsigned> *counter,
324 bool force)
325 : ImageRequestBase(io_ctx, throttle, image_name), m_counter(counter),
326 m_force(force) {
327 }
328
329 protected:
330 bool skip_action(const librbd::mirror_image_info_t &info) const override {
331 return info.primary;
332 }
333
334 void execute_action(librbd::Image &image,
335 librbd::RBD::AioCompletion *aio_comp) override {
336 image.aio_mirror_image_promote(m_force, aio_comp);
337 }
338
339 void handle_execute_action(int r) override {
340 if (r >= 0) {
341 (*m_counter)++;
342 }
343 }
344
345 std::string get_action_type() const override {
346 return "promote";
347 }
348
349 private:
350 std::atomic<unsigned> *m_counter = nullptr;
351 bool m_force;
352 };
353
354 class DemoteImageRequest : public ImageRequestBase {
355 public:
356 DemoteImageRequest(librados::IoCtx &io_ctx, OrderedThrottle &throttle,
357 const std::string &image_name, std::atomic<unsigned> *counter)
358 : ImageRequestBase(io_ctx, throttle, image_name), m_counter(counter) {
359 }
360
361 protected:
362 bool skip_action(const librbd::mirror_image_info_t &info) const override {
363 return !info.primary;
364 }
365
366 void execute_action(librbd::Image &image,
367 librbd::RBD::AioCompletion *aio_comp) override {
368 image.aio_mirror_image_demote(aio_comp);
369 }
370 void handle_execute_action(int r) override {
371 if (r >= 0) {
372 (*m_counter)++;
373 }
374 ImageRequestBase::handle_execute_action(r);
375 }
376
377 std::string get_action_type() const override {
378 return "demote";
379 }
380
381 private:
382 std::atomic<unsigned> *m_counter = nullptr;
383 };
384
385 class StatusImageRequest : public ImageRequestBase {
386 public:
387 StatusImageRequest(librados::IoCtx &io_ctx, OrderedThrottle &throttle,
388 const std::string &image_name,
389 at::Format::Formatter formatter)
390 : ImageRequestBase(io_ctx, throttle, image_name),
391 m_formatter(formatter) {
392 }
393
394 protected:
395 bool skip_get_info() const override {
396 return true;
397 }
398
399 void execute_action(librbd::Image &image,
400 librbd::RBD::AioCompletion *aio_comp) override {
401 image.aio_mirror_image_get_status(&m_mirror_image_status,
402 sizeof(m_mirror_image_status), aio_comp);
403 }
404
405 void finalize_action() override {
406 std::string state = utils::mirror_image_status_state(m_mirror_image_status);
407 std::string last_update = (
408 m_mirror_image_status.last_update == 0 ?
409 "" : utils::timestr(m_mirror_image_status.last_update));
410
411 if (m_formatter != nullptr) {
412 m_formatter->open_object_section("image");
413 m_formatter->dump_string("name", m_mirror_image_status.name);
414 m_formatter->dump_string("global_id",
415 m_mirror_image_status.info.global_id);
416 m_formatter->dump_string("state", state);
417 m_formatter->dump_string("description",
418 m_mirror_image_status.description);
419 m_formatter->dump_string("last_update", last_update);
420 m_formatter->close_section(); // image
421 } else {
422 std::cout << "\n" << m_mirror_image_status.name << ":\n"
423 << " global_id: "
424 << m_mirror_image_status.info.global_id << "\n"
425 << " state: " << state << "\n"
426 << " description: "
427 << m_mirror_image_status.description << "\n"
428 << " last_update: " << last_update << std::endl;
429 }
430 }
431
432 std::string get_action_type() const override {
433 return "status";
434 }
435
436 private:
437 at::Format::Formatter m_formatter;
438 librbd::mirror_image_status_t m_mirror_image_status;
439
440 };
441
442 template <typename RequestT>
443 class ImageRequestAllocator {
444 public:
445 template <class... Args>
446 RequestT *operator()(librados::IoCtx &io_ctx, OrderedThrottle &throttle,
447 const std::string &image_name, Args&&... args) {
448 return new RequestT(io_ctx, throttle, image_name,
449 std::forward<Args>(args)...);
450 }
451 };
452
453 template <typename RequestT>
454 class ImageRequestGenerator {
455 public:
456 template <class... Args>
457 ImageRequestGenerator(librados::IoCtx &io_ctx, Args&&... args)
458 : m_io_ctx(io_ctx),
459 m_factory(std::bind(ImageRequestAllocator<RequestT>(),
460 std::ref(m_io_ctx), std::ref(m_throttle),
461 std::placeholders::_1, std::forward<Args>(args)...)),
462 m_throttle(g_conf->rbd_concurrent_management_ops, true) {
463 }
464
465 int execute() {
466 // use the alphabetical list of image names for pool-level
467 // mirror image operations
468 librbd::RBD rbd;
469 int r = rbd.list(m_io_ctx, m_image_names);
470 if (r < 0 && r != -ENOENT) {
471 std::cerr << "rbd: failed to list images within pool" << std::endl;
472 return r;
473 }
474
475 for (auto &image_name : m_image_names) {
476 auto request = m_factory(image_name);
477 request->send();
478 }
479
480 return m_throttle.wait_for_ret();
481 }
482 private:
483 typedef std::function<RequestT*(const std::string&)> Factory;
484
485 librados::IoCtx &m_io_ctx;
486 Factory m_factory;
487
488 OrderedThrottle m_throttle;
489
490 std::vector<std::string> m_image_names;
491
492 };
493
494 } // anonymous namespace
495
496 void get_peer_add_arguments(po::options_description *positional,
497 po::options_description *options) {
498 at::add_pool_options(positional, options);
499 positional->add_options()
500 ("remote-cluster-spec", "remote cluster spec\n"
501 "(example: [<client name>@]<cluster name>");
502 options->add_options()
503 ("remote-client-name", po::value<std::string>(), "remote client name")
504 ("remote-cluster", po::value<std::string>(), "remote cluster name");
505 }
506
507 int execute_peer_add(const po::variables_map &vm) {
508 size_t arg_index = 0;
509 std::string pool_name = utils::get_pool_name(vm, &arg_index);
510
511 std::string remote_client_name = g_ceph_context->_conf->name.to_str();
512 std::string remote_cluster;
513 int r = get_remote_cluster_spec(
514 vm, utils::get_positional_argument(vm, arg_index),
515 &remote_client_name, &remote_cluster);
516 if (r < 0) {
517 return r;
518 }
519
520 std::string config_path;
521 if (vm.count(at::CONFIG_PATH)) {
522 config_path = vm[at::CONFIG_PATH].as<std::string>();
523 }
524
525 librados::Rados rados;
526 librados::IoCtx io_ctx;
527 r = utils::init(pool_name, &rados, &io_ctx);
528 if (r < 0) {
529 return r;
530 }
531
532 librbd::RBD rbd;
533 rbd_mirror_mode_t mirror_mode;
534 r = rbd.mirror_mode_get(io_ctx, &mirror_mode);
535 if (r < 0) {
536 std::cerr << "rbd: failed to retrieve mirror mode: "
537 << cpp_strerror(r) << std::endl;
538 return r;
539 }
540
541 if (mirror_mode == RBD_MIRROR_MODE_DISABLED) {
542 std::cerr << "rbd: failed to add mirror peer: "
543 << "mirroring must be enabled on the pool "
544 << pool_name << std::endl;
545 return -EINVAL;
546 }
547
548 // TODO: temporary restriction to prevent adding multiple peers
549 // until rbd-mirror daemon can properly handle the scenario
550 std::vector<librbd::mirror_peer_t> mirror_peers;
551 r = rbd.mirror_peer_list(io_ctx, &mirror_peers);
552 if (r < 0) {
553 std::cerr << "rbd: failed to list mirror peers" << std::endl;
554 return r;
555 }
556 if (!mirror_peers.empty()) {
557 std::cerr << "rbd: multiple peers are not currently supported" << std::endl;
558 return -EINVAL;
559 }
560
561 std::string uuid;
562 r = rbd.mirror_peer_add(io_ctx, &uuid, remote_cluster, remote_client_name);
563 if (r < 0) {
564 std::cerr << "rbd: error adding mirror peer" << std::endl;
565 return r;
566 }
567
568 std::cout << uuid << std::endl;
569 return 0;
570 }
571
572 void get_peer_remove_arguments(po::options_description *positional,
573 po::options_description *options) {
574 at::add_pool_options(positional, options);
575 add_uuid_option(positional);
576 }
577
578 int execute_peer_remove(const po::variables_map &vm) {
579 size_t arg_index = 0;
580 std::string pool_name = utils::get_pool_name(vm, &arg_index);
581
582 std::string uuid;
583 int r = get_uuid(vm, arg_index, &uuid);
584 if (r < 0) {
585 return r;
586 }
587
588 librados::Rados rados;
589 librados::IoCtx io_ctx;
590 r = utils::init(pool_name, &rados, &io_ctx);
591 if (r < 0) {
592 return r;
593 }
594
595 librbd::RBD rbd;
596 r = rbd.mirror_peer_remove(io_ctx, uuid);
597 if (r < 0) {
598 std::cerr << "rbd: error removing mirror peer" << std::endl;
599 return r;
600 }
601 return 0;
602 }
603
604 void get_peer_set_arguments(po::options_description *positional,
605 po::options_description *options) {
606 at::add_pool_options(positional, options);
607 add_uuid_option(positional);
608 positional->add_options()
609 ("key", "peer parameter [client or cluster]")
610 ("value", "new client or cluster name");
611 }
612
613 int execute_peer_set(const po::variables_map &vm) {
614 size_t arg_index = 0;
615 std::string pool_name = utils::get_pool_name(vm, &arg_index);
616
617 std::string uuid;
618 int r = get_uuid(vm, arg_index++, &uuid);
619 if (r < 0) {
620 return r;
621 }
622
623 std::string key = utils::get_positional_argument(vm, arg_index++);
624 if (key != "client" && key != "cluster") {
625 std::cerr << "rbd: must specify 'client' or 'cluster' key." << std::endl;
626 return -EINVAL;
627 }
628
629 std::string value = utils::get_positional_argument(vm, arg_index++);
630 if (value.empty()) {
631 std::cerr << "rbd: must specify new " << key << " value." << std::endl;
632 }
633
634 librados::Rados rados;
635 librados::IoCtx io_ctx;
636 r = utils::init(pool_name, &rados, &io_ctx);
637 if (r < 0) {
638 return r;
639 }
640
641 librbd::RBD rbd;
642 if (key == "client") {
643 r = rbd.mirror_peer_set_client(io_ctx, uuid.c_str(), value.c_str());
644 } else {
645 r = rbd.mirror_peer_set_cluster(io_ctx, uuid.c_str(), value.c_str());
646 }
647 if (r < 0) {
648 return r;
649 }
650 return 0;
651 }
652
653 void get_disable_arguments(po::options_description *positional,
654 po::options_description *options) {
655 at::add_pool_options(positional, options);
656 }
657
658 void get_enable_arguments(po::options_description *positional,
659 po::options_description *options) {
660 at::add_pool_options(positional, options);
661 positional->add_options()
662 ("mode", "mirror mode [image or pool]");
663 }
664
665 int execute_enable_disable(const std::string &pool_name,
666 rbd_mirror_mode_t next_mirror_mode,
667 const std::string &mode) {
668 librados::Rados rados;
669 librados::IoCtx io_ctx;
670 rbd_mirror_mode_t current_mirror_mode;
671
672 int r = utils::init(pool_name, &rados, &io_ctx);
673 if (r < 0) {
674 return r;
675 }
676
677 librbd::RBD rbd;
678 r = rbd.mirror_mode_get(io_ctx, &current_mirror_mode);
679 if (r < 0) {
680 std::cerr << "rbd: failed to retrieve mirror mode: "
681 << cpp_strerror(r) << std::endl;
682 return r;
683 }
684
685 if (current_mirror_mode == next_mirror_mode) {
686 if (mode == "disabled") {
687 std::cout << "mirroring is already " << mode << std::endl;
688 } else {
689 std::cout << "mirroring is already configured for "
690 << mode << " mode" << std::endl;
691 }
692 return 0;
693 } else if (next_mirror_mode == RBD_MIRROR_MODE_IMAGE &&
694 current_mirror_mode == RBD_MIRROR_MODE_POOL) {
695 std::cout << "note: changing mirroring mode from pool to image"
696 << std::endl;
697 } else if (next_mirror_mode == RBD_MIRROR_MODE_POOL &&
698 current_mirror_mode == RBD_MIRROR_MODE_IMAGE) {
699 std::cout << "note: changing mirroring mode from image to pool"
700 << std::endl;
701 }
702
703 r = rbd.mirror_mode_set(io_ctx, next_mirror_mode);
704 if (r < 0) {
705 return r;
706 }
707 return 0;
708 }
709
710 int execute_disable(const po::variables_map &vm) {
711 size_t arg_index = 0;
712 std::string pool_name = utils::get_pool_name(vm, &arg_index);
713
714 return execute_enable_disable(pool_name, RBD_MIRROR_MODE_DISABLED,
715 "disabled");
716 }
717
718 int execute_enable(const po::variables_map &vm) {
719 size_t arg_index = 0;
720 std::string pool_name = utils::get_pool_name(vm, &arg_index);
721
722 rbd_mirror_mode_t mirror_mode;
723 std::string mode = utils::get_positional_argument(vm, arg_index++);
724 if (mode == "image") {
725 mirror_mode = RBD_MIRROR_MODE_IMAGE;
726 } else if (mode == "pool") {
727 mirror_mode = RBD_MIRROR_MODE_POOL;
728 } else {
729 std::cerr << "rbd: must specify 'image' or 'pool' mode." << std::endl;
730 return -EINVAL;
731 }
732
733 return execute_enable_disable(pool_name, mirror_mode, mode);
734 }
735
736 void get_info_arguments(po::options_description *positional,
737 po::options_description *options) {
738 at::add_pool_options(positional, options);
739 at::add_format_options(options);
740 }
741
742 int execute_info(const po::variables_map &vm) {
743 size_t arg_index = 0;
744 std::string pool_name = utils::get_pool_name(vm, &arg_index);
745
746 at::Format::Formatter formatter;
747 int r = utils::get_formatter(vm, &formatter);
748 if (r < 0) {
749 return r;
750 }
751
752 std::string config_path;
753 if (vm.count(at::CONFIG_PATH)) {
754 config_path = vm[at::CONFIG_PATH].as<std::string>();
755 }
756
757 librados::Rados rados;
758 librados::IoCtx io_ctx;
759 r = utils::init(pool_name, &rados, &io_ctx);
760 if (r < 0) {
761 return r;
762 }
763
764 librbd::RBD rbd;
765 rbd_mirror_mode_t mirror_mode;
766 r = rbd.mirror_mode_get(io_ctx, &mirror_mode);
767 if (r < 0) {
768 return r;
769 }
770
771 std::vector<librbd::mirror_peer_t> mirror_peers;
772 r = rbd.mirror_peer_list(io_ctx, &mirror_peers);
773 if (r < 0) {
774 return r;
775 }
776
777 std::string mirror_mode_desc;
778 switch (mirror_mode) {
779 case RBD_MIRROR_MODE_DISABLED:
780 mirror_mode_desc = "disabled";
781 break;
782 case RBD_MIRROR_MODE_IMAGE:
783 mirror_mode_desc = "image";
784 break;
785 case RBD_MIRROR_MODE_POOL:
786 mirror_mode_desc = "pool";
787 break;
788 default:
789 mirror_mode_desc = "unknown";
790 break;
791 }
792
793 if (formatter != nullptr) {
794 formatter->open_object_section("mirror");
795 formatter->dump_string("mode", mirror_mode_desc);
796 } else {
797 std::cout << "Mode: " << mirror_mode_desc << std::endl;
798 }
799
800 if (mirror_mode != RBD_MIRROR_MODE_DISABLED) {
801 format_mirror_peers(config_path, formatter, mirror_peers);
802 }
803 if (formatter != nullptr) {
804 formatter->close_section();
805 formatter->flush(std::cout);
806 }
807 return 0;
808 }
809
810 void get_status_arguments(po::options_description *positional,
811 po::options_description *options) {
812 at::add_pool_options(positional, options);
813 at::add_format_options(options);
814 at::add_verbose_option(options);
815 }
816
817 int execute_status(const po::variables_map &vm) {
818 size_t arg_index = 0;
819 std::string pool_name = utils::get_pool_name(vm, &arg_index);
820
821 at::Format::Formatter formatter;
822 int r = utils::get_formatter(vm, &formatter);
823 if (r < 0) {
824 return r;
825 }
826
827 bool verbose = vm[at::VERBOSE].as<bool>();
828
829 std::string config_path;
830 if (vm.count(at::CONFIG_PATH)) {
831 config_path = vm[at::CONFIG_PATH].as<std::string>();
832 }
833
834 librados::Rados rados;
835 librados::IoCtx io_ctx;
836 r = utils::init(pool_name, &rados, &io_ctx);
837 if (r < 0) {
838 return r;
839 }
840
841 librbd::RBD rbd;
842
843 std::map<librbd::mirror_image_status_state_t, int> states;
844 r = rbd.mirror_image_status_summary(io_ctx, &states);
845 if (r < 0) {
846 std::cerr << "rbd: failed to get status summary for mirrored images: "
847 << cpp_strerror(r) << std::endl;
848 return r;
849 }
850
851 if (formatter != nullptr) {
852 formatter->open_object_section("status");
853 }
854
855 enum Health {Ok = 0, Warning = 1, Error = 2} health = Ok;
856 const char *names[] = {"OK", "WARNING", "ERROR"};
857 int total = 0;
858
859 for (auto &it : states) {
860 auto &state = it.first;
861 if (health < Warning &&
862 (state != MIRROR_IMAGE_STATUS_STATE_REPLAYING &&
863 state != MIRROR_IMAGE_STATUS_STATE_STOPPED)) {
864 health = Warning;
865 }
866 if (health < Error &&
867 state == MIRROR_IMAGE_STATUS_STATE_ERROR) {
868 health = Error;
869 }
870 total += it.second;
871 }
872
873 if (formatter != nullptr) {
874 formatter->open_object_section("summary");
875 formatter->dump_string("health", names[health]);
876 formatter->open_object_section("states");
877 for (auto &it : states) {
878 std::string state_name = utils::mirror_image_status_state(it.first);
879 formatter->dump_int(state_name.c_str(), it.second);
880 }
881 formatter->close_section(); // states
882 formatter->close_section(); // summary
883 } else {
884 std::cout << "health: " << names[health] << std::endl;
885 std::cout << "images: " << total << " total" << std::endl;
886 for (auto &it : states) {
887 std::cout << " " << it.second << " "
888 << utils::mirror_image_status_state(it.first) << std::endl;
889 }
890 }
891
892 int ret = 0;
893
894 if (verbose) {
895 if (formatter != nullptr) {
896 formatter->open_array_section("images");
897 }
898
899 ImageRequestGenerator<StatusImageRequest> generator(io_ctx, formatter);
900 ret = generator.execute();
901
902 if (formatter != nullptr) {
903 formatter->close_section(); // images
904 }
905 }
906
907 if (formatter != nullptr) {
908 formatter->close_section(); // status
909 formatter->flush(std::cout);
910 }
911
912 return ret;
913 }
914
915 void get_promote_arguments(po::options_description *positional,
916 po::options_description *options) {
917 options->add_options()
918 ("force", po::bool_switch(),
919 "promote even if not cleanly demoted by remote cluster");
920 at::add_pool_options(positional, options);
921 }
922
923 int execute_promote(const po::variables_map &vm) {
924 size_t arg_index = 0;
925 std::string pool_name = utils::get_pool_name(vm, &arg_index);
926
927 librados::Rados rados;
928 librados::IoCtx io_ctx;
929 int r = utils::init(pool_name, &rados, &io_ctx);
930 if (r < 0) {
931 return r;
932 }
933
934 std::atomic<unsigned> counter = { 0 };
935 ImageRequestGenerator<PromoteImageRequest> generator(io_ctx, &counter,
936 vm["force"].as<bool>());
937 r = generator.execute();
938
939 std::cout << "Promoted " << counter.load() << " mirrored images" << std::endl;
940 return r;
941 }
942
943 void get_demote_arguments(po::options_description *positional,
944 po::options_description *options) {
945 at::add_pool_options(positional, options);
946 }
947
948 int execute_demote(const po::variables_map &vm) {
949 size_t arg_index = 0;
950 std::string pool_name = utils::get_pool_name(vm, &arg_index);
951
952 librados::Rados rados;
953 librados::IoCtx io_ctx;
954 int r = utils::init(pool_name, &rados, &io_ctx);
955 if (r < 0) {
956 return r;
957 }
958
959 std::atomic<unsigned> counter { 0 };
960 ImageRequestGenerator<DemoteImageRequest> generator(io_ctx, &counter);
961 r = generator.execute();
962
963 std::cout << "Demoted " << counter.load() << " mirrored images" << std::endl;
964 return r;
965 }
966
967 Shell::Action action_add(
968 {"mirror", "pool", "peer", "add"}, {},
969 "Add a mirroring peer to a pool.", "",
970 &get_peer_add_arguments, &execute_peer_add);
971 Shell::Action action_remove(
972 {"mirror", "pool", "peer", "remove"}, {},
973 "Remove a mirroring peer from a pool.", "",
974 &get_peer_remove_arguments, &execute_peer_remove);
975 Shell::Action action_set(
976 {"mirror", "pool", "peer", "set"}, {},
977 "Update mirroring peer settings.", "",
978 &get_peer_set_arguments, &execute_peer_set);
979
980 Shell::Action action_disable(
981 {"mirror", "pool", "disable"}, {},
982 "Disable RBD mirroring by default within a pool.", "",
983 &get_disable_arguments, &execute_disable);
984 Shell::Action action_enable(
985 {"mirror", "pool", "enable"}, {},
986 "Enable RBD mirroring by default within a pool.", "",
987 &get_enable_arguments, &execute_enable);
988 Shell::Action action_info(
989 {"mirror", "pool", "info"}, {},
990 "Show information about the pool mirroring configuration.", {},
991 &get_info_arguments, &execute_info);
992 Shell::Action action_status(
993 {"mirror", "pool", "status"}, {},
994 "Show status for all mirrored images in the pool.", {},
995 &get_status_arguments, &execute_status);
996 Shell::Action action_promote(
997 {"mirror", "pool", "promote"}, {},
998 "Promote all non-primary images in the pool.", {},
999 &get_promote_arguments, &execute_promote);
1000 Shell::Action action_demote(
1001 {"mirror", "pool", "demote"}, {},
1002 "Demote all primary images in the pool.", {},
1003 &get_demote_arguments, &execute_demote);
1004
1005 } // namespace mirror_pool
1006 } // namespace action
1007 } // namespace rbd