1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2016 SUSE LINUX GmbH
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
14 #include "tools/rbd/ArgumentTypes.h"
15 #include "tools/rbd/MirrorDaemonServiceInfo.h"
16 #include "tools/rbd/Shell.h"
17 #include "tools/rbd/Utils.h"
18 #include "include/stringify.h"
19 #include "common/config.h"
20 #include "common/errno.h"
21 #include "common/Formatter.h"
22 #include "common/TextTable.h"
23 #include "global/global_context.h"
25 #include <boost/program_options.hpp>
29 namespace mirror_image
{
31 namespace at
= argument_types
;
32 namespace po
= boost::program_options
;
36 int validate_mirroring_enabled(librbd::Image
&image
, bool snapshot
= false) {
37 librbd::mirror_image_info_t mirror_image
;
38 int r
= image
.mirror_image_get_info(&mirror_image
, sizeof(mirror_image
));
40 std::cerr
<< "rbd: failed to retrieve mirror info: "
41 << cpp_strerror(r
) << std::endl
;
45 if (mirror_image
.state
!= RBD_MIRROR_IMAGE_ENABLED
) {
46 std::cerr
<< "rbd: mirroring not enabled on the image" << std::endl
;
51 librbd::mirror_image_mode_t mode
;
52 r
= image
.mirror_image_get_mode(&mode
);
54 std::cerr
<< "rbd: failed to retrieve mirror mode: "
55 << cpp_strerror(r
) << std::endl
;
59 if (mode
!= RBD_MIRROR_IMAGE_MODE_SNAPSHOT
) {
60 std::cerr
<< "rbd: snapshot based mirroring not enabled on the image"
69 } // anonymous namespace
71 void get_arguments(po::options_description
*positional
,
72 po::options_description
*options
) {
73 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
76 void get_arguments_enable(po::options_description
*positional
,
77 po::options_description
*options
) {
78 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
79 positional
->add_options()
80 ("mode", "mirror image mode (journal or snapshot) [default: journal]");
83 void get_arguments_disable(po::options_description
*positional
,
84 po::options_description
*options
) {
85 options
->add_options()
86 ("force", po::bool_switch(), "disable even if not primary");
87 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
90 int execute_enable_disable(const po::variables_map
&vm
, bool enable
,
93 std::string pool_name
;
94 std::string namespace_name
;
95 std::string image_name
;
96 std::string snap_name
;
97 int r
= utils::get_pool_image_snapshot_names(
98 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &namespace_name
,
99 &image_name
, &snap_name
, true, utils::SNAPSHOT_PRESENCE_NONE
,
100 utils::SPEC_VALIDATION_NONE
);
105 librados::Rados rados
;
106 librados::IoCtx io_ctx
;
108 r
= utils::init_and_open_image(pool_name
, namespace_name
, image_name
, "", "",
109 false, &rados
, &io_ctx
, &image
);
115 librbd::mirror_image_mode_t mode
= RBD_MIRROR_IMAGE_MODE_JOURNAL
;
116 std::string mode_arg
= utils::get_positional_argument(vm
, arg_index
++);
117 if (mode_arg
== "journal") {
118 mode
= RBD_MIRROR_IMAGE_MODE_JOURNAL
;
119 } else if (mode_arg
== "snapshot") {
120 mode
= RBD_MIRROR_IMAGE_MODE_SNAPSHOT
;
121 } else if (!mode_arg
.empty()) {
122 std::cerr
<< "rbd: invalid mode name: " << mode_arg
<< std::endl
;
125 r
= image
.mirror_image_enable2(mode
);
127 r
= image
.mirror_image_disable(force
);
133 std::cout
<< (enable
? "Mirroring enabled" : "Mirroring disabled")
139 int execute_disable(const po::variables_map
&vm
,
140 const std::vector
<std::string
> &ceph_global_init_args
) {
141 return execute_enable_disable(vm
, false, vm
["force"].as
<bool>());
144 int execute_enable(const po::variables_map
&vm
,
145 const std::vector
<std::string
> &ceph_global_init_args
) {
146 return execute_enable_disable(vm
, true, false);
149 void get_arguments_promote(po::options_description
*positional
,
150 po::options_description
*options
) {
151 options
->add_options()
152 ("force", po::bool_switch(), "promote even if not cleanly demoted by remote cluster");
153 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
156 int execute_promote(const po::variables_map
&vm
,
157 const std::vector
<std::string
> &ceph_global_init_args
) {
158 size_t arg_index
= 0;
159 std::string pool_name
;
160 std::string namespace_name
;
161 std::string image_name
;
162 std::string snap_name
;
163 int r
= utils::get_pool_image_snapshot_names(
164 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &namespace_name
,
165 &image_name
, &snap_name
, true, utils::SNAPSHOT_PRESENCE_NONE
,
166 utils::SPEC_VALIDATION_NONE
);
171 bool force
= vm
["force"].as
<bool>();
173 librados::Rados rados
;
174 librados::IoCtx io_ctx
;
176 r
= utils::init_and_open_image(pool_name
, namespace_name
, image_name
, "", "",
177 false, &rados
, &io_ctx
, &image
);
182 r
= validate_mirroring_enabled(image
);
187 r
= image
.mirror_image_promote(force
);
189 std::cerr
<< "rbd: error promoting image to primary" << std::endl
;
193 std::cout
<< "Image promoted to primary" << std::endl
;
197 int execute_demote(const po::variables_map
&vm
,
198 const std::vector
<std::string
> &ceph_global_init_args
) {
199 size_t arg_index
= 0;
200 std::string pool_name
;
201 std::string namespace_name
;
202 std::string image_name
;
203 std::string snap_name
;
204 int r
= utils::get_pool_image_snapshot_names(
205 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &namespace_name
,
206 &image_name
, &snap_name
, true, utils::SNAPSHOT_PRESENCE_NONE
,
207 utils::SPEC_VALIDATION_NONE
);
212 librados::Rados rados
;
213 librados::IoCtx io_ctx
;
215 r
= utils::init_and_open_image(pool_name
, namespace_name
, image_name
, "", "",
216 false, &rados
, &io_ctx
, &image
);
221 r
= validate_mirroring_enabled(image
);
226 r
= image
.mirror_image_demote();
228 std::cerr
<< "rbd: error demoting image to non-primary" << std::endl
;
232 std::cout
<< "Image demoted to non-primary" << std::endl
;
236 int execute_resync(const po::variables_map
&vm
,
237 const std::vector
<std::string
> &ceph_global_init_args
) {
238 size_t arg_index
= 0;
239 std::string pool_name
;
240 std::string namespace_name
;
241 std::string image_name
;
242 std::string snap_name
;
243 int r
= utils::get_pool_image_snapshot_names(
244 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &namespace_name
,
245 &image_name
, &snap_name
, true, utils::SNAPSHOT_PRESENCE_NONE
,
246 utils::SPEC_VALIDATION_NONE
);
251 librados::Rados rados
;
252 librados::IoCtx io_ctx
;
254 r
= utils::init_and_open_image(pool_name
, namespace_name
, image_name
, "", "",
255 false, &rados
, &io_ctx
, &image
);
260 r
= validate_mirroring_enabled(image
);
265 r
= image
.mirror_image_resync();
267 std::cerr
<< "rbd: error flagging image resync" << std::endl
;
271 std::cout
<< "Flagged image for resync from primary" << std::endl
;
275 void get_status_arguments(po::options_description
*positional
,
276 po::options_description
*options
) {
277 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
278 at::add_format_options(options
);
281 int execute_status(const po::variables_map
&vm
,
282 const std::vector
<std::string
> &ceph_global_init_args
) {
283 at::Format::Formatter formatter
;
284 int r
= utils::get_formatter(vm
, &formatter
);
289 size_t arg_index
= 0;
290 std::string pool_name
;
291 std::string namespace_name
;
292 std::string image_name
;
293 std::string snap_name
;
294 r
= utils::get_pool_image_snapshot_names(
295 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &namespace_name
,
296 &image_name
, &snap_name
, true, utils::SNAPSHOT_PRESENCE_NONE
,
297 utils::SPEC_VALIDATION_NONE
);
302 librados::Rados rados
;
303 librados::IoCtx io_ctx
;
305 r
= utils::init_and_open_image(pool_name
, namespace_name
, image_name
, "", "",
306 false, &rados
, &io_ctx
, &image
);
311 r
= validate_mirroring_enabled(image
);
316 librados::IoCtx default_ns_io_ctx
;
317 default_ns_io_ctx
.dup(io_ctx
);
318 default_ns_io_ctx
.set_namespace("");
320 std::vector
<librbd::mirror_peer_site_t
> mirror_peers
;
321 utils::get_mirror_peer_sites(default_ns_io_ctx
, &mirror_peers
);
323 std::map
<std::string
, std::string
> peer_mirror_uuids_to_name
;
324 utils::get_mirror_peer_mirror_uuids_to_names(mirror_peers
,
325 &peer_mirror_uuids_to_name
);
327 librbd::mirror_image_global_status_t status
;
328 r
= image
.mirror_image_get_global_status(&status
, sizeof(status
));
330 std::cerr
<< "rbd: failed to get status for image " << image_name
<< ": "
331 << cpp_strerror(r
) << std::endl
;
335 utils::populate_unknown_mirror_image_site_statuses(mirror_peers
, &status
);
337 std::string instance_id
;
338 MirrorDaemonServiceInfo
daemon_service_info(io_ctx
);
340 librbd::mirror_image_site_status_t local_status
;
341 int local_site_r
= utils::get_local_mirror_image_status(
342 status
, &local_status
);
343 status
.site_statuses
.erase(
344 std::remove_if(status
.site_statuses
.begin(),
345 status
.site_statuses
.end(),
347 return (status
.mirror_uuid
==
348 RBD_MIRROR_IMAGE_STATUS_LOCAL_MIRROR_UUID
);
350 status
.site_statuses
.end());
352 if (local_site_r
>= 0 && local_status
.up
) {
353 r
= image
.mirror_image_get_instance_id(&instance_id
);
354 if (r
== -EOPNOTSUPP
) {
355 std::cerr
<< "rbd: newer release of Ceph OSDs required to map image "
356 << "to rbd-mirror daemon instance" << std::endl
;
358 } else if (r
< 0 && r
!= -ENOENT
) {
359 std::cerr
<< "rbd: failed to get service id for image "
360 << image_name
<< ": " << cpp_strerror(r
) << std::endl
;
362 } else if (!instance_id
.empty()) {
363 daemon_service_info
.init();
367 std::vector
<librbd::snap_info_t
> snaps
;
368 if (status
.info
.primary
&& status
.info
.state
== RBD_MIRROR_IMAGE_ENABLED
) {
369 librbd::mirror_image_mode_t mode
= RBD_MIRROR_IMAGE_MODE_JOURNAL
;
370 r
= image
.mirror_image_get_mode(&mode
);
372 std::cerr
<< "rbd: failed to retrieve mirror mode: "
373 << cpp_strerror(r
) << std::endl
;
377 if (mode
== RBD_MIRROR_IMAGE_MODE_SNAPSHOT
) {
378 image
.snap_list(snaps
);
380 remove_if(snaps
.begin(),
382 [&image
](const librbd::snap_info_t
&snap
) {
383 librbd::snap_namespace_type_t type
;
384 int r
= image
.snap_get_namespace_type(snap
.id
, &type
);
388 return type
!= RBD_SNAP_NAMESPACE_TYPE_MIRROR
;
394 auto mirror_service
= daemon_service_info
.get_by_instance_id(instance_id
);
396 if (formatter
!= nullptr) {
397 formatter
->open_object_section("image");
398 formatter
->dump_string("name", image_name
);
399 formatter
->dump_string("global_id", status
.info
.global_id
);
400 if (local_site_r
>= 0) {
401 formatter
->dump_string("state", utils::mirror_image_site_status_state(
403 formatter
->dump_string("description", local_status
.description
);
404 if (mirror_service
!= nullptr) {
405 mirror_service
->dump_image(formatter
);
407 formatter
->dump_string("last_update", utils::timestr(
408 local_status
.last_update
));
410 if (!status
.site_statuses
.empty()) {
411 formatter
->open_array_section("peer_sites");
412 for (auto& status
: status
.site_statuses
) {
413 formatter
->open_object_section("peer_site");
415 auto name_it
= peer_mirror_uuids_to_name
.find(status
.mirror_uuid
);
416 formatter
->dump_string("site_name",
417 (name_it
!= peer_mirror_uuids_to_name
.end() ? name_it
->second
: ""));
418 formatter
->dump_string("mirror_uuids", status
.mirror_uuid
);
420 formatter
->dump_string("state", utils::mirror_image_site_status_state(
422 formatter
->dump_string("description", status
.description
);
423 formatter
->dump_string("last_update", utils::timestr(
424 status
.last_update
));
425 formatter
->close_section(); // peer_site
427 formatter
->close_section(); // peer_sites
429 if (!snaps
.empty()) {
430 formatter
->open_array_section("snapshots");
431 for (auto &snap
: snaps
) {
432 librbd::snap_mirror_namespace_t info
;
433 r
= image
.snap_get_mirror_namespace(snap
.id
, &info
, sizeof(info
));
435 (info
.state
!= RBD_SNAP_MIRROR_STATE_PRIMARY
&&
436 info
.state
!= RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED
)) {
439 formatter
->open_object_section("snapshot");
440 formatter
->dump_unsigned("id", snap
.id
);
441 formatter
->dump_string("name", snap
.name
);
442 formatter
->dump_bool("demoted",
443 info
.state
== RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED
);
444 formatter
->open_array_section("mirror_peer_uuids");
445 for (auto &peer
: info
.mirror_peer_uuids
) {
446 formatter
->dump_string("peer_uuid", peer
);
448 formatter
->close_section(); // mirror_peer_uuids
449 formatter
->close_section(); // snapshot
451 formatter
->close_section(); // snapshots
453 formatter
->close_section(); // image
454 formatter
->flush(std::cout
);
456 std::cout
<< image_name
<< ":\n"
457 << " global_id: " << status
.info
.global_id
<< "\n";
458 if (local_site_r
>= 0) {
459 std::cout
<< " state: " << utils::mirror_image_site_status_state(
460 local_status
) << "\n"
461 << " description: " << local_status
.description
<< "\n";
462 if (mirror_service
!= nullptr) {
463 std::cout
<< " service: " <<
464 mirror_service
->get_image_description() << "\n";
466 std::cout
<< " last_update: " << utils::timestr(
467 local_status
.last_update
) << std::endl
;
469 if (!status
.site_statuses
.empty()) {
470 std::cout
<< " peer_sites:" << std::endl
;
472 bool first_site
= true;
473 for (auto& site
: status
.site_statuses
) {
475 std::cout
<< std::endl
;
479 auto name_it
= peer_mirror_uuids_to_name
.find(site
.mirror_uuid
);
480 std::cout
<< " name: "
481 << (name_it
!= peer_mirror_uuids_to_name
.end() ?
482 name_it
->second
: site
.mirror_uuid
)
484 << " state: " << utils::mirror_image_site_status_state(
486 << " description: " << site
.description
<< std::endl
487 << " last_update: " << utils::timestr(
488 site
.last_update
) << std::endl
;
491 if (!snaps
.empty()) {
492 std::cout
<< " snapshots:" << std::endl
;
494 bool first_site
= true;
495 for (auto &snap
: snaps
) {
496 librbd::snap_mirror_namespace_t info
;
497 r
= image
.snap_get_mirror_namespace(snap
.id
, &info
, sizeof(info
));
499 (info
.state
!= RBD_SNAP_MIRROR_STATE_PRIMARY
&&
500 info
.state
!= RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED
)) {
505 std::cout
<< std::endl
;
509 std::cout
<< " " << snap
.id
<< " " << snap
.name
<< " ("
510 << (info
.state
== RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED
?
512 << "peer_uuids:[" << info
.mirror_peer_uuids
<< "])";
514 std::cout
<< std::endl
;
521 int execute_snapshot(const po::variables_map
&vm
,
522 const std::vector
<std::string
> &ceph_global_init_args
) {
523 size_t arg_index
= 0;
524 std::string pool_name
;
525 std::string namespace_name
;
526 std::string image_name
;
527 int r
= utils::get_pool_image_snapshot_names(
528 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &namespace_name
,
529 &image_name
, nullptr, true, utils::SNAPSHOT_PRESENCE_NONE
,
530 utils::SPEC_VALIDATION_NONE
);
535 librados::Rados rados
;
536 librados::IoCtx io_ctx
;
538 r
= utils::init_and_open_image(pool_name
, namespace_name
, image_name
, "", "",
539 false, &rados
, &io_ctx
, &image
);
544 r
= validate_mirroring_enabled(image
, true);
550 r
= image
.mirror_image_create_snapshot(&snap_id
);
552 std::cerr
<< "rbd: error creating snapshot: " << cpp_strerror(r
)
557 std::cout
<< "Snapshot ID: " << snap_id
<< std::endl
;
561 Shell::Action
action_enable(
562 {"mirror", "image", "enable"}, {},
563 "Enable RBD mirroring for an image.", "",
564 &get_arguments_enable
, &execute_enable
);
565 Shell::Action
action_disable(
566 {"mirror", "image", "disable"}, {},
567 "Disable RBD mirroring for an image.", "",
568 &get_arguments_disable
, &execute_disable
);
569 Shell::Action
action_promote(
570 {"mirror", "image", "promote"}, {},
571 "Promote an image to primary for RBD mirroring.", "",
572 &get_arguments_promote
, &execute_promote
);
573 Shell::Action
action_demote(
574 {"mirror", "image", "demote"}, {},
575 "Demote an image to non-primary for RBD mirroring.", "",
576 &get_arguments
, &execute_demote
);
577 Shell::Action
action_resync(
578 {"mirror", "image", "resync"}, {},
579 "Force resync to primary image for RBD mirroring.", "",
580 &get_arguments
, &execute_resync
);
581 Shell::Action
action_status(
582 {"mirror", "image", "status"}, {},
583 "Show RBD mirroring status for an image.", "",
584 &get_status_arguments
, &execute_status
);
585 Shell::Action
action_snapshot(
586 {"mirror", "image", "snapshot"}, {},
587 "Create RBD mirroring image snapshot.", "",
588 &get_arguments
, &execute_snapshot
);
590 } // namespace mirror_image
591 } // namespace action