]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd: add 'mirror group' commands
authorMykola Golub <mgolub@suse.com>
Sun, 13 Dec 2020 16:01:15 +0000 (16:01 +0000)
committerPrasanna Kumar Kalever <prasanna.kalever@redhat.com>
Thu, 24 Apr 2025 15:56:22 +0000 (21:26 +0530)
Signed-off-by: Mykola Golub <mgolub@suse.com>
Signed-off-by: Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
src/test/cli/rbd/help.t
src/tools/rbd/CMakeLists.txt
src/tools/rbd/MirrorDaemonServiceInfo.cc
src/tools/rbd/MirrorDaemonServiceInfo.h
src/tools/rbd/Utils.cc
src/tools/rbd/Utils.h
src/tools/rbd/action/MirrorGroup.cc [new file with mode: 0644]
src/tools/rbd/action/MirrorImage.cc
src/tools/rbd/action/MirrorPool.cc

index b94bd9bd7eeb1b90990ea40b404979f8416364f9..b6168c553a31f8b5b7be42be4e38cd84d3f2c721 100644 (file)
       migration commit                  Commit image migration.
       migration execute                 Execute image migration.
       migration prepare                 Prepare image migration.
+      mirror group demote               Demote an group to non-primary for RBD
+                                        mirroring.
+      mirror group disable              Disable RBD mirroring for an group.
+      mirror group enable               Enable RBD mirroring for an group.
+      mirror group promote              Promote an group to primary for RBD
+                                        mirroring.
+      mirror group resync               Force resync to primary group for RBD
+                                        mirroring.
+      mirror group snapshot             Create RBD mirroring group snapshot.
+      mirror group status               Show RBD mirroring status for an group.
       mirror image demote               Demote an image to non-primary for RBD
                                         mirroring.
       mirror image disable              Disable RBD mirroring for an image.
     (-) supports disabling-only on existing images
     (+) enabled by default for new images if features not specified
   
+  rbd help mirror group demote
+  usage: rbd mirror group demote [--pool <pool>] [--namespace <namespace>] 
+                                 [--group <group>] 
+                                 <group-spec> 
+  
+  Demote an group to non-primary for RBD mirroring.
+  
+  Positional arguments
+    <group-spec>         group specification
+                         (example: [<pool-name>/[<namespace>/]]<group-name>)
+  
+  Optional arguments
+    -p [ --pool ] arg    pool name
+    --namespace arg      namespace name
+    --group arg          group name
+  
+  rbd help mirror group disable
+  usage: rbd mirror group disable [--force] [--pool <pool>] 
+                                  [--namespace <namespace>] [--group <group>] 
+                                  <group-spec> 
+  
+  Disable RBD mirroring for an group.
+  
+  Positional arguments
+    <group-spec>         group specification
+                         (example: [<pool-name>/[<namespace>/]]<group-name>)
+  
+  Optional arguments
+    --force              disable even if not primary
+    -p [ --pool ] arg    pool name
+    --namespace arg      namespace name
+    --group arg          group name
+  
+  rbd help mirror group enable
+  usage: rbd mirror group enable [--pool <pool>] [--namespace <namespace>] 
+                                 [--group <group>] 
+                                 <group-spec> <mode> 
+  
+  Enable RBD mirroring for an group.
+  
+  Positional arguments
+    <group-spec>         group specification
+                         (example: [<pool-name>/[<namespace>/]]<group-name>)
+    <mode>               mirror image mode (journal or snapshot) [default:
+                         snapshot]
+  
+  Optional arguments
+    -p [ --pool ] arg    pool name
+    --namespace arg      namespace name
+    --group arg          group name
+  
+  rbd help mirror group promote
+  usage: rbd mirror group promote [--force] [--pool <pool>] 
+                                  [--namespace <namespace>] [--group <group>] 
+                                  <group-spec> 
+  
+  Promote an group to primary for RBD mirroring.
+  
+  Positional arguments
+    <group-spec>         group specification
+                         (example: [<pool-name>/[<namespace>/]]<group-name>)
+  
+  Optional arguments
+    --force              promote even if not cleanly demoted by remote cluster
+    -p [ --pool ] arg    pool name
+    --namespace arg      namespace name
+    --group arg          group name
+  
+  rbd help mirror group resync
+  usage: rbd mirror group resync [--pool <pool>] [--namespace <namespace>] 
+                                 [--group <group>] 
+                                 <group-spec> 
+  
+  Force resync to primary group for RBD mirroring.
+  
+  Positional arguments
+    <group-spec>         group specification
+                         (example: [<pool-name>/[<namespace>/]]<group-name>)
+  
+  Optional arguments
+    -p [ --pool ] arg    pool name
+    --namespace arg      namespace name
+    --group arg          group name
+  
+  rbd help mirror group snapshot
+  usage: rbd mirror group snapshot [--pool <pool>] [--namespace <namespace>] 
+                                   [--group <group>] [--skip-quiesce] 
+                                   [--ignore-quiesce-error] 
+                                   <group-spec> 
+  
+  Create RBD mirroring group snapshot.
+  
+  Positional arguments
+    <group-spec>            group specification
+                            (example: [<pool-name>/[<namespace>/]]<group-name>)
+  
+  Optional arguments
+    -p [ --pool ] arg       pool name
+    --namespace arg         namespace name
+    --group arg             group name
+    --skip-quiesce          do not run quiesce hooks
+    --ignore-quiesce-error  ignore quiesce hook error
+  
+  rbd help mirror group status
+  usage: rbd mirror group status [--pool <pool>] [--namespace <namespace>] 
+                                 [--group <group>] [--format <format>] 
+                                 [--pretty-format] 
+                                 <group-spec> 
+  
+  Show RBD mirroring status for an group.
+  
+  Positional arguments
+    <group-spec>         group specification
+                         (example: [<pool-name>/[<namespace>/]]<group-name>)
+  
+  Optional arguments
+    -p [ --pool ] arg    pool name
+    --namespace arg      namespace name
+    --group arg          group name
+    --format arg         output format (plain, json, or xml) [default: plain]
+    --pretty-format      pretty formatting (json and xml)
+  
   rbd help mirror image demote
   usage: rbd mirror image demote [--pool <pool>] [--namespace <namespace>] 
                                  [--image <image>] 
index dac1d8babf92de0f6b2db82d80e5f3918aa7d250..6e4d31bef0d7067bd26258fe0a54ad850fe45af3 100644 (file)
@@ -35,6 +35,7 @@ set(rbd_srcs
   action/Lock.cc
   action/MergeDiff.cc
   action/Migration.cc
+  action/MirrorGroup.cc
   action/MirrorImage.cc
   action/MirrorPool.cc
   action/MirrorSnapshotSchedule.cc
index e7422e66a4d362af5d5b9cd51a0f40f4cf53980c..192bd615b5d486bc79032bad9716fb150a13a44a 100644 (file)
@@ -32,7 +32,7 @@ std::ostream& operator<<(std::ostream& os, MirrorHealth mirror_health) {
   return os;
 }
 
-std::string MirrorService::get_image_description() const {
+std::string MirrorService::get_description() const {
   std::string description = (!client_id.empty() ? client_id :
                                                   stringify(service_id));
   if (!hostname.empty()) {
@@ -41,7 +41,7 @@ std::string MirrorService::get_image_description() const {
   return description;
 }
 
-void MirrorService::dump_image(
+void MirrorService::dump(
     argument_types::Format::Formatter formatter) const {
   formatter->open_object_section("daemon_service");
   formatter->dump_string("service_id", service_id);
index d667332e5131dc6a1a38e2e9a69c76c65cc59bd4..ff1f11464ba2fa5b3a93ffa457eed1916be02b54 100644 (file)
@@ -39,8 +39,8 @@ struct MirrorService {
 
   MirrorHealth health = MIRROR_HEALTH_UNKNOWN;
 
-  std::string get_image_description() const;
-  void dump_image(argument_types::Format::Formatter formatter) const;
+  std::string get_description() const;
+  void dump(argument_types::Format::Formatter formatter) const;
 };
 
 typedef std::list<MirrorService> MirrorServices;
index bcc2f507acba70e35ba75b12635c15e93805c3ba..9ac02222a472a50a02764dbebeaac858b7a90ddb 100644 (file)
@@ -1069,6 +1069,32 @@ std::string mirror_image_global_status_state(
   return mirror_image_site_status_state(local_status);
 }
 
+std::string mirror_group_status_state(
+    librbd::mirror_group_status_state_t state) {
+  switch (state) {
+  case MIRROR_GROUP_STATUS_STATE_UNKNOWN:
+    return "unknown";
+  case MIRROR_GROUP_STATUS_STATE_ERROR:
+    return "error";
+  case MIRROR_GROUP_STATUS_STATE_STARTING_REPLAY:
+    return "starting_replay";
+  case MIRROR_GROUP_STATUS_STATE_REPLAYING:
+    return "replaying";
+  case MIRROR_GROUP_STATUS_STATE_STOPPING_REPLAY:
+    return "stopping_replay";
+  case MIRROR_GROUP_STATUS_STATE_STOPPED:
+    return "stopped";
+  default:
+    return "unknown (" + stringify(static_cast<uint32_t>(state)) + ")";
+  }
+}
+
+std::string mirror_group_site_status_state(
+    const librbd::mirror_group_site_status_t& status) {
+  return (status.up ? "up+" : "down+") +
+    mirror_group_status_state(status.state);
+}
+
 int get_local_mirror_image_status(
     const librbd::mirror_image_global_status_t& status,
     librbd::mirror_image_site_status_t* local_status) {
@@ -1086,6 +1112,23 @@ int get_local_mirror_image_status(
   return 0;
 }
 
+int get_local_mirror_group_status(
+    const librbd::mirror_group_global_status_t& status,
+    librbd::mirror_group_site_status_t* local_status) {
+  auto it = std::find_if(status.site_statuses.begin(),
+                         status.site_statuses.end(),
+                         [](auto& site_status) {
+      return (site_status.mirror_uuid ==
+                RBD_MIRROR_GROUP_STATUS_LOCAL_MIRROR_UUID);
+    });
+  if (it == status.site_statuses.end()) {
+    return -ENOENT;
+  }
+
+  *local_status = *it;
+  return 0;
+}
+
 std::string timestr(time_t t) {
   if (t == 0) {
     return "";
@@ -1186,6 +1229,48 @@ void populate_unknown_mirror_image_site_statuses(
   std::swap(global_status->site_statuses, site_statuses);
 }
 
+void populate_unknown_mirror_group_site_statuses(
+    const std::vector<librbd::mirror_peer_site_t>& mirror_peers,
+    librbd::mirror_group_global_status_t* status) {
+  std::set<std::string> missing_mirror_uuids;
+  librbd::mirror_peer_direction_t mirror_peer_direction =
+    RBD_MIRROR_PEER_DIRECTION_RX_TX;
+  for (auto& peer : mirror_peers) {
+    if (peer.uuid == mirror_peers.begin()->uuid) {
+      mirror_peer_direction = peer.direction;
+    } else if (mirror_peer_direction != RBD_MIRROR_PEER_DIRECTION_RX_TX &&
+               mirror_peer_direction != peer.direction) {
+      mirror_peer_direction = RBD_MIRROR_PEER_DIRECTION_RX_TX;
+    }
+
+    if (!peer.mirror_uuid.empty() &&
+        peer.direction != RBD_MIRROR_PEER_DIRECTION_TX) {
+      missing_mirror_uuids.insert(peer.mirror_uuid);
+    }
+  }
+
+  if (mirror_peer_direction != RBD_MIRROR_PEER_DIRECTION_TX) {
+    missing_mirror_uuids.insert(RBD_MIRROR_GROUP_STATUS_LOCAL_MIRROR_UUID);
+  }
+
+  std::vector<librbd::mirror_group_site_status_t> site_statuses;
+  site_statuses.reserve(missing_mirror_uuids.size());
+
+  for (auto& site_status : status->site_statuses) {
+    if (missing_mirror_uuids.count(site_status.mirror_uuid) > 0) {
+      missing_mirror_uuids.erase(site_status.mirror_uuid);
+      site_statuses.push_back(site_status);
+    }
+  }
+
+  for (auto& mirror_uuid : missing_mirror_uuids) {
+    site_statuses.push_back({mirror_uuid, MIRROR_GROUP_STATUS_STATE_UNKNOWN,
+                             "status not found", {}, 0, false});
+  }
+
+  std::swap(status->site_statuses, site_statuses);
+}
+
 int mgr_command(librados::Rados& rados, const std::string& cmd,
                 const std::map<std::string, std::string> &args,
                 std::ostream *out_os, std::ostream *err_os) {
index 6aa0f2fdbdf967a0d2e2a9373e88256290eada7e..ee3c48045386abaee4eb7e362881e2ad88b54536 100644 (file)
@@ -255,9 +255,17 @@ std::string mirror_image_site_status_state(
 std::string mirror_image_global_status_state(
     const librbd::mirror_image_global_status_t& status);
 
+std::string mirror_group_status_state(
+    librbd::mirror_group_status_state_t state);
+std::string mirror_group_site_status_state(
+    const librbd::mirror_group_site_status_t& status);
+
 int get_local_mirror_image_status(
     const librbd::mirror_image_global_status_t& status,
     librbd::mirror_image_site_status_t* local_status);
+int get_local_mirror_group_status(
+    const librbd::mirror_group_global_status_t& status,
+    librbd::mirror_group_site_status_t* local_status);
 
 std::string timestr(time_t t);
 
@@ -273,6 +281,9 @@ void get_mirror_peer_mirror_uuids_to_names(
 void populate_unknown_mirror_image_site_statuses(
     const std::vector<librbd::mirror_peer_site_t>& mirror_peers,
     librbd::mirror_image_global_status_t* global_status);
+void populate_unknown_mirror_group_site_statuses(
+    const std::vector<librbd::mirror_peer_site_t>& mirror_peers,
+    librbd::mirror_group_global_status_t* status);
 
 int mgr_command(librados::Rados& rados, const std::string& cmd,
                 const std::map<std::string, std::string> &args,
diff --git a/src/tools/rbd/action/MirrorGroup.cc b/src/tools/rbd/action/MirrorGroup.cc
new file mode 100644 (file)
index 0000000..10a7c45
--- /dev/null
@@ -0,0 +1,646 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 SUSE LINUX GmbH
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/MirrorDaemonServiceInfo.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "include/stringify.h"
+#include "common/config.h"
+#include "common/errno.h"
+#include "common/Formatter.h"
+#include "common/TextTable.h"
+#include "global/global_context.h"
+#include <iostream>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace mirror_group {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+// TODO: move code common with Group.cc to shared ArgumentTypes.cc
+
+static const std::string GROUP_SPEC("group-spec");
+
+static const std::string GROUP_NAME("group");
+
+static const std::string GROUP_POOL_NAME("group-" + at::POOL_NAME);
+
+void add_group_option(po::options_description *opt) {
+  opt->add_options()
+    (GROUP_NAME.c_str(), po::value<std::string>(), "group name");
+}
+
+void add_group_spec_options(po::options_description *pos,
+                           po::options_description *opt) {
+  at::add_pool_option(opt, at::ARGUMENT_MODIFIER_NONE);
+  at::add_namespace_option(opt, at::ARGUMENT_MODIFIER_NONE);
+  add_group_option(opt);
+  pos->add_options()
+    (GROUP_SPEC.c_str(),
+     ("group specification\n"
+      "(example: [<pool-name>/[<namespace>/]]<group-name>)"));
+}
+
+namespace {
+
+int validate_mirroring_enabled(librados::IoCtx io_ctx,
+                               const std::string group_name) {
+  librbd::RBD rbd;
+  librbd::mirror_group_info_t info;
+  int r = rbd.mirror_group_get_info(io_ctx, group_name.c_str(), &info,
+                                sizeof(info));
+  if (r < 0) {
+    std::cerr << "rbd: failed to get mirror info for group " << group_name
+              << ": " << cpp_strerror(r) << std::endl;
+    return r;
+  }
+
+  if (info.state != RBD_MIRROR_GROUP_ENABLED) {
+    std::cerr << "rbd: mirroring not enabled for group: "
+              << group_name << std::endl;
+    return -EINVAL;
+  }
+
+  if (info.mirror_image_mode != RBD_MIRROR_IMAGE_MODE_SNAPSHOT) {
+    std::cerr << "rbd: snapshot based mirroring not enabled on the group"
+              << std::endl;
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+} // anonymous namespace
+
+void get_arguments(po::options_description *positional,
+                   po::options_description *options) {
+  add_group_spec_options(positional, options);
+}
+
+void get_arguments_enable(po::options_description *positional,
+                          po::options_description *options) {
+  add_group_spec_options(positional, options);
+  positional->add_options()
+    ("mode", "mirror group mode [default: snapshot]");
+}
+
+void get_arguments_disable(po::options_description *positional,
+                           po::options_description *options) {
+  options->add_options()
+    ("force", po::bool_switch(), "disable even if not primary");
+  add_group_spec_options(positional, options);
+}
+
+int execute_enable_disable(const po::variables_map &vm, bool enable,
+                           bool force) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string namespace_name;
+  std::string group_name;
+
+  int r = utils::get_pool_generic_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, at::POOL_NAME, &pool_name,
+    at::NAMESPACE_NAME, &namespace_name, GROUP_NAME, "group", &group_name,
+    nullptr, true, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_FULL);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+
+  r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  librbd::RBD rbd;
+
+  if (enable) {
+    librbd::mirror_image_mode_t mode = RBD_MIRROR_IMAGE_MODE_SNAPSHOT;
+    std::string mode_arg = utils::get_positional_argument(vm, arg_index++);
+    if (mode_arg == "journal") {
+      mode = RBD_MIRROR_IMAGE_MODE_JOURNAL;
+      std::cerr << "rbd: journal mode not supported with group mirroring"
+                << std::endl;
+      return -EINVAL;
+    } else if (mode_arg == "snapshot") {
+      mode = RBD_MIRROR_IMAGE_MODE_SNAPSHOT;
+    } else if (!mode_arg.empty()) {
+      std::cerr << "rbd: invalid mode name: " << mode_arg << std::endl;
+      return -EINVAL;
+    }
+    r = rbd.mirror_group_enable(io_ctx, group_name.c_str(), mode);
+  } else {
+    r = rbd.mirror_group_disable(io_ctx, group_name.c_str(), force);
+  }
+  if (r < 0) {
+    return r;
+  }
+
+  std::cout << (enable ? "Mirroring enabled" : "Mirroring disabled")
+    << std::endl;
+  return 0;
+}
+
+int execute_disable(const po::variables_map &vm,
+                    const std::vector<std::string> &ceph_global_init_args) {
+  return execute_enable_disable(vm, false, vm["force"].as<bool>());
+}
+
+int execute_enable(const po::variables_map &vm,
+                   const std::vector<std::string> &ceph_global_init_args) {
+  return execute_enable_disable(vm, true, false);
+}
+
+void get_arguments_promote(po::options_description *positional,
+                           po::options_description *options) {
+  options->add_options()
+    ("force", po::bool_switch(), "promote even if not cleanly demoted by remote cluster");
+  add_group_spec_options(positional, options);
+}
+
+int execute_promote(const po::variables_map &vm,
+                    const std::vector<std::string> &ceph_global_init_args) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string namespace_name;
+  std::string group_name;
+
+  int r = utils::get_pool_generic_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, at::POOL_NAME, &pool_name,
+    at::NAMESPACE_NAME, &namespace_name, GROUP_NAME, "group", &group_name,
+    nullptr, true, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_FULL);
+  if (r < 0) {
+    return r;
+  }
+
+  bool force = vm["force"].as<bool>();
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+
+  r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  r = validate_mirroring_enabled(io_ctx, group_name);
+  if (r < 0) {
+    return r;
+  }
+
+  librbd::RBD rbd;
+
+  r = rbd.mirror_group_promote(io_ctx, group_name.c_str(), force);
+  if (r < 0) {
+    std::cerr << "rbd: error promoting group to primary" << std::endl;
+    return r;
+  }
+
+  std::cout << "Group promoted to primary" << std::endl;
+  return 0;
+}
+
+int execute_demote(const po::variables_map &vm,
+                   const std::vector<std::string> &ceph_global_init_args) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string namespace_name;
+  std::string group_name;
+
+  int r = utils::get_pool_generic_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, at::POOL_NAME, &pool_name,
+    at::NAMESPACE_NAME, &namespace_name, GROUP_NAME, "group", &group_name,
+    nullptr, true, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_FULL);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+
+  r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  r = validate_mirroring_enabled(io_ctx, group_name);
+  if (r < 0) {
+    return r;
+  }
+
+  librbd::RBD rbd;
+
+  r = rbd.mirror_group_demote(io_ctx, group_name.c_str());
+  if (r < 0) {
+    std::cerr << "rbd: error demoting group to non-primary" << std::endl;
+    return r;
+  }
+
+  std::cout << "Group demoted to non-primary" << std::endl;
+  return 0;
+}
+
+int execute_resync(const po::variables_map &vm,
+                   const std::vector<std::string> &ceph_global_init_args) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string namespace_name;
+  std::string group_name;
+
+  int r = utils::get_pool_generic_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, at::POOL_NAME, &pool_name,
+    at::NAMESPACE_NAME, &namespace_name, GROUP_NAME, "group", &group_name,
+    nullptr, true, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_FULL);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+
+  r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  r = validate_mirroring_enabled(io_ctx, group_name);
+  if (r < 0) {
+    return r;
+  }
+
+  librbd::RBD rbd;
+
+  r = rbd.mirror_group_resync(io_ctx, group_name.c_str());
+  if (r < 0) {
+    std::cerr << "rbd: error flagging group resync" << std::endl;
+    return r;
+  }
+
+  std::cout << "Flagged group for resync from primary" << std::endl;
+  return 0;
+}
+
+void get_status_arguments(po::options_description *positional,
+                         po::options_description *options) {
+  add_group_spec_options(positional, options);
+  at::add_format_options(options);
+}
+
+int execute_status(const po::variables_map &vm,
+                   const std::vector<std::string> &ceph_global_init_args) {
+  at::Format::Formatter formatter;
+  int r = utils::get_formatter(vm, &formatter);
+  if (r < 0) {
+    return r;
+  }
+
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string namespace_name;
+  std::string group_name;
+
+  r = utils::get_pool_generic_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, at::POOL_NAME, &pool_name,
+    at::NAMESPACE_NAME, &namespace_name, GROUP_NAME, "group", &group_name,
+    nullptr, true, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_FULL);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+
+  r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  r = validate_mirroring_enabled(io_ctx, group_name);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::IoCtx default_ns_io_ctx;
+  default_ns_io_ctx.dup(io_ctx);
+  default_ns_io_ctx.set_namespace("");
+
+  std::vector<librbd::mirror_peer_site_t> mirror_peers;
+  utils::get_mirror_peer_sites(default_ns_io_ctx, &mirror_peers);
+
+  std::map<std::string, std::string> peer_mirror_uuids_to_name;
+  utils::get_mirror_peer_mirror_uuids_to_names(mirror_peers,
+                                               &peer_mirror_uuids_to_name);
+  librbd::RBD rbd;
+  librbd::mirror_group_global_status_t status;
+  r = rbd.mirror_group_get_status(io_ctx, group_name.c_str(), &status,
+                                  sizeof(status));
+  if (r < 0) {
+    std::cerr << "rbd: failed to get status for group " << group_name << ": "
+             << cpp_strerror(r) << std::endl;
+    return r;
+  }
+
+
+  utils::populate_unknown_mirror_group_site_statuses(mirror_peers, &status);
+
+  std::string instance_id;
+  MirrorDaemonServiceInfo daemon_service_info(io_ctx);
+
+  librbd::mirror_group_site_status_t local_status;
+  int local_site_r = utils::get_local_mirror_group_status(
+    status, &local_status);
+  status.site_statuses.erase(
+    std::remove_if(status.site_statuses.begin(),
+                   status.site_statuses.end(),
+                   [](auto& status) {
+        return (status.mirror_uuid ==
+                  RBD_MIRROR_GROUP_STATUS_LOCAL_MIRROR_UUID);
+      }),
+    status.site_statuses.end());
+
+  if (local_site_r >= 0 && local_status.up) {
+    r = rbd.mirror_group_get_instance_id(io_ctx, group_name.c_str(),
+                                         &instance_id);
+    if (r < 0 && r != -ENOENT) {
+      std::cerr << "rbd: failed to get service id for group "
+                << group_name << ": " << cpp_strerror(r) << std::endl;
+      // not fatal
+    } else if (!instance_id.empty()) {
+      daemon_service_info.init();
+    }
+  }
+
+  std::vector<librbd::group_snap_info_t> snaps;
+  if (status.info.primary && status.info.state == RBD_MIRROR_GROUP_ENABLED) {
+    if (status.info.mirror_image_mode == RBD_MIRROR_IMAGE_MODE_SNAPSHOT) {
+      rbd.group_snap_list(io_ctx, group_name.c_str(), &snaps,
+                          sizeof(librbd::group_snap_info_t));
+      snaps.erase(
+        remove_if(snaps.begin(),
+                  snaps.end(),
+                  [](const librbd::group_snap_info_t &snap) {
+                    // TODO: a more reliable way to filter mirror snapshots
+                    return !boost::starts_with(snap.name, ".mirror.");
+                  }),
+        snaps.end());
+    }
+  }
+
+  auto mirror_service = daemon_service_info.get_by_instance_id(instance_id);
+
+  if (formatter != nullptr) {
+    formatter->open_object_section("group");
+    formatter->dump_string("name", group_name);
+    formatter->dump_string("global_id", status.info.global_id);
+    if (local_site_r >= 0) {
+      formatter->dump_string("state", utils::mirror_group_site_status_state(
+        local_status));
+      formatter->dump_string("description", local_status.description);
+      if (mirror_service != nullptr) {
+        mirror_service->dump(formatter);
+      }
+      formatter->dump_string("last_update", utils::timestr(
+        local_status.last_update));
+      formatter->open_array_section("images");
+      for (auto &[p, image_status] : local_status.mirror_images) {
+        formatter->open_object_section("image");
+        formatter->dump_int("pool_id", p.first);
+        formatter->dump_string("global_image_id", p.second);
+        formatter->dump_string(
+            "status", utils::mirror_image_site_status_state(image_status));
+        formatter->dump_string("description", image_status.description);
+        formatter->close_section(); // image
+      }
+      formatter->close_section(); // images
+    }
+    if (!status.site_statuses.empty()) {
+      formatter->open_array_section("peer_sites");
+      for (auto& status : status.site_statuses) {
+        formatter->open_object_section("peer_site");
+
+        auto name_it = peer_mirror_uuids_to_name.find(status.mirror_uuid);
+        formatter->dump_string("site_name",
+          (name_it != peer_mirror_uuids_to_name.end() ? name_it->second : ""));
+        formatter->dump_string("mirror_uuids", status.mirror_uuid);
+
+        formatter->dump_string("state", utils::mirror_group_site_status_state(
+          status));
+        formatter->dump_string("description", status.description);
+        formatter->dump_string("last_update", utils::timestr(
+          status.last_update));
+        formatter->open_array_section("images");
+        for (auto &[p, image_status] : status.mirror_images) {
+          formatter->open_object_section("image");
+          formatter->dump_int("pool_id", p.first);
+          formatter->dump_string("global_image_id", p.second);
+          formatter->dump_string(
+              "status", utils::mirror_image_site_status_state(image_status));
+          formatter->dump_string("description", image_status.description);
+          formatter->close_section(); // image
+        }
+        formatter->close_section(); // images
+        formatter->close_section(); // peer_site
+      }
+      formatter->close_section(); // peer_sites
+    }
+    if (!snaps.empty()) {
+      formatter->open_array_section("snapshots");
+      for (auto &snap : snaps) {
+        std::string state_string;
+        if (snap.state == RBD_GROUP_SNAP_STATE_INCOMPLETE) {
+          state_string = "incomplete";
+        } else {
+          state_string = "ok";
+        }
+        formatter->open_object_section("snapshot");
+        formatter->dump_string("name", snap.name);
+        formatter->dump_string("state", state_string);
+        formatter->close_section(); // snapshot
+      }
+      formatter->close_section(); // snapshots
+    }
+    formatter->close_section(); // group
+    formatter->flush(std::cout);
+  } else {
+    std::cout << group_name << ":\n"
+             << "  global_id:   " << status.info.global_id << "\n";
+    if (local_site_r >= 0) {
+      std::cout << "  state:       " << utils::mirror_group_site_status_state(
+                  local_status) << "\n"
+                << "  description: " << local_status.description << "\n";
+      if (mirror_service != nullptr) {
+        std::cout << "  service:     " <<
+          mirror_service->get_description() << "\n";
+      }
+      std::cout << "  last_update: " << utils::timestr(
+        local_status.last_update) << std::endl;
+      std::cout << "  images:" << std::endl;
+      bool first_image = true;
+      for (auto &[p, image_status] : local_status.mirror_images) {
+        if (!first_image) {
+          std::cout << std::endl;
+        }
+        first_image = false;
+        // TODO: resolve pool_id/global_image_id into pool_name/image_name?
+        std::cout << "    image:       " << p.first << "/" << p.second << "\n"
+                  << "    state:       " << utils::mirror_image_site_status_state(
+                                              image_status) << "\n"
+                  << "    description: " << image_status.description << "\n";
+      }
+    }
+    if (!status.site_statuses.empty()) {
+      std::cout << "  peer_sites:" << std::endl;
+
+      bool first_site = true;
+      for (auto& site : status.site_statuses) {
+        if (!first_site) {
+          std::cout << std::endl;
+        }
+        first_site = false;
+
+        auto name_it = peer_mirror_uuids_to_name.find(site.mirror_uuid);
+        std::cout << "    name: "
+                  << (name_it != peer_mirror_uuids_to_name.end() ?
+                        name_it->second : site.mirror_uuid)
+                  << std::endl
+                  << "    state: " << utils::mirror_group_site_status_state(
+                    site) << std::endl
+                  << "    description: " << site.description << std::endl
+                  << "    last_update: " << utils::timestr(
+                    site.last_update) << std::endl;
+        std::cout << "    images:" << std::endl;
+        bool first_image = true;
+        for (auto &[p, image_status] : site.mirror_images) {
+          if (!first_image) {
+            std::cout << std::endl;
+          }
+          first_image = false;
+          // TODO: resolve pool_id/global_image_id into pool_name/image_name?
+          std::cout << "      image:       " << p.first << "/" << p.second << "\n"
+                    << "      state:       " << utils::mirror_image_site_status_state(
+                                                  image_status) << "\n"
+                    << "      description: " << image_status.description << "\n";
+        }
+      }
+    }
+    if (!snaps.empty()) {
+      std::cout << "  snapshots:" << std::endl;
+
+      bool first_snap = true;
+      for (auto &snap : snaps) {
+        if (!first_snap) {
+          std::cout << std::endl;
+        }
+        first_snap = false;
+        std::cout << "    " << snap.name;
+      }
+      std::cout << std::endl;
+    }
+  }
+
+  return 0;
+}
+
+void get_snapshot_arguments(po::options_description *positional,
+                            po::options_description *options) {
+  add_group_spec_options(positional, options);
+  at::add_snap_create_options(options);
+}
+
+int execute_snapshot(const po::variables_map &vm,
+                     const std::vector<std::string> &ceph_global_init_args) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string namespace_name;
+  std::string group_name;
+
+  int r = utils::get_pool_generic_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, at::POOL_NAME, &pool_name,
+    at::NAMESPACE_NAME, &namespace_name, GROUP_NAME, "group", &group_name,
+    nullptr, true, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_FULL);
+  if (r < 0) {
+    return r;
+  }
+
+  uint32_t flags;
+  r = utils::get_snap_create_flags(vm, &flags);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+
+  r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  r = validate_mirroring_enabled(io_ctx, group_name);
+  if (r < 0) {
+    return r;
+  }
+
+  librbd::RBD rbd;
+  std::string snap_id;
+  r = rbd.mirror_group_create_snapshot(io_ctx, group_name.c_str(), flags,
+                                       &snap_id);
+  if (r < 0) {
+    std::cerr << "rbd: error creating snapshot: " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
+
+  std::cout << "Snapshot ID: " << snap_id << std::endl;
+  return 0;
+}
+
+Shell::Action action_enable(
+  {"mirror", "group", "enable"}, {},
+  "Enable RBD mirroring for an group.", "",
+  &get_arguments_enable, &execute_enable);
+Shell::Action action_disable(
+  {"mirror", "group", "disable"}, {},
+  "Disable RBD mirroring for an group.", "",
+  &get_arguments_disable, &execute_disable);
+Shell::Action action_promote(
+  {"mirror", "group", "promote"}, {},
+  "Promote an group to primary for RBD mirroring.", "",
+  &get_arguments_promote, &execute_promote);
+Shell::Action action_demote(
+  {"mirror", "group", "demote"}, {},
+  "Demote an group to non-primary for RBD mirroring.", "",
+  &get_arguments, &execute_demote);
+Shell::Action action_resync(
+  {"mirror", "group", "resync"}, {},
+  "Force resync to primary group for RBD mirroring.", "",
+  &get_arguments, &execute_resync);
+Shell::Action action_status(
+  {"mirror", "group", "status"}, {},
+  "Show RBD mirroring status for an group.", "",
+  &get_status_arguments, &execute_status);
+Shell::Action action_snapshot(
+  {"mirror", "group", "snapshot"}, {},
+  "Create RBD mirroring group snapshot.", "",
+  &get_snapshot_arguments, &execute_snapshot);
+
+} // namespace mirror_image
+} // namespace action
+} // namespace rbd
index 1a392ed9894590e7920c70533bd6afb8375a17d1..1ff8de897f8ad6c170b5123ec75ba950dac7e8bc 100644 (file)
@@ -399,7 +399,7 @@ int execute_status(const po::variables_map &vm,
         local_status));
       formatter->dump_string("description", local_status.description);
       if (mirror_service != nullptr) {
-        mirror_service->dump_image(formatter);
+        mirror_service->dump(formatter);
       }
       formatter->dump_string("last_update", utils::timestr(
         local_status.last_update));
@@ -458,7 +458,7 @@ int execute_status(const po::variables_map &vm,
                 << "  description: " << local_status.description << "\n";
       if (mirror_service != nullptr) {
         std::cout << "  service:     " <<
-          mirror_service->get_image_description() << "\n";
+          mirror_service->get_description() << "\n";
       }
       std::cout << "  last_update: " << utils::timestr(
         local_status.last_update) << std::endl;
index b0678a38d113763c8f05c7b0efbde46f81cfb152..37a81dada16ff84b964fa6cb8ffca2624536b173 100644 (file)
@@ -666,7 +666,7 @@ protected:
           local_status));
         m_formatter->dump_string("description", local_status.description);
         if (mirror_service != nullptr) {
-          mirror_service->dump_image(m_formatter);
+          mirror_service->dump(m_formatter);
         }
         m_formatter->dump_string("last_update", utils::timestr(
           local_status.last_update));
@@ -704,7 +704,7 @@ protected:
                   << "  description: " << local_status.description << std::endl;
         if (mirror_service != nullptr) {
           std::cout << "  service:     " <<
-            mirror_service->get_image_description() << std::endl;
+            mirror_service->get_description() << std::endl;
         }
         std::cout << "  last_update: " << utils::timestr(
           local_status.last_update) << std::endl;