From 84c5d857648384e5ae635f14af9ab63c0b3bf1ca Mon Sep 17 00:00:00 2001 From: Stratos Psomadakis Date: Wed, 26 Dec 2012 16:14:39 -0800 Subject: [PATCH] rbd: support plain/json/xml output formatting This patch renames the --format option to --image-format, for specifying the RBD image format, and uses --format to specify the output formatting (to be consistent with the other ceph tools). To avoid breaking backwards compatibility with existing scripts, rbd will still accept --format [1|2] for the image format, but will print a warning message, noting its use is deprecated. The rbd subcommands that support the new --format option are : ls, info, snap list, children, showmapped, lock list. Signed-off-by: Stratos Psomadakis Signed-off-by: Josh Durgin --- doc/man/8/rbd.rst | 6 +- src/rbd.cc | 437 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 334 insertions(+), 109 deletions(-) diff --git a/doc/man/8/rbd.rst b/doc/man/8/rbd.rst index e6a02b0588f75..344636463535f 100644 --- a/doc/man/8/rbd.rst +++ b/doc/man/8/rbd.rst @@ -41,7 +41,7 @@ Options Parameters ========== -.. option:: --format format +.. option:: --image-format format Specifies which object layout to use. The default is 1. @@ -100,6 +100,10 @@ Parameters live migration of a virtual machine, or for use underneath a clustered filesystem. +.. option:: --format format + + Specifies output formatting (default: plain, json, xml) + Commands ======== diff --git a/src/rbd.cc b/src/rbd.cc index 2a13706f18ed5..99a10b10d4bc5 100644 --- a/src/rbd.cc +++ b/src/rbd.cc @@ -19,6 +19,7 @@ #include "auth/KeyRing.h" #include "common/errno.h" #include "common/ceph_argparse.h" +#include "common/strtol.h" #include "global/global_init.h" #include "common/safe_io.h" #include "common/secret.h" @@ -48,6 +49,8 @@ #include "common/TextTable.h" #include "include/util.h" +#include "common/Formatter.h" + #if defined(__linux__) #include #endif @@ -113,21 +116,22 @@ void usage() "individual pieces of names with -p/--pool, --image, and/or --snap.\n" "\n" "Other input options:\n" -" -p, --pool source pool name\n" -" --image image name\n" -" --dest destination [pool and] image name\n" -" --snap snapshot name\n" -" --dest-pool destination pool name\n" -" --path path name for import/export\n" -" --size size of image for create and resize\n" -" --order the object size in bits; object size will be\n" -" (1 << order) bytes. Default is 22 (4 MB).\n" -" --format format to use when creating an image\n" -" format 1 is the original format (default)\n" -" format 2 supports cloning\n" -" --id rados user (without 'client.' prefix) to authenticate as\n" -" --keyfile file containing secret key for use with cephx\n" -" --shared take a shared (rather than exclusive) lock\n"; +" -p, --pool source pool name\n" +" --image image name\n" +" --dest destination [pool and] image name\n" +" --snap snapshot name\n" +" --dest-pool destination pool name\n" +" --path path name for import/export\n" +" --size size of image for create and resize\n" +" --order the object size in bits; object size will be\n" +" (1 << order) bytes. Default is 22 (4 MB).\n" +" --image-format format to use when creating an image\n" +" format 1 is the original format (default)\n" +" format 2 supports cloning\n" +" --id rados user (without 'client.' prefix) to authenticate as\n" +" --keyfile file containing secret key for use with cephx\n" +" --shared take a shared (rather than exclusive) lock\n" +" --format output format (default: plain, json, xml)\n"; } static string feature_str(uint64_t features) @@ -170,28 +174,62 @@ struct MyProgressContext : public librbd::ProgressContext { } }; -static int do_list(librbd::RBD &rbd, librados::IoCtx& io_ctx, bool lflag) +static int get_outfmt(const char *output_format, Formatter **f) +{ + if (!strcmp(output_format, "json")) + *f = new JSONFormatter(false); + else if (!strcmp(output_format, "xml")) + *f = new XMLFormatter(false); + else if (strcmp(output_format, "plain")) { + cerr << "rbd unknown format '" << output_format << "'" << std::endl; + return -EINVAL; + } + + return 0; +} + +static int do_list(librbd::RBD &rbd, librados::IoCtx& io_ctx, bool lflag, const char *output_format) { std::vector names; int r = rbd.list(io_ctx, names); if (r < 0 || (names.size() == 0)) return r; + Formatter *f = 0; + + r = get_outfmt(output_format, &f); + if (r < 0) + return r; + if (!lflag) { + if (f) + f->open_array_section("images"); for (std::vector::const_iterator i = names.begin(); i != names.end(); ++i) { - cout << *i << std::endl; + if (f) + f->dump_string("name", *i); + else + cout << *i << std::endl; + } + if (f) { + f->close_section(); + f->flush(cout); } return 0; } TextTable tbl; - tbl.define_column("NAME", TextTable::LEFT, TextTable::LEFT); - tbl.define_column("SIZE", TextTable::RIGHT, TextTable::RIGHT); - tbl.define_column("PARENT", TextTable::LEFT, TextTable::LEFT); - tbl.define_column("FMT", TextTable::RIGHT, TextTable::RIGHT); - tbl.define_column("PROT", TextTable::LEFT, TextTable::LEFT); - tbl.define_column("LOCK", TextTable::LEFT, TextTable::LEFT); + + if (f) { + f->open_object_section("images"); + } else { + tbl.define_column("NAME", TextTable::LEFT, TextTable::LEFT); + tbl.define_column("SIZE", TextTable::RIGHT, TextTable::RIGHT); + tbl.define_column("PARENT", TextTable::LEFT, TextTable::LEFT); + tbl.define_column("FMT", TextTable::RIGHT, TextTable::RIGHT); + tbl.define_column("PROT", TextTable::LEFT, TextTable::LEFT); + tbl.define_column("LOCK", TextTable::LEFT, TextTable::LEFT); + } string pool, image, snap, parent; @@ -239,38 +277,64 @@ static int do_list(librbd::RBD &rbd, librados::IoCtx& io_ctx, bool lflag) lockstr = (exclusive) ? "excl" : "shr"; } - tbl << *i - << stringify(si_t(info.size)) - << parent - << ((old_format) ? '1' : '2') - << "" // protect doesn't apply to images - << lockstr - << TextTable::endrow; + if (f) { + f->open_object_section(i->c_str()); + f->dump_string("size", stringify(si_t(info.size))); + f->dump_string("parent", parent); + f->dump_string("fmt", (old_format) ? "1" : "2"); + f->dump_string("prot", ""); // protect doesn't apply to images + f->dump_string("lock", lockstr); + f->close_section(); + } else { + tbl << *i + << stringify(si_t(info.size)) + << parent + << ((old_format) ? '1' : '2') + << "" // protect doesn't apply to images + << lockstr + << TextTable::endrow; + } vector snaplist; if (im.snap_list(snaplist) >= 0 && !snaplist.empty()) { for (std::vector::iterator s = snaplist.begin(); - s != snaplist.end(); ++s) { + s != snaplist.end(); ++s) { bool is_protected; parent.clear(); im.snap_set(s->name.c_str()); - r = im.snap_is_protected(s->name.c_str(), &is_protected); + r = im.snap_is_protected(s->name.c_str(), &is_protected); if (r < 0) return r; if (im.parent_info(&pool, &image, &snap) >= 0) { parent = pool + "/" + image + "@" + snap; } - tbl << *i + "@" + s->name - << stringify(si_t(s->size)) - << parent - << ((old_format) ? '1' : '2') - << (is_protected ? "yes" : "") - << "" // locks don't apply to snaps - << TextTable::endrow; + if (f) { + f->open_object_section((*i + "@" + s->name).c_str()); + f->dump_string("size", stringify(si_t(s->size))); + f->dump_string("parent", parent); + f->dump_string("fmt", (old_format) ? "1" : "2"); + f->dump_string("prot", is_protected ? "yes" : ""); + f->dump_string("lock", ""); // locks don't apply to snaps + f->close_section(); + } else { + tbl << *i + "@" + s->name + << stringify(si_t(s->size)) + << parent + << ((old_format) ? '1' : '2') + << (is_protected ? "yes" : "") + << "" // locks don't apply to snaps + << TextTable::endrow; + } } } } - cout << tbl; + if (f) { + f->close_section(); + f->flush(cout); + } else { + cout << tbl; + } + return 0; } @@ -338,15 +402,21 @@ static int do_rename(librbd::RBD &rbd, librados::IoCtx& io_ctx, } static int do_show_info(const char *imgname, librbd::Image& image, - const char *snapname) + const char *snapname, const char *output_format) { librbd::image_info_t info; string parent_pool, parent_name, parent_snapname; uint8_t old_format; uint64_t overlap, features; bool snap_protected; + Formatter *f = 0; + int r; + + r = get_outfmt(output_format, &f); + if (r < 0) + return r; - int r = image.stat(info, sizeof(info)); + r = image.stat(info, sizeof(info)); if (r < 0) return r; @@ -368,41 +438,75 @@ static int do_show_info(const char *imgname, librbd::Image& image, return r; } - cout << "rbd image '" << imgname << "':\n" - << "\tsize " << prettybyte_t(info.size) << " in " - << info.num_objs << " objects" - << std::endl - << "\torder " << info.order - << " (" << prettybyte_t(info.obj_size) << " objects)" - << std::endl - << "\tblock_name_prefix: " << info.block_name_prefix - << std::endl - << "\tformat: " << (old_format ? "1" : "2") - << std::endl; + if (f) { + f->open_object_section("image"); + f->dump_string("name", imgname); + f->dump_string("size", stringify(prettybyte_t(info.size))); + f->dump_int("objects", info.num_objs); + f->dump_int("order", info.order); + f->dump_string("obj_size", stringify(prettybyte_t(info.obj_size))); + f->dump_string("block_name_prefix", info.block_name_prefix); + f->dump_string("format", (old_format ? "1" : "2")); + } else { + cout << "rbd image '" << imgname << "':\n" + << "\tsize " << prettybyte_t(info.size) << " in " + << info.num_objs << " objects" + << std::endl + << "\torder " << info.order + << " (" << prettybyte_t(info.obj_size) << " objects)" + << std::endl + << "\tblock_name_prefix: " << info.block_name_prefix + << std::endl + << "\tformat: " << (old_format ? "1" : "2") + << std::endl; + } + if (!old_format) { - cout << "\tfeatures: " << feature_str(features) << std::endl; + if (f) + f->dump_string("features", feature_str(features)); + else + cout << "\tfeatures: " << feature_str(features) << std::endl; } // snapshot info, if present if (snapname) { - cout << "\tprotected: " << (snap_protected ? "True" : "False") - << std::endl; + if (f) { + f->dump_string("protected", stringify(snap_protected)); + } else { + cout << "\tprotected: " << (snap_protected ? "True" : "False") + << std::endl; + } } // parent info, if present if ((image.parent_info(&parent_pool, &parent_name, &parent_snapname) == 0) && parent_name.length() > 0) { - - cout << "\tparent: " << parent_pool << "/" << parent_name - << "@" << parent_snapname << std::endl; - cout << "\toverlap: " << prettybyte_t(overlap) << std::endl; + if (f) { + f->dump_string("parent", parent_pool + "/" + parent_name); + f->dump_string("overlap", stringify(prettybyte_t(overlap))); + } else { + cout << "\tparent: " << parent_pool << "/" << parent_name + << "@" << parent_snapname << std::endl; + cout << "\toverlap: " << prettybyte_t(overlap) << std::endl; + } } // striping info, if feature is set if (features & RBD_FEATURE_STRIPINGV2) { - cout << "\tstripe unit: " << prettybyte_t(image.get_stripe_unit()) << std::endl - << "\tstripe count: " << image.get_stripe_count() << std::endl; + if (f) { + f->dump_string("stripe_unit", stringify(prettybyte_t(image.get_stripe_unit()))); + f->dump_string("stripe_count", stringify(image.get_stripe_count())); + } else { + cout << "\tstripe unit: " << prettybyte_t(image.get_stripe_unit()) << std::endl + << "\tstripe count: " << image.get_stripe_count() << std::endl; + } + } + + if (f) { + f->close_section(); + f->flush(cout); } + return 0; } @@ -430,24 +534,49 @@ static int do_resize(librbd::Image& image, uint64_t size) return 0; } -static int do_list_snaps(librbd::Image& image) +static int do_list_snaps(librbd::Image& image, const char *output_format) { std::vector snaps; - int r = image.snap_list(snaps); + Formatter *f = 0; + TextTable t; + int r; + + r = get_outfmt(output_format, &f); + if (r < 0) + return r; + + r = image.snap_list(snaps); if (r < 0 || snaps.empty()) return r; - TextTable t; - t.define_column("SNAPID", TextTable::RIGHT, TextTable::RIGHT); - t.define_column("NAME", TextTable::LEFT, TextTable::LEFT); - t.define_column("SIZE", TextTable::RIGHT, TextTable::RIGHT); + if (f) { + f->open_object_section("snaps"); + } else { + t.define_column("SNAPID", TextTable::RIGHT, TextTable::RIGHT); + t.define_column("NAME", TextTable::LEFT, TextTable::LEFT); + t.define_column("SIZE", TextTable::RIGHT, TextTable::RIGHT); + } for (std::vector::iterator s = snaps.begin(); s != snaps.end(); ++s) { - t << s->id << s->name << stringify(prettybyte_t(s->size)) - << TextTable::endrow; + if (f) { + f->open_object_section(stringify(s->id).c_str()); + f->dump_string("name", s->name); + f->dump_string("size", stringify(prettybyte_t(s->size))); + f->close_section(); + } else { + t << s->id << s->name << stringify(prettybyte_t(s->size)) + << TextTable::endrow; + } + } + + if (f) { + f->close_section(); + f->flush(cout); + } else { + cout << t; } - cout << t; + return 0; } @@ -522,47 +651,93 @@ static int do_unprotect_snap(librbd::Image& image, const char *snapname) return 0; } -static int do_list_children(librbd::Image &image) +static int do_list_children(librbd::Image &image, const char *output_format) { set > children; - int r = image.list_children(&children); + Formatter *f = 0; + int r; + + r = get_outfmt(output_format, &f); if (r < 0) return r; + r = image.list_children(&children); + if (r < 0) + return r; + + if (f) + f->open_array_section("children"); + for (set >::const_iterator child_it = children.begin(); child_it != children.end(); child_it++) { - cout << child_it->first << "/" << child_it->second << std::endl; + if (f) + f->dump_string("child", child_it->first + "/" + child_it->second); + else + cout << child_it->first << "/" << child_it->second << std::endl; } + + if (f) { + f->close_section(); + f->flush(cout); + } + return 0; } -static int do_lock_list(librbd::Image& image) +static int do_lock_list(librbd::Image& image, const char *output_format) { list lockers; bool exclusive; string tag; - int r = image.list_lockers(&lockers, &exclusive, &tag); + Formatter *f = 0; + TextTable tbl; + int r; + + r = get_outfmt(output_format, &f); + if (r < 0) + return r; + + r = image.list_lockers(&lockers, &exclusive, &tag); if (r < 0) return r; if (lockers.size()) { bool one = (lockers.size() == 1); - cout << "There " << (one ? "is " : "are ") << lockers.size() - << (exclusive ? " exclusive" : " shared") - << " lock" << (one ? "" : "s") << " on this image.\n"; - if (!exclusive) - cout << "Lock tag: " << tag << "\n"; - TextTable tbl; - tbl.define_column("Locker", TextTable::LEFT, TextTable::LEFT); - tbl.define_column("ID", TextTable::LEFT, TextTable::LEFT); - tbl.define_column("Address", TextTable::LEFT, TextTable::LEFT); + if (!f) { + cout << "There " << (one ? "is " : "are ") << lockers.size() + << (exclusive ? " exclusive" : " shared") + << " lock" << (one ? "" : "s") << " on this image.\n"; + if (!exclusive) + cout << "Lock tag: " << tag << "\n"; + } + + if (f) { + f->open_object_section("locks"); + } else { + tbl.define_column("Locker", TextTable::LEFT, TextTable::LEFT); + tbl.define_column("ID", TextTable::LEFT, TextTable::LEFT); + tbl.define_column("Address", TextTable::LEFT, TextTable::LEFT); + } for (list::const_iterator it = lockers.begin(); it != lockers.end(); ++it) { - tbl << it->client << it->cookie << it->address << TextTable::endrow; + if (f) { + f->open_object_section(it->cookie.c_str()); + f->dump_string("locker", it->client); + f->dump_string("address", it->address); + f->close_section(); + } else { + tbl << it->client << it->cookie << it->address << TextTable::endrow; + } + } + + if (f) { + f->close_section(); + f->flush(cout); + } else { + cout << tbl; } - cout << tbl; } return 0; } @@ -1237,10 +1412,17 @@ void do_closedir(DIR *dp) closedir(dp); } -static int do_kernel_showmapped() +static int do_kernel_showmapped(const char *output_format) { int r; bool have_output = false; + Formatter *f = 0; + TextTable tbl; + + r = get_outfmt(output_format, &f); + if (r < 0) + return r; + const char *devices_path = "/sys/bus/rbd/devices"; std::tr1::shared_ptr device_dir(opendir(devices_path), do_closedir); if (!device_dir.get()) { @@ -1259,12 +1441,15 @@ static int do_kernel_showmapped() return r; } - TextTable tbl; - tbl.define_column("id", TextTable::LEFT, TextTable::LEFT); - tbl.define_column("pool", TextTable::LEFT, TextTable::LEFT); - tbl.define_column("image", TextTable::LEFT, TextTable::LEFT); - tbl.define_column("snap", TextTable::LEFT, TextTable::LEFT); - tbl.define_column("device", TextTable::LEFT, TextTable::LEFT); + if (f) { + f->open_object_section("devices"); + } else { + tbl.define_column("id", TextTable::LEFT, TextTable::LEFT); + tbl.define_column("pool", TextTable::LEFT, TextTable::LEFT); + tbl.define_column("image", TextTable::LEFT, TextTable::LEFT); + tbl.define_column("snap", TextTable::LEFT, TextTable::LEFT); + tbl.define_column("device", TextTable::LEFT, TextTable::LEFT); + } do { if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) @@ -1302,12 +1487,27 @@ static int do_kernel_showmapped() continue; } - tbl << dent->d_name << pool << name << snap << dev << TextTable::endrow; + if (f) { + f->open_object_section(dent->d_name); + f->dump_string("pool", pool); + f->dump_string("name", name); + f->dump_string("snap", snap); + f->dump_string("device", dev); + f->close_section(); + } else { + tbl << dent->d_name << pool << name << snap << dev << TextTable::endrow; + } have_output = true; } while ((dent = readdir(device_dir.get()))); - if (have_output) - cout << tbl; + + if (f) { + f->close_section(); + f->flush(cout); + } else { + if (have_output) + cout << tbl; + } return 0; } @@ -1544,13 +1744,13 @@ int main(int argc, const char **argv) const char *poolname = NULL; uint64_t size = 0; // in bytes int order = 0; - bool format_specified = false; + bool format_specified = false, output_format_specified = false; int format = 1; uint64_t features = RBD_FEATURE_LAYERING; const char *imgname = NULL, *snapname = NULL, *destname = NULL, *dest_poolname = NULL, *dest_snapname = NULL, *path = NULL, *devpath = NULL, *lock_cookie = NULL, *lock_client = NULL, - *lock_tag = NULL; + *lock_tag = NULL, *output_format = "plain"; bool lflag = false; long long stripe_unit = 0, stripe_count = 0; long long bench_io_size = 4096, bench_io_threads = 16, bench_bytes = 1 << 30; @@ -1571,7 +1771,7 @@ int main(int argc, const char **argv) } else if (ceph_argparse_flag(args, i, "--new-format", (char*)NULL)) { format = 2; format_specified = true; - } else if (ceph_argparse_withint(args, i, &format, &err, "--format", + } else if (ceph_argparse_withint(args, i, &format, &err, "--image-format", (char*)NULL)) { if (!err.str().empty()) { cerr << "rbd: " << err.str() << std::endl; @@ -1618,6 +1818,19 @@ int main(int argc, const char **argv) imgname = strdup(val.c_str()); } else if (ceph_argparse_witharg(args, i, &val, "--shared", (char *)NULL)) { lock_tag = strdup(val.c_str()); + } else if (ceph_argparse_witharg(args, i, &val, "--format", (char *) NULL)) { + std::string err; + long long ret = strict_strtoll(val.c_str(), 10, &err); + if (err.empty()) { + format = ret; + format_specified = true; + cerr << "rbd: using --format for specifying the rbd image format is deprecated," + << " use --image-format instead" + << std::endl; + } else { + output_format = strdup(val.c_str()); + output_format_specified = true; + } } else { ++i; } @@ -1719,14 +1932,22 @@ if (!set_conf_param(v, p1, p2, p3)) { \ } if (format_specified && opt_cmd != OPT_IMPORT && opt_cmd != OPT_CREATE) { - cerr << "rbd: format can only be set when " + cerr << "rbd: image format can only be set when " << "creating or importing an image" << std::endl; return EXIT_FAILURE; } + if (output_format_specified && opt_cmd != OPT_SHOWMAPPED && opt_cmd != OPT_INFO && + opt_cmd != OPT_LIST && opt_cmd != OPT_SNAP_LIST && opt_cmd != OPT_LOCK_LIST && + opt_cmd != OPT_CHILDREN) { + cerr << "rbd: command doesn't use output formatting" + << std::endl; + return EXIT_FAILURE; + } + if (format_specified) { if (format < 1 || format > 2) { - cerr << "rbd: format must be 1 or 2" << std::endl; + cerr << "rbd: image format must be 1 or 2" << std::endl; return EXIT_FAILURE; } } @@ -1917,7 +2138,7 @@ if (!set_conf_param(v, p1, p2, p3)) { \ switch (opt_cmd) { case OPT_LIST: - r = do_list(rbd, io_ctx, lflag); + r = do_list(rbd, io_ctx, lflag, output_format); if (r < 0) { switch (r) { case -ENOENT: @@ -1981,7 +2202,7 @@ if (!set_conf_param(v, p1, p2, p3)) { \ break; case OPT_INFO: - r = do_show_info(imgname, image, snapname); + r = do_show_info(imgname, image, snapname, output_format); if (r < 0) { cerr << "rbd: info: " << cpp_strerror(-r) << std::endl; return EXIT_FAILURE; @@ -2022,7 +2243,7 @@ if (!set_conf_param(v, p1, p2, p3)) { \ cerr << "rbd: snap list requires an image parameter" << std::endl; return EXIT_FAILURE; } - r = do_list_snaps(image); + r = do_list_snaps(image, output_format); if (r < 0) { cerr << "rbd: failed to list snapshots: " << cpp_strerror(-r) << std::endl; @@ -2111,7 +2332,7 @@ if (!set_conf_param(v, p1, p2, p3)) { \ break; case OPT_CHILDREN: - r = do_list_children(image); + r = do_list_children(image, output_format); if (r < 0) { cerr << "rbd: listing children failed: " << cpp_strerror(-r) << std::endl; return EXIT_FAILURE; @@ -2176,7 +2397,7 @@ if (!set_conf_param(v, p1, p2, p3)) { \ break; case OPT_SHOWMAPPED: - r = do_kernel_showmapped(); + r = do_kernel_showmapped(output_format); if (r < 0) { cerr << "rbd: showmapped failed: " << cpp_strerror(-r) << std::endl; return EXIT_FAILURE; @@ -2184,7 +2405,7 @@ if (!set_conf_param(v, p1, p2, p3)) { \ break; case OPT_LOCK_LIST: - r = do_lock_list(image); + r = do_lock_list(image, output_format); if (r < 0) { cerr << "rbd: listing locks failed: " << cpp_strerror(r) << std::endl; return EXIT_FAILURE; -- 2.39.5