mirror group resync Force resync to primary group for RBD
mirroring.
mirror group snapshot Create RBD mirroring group snapshot.
+ mirror group snapshot schedule add
+ Add mirror group snapshot schedule.
+ mirror group snapshot schedule list (... ls)
+ List mirror group snapshot schedule.
+ mirror group snapshot schedule remove (... rm)
+ Remove mirror group snapshot schedule.
+ mirror group snapshot schedule status
+ Show mirror group snapshot schedule
+ status.
mirror group status Show RBD mirroring status for an group.
mirror image demote Demote an image to non-primary for RBD
mirroring.
--skip-quiesce do not run quiesce hooks
--ignore-quiesce-error ignore quiesce hook error
+ rbd help mirror group snapshot schedule add
+ usage: rbd mirror group snapshot schedule add
+ [--pool <pool>]
+ [--namespace <namespace>]
+ [--group <group>]
+ <interval> [<start-time>]
+
+ Add mirror group snapshot schedule.
+
+ Positional arguments
+ <interval> schedule interval
+ <start-time> schedule start time
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --group arg group name
+
+ rbd help mirror group snapshot schedule list
+ usage: rbd mirror group snapshot schedule list
+ [--pool <pool>]
+ [--namespace <namespace>]
+ [--group <group>] [--recursive]
+ [--format <format>] [--pretty-format]
+
+ List mirror group snapshot schedule.
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --group arg group name
+ -R [ --recursive ] list all schedules
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
+ rbd help mirror group snapshot schedule remove
+ usage: rbd mirror group snapshot schedule remove
+ [--pool <pool>]
+ [--namespace <namespace>]
+ [--group <group>]
+ [<interval>] [<start-time>]
+
+ Remove mirror group snapshot schedule.
+
+ Positional arguments
+ <interval> schedule interval
+ <start-time> schedule start time
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --group arg group name
+
+ rbd help mirror group snapshot schedule status
+ usage: rbd mirror group snapshot schedule status
+ [--pool <pool>]
+ [--namespace <namespace>]
+ [--group <group>] [--format <format>]
+ [--pretty-format]
+
+ Show mirror group snapshot schedule status.
+
+ 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 group status
usage: rbd mirror group status [--pool <pool>] [--namespace <namespace>]
[--group <group>] [--format <format>]
(name.c_str(), po::value<std::string>(), description.c_str());
}
+void add_group_option(po::options_description *opt,
+ ArgumentModifier modifier) {
+ std::string name = GROUP_NAME;
+ std::string description = get_description_prefix(modifier) + "group name";
+ switch (modifier) {
+ case ARGUMENT_MODIFIER_NONE:
+ case ARGUMENT_MODIFIER_SOURCE:
+ break;
+ case ARGUMENT_MODIFIER_DEST:
+ name = DEST_GROUP_NAME;
+ break;
+ }
+
+ // TODO add validator
+ opt->add_options()
+ (name.c_str(), po::value<std::string>(), description.c_str());
+}
+
void add_image_id_option(po::options_description *opt,
const std::string &desc_suffix) {
std::string name = IMAGE_ID;
static const std::string PATH("path");
static const std::string FROM_SNAPSHOT_NAME("from-snap");
static const std::string WHOLE_OBJECT("whole-object");
+static const std::string GROUP_NAME("group");
+static const std::string DEST_GROUP_NAME("dest-group");
// encryption arguments
static const std::string ENCRYPTION_FORMAT("encryption-format");
void add_namespace_option(boost::program_options::options_description *opt,
ArgumentModifier modifier);
+void add_group_option(boost::program_options::options_description *opt,
+ ArgumentModifier modifier);
+
void add_image_option(boost::program_options::options_description *opt,
ArgumentModifier modifier,
const std::string &desc_suffix = "");
action/MirrorImage.cc
action/MirrorPool.cc
action/MirrorSnapshotSchedule.cc
+ action/MirrorGroupSnapshotSchedule.cc
action/Namespace.cc
action/Nbd.cc
action/ObjectMap.cc
namespace {
int parse_schedule_name(const std::string &name, bool allow_images,
- std::string *pool_name, std::string *namespace_name,
- std::string *image_name) {
+ bool allow_groups, std::string *pool_name,
+ std::string *namespace_name, std::string *image_name,
+ std::string *group_name) {
// parse names like:
// '', 'rbd/', 'rbd/ns/', 'rbd/image', 'rbd/ns/image'
std::regex pattern("^(?:([^/]+)/(?:(?:([^/]+)/|)(?:([^/@]+))?)?)?$");
}
if (match[3].matched) {
- if (!allow_images) {
+ if (!allow_images and !allow_groups) {
return -EINVAL;
}
- *image_name = match[3];
+ if (allow_images) {
+ *image_name = match[3];
+ } else if (allow_groups) {
+ *group_name = match[3];
+ }
+
} else {
*image_name = "-";
+ *group_name = "-";
}
return 0;
} // anonymous namespace
void add_level_spec_options(po::options_description *options,
- bool allow_image) {
+ bool allow_image, bool allow_group) {
at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE);
at::add_namespace_option(options, at::ARGUMENT_MODIFIER_NONE);
if (allow_image) {
at::add_image_option(options, at::ARGUMENT_MODIFIER_NONE);
}
+ if (allow_group) {
+ at::add_group_option(options, at::ARGUMENT_MODIFIER_NONE);
+ }
}
int get_level_spec_args(const po::variables_map &vm,
std::map<std::string, std::string> *args) {
- if (vm.count(at::IMAGE_NAME)) {
+ if (vm.count(at::IMAGE_NAME) or vm.count(at::GROUP_NAME)) {
std::string pool_name;
std::string namespace_name;
- std::string image_name;
+ std::string image_or_group_name;
- int r = utils::extract_spec(vm[at::IMAGE_NAME].as<std::string>(),
- &pool_name, &namespace_name, &image_name,
- nullptr, utils::SPEC_VALIDATION_FULL);
- if (r < 0) {
- return r;
+ if (vm.count(at::IMAGE_NAME)) {
+ int r = utils::extract_spec(vm[at::IMAGE_NAME].as<std::string>(),
+ &pool_name, &namespace_name, &image_or_group_name,
+ nullptr, utils::SPEC_VALIDATION_FULL);
+ if (r < 0) {
+ return r;
+ }
+ }
+
+ if (vm.count(at::GROUP_NAME)) {
+ int r = utils::extract_spec(vm[at::GROUP_NAME].as<std::string>(),
+ &pool_name, &namespace_name, &image_or_group_name,
+ nullptr, utils::SPEC_VALIDATION_FULL);
+ if (r < 0) {
+ return r;
+ }
}
if (!pool_name.empty()) {
}
if (namespace_name.empty()) {
- (*args)["level_spec"] = pool_name + "/" + image_name;
+ (*args)["level_spec"] = pool_name + "/" + image_or_group_name;
} else {
(*args)["level_spec"] = pool_name + "/" + namespace_name + "/" +
- image_name;
+ image_or_group_name;
}
return 0;
}
std::string pool_name;
std::string namespace_name;
std::string image_name;
+ std::string group_name;
- int r = parse_schedule_name(name, allow_images, &pool_name, &namespace_name,
- &image_name);
+ int r = parse_schedule_name(name, allow_images, allow_groups, &pool_name,
+ &namespace_name, &image_name, &group_name);
if (r < 0) {
continue;
}
f->dump_string("namespace", namespace_name);
if (allow_images) {
f->dump_string("image", image_name);
+ } else if (allow_groups) {
+ f->dump_string("group", group_name);
}
s.dump(f);
f->close_section();
tbl.define_column("NAMESPACE", TextTable::LEFT, TextTable::LEFT);
if (l.allow_images) {
tbl.define_column("IMAGE", TextTable::LEFT, TextTable::LEFT);
+ } else if (l.allow_groups) {
+ tbl.define_column("GROUP", TextTable::LEFT, TextTable::LEFT);
}
tbl.define_column("SCHEDULE", TextTable::LEFT, TextTable::LEFT);
std::string pool_name;
std::string namespace_name;
std::string image_name;
+ std::string group_name;
- int r = parse_schedule_name(name, l.allow_images, &pool_name,
- &namespace_name, &image_name);
+ int r = parse_schedule_name(name, l.allow_images, l.allow_groups,
+ &pool_name, &namespace_name, &image_name,
+ &group_name);
if (r < 0) {
continue;
}
tbl << pool_name << namespace_name;
if (l.allow_images) {
tbl << image_name;
+ } else if (l.allow_groups) {
+ tbl << group_name;
}
tbl << ss.str() << TextTable::endrow;
}
namespace rbd {
void add_level_spec_options(
- boost::program_options::options_description *options, bool allow_image=true);
+ boost::program_options::options_description *options, bool allow_image=true,
+ bool allow_group=false);
int get_level_spec_args(const boost::program_options::variables_map &vm,
std::map<std::string, std::string> *args);
void normalize_level_spec_args(std::map<std::string, std::string> *args);
class ScheduleList {
public:
- ScheduleList(bool allow_images=true) : allow_images(allow_images) {
+ ScheduleList(bool allow_images=true, bool allow_groups=false) :
+ allow_images(allow_images), allow_groups(allow_groups) {
+ //TODO: throw exception when both allow_images and allow_groups are True
}
int parse(const std::string &list);
private:
bool allow_images;
+ bool allow_groups;
std::map<std::string, Schedule> schedules;
};
static const std::string GROUP_NAMESPACE_NAME("group-" + at::NAMESPACE_NAME);
static const std::string IMAGE_NAMESPACE_NAME("image-" + at::NAMESPACE_NAME);
-void add_group_option(po::options_description *opt,
- at::ArgumentModifier modifier) {
- std::string name = GROUP_NAME;
- std::string description = at::get_description_prefix(modifier) + "group name";
- switch (modifier) {
- case at::ARGUMENT_MODIFIER_NONE:
- case at::ARGUMENT_MODIFIER_SOURCE:
- break;
- case at::ARGUMENT_MODIFIER_DEST:
- name = DEST_GROUP_NAME;
- break;
- }
-
- // TODO add validator
- opt->add_options()
- (name.c_str(), po::value<std::string>(), description.c_str());
-}
-
void add_prefixed_pool_option(po::options_description *opt,
const std::string &prefix) {
std::string name = prefix + "-" + at::POOL_NAME;
bool snap) {
at::add_pool_option(opt, modifier);
at::add_namespace_option(opt, modifier);
- add_group_option(opt, modifier);
+ at::add_group_option(opt, modifier);
if (!snap) {
pos->add_options()
((get_name_prefix(modifier) + GROUP_SPEC).c_str(),
add_prefixed_pool_option(options, "group");
add_prefixed_namespace_option(options, "group");
- add_group_option(options, at::ARGUMENT_MODIFIER_NONE);
+ at::add_group_option(options, at::ARGUMENT_MODIFIER_NONE);
positional->add_options()
(at::IMAGE_SPEC.c_str(),
add_prefixed_pool_option(options, "group");
add_prefixed_namespace_option(options, "group");
- add_group_option(options, at::ARGUMENT_MODIFIER_NONE);
+ at::add_group_option(options, at::ARGUMENT_MODIFIER_NONE);
positional->add_options()
(at::IMAGE_SPEC.c_str(),
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Schedule.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+#include "common/ceph_context.h"
+#include "common/ceph_json.h"
+#include "common/errno.h"
+#include "common/Formatter.h"
+#include "common/TextTable.h"
+#include "global/global_context.h"
+#include "include/stringify.h"
+
+#include <iostream>
+#include <list>
+#include <map>
+#include <string>
+#include <boost/program_options.hpp>
+
+#include "json_spirit/json_spirit.h"
+
+namespace rbd {
+namespace action {
+namespace mirror_group_snapshot_schedule {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+namespace {
+
+class ScheduleStatus {
+public:
+ ScheduleStatus() {
+ }
+
+ int parse(const std::string &status) {
+ json_spirit::mValue json_root;
+ if(!json_spirit::read(status, json_root)) {
+ std::cerr << "rbd: invalid schedule status JSON received" << std::endl;
+ return -EBADMSG;
+ }
+
+ try {
+ auto &s = json_root.get_obj();
+
+ if (s["scheduled_groups"].type() != json_spirit::array_type) {
+ std::cerr << "rbd: unexpected schedule JSON received: "
+ << "scheduled_groups is not array" << std::endl;
+ return -EBADMSG;
+ }
+
+ for (auto &item_val : s["scheduled_groups"].get_array()) {
+ if (item_val.type() != json_spirit::obj_type) {
+ std::cerr << "rbd: unexpected schedule status JSON received: "
+ << "schedule item is not object" << std::endl;
+ return -EBADMSG;
+ }
+
+ auto &item = item_val.get_obj();
+
+ if (item["schedule_time"].type() != json_spirit::str_type) {
+ std::cerr << "rbd: unexpected schedule JSON received: "
+ << "schedule_time is not string" << std::endl;
+ return -EBADMSG;
+ }
+ auto schedule_time = item["schedule_time"].get_str();
+
+ if (item["group"].type() != json_spirit::str_type) {
+ std::cerr << "rbd: unexpected schedule JSON received: "
+ << "group is not string" << std::endl;
+ return -EBADMSG;
+ }
+ auto group = item["group"].get_str();
+
+ scheduled_groups.push_back({schedule_time, group});
+ }
+
+ } catch (std::runtime_error &) {
+ std::cerr << "rbd: invalid schedule JSON received" << std::endl;
+ return -EBADMSG;
+ }
+
+ return 0;
+ }
+
+ void dump(Formatter *f) {
+ f->open_array_section("scheduled_groups");
+ for (auto &group : scheduled_groups) {
+ f->open_object_section("group");
+ f->dump_string("schedule_time", group.first);
+ f->dump_string("group", group.second);
+ f->close_section(); // group
+ }
+ f->close_section(); // scheduled_groups
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, ScheduleStatus &d);
+
+private:
+
+ std::list<std::pair<std::string, std::string>> scheduled_groups;
+};
+
+std::ostream& operator<<(std::ostream& os, ScheduleStatus &s) {
+ TextTable tbl;
+ tbl.define_column("SCHEDULE TIME", TextTable::LEFT, TextTable::LEFT);
+ tbl.define_column("GROUP", TextTable::LEFT, TextTable::LEFT);
+
+ for (auto &[schedule_time, group] : s.scheduled_groups) {
+ tbl << schedule_time << group << TextTable::endrow;
+ }
+
+ os << tbl;
+ return os;
+}
+
+} // anonymous namespace
+
+void get_arguments_add(po::options_description *positional,
+ po::options_description *options) {
+ add_level_spec_options(options, false, true);
+ add_schedule_options(positional, true);
+}
+
+int execute_add(const po::variables_map &vm,
+ const std::vector<std::string> &ceph_global_init_args) {
+ std::map<std::string, std::string> args;
+
+ int r = get_level_spec_args(vm, &args);
+ if (r < 0) {
+ return r;
+ }
+ r = get_schedule_args(vm, true, &args);
+ if (r < 0) {
+ return r;
+ }
+
+ librados::Rados rados;
+ r = utils::init_rados(&rados);
+ if (r < 0) {
+ return r;
+ }
+
+ normalize_level_spec_args(&args);
+ r = utils::mgr_command(rados, "rbd mirror group snapshot schedule add", args,
+ &std::cout, &std::cerr);
+ if (r < 0) {
+ return r;
+ }
+
+ return 0;
+}
+
+void get_arguments_remove(po::options_description *positional,
+ po::options_description *options) {
+ add_level_spec_options(options, false, true);
+ add_schedule_options(positional, false);
+}
+
+int execute_remove(const po::variables_map &vm,
+ const std::vector<std::string> &ceph_global_init_args) {
+ std::map<std::string, std::string> args;
+
+ int r = get_level_spec_args(vm, &args);
+ if (r < 0) {
+ return r;
+ }
+ r = get_schedule_args(vm, false, &args);
+ if (r < 0) {
+ return r;
+ }
+
+ librados::Rados rados;
+ r = utils::init_rados(&rados);
+ if (r < 0) {
+ return r;
+ }
+
+ normalize_level_spec_args(&args);
+ r = utils::mgr_command(rados, "rbd mirror group snapshot schedule remove", args,
+ &std::cout, &std::cerr);
+ if (r < 0) {
+ return r;
+ }
+
+ return 0;
+}
+
+void get_arguments_list(po::options_description *positional,
+ po::options_description *options) {
+ add_level_spec_options(options, false, true);
+ options->add_options()
+ ("recursive,R", po::bool_switch(), "list all schedules");
+ at::add_format_options(options);
+}
+
+int execute_list(const po::variables_map &vm,
+ const std::vector<std::string> &ceph_global_init_args) {
+ std::map<std::string, std::string> args;
+
+ int r = get_level_spec_args(vm, &args);
+ if (r < 0) {
+ return r;
+ }
+
+ at::Format::Formatter formatter;
+ r = utils::get_formatter(vm, &formatter);
+ if (r < 0) {
+ return r;
+ }
+
+ librados::Rados rados;
+ r = utils::init_rados(&rados);
+ if (r < 0) {
+ return r;
+ }
+
+ normalize_level_spec_args(&args);
+ std::stringstream out;
+ r = utils::mgr_command(rados, "rbd mirror group snapshot schedule list", args, &out,
+ &std::cerr);
+ if (r < 0) {
+ return r;
+ }
+
+ ScheduleList schedule_list(false, true);
+ r = schedule_list.parse(out.str());
+ if (r < 0) {
+ return r;
+ }
+
+ if (vm["recursive"].as<bool>()) {
+ if (formatter.get()) {
+ schedule_list.dump(formatter.get());
+ formatter->flush(std::cout);
+ } else {
+ std::cout << schedule_list;
+ }
+ } else {
+ auto schedule = schedule_list.find(args["level_spec"]);
+ if (schedule == nullptr) {
+ return -ENOENT;
+ }
+
+ if (formatter.get()) {
+ schedule->dump(formatter.get());
+ formatter->flush(std::cout);
+ } else {
+ std::cout << *schedule << std::endl;
+ }
+ }
+
+ return 0;
+}
+
+void get_arguments_status(po::options_description *positional,
+ po::options_description *options) {
+ add_level_spec_options(options, false, true);
+ at::add_format_options(options);
+}
+
+int execute_status(const po::variables_map &vm,
+ const std::vector<std::string> &ceph_global_init_args) {
+ std::map<std::string, std::string> args;
+
+ int r = get_level_spec_args(vm, &args);
+ if (r < 0) {
+ return r;
+ }
+
+ at::Format::Formatter formatter;
+ r = utils::get_formatter(vm, &formatter);
+ if (r < 0) {
+ return r;
+ }
+
+ librados::Rados rados;
+ r = utils::init_rados(&rados);
+ if (r < 0) {
+ return r;
+ }
+
+ normalize_level_spec_args(&args);
+ std::stringstream out;
+ r = utils::mgr_command(rados, "rbd mirror group snapshot schedule status",
+ args, &out, &std::cerr);
+ ScheduleStatus schedule_status;
+ r = schedule_status.parse(out.str());
+ if (r < 0) {
+ return r;
+ }
+
+ if (formatter.get()) {
+ schedule_status.dump(formatter.get());
+ formatter->flush(std::cout);
+ } else {
+ std::cout << schedule_status;
+ }
+
+ return 0;
+}
+
+Shell::Action add_action(
+ {"mirror", "group", "snapshot", "schedule", "add"}, {},
+ "Add mirror group snapshot schedule.", "", &get_arguments_add, &execute_add);
+Shell::Action remove_action(
+ {"mirror", "group", "snapshot", "schedule", "remove"},
+ {"mirror", "group", "snapshot", "schedule", "rm"},
+ "Remove mirror group snapshot schedule.", "", &get_arguments_remove,
+ &execute_remove);
+Shell::Action list_action(
+ {"mirror", "group", "snapshot", "schedule", "list"},
+ {"mirror", "group", "snapshot", "schedule", "ls"},
+ "List mirror group snapshot schedule.", "", &get_arguments_list,
+ &execute_list);
+Shell::Action status_action(
+ {"mirror", "group", "snapshot", "schedule", "status"}, {},
+ "Show mirror group snapshot schedule status.", "", &get_arguments_status,
+ &execute_status);
+
+} // namespace mirror_group_snapshot_schedule
+} // namespace action
+} // namespace rbd