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
#
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}
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`" ]
# 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 \
# 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=
# 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=
# 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
#include <sys/ioctl.h>
#include <sys/socket.h>
-#include <iostream>
#include <fstream>
+#include <iostream>
+#include <memory>
#include <boost/regex.hpp>
#include <boost/algorithm/string/predicate.hpp>
-#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"
#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
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 <image-or-snap-spec> Map an image to nbd device\n"
<< " unmap <device|image-or-snap-spec> 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 <device path> Specify nbd device path\n"
<< " --read-only Map read-only\n"
<< " --nbds_max <limit> Override for module param nbds_max\n"
<< " --max_part <limit> Override for module param max_part\n"
<< " --exclusive Forbid writes by other clients\n"
<< " --timeout <seconds> 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();
}
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<ceph::Formatter> 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;
}
*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;
}
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;