From e372e9492bb7f535c0f15a078f1eab2de3fad113 Mon Sep 17 00:00:00 2001 From: Mykola Golub Date: Wed, 27 Dec 2017 20:50:40 +0200 Subject: [PATCH] rbd-nbd: output format support for list-mapped command Signed-off-by: Mykola Golub --- qa/workunits/rbd/rbd-nbd.sh | 43 ++++++++---------- src/test/cli/rbd/help.t | 6 ++- src/tools/rbd/action/Nbd.cc | 13 +++++- src/tools/rbd_nbd/rbd-nbd.cc | 84 +++++++++++++++++++++++++++--------- 4 files changed, 98 insertions(+), 48 deletions(-) diff --git a/qa/workunits/rbd/rbd-nbd.sh b/qa/workunits/rbd/rbd-nbd.sh index 15b3d1706fa7f..900a6075644bb 100755 --- a/qa/workunits/rbd/rbd-nbd.sh +++ b/qa/workunits/rbd/rbd-nbd.sh @@ -75,6 +75,14 @@ function expect_false() if "$@"; then return 1; else return 0; fi } +function get_pid() +{ + PID=$(rbd-nbd --format xml list-mapped | $XMLSTARLET sel -t -v \ + "//devices/device[pool='${POOL}'][image='${IMAGE}'][device='${DEV}']/id") + test -n "${PID}" + ps -p ${PID} -o cmd | grep rbd-nbd +} + # # main # @@ -91,12 +99,14 @@ fi expect_false _sudo rbd-nbd map INVALIDIMAGE expect_false _sudo rbd-nbd --device INVALIDDEV map ${IMAGE} +# list format test +expect_false rbd-nbd --format INVALID list-mapped +rbd-nbd --format json --pretty-format list-mapped +rbd-nbd --format xml list-mapped + # map test using the first unused device DEV=`_sudo rbd-nbd map ${POOL}/${IMAGE}` -PID=$(rbd-nbd list-mapped | awk -v pool=${POOL} -v img=${IMAGE} -v dev=${DEV} \ - '$2 == pool && $3 == img && $5 == dev {print $1}') -test -n "${PID}" -ps -p ${PID} -o cmd | grep rbd-nbd +get_pid # map test specifying the device expect_false _sudo rbd-nbd --device ${DEV} map ${POOL}/${IMAGE} dev1=${DEV} @@ -107,10 +117,7 @@ DEV= DEV=`_sudo rbd-nbd --device ${dev1} map ${POOL}/${IMAGE}` [ "${DEV}" = "${dev1}" ] rbd-nbd list-mapped | grep "${IMAGE}" -PID=$(rbd-nbd list-mapped | awk -v pool=${POOL} -v img=${IMAGE} -v dev=${DEV} \ - '$2 == pool && $3 == img && $5 == dev {print $1}') -test -n "${PID}" -ps -p ${PID} -o cmd | grep rbd-nbd +get_pid # read test [ "`dd if=${DATA} bs=1M | md5sum`" = "`_sudo dd if=${DEV} bs=1M | md5sum`" ] @@ -162,10 +169,7 @@ _sudo rbd-nbd unmap ${DEV} # exclusive option test DEV=`_sudo rbd-nbd map --exclusive ${POOL}/${IMAGE}` -PID=$(rbd-nbd list-mapped | awk -v pool=${POOL} -v img=${IMAGE} -v dev=${DEV} \ - '$2 == pool && $3 == img && $5 == dev {print $1}') -test -n "${PID}" -ps -p ${PID} -o cmd | grep rbd-nbd +get_pid _sudo dd if=${DATA} of=${DEV} bs=1M oflag=direct expect_false timeout 10 \ @@ -176,10 +180,7 @@ rbd bench ${IMAGE} --io-type write --io-size=1024 --io-total=1024 # unmap by image name test DEV=`_sudo rbd-nbd map ${POOL}/${IMAGE}` -PID=$(rbd-nbd list-mapped | awk -v pool=${POOL} -v img=${IMAGE} -v dev=${DEV} \ - '$2 == pool && $3 == img && $5 == dev {print $1}') -test -n "${PID}" -ps -p ${PID} -o cmd | grep rbd-nbd +get_pid _sudo rbd-nbd unmap "${IMAGE}" rbd-nbd list-mapped | expect_false grep "${DEV} $" DEV= @@ -188,10 +189,7 @@ ps -p ${PID} -o cmd | expect_false grep rbd-nbd # map/unmap snap test rbd snap create ${POOL}/${IMAGE}@snap DEV=`_sudo rbd-nbd map ${POOL}/${IMAGE}@snap` -PID=$(rbd-nbd list-mapped | - awk -v pool=${POOL} -v img=${IMAGE} -v snap=snap -v dev=${DEV} \ - '$2 == pool && $3 == img && $4 == snap && $5 == dev {print $1}') -test -n "${PID}" +get_pid _sudo rbd-nbd unmap "${IMAGE}@snap" rbd-nbd list-mapped | expect_false grep "${DEV} $" DEV= @@ -199,10 +197,7 @@ ps -p ${PID} -o cmd | expect_false grep rbd-nbd # auto unmap test DEV=`_sudo rbd-nbd map ${POOL}/${IMAGE}` -PID=$(rbd-nbd list-mapped | awk -v pool=${POOL} -v img=${IMAGE} -v dev=${DEV} \ - '$2 == pool && $3 == img && $5 == dev {print $1}') -test -n "${PID}" -ps -p ${PID} -o cmd | grep rbd-nbd +get_pid _sudo kill ${PID} for i in `seq 10`; do rbd-nbd list-mapped | expect_false grep "^${PID} *${POOL} *${IMAGE}" && break diff --git a/src/test/cli/rbd/help.t b/src/test/cli/rbd/help.t index f5440dba7d663..b41d56c0e39ff 100644 --- a/src/test/cli/rbd/help.t +++ b/src/test/cli/rbd/help.t @@ -1140,10 +1140,14 @@ Skip test on FreeBSD as it generates different output there. --verbose be verbose rbd help nbd list - usage: rbd nbd list + usage: rbd nbd list [--format ] [--pretty-format] List the nbd devices already used. + Optional arguments + --format arg output format (plain, json, or xml) [default: plain] + --pretty-format pretty formatting (json and xml) + rbd help nbd map usage: rbd nbd map [--pool ] [--image ] [--snap ] [--read-only] [--exclusive] [--device ] diff --git a/src/tools/rbd/action/Nbd.cc b/src/tools/rbd/action/Nbd.cc index 43f547704e5fe..b599f525449ae 100644 --- a/src/tools/rbd/action/Nbd.cc +++ b/src/tools/rbd/action/Nbd.cc @@ -105,8 +105,9 @@ int get_image_or_snap_spec(const po::variables_map &vm, std::string *spec) { } void get_show_arguments(po::options_description *positional, - po::options_description *options) -{ } + po::options_description *options) { + at::add_format_options(options); +} int execute_show(const po::variables_map &vm) { @@ -114,6 +115,14 @@ int execute_show(const po::variables_map &vm) args.push_back("list-mapped"); + if (vm.count("format")) { + args.push_back("--format"); + args.push_back(vm["format"].as().value.c_str()); + } + if (vm["pretty-format"].as()) { + args.push_back("--pretty-format"); + } + return call_nbd_cmd(vm, args); } diff --git a/src/tools/rbd_nbd/rbd-nbd.cc b/src/tools/rbd_nbd/rbd-nbd.cc index e5006592e052a..647cbb1de722a 100644 --- a/src/tools/rbd_nbd/rbd-nbd.cc +++ b/src/tools/rbd_nbd/rbd-nbd.cc @@ -32,22 +32,23 @@ #include #include -#include #include +#include +#include #include #include -#include "mon/MonClient.h" +#include "common/Formatter.h" +#include "common/Preforker.h" +#include "common/TextTable.h" +#include "common/ceph_argparse.h" #include "common/config.h" #include "common/dout.h" - #include "common/errno.h" #include "common/module.h" #include "common/safe_io.h" -#include "common/TextTable.h" -#include "common/ceph_argparse.h" -#include "common/Preforker.h" #include "common/version.h" + #include "global/global_init.h" #include "global/signal_handler.h" @@ -56,6 +57,8 @@ #include "include/stringify.h" #include "include/xlist.h" +#include "mon/MonClient.h" + #define dout_context g_ceph_context #define dout_subsys ceph_subsys_rbd #undef dout_prefix @@ -74,20 +77,27 @@ struct Config { std::string imgname; std::string snapname; std::string devpath; + + std::string format; + bool pretty_format = false; }; static void usage() { std::cout << "Usage: rbd-nbd [options] map Map an image to nbd device\n" << " unmap Unmap nbd device\n" - << " list-mapped List mapped nbd devices\n" - << "Options:\n" + << " [options] list-mapped List mapped nbd devices\n" + << "Map options:\n" << " --device Specify nbd device path\n" << " --read-only Map read-only\n" << " --nbds_max Override for module param nbds_max\n" << " --max_part Override for module param max_part\n" << " --exclusive Forbid writes by other clients\n" << " --timeout Set nbd request timeout\n" + << "\n" + << "List options:\n" + << " --format plain|json|xml Output format (default: plain)\n" + << " --pretty-format Pretty formatting (json and xml)\n" << std::endl; generic_server_usage(); } @@ -932,31 +942,59 @@ static int parse_imgpath(const std::string &imgpath, Config *cfg, return 0; } -static int do_list_mapped_devices() +static int do_list_mapped_devices(const std::string &format, bool pretty_format) { bool should_print = false; + std::unique_ptr f; TextTable tbl; - tbl.define_column("pid", 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 (format == "json") { + f.reset(new JSONFormatter(pretty_format)); + } else if (format == "xml") { + f.reset(new XMLFormatter(pretty_format)); + } else if (!format.empty() && format != "plain") { + std::cerr << "rbd-nbd: invalid output format: " << format << std::endl; + return -EINVAL; + } + + if (f) { + f->open_array_section("devices"); + } else { + tbl.define_column("pid", 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); + } int pid; Config cfg; NBDListIterator it; while (it.get(&pid, &cfg)) { - should_print = true; - if (cfg.snapname.empty()) { - cfg.snapname = "-"; + if (f) { + f->open_object_section("device"); + f->dump_int("id", pid); + f->dump_string("pool", cfg.poolname); + f->dump_string("image", cfg.imgname); + f->dump_string("snap", cfg.snapname); + f->dump_string("device", cfg.devpath); + f->close_section(); + } else { + should_print = true; + if (cfg.snapname.empty()) { + cfg.snapname = "-"; + } + tbl << pid << cfg.poolname << cfg.imgname << cfg.snapname << cfg.devpath + << TextTable::endrow; } - tbl << pid << cfg.poolname << cfg.imgname << cfg.snapname << cfg.devpath - << TextTable::endrow; } + if (f) { + f->close_section(); // devices + f->flush(std::cout); + } if (should_print) { - cout << tbl; + std::cout << tbl; } return 0; } @@ -1038,6 +1076,10 @@ static int parse_args(vector& args, std::ostream *err_msg, *err_msg << "rbd-nbd: Invalid argument for timeout!"; return -EINVAL; } + } else if (ceph_argparse_witharg(args, i, &cfg->format, err, "--format", + (char *)NULL)) { + } else if (ceph_argparse_flag(args, i, "--pretty-format", (char *)NULL)) { + cfg->pretty_format = true; } else { ++i; } @@ -1144,7 +1186,7 @@ static int rbd_nbd(int argc, const char *argv[]) return -EINVAL; break; case List: - r = do_list_mapped_devices(); + r = do_list_mapped_devices(cfg.format, cfg.pretty_format); if (r < 0) return -EINVAL; break; -- 2.39.5