#define dout_subsys ceph_subsys_rbd
+static std::map<uint64_t, std::string> feature_mapping =
+ boost::assign::map_list_of(
+ RBD_FEATURE_LAYERING, "layering")(
+ RBD_FEATURE_STRIPINGV2, "striping")(
+ RBD_FEATURE_EXCLUSIVE_LOCK, "exclusive-lock")(
+ RBD_FEATURE_OBJECT_MAP, "object-map");
+
void usage()
{
cout <<
" mapped by the kernel\n"
" showmapped show the rbd images mapped\n"
" by the kernel\n"
+" feature disable <image-name> <feature> disable the specified image feature\n"
+" feature enable <image-name> <feature> enable the specified image feature\n"
" lock list <image-name> show locks held on an image\n"
" lock add <image-name> <id> [--shared <tag>] take a lock called id on an image\n"
" lock remove <image-name> <id> <locker> release a lock on an image\n"
" --image-format <format-number> format to use when creating an image\n"
" format 1 is the original format (default)\n"
" format 2 supports cloning\n"
-" --image-features <features> optional format 2 features to enable\n"
-" +1 layering support, +2 striping v2,\n"
-" +4 exclusive lock, +8 object map\n"
+" --image-feature <feature> optional format 2 feature to enable.\n"
+" use multiple times to enable multiple features\n"
" --image-shared image will be used concurrently (disables\n"
" RBD exclusive lock and dependent features)\n"
" --stripe-unit <size-in-bytes> size (in bytes) of a block of data\n"
" --no-progress do not show progress for long-running commands\n"
" -o, --options <map-options> options to use when mapping an image\n"
" --read-only set device readonly when mapping image\n"
-" --allow-shrink allow shrinking of an image when resizing\n";
+" --allow-shrink allow shrinking of an image when resizing\n"
+"\n"
+"Supported image features:\n"
+" ";
+
+for (std::map<uint64_t, std::string>::const_iterator it = feature_mapping.begin();
+ it != feature_mapping.end(); ++it) {
+ if (it != feature_mapping.begin()) {
+ cout << ", ";
+ }
+ cout << it->second;
+ if ((it->first & RBD_FEATURES_MUTABLE) != 0) {
+ cout << " (*)";
+ }
+ if ((it->first & g_conf->rbd_default_features) != 0) {
+ cout << " (+)";
+ }
+}
+cout << "\n\n"
+ << " (*) supports enabling/disabling on existing images\n"
+ << " (+) enabled by default for new images if features are not specified\n";
}
static void format_bitmask(Formatter *f, const std::string &name,
static void format_features(Formatter *f, uint64_t features)
{
- std::map<uint64_t, std::string> mapping = boost::assign::map_list_of(
- RBD_FEATURE_LAYERING, "layering")(
- RBD_FEATURE_STRIPINGV2, "striping")(
- RBD_FEATURE_EXCLUSIVE_LOCK, "exclusive")(
- RBD_FEATURE_OBJECT_MAP, "object map");
- format_bitmask(f, "feature", mapping, features);
+ format_bitmask(f, "feature", feature_mapping, features);
}
static void format_flags(Formatter *f, uint64_t flags)
format_bitmask(f, "flag", mapping, flags);
}
+static bool decode_feature(const char* feature_name, uint64_t *feature) {
+ for (std::map<uint64_t, std::string>::const_iterator it = feature_mapping.begin();
+ it != feature_mapping.end(); ++it) {
+ if (strcmp(feature_name, it->second.c_str()) == 0) {
+ *feature = it->first;
+ return true;
+ }
+ }
+ return false;
+}
+
struct MyProgressContext : public librbd::ProgressContext {
const char *operation;
int last_pc;
return 0;
}
+enum CommandType{
+ COMMAND_TYPE_NONE,
+ COMMAND_TYPE_SNAP,
+ COMMAND_TYPE_LOCK,
+ COMMAND_TYPE_METADATA,
+ COMMAND_TYPE_FEATURE
+};
+
enum {
OPT_NO_CMD = 0,
OPT_LIST,
OPT_MAP,
OPT_UNMAP,
OPT_SHOWMAPPED,
+ OPT_FEATURE_DISABLE,
+ OPT_FEATURE_ENABLE,
OPT_LOCK_LIST,
OPT_LOCK_ADD,
OPT_LOCK_REMOVE,
OPT_METADATA_REMOVE,
};
-static int get_cmd(const char *cmd, bool snapcmd, bool lockcmd, bool metacmd)
+static int get_cmd(const char *cmd, CommandType command_type)
{
- if (!snapcmd && !lockcmd) {
+ switch (command_type)
+ {
+ case COMMAND_TYPE_NONE:
if (strcmp(cmd, "ls") == 0 ||
strcmp(cmd, "list") == 0)
return OPT_LIST;
return OPT_UNMAP;
if (strcmp(cmd, "bench-write") == 0)
return OPT_BENCH_WRITE;
- } else if (snapcmd) {
+ break;
+ case COMMAND_TYPE_SNAP:
if (strcmp(cmd, "create") == 0 ||
strcmp(cmd, "add") == 0)
return OPT_SNAP_CREATE;
return OPT_SNAP_PROTECT;
if (strcmp(cmd, "unprotect") == 0)
return OPT_SNAP_UNPROTECT;
- } else if (metacmd) {
+ break;
+ case COMMAND_TYPE_METADATA:
if (strcmp(cmd, "list") == 0)
return OPT_METADATA_LIST;
if (strcmp(cmd, "set") == 0)
return OPT_METADATA_GET;
if (strcmp(cmd, "remove") == 0)
return OPT_METADATA_REMOVE;
- } else {
+ break;
+ case COMMAND_TYPE_LOCK:
if (strcmp(cmd, "ls") == 0 ||
strcmp(cmd, "list") == 0)
return OPT_LOCK_LIST;
if (strcmp(cmd, "remove") == 0 ||
strcmp(cmd, "rm") == 0)
return OPT_LOCK_REMOVE;
+ break;
+ case COMMAND_TYPE_FEATURE:
+ if (strcmp(cmd, "disable") == 0) {
+ return OPT_FEATURE_DISABLE;
+ } else if (strcmp(cmd, "enable") == 0) {
+ return OPT_FEATURE_ENABLE;
+ }
+ break;
}
return OPT_NO_CMD;
output_format_specified = false;
int format = 1;
- uint64_t features = g_conf->rbd_default_features;
+ uint64_t features = 0;
bool shared = false;
const char *imgname = NULL, *snapname = NULL, *destname = NULL,
*fromsnapname = NULL,
*first_diff = NULL, *second_diff = NULL, *key = NULL, *value = NULL;
char *cli_map_options = NULL;
+ std::vector<const char*> feature_names;
bool lflag = false;
int pretty_format = 0;
long long stripe_unit = 0, stripe_count = 0;
progress = false;
} else if (ceph_argparse_flag(args, i , "--allow-shrink", (char *)NULL)) {
resize_allow_shrink = true;
+ } else if (ceph_argparse_witharg(args, i, &val, "--image-feature", (char *)NULL)) {
+ uint64_t feature;
+ if (!decode_feature(val.c_str(), &feature)) {
+ cerr << "rbd: invalid image feature: " << val << std::endl;
+ return EXIT_FAILURE;
+ }
+ features |= feature;
} else if (ceph_argparse_witharg(args, i, &val, "--image-features", (char *)NULL)) {
+ cerr << "rbd: using --image-features for specifying the rbd image format is"
+ << " deprecated, use --image-feature instead" << std::endl;
features = strict_strtol(val.c_str(), 10, &parse_err);
if (!parse_err.empty()) {
cerr << "rbd: error parsing --image-features: " << parse_err
}
}
+ if (features != 0 && !format_specified) {
+ format = 2;
+ format_specified = true;
+ } else if (features == 0) {
+ features = g_conf->rbd_default_features;
+ }
if (shared) {
features &= ~(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_OBJECT_MAP);
}
cerr << "rbd: which snap command do you want?" << std::endl;
return EXIT_FAILURE;
}
- opt_cmd = get_cmd(*i, true, false, false);
+ opt_cmd = get_cmd(*i, COMMAND_TYPE_SNAP);
} else if (strcmp(*i, "lock") == 0) {
i = args.erase(i);
if (i == args.end()) {
cerr << "rbd: which lock command do you want?" << std::endl;
return EXIT_FAILURE;
}
- opt_cmd = get_cmd(*i, false, true, false);
+ opt_cmd = get_cmd(*i, COMMAND_TYPE_LOCK);
} else if (strcmp(*i, "image-meta") == 0) {
i = args.erase(i);
if (i == args.end()) {
cerr << "rbd: which image-meta command do you want?" << std::endl;
return EXIT_FAILURE;
}
- opt_cmd = get_cmd(*i, false, false, true);
+ opt_cmd = get_cmd(*i, COMMAND_TYPE_METADATA);
+ } else if (strcmp(*i, "feature") == 0) {
+ i = args.erase(i);
+ if (i == args.end()) {
+ cerr << "rbd: which feature command do you want?" << std::endl;
+ return EXIT_FAILURE;
+ }
+ opt_cmd = get_cmd(*i, COMMAND_TYPE_FEATURE);
} else {
- opt_cmd = get_cmd(*i, false, false, false);
+ opt_cmd = get_cmd(*i, COMMAND_TYPE_NONE);
}
if (opt_cmd == OPT_NO_CMD) {
cerr << "rbd: error parsing command '" << *i << "'; -h or --help for usage" << std::endl;
case OPT_METADATA_REMOVE:
SET_CONF_PARAM(v, &imgname, &key, NULL);
break;
+ case OPT_FEATURE_DISABLE:
+ case OPT_FEATURE_ENABLE:
+ if (imgname == NULL) {
+ imgname = v;
+ } else {
+ feature_names.push_back(v);
+ }
+ break;
default:
assert(0);
break;
return EXIT_FAILURE;
}
+ if (opt_cmd == OPT_FEATURE_DISABLE || opt_cmd == OPT_FEATURE_ENABLE) {
+ if (feature_names.empty()) {
+ cerr << "rbd: at least one feature name must be specified" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ features = 0;
+ for (size_t i = 0; i < feature_names.size(); ++i) {
+ uint64_t feature;
+ if (!decode_feature(feature_names[i], &feature)) {
+ cerr << "rbd: invalid feature name specified: " << feature_names[i]
+ << std::endl;
+ return EXIT_FAILURE;
+ }
+ features |= feature;
+ }
+ }
+
// do this unconditionally so we can parse pool/image@snapshot into
// the relevant parts
set_pool_image_name(imgname, (char **)&poolname,
opt_cmd == OPT_INFO || opt_cmd == OPT_SNAP_LIST ||
opt_cmd == OPT_IMPORT_DIFF ||
opt_cmd == OPT_EXPORT || opt_cmd == OPT_EXPORT_DIFF || opt_cmd == OPT_COPY ||
- opt_cmd == OPT_DIFF ||
+ opt_cmd == OPT_DIFF || opt_cmd == OPT_STATUS ||
opt_cmd == OPT_CHILDREN || opt_cmd == OPT_LOCK_LIST ||
opt_cmd == OPT_METADATA_SET || opt_cmd == OPT_METADATA_LIST ||
opt_cmd == OPT_METADATA_REMOVE || opt_cmd == OPT_METADATA_GET ||
- opt_cmd == OPT_STATUS)) {
+ opt_cmd == OPT_FEATURE_DISABLE || opt_cmd == OPT_FEATURE_ENABLE)) {
if (opt_cmd == OPT_INFO || opt_cmd == OPT_SNAP_LIST ||
opt_cmd == OPT_EXPORT || opt_cmd == OPT_EXPORT || opt_cmd == OPT_COPY ||
return -r;
}
break;
- }
+ case OPT_FEATURE_DISABLE:
+ case OPT_FEATURE_ENABLE:
+ r = image.update_features(features, opt_cmd == OPT_FEATURE_ENABLE);
+ if (r < 0) {
+ cerr << "rbd: failed to update image features: " << cpp_strerror(r)
+ << std::endl;
+ return -r;
+ }
+ break;
+ }
return 0;
}