// vim: ts=8 sw=2 smarttab
#include "tools/rbd/Utils.h"
-#include "include/assert.h"
+#include "include/ceph_assert.h"
#include "include/Context.h"
#include "include/encoding.h"
#include "common/common_init.h"
#include "include/rbd/features.h"
#include "common/config.h"
#include "common/errno.h"
+#include "common/escape.h"
#include "common/safe_io.h"
#include "global/global_context.h"
+#include <fstream>
#include <iostream>
-#include <boost/regex.hpp>
+#include <regex>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
namespace at = argument_types;
namespace po = boost::program_options;
+namespace {
+
+static std::string mgr_command_args_to_str(
+ const std::map<std::string, std::string> &args) {
+ std::string out = "";
+
+ std::string delimiter;
+ for (auto &it : args) {
+ out += delimiter + "\"" + it.first + "\": \"" +
+ stringify(json_stream_escaper(it.second)) + "\"";
+ delimiter = ",\n";
+ }
+
+ return out;
+}
+
+} // anonymous namespace
+
int ProgressContext::update_progress(uint64_t offset, uint64_t total) {
if (progress) {
- int pc = total ? (offset * 100ull / total) : 0;
- if (pc != last_pc) {
- cerr << "\r" << operation << ": "
- << pc << "% complete...";
- cerr.flush();
+ int pc = get_percentage(offset, total);
+ if (pc > last_pc) {
+ std::cerr << "\r" << operation << ": "
+ << pc << "% complete..." << std::flush;
last_pc = pc;
}
}
void ProgressContext::finish() {
if (progress) {
- cerr << "\r" << operation << ": 100% complete...done." << std::endl;
+ std::cerr << "\r" << operation << ": 100% complete...done." << std::endl;
}
}
void ProgressContext::fail() {
if (progress) {
- cerr << "\r" << operation << ": " << last_pc << "% complete...failed."
- << std::endl;
+ std::cerr << "\r" << operation << ": " << last_pc << "% complete...failed."
+ << std::endl;
}
}
+int get_percentage(uint64_t part, uint64_t whole) {
+ return whole ? (100 * part / whole) : 0;
+}
+
void aio_context_callback(librbd::completion_t completion, void *arg)
{
librbd::RBD::AioCompletion *aio_completion =
bufferlist bl;
bl.append(buf, 4);
- bufferlist::iterator p = bl.begin();
+ auto p = bl.cbegin();
uint32_t len;
- ::decode(len, p);
+ decode(len, p);
if (len > max)
return -EINVAL;
}
int extract_spec(const std::string &spec, std::string *pool_name,
- std::string *image_name, std::string *snap_name,
- SpecValidation spec_validation) {
- if (!g_ceph_context->_conf->get_val<bool>("rbd_validate_names")) {
+ std::string *namespace_name, std::string *name,
+ std::string *snap_name, SpecValidation spec_validation) {
+ if (!g_ceph_context->_conf.get_val<bool>("rbd_validate_names")) {
spec_validation = SPEC_VALIDATION_NONE;
}
- boost::regex pattern;
+ std::regex pattern;
switch (spec_validation) {
case SPEC_VALIDATION_FULL:
- // disallow "/" and "@" in image and snap name
- pattern = "^(?:([^/@]+)/)?([^/@]+)(?:@([^/@]+))?$";
+ // disallow "/" and "@" in all names
+ pattern = "^(?:([^/@]+)/(?:([^/@]+)/)?)?([^/@]+)(?:@([^/@]+))?$";
break;
case SPEC_VALIDATION_SNAP:
// disallow "/" and "@" in snap name
- pattern = "^(?:([^/]+)/)?([^@]+)(?:@([^/@]+))?$";
+ pattern = "^(?:([^/]+)/(?:([^/@]+)/)?)?([^@]+)(?:@([^/@]+))?$";
break;
case SPEC_VALIDATION_NONE:
- // relaxed pattern assumes pool is before first "/" and snap
- // name is after first "@"
- pattern = "^(?:([^/]+)/)?([^@]+)(?:@(.+))?$";
+ // relaxed pattern assumes pool is before first "/",
+ // namespace is before second "/", and snap name is after first "@"
+ pattern = "^(?:([^/]+)/(?:([^/@]+)/)?)?([^@]+)(?:@(.+))?$";
break;
default:
- assert(false);
+ ceph_abort();
break;
}
- boost::smatch match;
- if (!boost::regex_match(spec, match, pattern)) {
+ std::smatch match;
+ if (!std::regex_match(spec, match, pattern)) {
std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl;
return -EINVAL;
}
- if (pool_name != nullptr && match[1].matched) {
- *pool_name = match[1];
- }
- if (image_name != nullptr) {
- *image_name = match[2];
- }
- if (snap_name != nullptr && match[3].matched) {
- *snap_name = match[3];
- }
- return 0;
-}
-
-int extract_group_spec(const std::string &spec,
- std::string *pool_name,
- std::string *group_name) {
- boost::regex pattern;
- pattern = "^(?:([^/]+)/)?(.+)?$";
-
- boost::smatch match;
- if (!boost::regex_match(spec, match, pattern)) {
- std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl;
- return -EINVAL;
+ if (match[1].matched) {
+ if (pool_name != nullptr) {
+ *pool_name = match[1];
+ } else {
+ std::cerr << "rbd: pool name specified for a command that doesn't use it"
+ << std::endl;
+ return -EINVAL;
+ }
}
- if (pool_name != nullptr && match[1].matched) {
- *pool_name = match[1];
- }
- if (group_name != nullptr) {
- *group_name = match[2];
+ if (match[2].matched) {
+ if (namespace_name != nullptr) {
+ *namespace_name = match[2];
+ } else {
+ std::cerr << "rbd: namespace name specified for a command that doesn't "
+ << "use it" << std::endl;
+ return -EINVAL;
+ }
}
- return 0;
-}
-
-int extract_image_id_spec(const std::string &spec, std::string *pool_name,
- std::string *image_id) {
- boost::regex pattern;
- pattern = "^(?:([^/]+)/)?(.+)?$";
-
- boost::smatch match;
- if (!boost::regex_match(spec, match, pattern)) {
- std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl;
- return -EINVAL;
+ if (name != nullptr) {
+ *name = match[3];
}
- if (pool_name != nullptr && match[1].matched) {
- *pool_name = match[1];
- }
- if (image_id != nullptr) {
- *image_id = match[2];
+ if (match[4].matched) {
+ if (snap_name != nullptr) {
+ *snap_name = match[4];
+ } else {
+ std::cerr << "rbd: snapshot name specified for a command that doesn't "
+ << "use it" << std::endl;
+ return -EINVAL;
+ }
}
-
return 0;
}
return "";
}
-std::string get_default_pool_name() {
- return g_ceph_context->_conf->get_val<std::string>("rbd_default_pool");
-}
-
-std::string get_pool_name(const po::variables_map &vm, size_t *arg_index) {
- std::string pool_name;
- if (vm.count(at::POOL_NAME)) {
- pool_name = vm[at::POOL_NAME].as<std::string>();
- } else {
- pool_name = get_positional_argument(vm, *arg_index);
- if (!pool_name.empty()) {
- ++(*arg_index);
- }
- }
-
- if (pool_name.empty()) {
- pool_name = get_default_pool_name();
+void normalize_pool_name(std::string* pool_name) {
+ if (pool_name->empty()) {
+ *pool_name = get_default_pool_name();
}
- return pool_name;
}
-int get_special_pool_group_names(const po::variables_map &vm,
- size_t *arg_index,
- std::string *group_pool_name,
- std::string *group_name) {
- if (nullptr == group_pool_name) return -EINVAL;
- if (nullptr == group_name) return -EINVAL;
- std::string pool_key = at::POOL_NAME;
-
- std::string group_pool_key = "group-" + at::POOL_NAME;
- std::string group_key = at::GROUP_NAME;
-
- if (vm.count(group_pool_key)) {
- *group_pool_name = vm[group_pool_key].as<std::string>();
- }
-
- if (vm.count(group_key)) {
- *group_name = vm[group_key].as<std::string>();
- }
-
- int r;
- if (group_name->empty()) {
- std::string spec = utils::get_positional_argument(vm, (*arg_index)++);
- if (!spec.empty()) {
- r = utils::extract_group_spec(spec, group_pool_name, group_name);
- if (r < 0) {
- return r;
- }
- }
- }
-
- if (group_pool_name->empty() && vm.count(pool_key)) {
- *group_pool_name = vm[pool_key].as<std::string>();
- }
-
- if (group_pool_name->empty()) {
- *group_pool_name = get_default_pool_name();
- }
-
- if (group_name->empty()) {
- std::cerr << "rbd: consistency group name was not specified" << std::endl;
- return -EINVAL;
- }
-
- return 0;
+std::string get_default_pool_name() {
+ return g_ceph_context->_conf.get_val<std::string>("rbd_default_pool");
}
-int get_special_pool_image_names(const po::variables_map &vm,
- size_t *arg_index,
- std::string *image_pool_name,
- std::string *image_name) {
- if (nullptr == image_pool_name) return -EINVAL;
- if (nullptr == image_name) return -EINVAL;
-
- std::string pool_key = at::POOL_NAME;
-
- std::string image_pool_key = "image-" + at::POOL_NAME;
- std::string image_key = at::IMAGE_NAME;
-
- if (vm.count(image_pool_key)) {
- *image_pool_name = vm[image_pool_key].as<std::string>();
+int get_pool_and_namespace_names(
+ const boost::program_options::variables_map &vm, bool validate_pool_name,
+ std::string* pool_name, std::string* namespace_name, size_t *arg_index) {
+ if (namespace_name != nullptr && vm.count(at::NAMESPACE_NAME)) {
+ *namespace_name = vm[at::NAMESPACE_NAME].as<std::string>();
}
- if (vm.count(image_key)) {
- *image_name = vm[image_key].as<std::string>();
- }
-
- int r;
- if (image_name->empty()) {
- std::string spec = utils::get_positional_argument(vm, (*arg_index)++);
- if (!spec.empty()) {
- r = utils::extract_spec(spec, image_pool_name,
- image_name, nullptr,
- utils::SPEC_VALIDATION_NONE);
- if (r < 0) {
- return r;
+ if (vm.count(at::POOL_NAME)) {
+ *pool_name = vm[at::POOL_NAME].as<std::string>();
+ } else {
+ *pool_name = get_positional_argument(vm, *arg_index);
+ if (!pool_name->empty()) {
+ if (namespace_name != nullptr) {
+ auto slash_pos = pool_name->find_last_of('/');
+ if (slash_pos != std::string::npos) {
+ *namespace_name = pool_name->substr(slash_pos + 1);
+ }
+ *pool_name = pool_name->substr(0, slash_pos);
}
+ ++(*arg_index);
}
}
- if (image_pool_name->empty() && vm.count(pool_key)) {
- *image_pool_name = vm[pool_key].as<std::string>();
- }
-
- if (image_pool_name->empty()) {
- *image_pool_name = get_default_pool_name();
+ if (!g_ceph_context->_conf.get_val<bool>("rbd_validate_names")) {
+ validate_pool_name = false;
}
- if (image_name->empty()) {
- std::cerr << "rbd: image name was not specified" << std::endl;
+ if (validate_pool_name &&
+ pool_name->find_first_of("/@") != std::string::npos) {
+ std::cerr << "rbd: invalid pool '" << *pool_name << "'" << std::endl;
+ return -EINVAL;
+ } else if (namespace_name != nullptr &&
+ namespace_name->find_first_of("/@") != std::string::npos) {
+ std::cerr << "rbd: invalid namespace '" << *namespace_name << "'"
+ << std::endl;
return -EINVAL;
}
}
int get_pool_image_id(const po::variables_map &vm,
- size_t *spec_arg_index,
- std::string *pool_name,
- std::string *image_id) {
+ size_t *spec_arg_index,
+ std::string *pool_name,
+ std::string *namespace_name,
+ std::string *image_id) {
if (vm.count(at::POOL_NAME) && pool_name != nullptr) {
*pool_name = vm[at::POOL_NAME].as<std::string>();
}
+ if (vm.count(at::NAMESPACE_NAME) && namespace_name != nullptr) {
+ *namespace_name = vm[at::NAMESPACE_NAME].as<std::string>();
+ }
if (vm.count(at::IMAGE_ID) && image_id != nullptr) {
*image_id = vm[at::IMAGE_ID].as<std::string>();
}
if (image_id != nullptr && spec_arg_index != nullptr && image_id->empty()) {
std::string spec = get_positional_argument(vm, (*spec_arg_index)++);
if (!spec.empty()) {
- r = extract_image_id_spec(spec, pool_name, image_id);
+ r = extract_spec(spec, pool_name, namespace_name, image_id, nullptr,
+ SPEC_VALIDATION_FULL);
if (r < 0) {
return r;
}
}
}
- if (pool_name != nullptr && pool_name->empty()) {
- *pool_name = get_default_pool_name();
- }
-
if (image_id != nullptr && image_id->empty()) {
std::cerr << "rbd: image id was not specified" << std::endl;
return -EINVAL;
return 0;
}
-int get_pool_group_names(const po::variables_map &vm,
- at::ArgumentModifier mod,
- size_t *spec_arg_index,
- std::string *pool_name,
- std::string *group_name) {
- std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
- at::DEST_POOL_NAME : at::POOL_NAME);
- std::string group_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
- at::DEST_GROUP_NAME : at::GROUP_NAME);
-
- if (vm.count(pool_key) && pool_name != nullptr) {
- *pool_name = vm[pool_key].as<std::string>();
- }
- if (vm.count(group_key) && group_name != nullptr) {
- *group_name = vm[group_key].as<std::string>();
+int get_image_or_snap_spec(const po::variables_map &vm, std::string *spec) {
+ size_t arg_index = 0;
+ std::string pool_name;
+ std::string nspace_name;
+ std::string image_name;
+ std::string snap_name;
+ int r = get_pool_image_snapshot_names(
+ vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &nspace_name,
+ &image_name, &snap_name, true, SNAPSHOT_PRESENCE_PERMITTED,
+ SPEC_VALIDATION_NONE);
+ if (r < 0) {
+ return r;
}
- int r;
- if (group_name != nullptr && spec_arg_index != nullptr &&
- group_name->empty()) {
- std::string spec = get_positional_argument(vm, (*spec_arg_index)++);
- if (!spec.empty()) {
- r = extract_group_spec(spec, pool_name, group_name);
- if (r < 0) {
- return r;
- }
+ if (pool_name.empty()) {
+ // connect to the cluster to get the default pool
+ librados::Rados rados;
+ r = init_rados(&rados);
+ if (r < 0) {
+ return r;
}
- }
- if (pool_name != nullptr && pool_name->empty()) {
- *pool_name = get_default_pool_name();
+ normalize_pool_name(&pool_name);
}
- if (group_name != nullptr && group_name->empty()) {
- std::string prefix = at::get_description_prefix(mod);
- std::cerr << "rbd: "
- << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
- << "group name was not specified" << std::endl;
- return -EINVAL;
+ spec->append(pool_name);
+ spec->append("/");
+ if (!nspace_name.empty()) {
+ spec->append(nspace_name);
+ spec->append("/");
+ }
+ spec->append(image_name);
+ if (!snap_name.empty()) {
+ spec->append("@");
+ spec->append(snap_name);
}
return 0;
}
+void append_options_as_args(const std::vector<std::string> &options,
+ std::vector<std::string> *args) {
+ for (auto &opts : options) {
+ std::vector<std::string> args_;
+ boost::split(args_, opts, boost::is_any_of(","));
+ for (auto &o : args_) {
+ args->push_back("--" + o);
+ }
+ }
+}
+
int get_pool_image_snapshot_names(const po::variables_map &vm,
at::ArgumentModifier mod,
size_t *spec_arg_index,
std::string *pool_name,
+ std::string *namespace_name,
std::string *image_name,
std::string *snap_name,
+ bool image_name_required,
SnapshotPresence snapshot_presence,
- SpecValidation spec_validation,
- bool image_required) {
+ SpecValidation spec_validation) {
std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
at::DEST_POOL_NAME : at::POOL_NAME);
std::string image_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
at::DEST_IMAGE_NAME : at::IMAGE_NAME);
+ return get_pool_generic_snapshot_names(vm, mod, spec_arg_index, pool_key,
+ pool_name, namespace_name, image_key,
+ "image", image_name, snap_name,
+ image_name_required, snapshot_presence,
+ spec_validation);
+}
+
+int get_pool_generic_snapshot_names(const po::variables_map &vm,
+ at::ArgumentModifier mod,
+ size_t *spec_arg_index,
+ const std::string& pool_key,
+ std::string *pool_name,
+ std::string *namespace_name,
+ const std::string& generic_key,
+ const std::string& generic_key_desc,
+ std::string *generic_name,
+ std::string *snap_name,
+ bool generic_name_required,
+ SnapshotPresence snapshot_presence,
+ SpecValidation spec_validation) {
+ std::string namespace_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
+ at::DEST_NAMESPACE_NAME : at::NAMESPACE_NAME);
std::string snap_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
- at::DEST_SNAPSHOT_NAME : at::SNAPSHOT_NAME);
+ at::DEST_SNAPSHOT_NAME : at::SNAPSHOT_NAME);
if (vm.count(pool_key) && pool_name != nullptr) {
*pool_name = vm[pool_key].as<std::string>();
}
- if (vm.count(image_key) && image_name != nullptr) {
- *image_name = vm[image_key].as<std::string>();
+ if (vm.count(namespace_key) && namespace_name != nullptr) {
+ *namespace_name = vm[namespace_key].as<std::string>();
+ }
+ if (vm.count(generic_key) && generic_name != nullptr) {
+ *generic_name = vm[generic_key].as<std::string>();
}
if (vm.count(snap_key) && snap_name != nullptr) {
- *snap_name = vm[snap_key].as<std::string>();
+ *snap_name = vm[snap_key].as<std::string>();
}
int r;
- if (image_name != nullptr && !image_name->empty()) {
+ if ((generic_key == at::IMAGE_NAME || generic_key == at::DEST_IMAGE_NAME) &&
+ generic_name != nullptr && !generic_name->empty()) {
// despite the separate pool and snapshot name options,
// we can also specify them via the image option
- std::string image_name_copy(*image_name);
- r = extract_spec(image_name_copy, pool_name, image_name, snap_name,
- spec_validation);
+ std::string image_name_copy(*generic_name);
+ r = extract_spec(image_name_copy, pool_name, namespace_name, generic_name,
+ snap_name, spec_validation);
if (r < 0) {
return r;
}
}
- if (image_name != nullptr && spec_arg_index != nullptr &&
- image_name->empty()) {
+ if (generic_name != nullptr && spec_arg_index != nullptr &&
+ generic_name->empty()) {
std::string spec = get_positional_argument(vm, (*spec_arg_index)++);
if (!spec.empty()) {
- r = extract_spec(spec, pool_name, image_name, snap_name, spec_validation);
+ r = extract_spec(spec, pool_name, namespace_name, generic_name, snap_name,
+ spec_validation);
if (r < 0) {
return r;
}
}
}
- if (pool_name != nullptr && pool_name->empty()) {
- *pool_name = get_default_pool_name();
- }
-
- if (image_name != nullptr && image_required && image_name->empty()) {
+ if (generic_name != nullptr && generic_name_required &&
+ generic_name->empty()) {
std::string prefix = at::get_description_prefix(mod);
std::cerr << "rbd: "
<< (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
- << "image name was not specified" << std::endl;
+ << generic_key_desc << " name was not specified" << std::endl;
return -EINVAL;
}
- //Validate pool name while creating/renaming/copying/cloning/importing/etc
+ std::regex pattern("^[^@/]*?$");
if (spec_validation == SPEC_VALIDATION_FULL) {
- boost::regex pattern("^[^@/]+?$");
- if (!boost::regex_match (*pool_name, pattern)) {
+ // validate pool name while creating/renaming/copying/cloning/importing/etc
+ if ((pool_name != nullptr) && !std::regex_match (*pool_name, pattern)) {
std::cerr << "rbd: invalid pool name '" << *pool_name << "'" << std::endl;
return -EINVAL;
}
}
- if (snap_name != nullptr) {
- r = validate_snapshot_name(mod, *snap_name, snapshot_presence,
- spec_validation);
- if (r < 0) {
- return r;
- }
- }
- return 0;
-}
-
-int get_pool_snapshot_names(const po::variables_map &vm,
- at::ArgumentModifier mod,
- size_t *spec_arg_index,
- std::string *pool_name,
- std::string *snap_name,
- SnapshotPresence snapshot_presence,
- SpecValidation spec_validation) {
- std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
- at::DEST_POOL_NAME : at::POOL_NAME);
- std::string snap_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
- at::DEST_SNAPSHOT_NAME : at::SNAPSHOT_NAME);
-
- if (vm.count(pool_key) && pool_name != nullptr) {
- *pool_name = vm[pool_key].as<std::string>();
- }
- if (vm.count(snap_key) && snap_name != nullptr) {
- *snap_name = vm[snap_key].as<std::string>();
- }
-
- if (pool_name != nullptr && pool_name->empty()) {
- *pool_name = get_default_pool_name();
+ if (namespace_name != nullptr && !namespace_name->empty() &&
+ !std::regex_match (*namespace_name, pattern)) {
+ std::cerr << "rbd: invalid namespace name '" << *namespace_name << "'"
+ << std::endl;
+ return -EINVAL;
}
if (snap_name != nullptr) {
- int r = validate_snapshot_name(mod, *snap_name, snapshot_presence,
- spec_validation);
- if (r < 0) {
- return r;
- }
- }
- return 0;
-}
-
-int get_pool_journal_names(const po::variables_map &vm,
- at::ArgumentModifier mod,
- size_t *spec_arg_index,
- std::string *pool_name,
- std::string *journal_name) {
- std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
- at::DEST_POOL_NAME : at::POOL_NAME);
- std::string image_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
- at::DEST_IMAGE_NAME : at::IMAGE_NAME);
- std::string journal_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
- at::DEST_JOURNAL_NAME : at::JOURNAL_NAME);
-
- if (vm.count(pool_key) && pool_name != nullptr) {
- *pool_name = vm[pool_key].as<std::string>();
- }
- if (vm.count(journal_key) && journal_name != nullptr) {
- *journal_name = vm[journal_key].as<std::string>();
- }
-
- std::string image_name;
- if (vm.count(image_key)) {
- image_name = vm[image_key].as<std::string>();
- }
-
- int r;
- if (journal_name != nullptr && !journal_name->empty()) {
- // despite the separate pool option,
- // we can also specify them via the journal option
- std::string journal_name_copy(*journal_name);
- r = extract_spec(journal_name_copy, pool_name, journal_name, nullptr,
- SPEC_VALIDATION_FULL);
- if (r < 0) {
- return r;
- }
- }
-
- if (!image_name.empty()) {
- // despite the separate pool option,
- // we can also specify them via the image option
- std::string image_name_copy(image_name);
- r = extract_spec(image_name_copy, pool_name, &image_name, nullptr,
- SPEC_VALIDATION_NONE);
- if (r < 0) {
- return r;
- }
- }
-
- if (journal_name != nullptr && spec_arg_index != nullptr &&
- journal_name->empty()) {
- std::string spec = get_positional_argument(vm, (*spec_arg_index)++);
- if (!spec.empty()) {
- r = extract_spec(spec, pool_name, journal_name, nullptr,
- SPEC_VALIDATION_FULL);
- if (r < 0) {
- return r;
- }
- }
- }
-
- if (pool_name != nullptr && pool_name->empty()) {
- *pool_name = get_default_pool_name();
- }
-
- if (pool_name != nullptr && journal_name != nullptr &&
- journal_name->empty() && !image_name.empty()) {
- // Try to get journal name from image info.
- librados::Rados rados;
- librados::IoCtx io_ctx;
- librbd::Image image;
- int r = init_and_open_image(*pool_name, image_name, "", "", true, &rados,
- &io_ctx, &image);
- if (r < 0) {
- std::cerr << "rbd: failed to open image " << image_name
- << " to get journal name: " << cpp_strerror(r) << std::endl;
- return r;
- }
-
- uint64_t features;
- r = image.features(&features);
+ r = validate_snapshot_name(mod, *snap_name, snapshot_presence,
+ spec_validation);
if (r < 0) {
return r;
}
- if ((features & RBD_FEATURE_JOURNALING) == 0) {
- std::cerr << "rbd: journaling is not enabled for image " << image_name
- << std::endl;
- return -EINVAL;
- }
- *journal_name = image_id(image);
}
-
- if (journal_name != nullptr && journal_name->empty()) {
- std::string prefix = at::get_description_prefix(mod);
- std::cerr << "rbd: "
- << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
- << "journal was not specified" << std::endl;
- return -EINVAL;
- }
-
return 0;
}
if (!snap_name.empty()) {
std::cerr << "rbd: "
<< (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
- << "snapname specified for a command that doesn't use it"
+ << "snapshot name specified for a command that doesn't use it"
<< std::endl;
return -EINVAL;
}
if (snap_name.empty()) {
std::cerr << "rbd: "
<< (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
- << "snap name was not specified" << std::endl;
+ << "snapshot name was not specified" << std::endl;
return -EINVAL;
}
break;
if (spec_validation == SPEC_VALIDATION_SNAP) {
// disallow "/" and "@" in snap name
- boost::regex pattern("^[^@/]*?$");
- if (!boost::regex_match (snap_name, pattern)) {
+ std::regex pattern("^[^@/]*?$");
+ if (!std::regex_match (snap_name, pattern)) {
std::cerr << "rbd: invalid snap name '" << snap_name << "'" << std::endl;
return -EINVAL;
}
int get_image_options(const boost::program_options::variables_map &vm,
bool get_format, librbd::ImageOptions *opts) {
uint64_t order = 0, stripe_unit = 0, stripe_count = 0, object_size = 0;
- uint64_t features = 0, features_clear = 0, features_set = 0;
+ uint64_t features = 0, features_clear = 0;
std::string data_pool;
bool order_specified = true;
bool features_specified = false;
bool features_clear_specified = false;
- bool features_set_specified = false;
bool stripe_specified = false;
if (vm.count(at::IMAGE_ORDER)) {
order = vm[at::IMAGE_ORDER].as<uint64_t>();
- std::cerr << "rbd: --order is deprecated, use --object-size"
- << std::endl;
} else if (vm.count(at::IMAGE_OBJECT_SIZE)) {
object_size = vm[at::IMAGE_OBJECT_SIZE].as<uint64_t>();
- order = std::round(std::log2(object_size));
+ order = std::round(std::log2(object_size));
} else {
order_specified = false;
}
if (vm.count(at::IMAGE_FEATURES)) {
features = vm[at::IMAGE_FEATURES].as<uint64_t>();
features_specified = true;
- } else {
- features = get_rbd_default_features(g_ceph_context);
}
if (vm.count(at::IMAGE_STRIPE_UNIT)) {
}
if (format_specified) {
- int r = g_conf->set_val("rbd_default_format", stringify(format));
- assert(r == 0);
+ int r = g_conf().set_val("rbd_default_format", stringify(format));
+ ceph_assert(r == 0);
opts->set(RBD_IMAGE_OPTION_FORMAT, format);
}
}
if (features_clear_specified) {
opts->set(RBD_IMAGE_OPTION_FEATURES_CLEAR, features_clear);
}
- if (features_set_specified)
- opts->set(RBD_IMAGE_OPTION_FEATURES_SET, features_set);
if (stripe_specified) {
opts->set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit);
opts->set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count);
return r;
}
+ r = get_flatten_option(vm, opts);
+ if (r < 0) {
+ return r;
+ }
+
+ if (vm.count(at::IMAGE_MIRROR_IMAGE_MODE)) {
+ opts->set(RBD_IMAGE_OPTION_MIRROR_IMAGE_MODE,
+ vm[at::IMAGE_MIRROR_IMAGE_MODE].as<librbd::mirror_image_mode_t>());
+ }
+
return 0;
}
}
opts->set(RBD_IMAGE_OPTION_JOURNAL_ORDER, order);
- int r = g_conf->set_val("rbd_journal_order", stringify(order));
- assert(r == 0);
+ int r = g_conf().set_val("rbd_journal_order", stringify(order));
+ ceph_assert(r == 0);
}
if (vm.count(at::JOURNAL_SPLAY_WIDTH)) {
opts->set(RBD_IMAGE_OPTION_JOURNAL_SPLAY_WIDTH,
vm[at::JOURNAL_SPLAY_WIDTH].as<uint64_t>());
- int r = g_conf->set_val("rbd_journal_splay_width",
+ int r = g_conf().set_val("rbd_journal_splay_width",
stringify(
vm[at::JOURNAL_SPLAY_WIDTH].as<uint64_t>()));
- assert(r == 0);
+ ceph_assert(r == 0);
}
if (vm.count(at::JOURNAL_POOL)) {
opts->set(RBD_IMAGE_OPTION_JOURNAL_POOL,
vm[at::JOURNAL_POOL].as<std::string>());
- int r = g_conf->set_val("rbd_journal_pool",
+ int r = g_conf().set_val("rbd_journal_pool",
vm[at::JOURNAL_POOL].as<std::string>());
- assert(r == 0);
+ ceph_assert(r == 0);
}
return 0;
}
+int get_flatten_option(const boost::program_options::variables_map &vm,
+ librbd::ImageOptions *opts) {
+ if (vm.count(at::IMAGE_FLATTEN) && vm[at::IMAGE_FLATTEN].as<bool>()) {
+ uint64_t flatten = 1;
+ opts->set(RBD_IMAGE_OPTION_FLATTEN, flatten);
+ }
+ return 0;
+}
+
int get_image_size(const boost::program_options::variables_map &vm,
uint64_t *size) {
if (vm.count(at::IMAGE_SIZE) == 0) {
}
int get_path(const boost::program_options::variables_map &vm,
- const std::string &positional_path, std::string *path) {
- if (!positional_path.empty()) {
- *path = positional_path;
- } else if (vm.count(at::PATH)) {
+ size_t *arg_index, std::string *path) {
+ if (vm.count(at::PATH)) {
*path = vm[at::PATH].as<std::string>();
+ } else {
+ *path = get_positional_argument(vm, *arg_index);
+ if (!path->empty()) {
+ ++(*arg_index);
+ }
}
if (path->empty()) {
return 0;
}
+int get_snap_create_flags(const po::variables_map &vm, uint32_t *flags) {
+ if (vm[at::SKIP_QUIESCE].as<bool>() &&
+ vm[at::IGNORE_QUIESCE_ERROR].as<bool>()) {
+ std::cerr << "rbd: " << at::IGNORE_QUIESCE_ERROR
+ << " cannot be used together with " << at::SKIP_QUIESCE
+ << std::endl;
+ return -EINVAL;
+ }
+
+ *flags = 0;
+ if (vm[at::SKIP_QUIESCE].as<bool>()) {
+ *flags |= RBD_SNAP_CREATE_SKIP_QUIESCE;
+ } else if (vm[at::IGNORE_QUIESCE_ERROR].as<bool>()) {
+ *flags |= RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR;
+ }
+ return 0;
+}
+
+int get_encryption_options(const boost::program_options::variables_map &vm,
+ EncryptionOptions* result) {
+ std::vector<std::string> passphrase_files;
+ if (vm.count(at::ENCRYPTION_PASSPHRASE_FILE)) {
+ passphrase_files =
+ vm[at::ENCRYPTION_PASSPHRASE_FILE].as<std::vector<std::string>>();
+ }
+
+ std::vector<at::EncryptionFormat> formats;
+ if (vm.count(at::ENCRYPTION_FORMAT)) {
+ formats = vm[at::ENCRYPTION_FORMAT].as<decltype(formats)>();
+ } else if (vm.count(at::ENCRYPTION_PASSPHRASE_FILE)) {
+ formats.resize(passphrase_files.size(),
+ at::EncryptionFormat{RBD_ENCRYPTION_FORMAT_LUKS});
+ }
+
+ if (formats.size() != passphrase_files.size()) {
+ std::cerr << "rbd: encryption formats count does not match "
+ << "passphrase files count" << std::endl;
+ return -EINVAL;
+ }
+
+ result->specs.clear();
+ result->specs.reserve(formats.size());
+ for (size_t i = 0; i < formats.size(); ++i) {
+ std::ifstream file(passphrase_files[i], std::ios::in | std::ios::binary);
+ if (file.fail()) {
+ std::cerr << "rbd: unable to open passphrase file '"
+ << passphrase_files[i] << "': " << cpp_strerror(errno)
+ << std::endl;
+ return -errno;
+ }
+ std::string passphrase((std::istreambuf_iterator<char>(file)),
+ std::istreambuf_iterator<char>());
+ file.close();
+
+ switch (formats[i].format) {
+ case RBD_ENCRYPTION_FORMAT_LUKS: {
+ auto opts = new librbd::encryption_luks_format_options_t{
+ std::move(passphrase)};
+ result->specs.push_back(
+ {RBD_ENCRYPTION_FORMAT_LUKS, opts, sizeof(*opts)});
+ break;
+ }
+ case RBD_ENCRYPTION_FORMAT_LUKS1: {
+ auto opts = new librbd::encryption_luks1_format_options_t{
+ .passphrase = std::move(passphrase)};
+ result->specs.push_back(
+ {RBD_ENCRYPTION_FORMAT_LUKS1, opts, sizeof(*opts)});
+ break;
+ }
+ case RBD_ENCRYPTION_FORMAT_LUKS2: {
+ auto opts = new librbd::encryption_luks2_format_options_t{
+ .passphrase = std::move(passphrase)};
+ result->specs.push_back(
+ {RBD_ENCRYPTION_FORMAT_LUKS2, opts, sizeof(*opts)});
+ break;
+ }
+ default:
+ ceph_abort();
+ }
+ }
+
+ return 0;
+}
+
void init_context() {
- g_conf->set_val_or_die("rbd_cache_writethrough_until_flush", "false");
- g_conf->apply_changes(NULL);
- common_init_finish(g_ceph_context);
+ g_conf().set_val_or_die("rbd_cache_writethrough_until_flush", "false");
+ g_conf().apply_changes(nullptr);
}
-int init(const std::string &pool_name, librados::Rados *rados,
- librados::IoCtx *io_ctx) {
+int init_rados(librados::Rados *rados) {
init_context();
int r = rados->init_with_context(g_ceph_context);
return r;
}
- r = init_io_ctx(*rados, pool_name, io_ctx);
+ return 0;
+}
+
+int init(const std::string &pool_name, const std::string& namespace_name,
+ librados::Rados *rados, librados::IoCtx *io_ctx) {
+ init_context();
+
+ int r = init_rados(rados);
+ if (r < 0) {
+ return r;
+ }
+
+ r = init_io_ctx(*rados, pool_name, namespace_name, io_ctx);
if (r < 0) {
return r;
}
return 0;
}
-int init_io_ctx(librados::Rados &rados, const std::string &pool_name,
- librados::IoCtx *io_ctx) {
+int init_io_ctx(librados::Rados &rados, std::string pool_name,
+ const std::string& namespace_name, librados::IoCtx *io_ctx) {
+ normalize_pool_name(&pool_name);
+
int r = rados.ioctx_create(pool_name.c_str(), *io_ctx);
if (r < 0) {
if (r == -ENOENT && pool_name == get_default_pool_name()) {
}
return r;
}
+
+ return set_namespace(namespace_name, io_ctx);
+}
+
+int set_namespace(const std::string& namespace_name, librados::IoCtx *io_ctx) {
+ if (!namespace_name.empty()) {
+ librbd::RBD rbd;
+ bool exists = false;
+ int r = rbd.namespace_exists(*io_ctx, namespace_name.c_str(), &exists);
+ if (r < 0) {
+ std::cerr << "rbd: error asserting namespace: "
+ << cpp_strerror(r) << std::endl;
+ return r;
+ }
+ if (!exists) {
+ std::cerr << "rbd: namespace '" << namespace_name << "' does not exist."
+ << std::endl;
+ return -ENOENT;
+ }
+ }
+ io_ctx->set_namespace(namespace_name);
return 0;
}
+void disable_cache() {
+ g_conf().set_val_or_die("rbd_cache", "false");
+}
+
int open_image(librados::IoCtx &io_ctx, const std::string &image_name,
bool read_only, librbd::Image *image) {
int r;
}
int init_and_open_image(const std::string &pool_name,
+ const std::string &namespace_name,
const std::string &image_name,
const std::string &image_id,
const std::string &snap_name, bool read_only,
librados::Rados *rados, librados::IoCtx *io_ctx,
librbd::Image *image) {
- int r = init(pool_name, rados, io_ctx);
+ int r = init(pool_name, namespace_name, rados, io_ctx);
if (r < 0) {
return r;
}
return r;
}
}
+
return 0;
}
bool *zeroed) {
if (sparse_size == 0) {
// sparse writes are disabled -- write the full extent
- assert(buffer_offset == 0);
+ ceph_assert(buffer_offset == 0);
*write_length = buffer_length;
*zeroed = false;
return;
if (original_offset == buffer_offset) {
*zeroed = extent_is_zero;
} else if (*zeroed != extent_is_zero) {
- assert(*write_length > 0);
+ ceph_assert(*write_length > 0);
return;
}
return id;
}
+std::string mirror_image_mode(librbd::mirror_image_mode_t mode) {
+ switch (mode) {
+ case RBD_MIRROR_IMAGE_MODE_JOURNAL:
+ return "journal";
+ case RBD_MIRROR_IMAGE_MODE_SNAPSHOT:
+ return "snapshot";
+ default:
+ return "unknown";
+ }
+}
+
std::string mirror_image_state(librbd::mirror_image_state_t state) {
switch (state) {
case RBD_MIRROR_IMAGE_DISABLING:
}
}
-std::string mirror_image_status_state(librbd::mirror_image_status_state_t state) {
+std::string mirror_image_status_state(
+ librbd::mirror_image_status_state_t state) {
switch (state) {
case MIRROR_IMAGE_STATUS_STATE_UNKNOWN:
return "unknown";
}
}
-std::string mirror_image_status_state(librbd::mirror_image_status_t status) {
+std::string mirror_image_site_status_state(
+ const librbd::mirror_image_site_status_t& status) {
return (status.up ? "up+" : "down+") +
mirror_image_status_state(status.state);
}
+std::string mirror_image_global_status_state(
+ const librbd::mirror_image_global_status_t& status) {
+ librbd::mirror_image_site_status_t local_status;
+ int r = get_local_mirror_image_status(status, &local_status);
+ if (r < 0) {
+ return "down+unknown";
+ }
+
+ return mirror_image_site_status_state(local_status);
+}
+
+int get_local_mirror_image_status(
+ const librbd::mirror_image_global_status_t& status,
+ librbd::mirror_image_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_IMAGE_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 "";
+ }
+
struct tm tm;
localtime_r(&t, &tm);
char buf[32];
- strftime(buf, sizeof(buf), "%F %T", &tm);
+ strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
return buf;
}
uint64_t get_rbd_default_features(CephContext* cct) {
- auto features = cct->_conf->get_val<std::string>("rbd_default_features");
+ auto features = cct->_conf.get_val<std::string>("rbd_default_features");
return boost::lexical_cast<uint64_t>(features);
}
-bool check_if_image_spec_present(const po::variables_map &vm,
- at::ArgumentModifier mod,
- size_t spec_arg_index) {
- std::string image_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
- at::DEST_IMAGE_NAME : at::IMAGE_NAME);
+bool is_not_user_snap_namespace(librbd::Image* image,
+ const librbd::snap_info_t &snap_info)
+{
+ librbd::snap_namespace_type_t namespace_type;
+ int r = image->snap_get_namespace_type(snap_info.id, &namespace_type);
+ if (r < 0) {
+ return false;
+ }
+ return namespace_type != RBD_SNAP_NAMESPACE_TYPE_USER;
+}
+
+void get_mirror_peer_sites(
+ librados::IoCtx& io_ctx,
+ std::vector<librbd::mirror_peer_site_t>* mirror_peers) {
+ librados::IoCtx default_io_ctx;
+ default_io_ctx.dup(io_ctx);
+ default_io_ctx.set_namespace("");
+
+ mirror_peers->clear();
- if (vm.count(image_key)) {
- return true;
+ librbd::RBD rbd;
+ int r = rbd.mirror_peer_site_list(default_io_ctx, mirror_peers);
+ if (r < 0 && r != -ENOENT) {
+ std::cerr << "rbd: failed to list mirror peers" << std::endl;
}
+}
- std::string spec = get_positional_argument(vm, spec_arg_index);
- if (!spec.empty()) {
- return true;
+void get_mirror_peer_mirror_uuids_to_names(
+ const std::vector<librbd::mirror_peer_site_t>& mirror_peers,
+ std::map<std::string, std::string>* mirror_uuids_to_name) {
+ mirror_uuids_to_name->clear();
+ for (auto& peer : mirror_peers) {
+ if (!peer.mirror_uuid.empty() && !peer.site_name.empty()) {
+ (*mirror_uuids_to_name)[peer.mirror_uuid] = peer.site_name;
+ }
}
+}
- return false;
+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) {
+ 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_IMAGE_STATUS_LOCAL_MIRROR_UUID);
+ }
+
+ std::vector<librbd::mirror_image_site_status_t> site_statuses;
+ site_statuses.reserve(missing_mirror_uuids.size());
+
+ for (auto& site_status : global_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_IMAGE_STATUS_STATE_UNKNOWN,
+ "status not found", 0, false});
+ }
+
+ std::swap(global_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) {
+ std::string command = R"(
+ {
+ "prefix": ")" + cmd + R"(", )" + mgr_command_args_to_str(args) + R"(
+ })";
+
+ bufferlist in_bl;
+ bufferlist out_bl;
+ std::string outs;
+ int r = rados.mgr_command(command, in_bl, &out_bl, &outs);
+ if (r < 0) {
+ (*err_os) << "rbd: " << cmd << " failed: " << cpp_strerror(r);
+ if (!outs.empty()) {
+ (*err_os) << ": " << outs;
+ }
+ (*err_os) << std::endl;
+ return r;
+ }
+
+ if (out_bl.length() != 0) {
+ (*out_os) << out_bl.c_str();
+ }
+
+ return 0;
}
} // namespace utils