Merge pull request #19679 from trociny/wip-ggate-list-unmap

rbd-ggate: small fixes and improvements

Reviewed-by: Willem Jan Withagen <wjw@digiware.nl>
This commit is contained in:
Sage Weil 2017-12-29 10:31:36 -06:00 committed by GitHub
commit 1a9a44247d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 278 additions and 76 deletions

View File

@ -92,18 +92,23 @@ expect_false _sudo rbd-ggate map INVALIDIMAGE
# map test using the first unused device
DEV=`_sudo rbd-ggate map ${POOL}/${IMAGE}`
_sudo rbd-ggate list | grep "^${DEV}$"
_sudo rbd-ggate list | grep " ${DEV} *$"
# map test specifying the device
expect_false _sudo rbd-ggate --device ${DEV} map ${POOL}/${IMAGE}
dev1=${DEV}
_sudo rbd-ggate unmap ${DEV}
_sudo rbd-ggate list | expect_false grep "^${DEV}$"
_sudo rbd-ggate list | expect_false grep " ${DEV} *$"
DEV=
# XXX: race possible when the device is reused by other process
DEV=`_sudo rbd-ggate --device ${dev1} map ${POOL}/${IMAGE}`
[ "${DEV}" = "${dev1}" ]
_sudo rbd-ggate list | grep "^${DEV}$"
_sudo rbd-ggate list | grep " ${DEV} *$"
# list format test
expect_false _sudo rbd-ggate --format INVALID list
_sudo rbd-ggate --format json --pretty-format list
_sudo rbd-ggate --format xml list
# read test
[ "`dd if=${DATA} bs=1M | md5`" = "`_sudo dd if=${DEV} bs=1M | md5`" ]
@ -137,9 +142,9 @@ rbd info ${POOL}/${IMAGE}
if [ -z "$RBD_GGATE_RESIZE_SUPPORTED" ]; then
# XXX: ggate device resize is not supported by vanila kernel.
# rbd-ggate should terminate when detecting resize.
_sudo rbd-ggate list | expect_false grep "^${DEV}$"
_sudo rbd-ggate list | expect_false grep " ${DEV} *$"
else
_sudo rbd-ggate list | grep "^${DEV}$"
_sudo rbd-ggate list | grep " ${DEV} *$"
size2=$(geom gate list ${devname} | awk '$1 ~ /Mediasize:/ {print $2}')
test -n "${size2}"
test ${size2} -eq $((size * 2))
@ -161,7 +166,7 @@ DEV=
# read-only option test
DEV=`_sudo rbd-ggate map --read-only ${POOL}/${IMAGE}`
devname=$(basename ${DEV})
_sudo rbd-ggate list | grep "^${DEV}$"
_sudo rbd-ggate list | grep " ${DEV} *$"
access=$(geom gate list ${devname} | awk '$1 == "access:" {print $2}')
test "${access}" = "read-only"
_sudo dd if=${DEV} of=/dev/null bs=1M
@ -170,7 +175,7 @@ _sudo rbd-ggate unmap ${DEV}
# exclusive option test
DEV=`_sudo rbd-ggate map --exclusive ${POOL}/${IMAGE}`
_sudo rbd-ggate list | grep "^${DEV}$"
_sudo rbd-ggate list | grep " ${DEV} *$"
_sudo dd if=${DATA} of=${DEV} bs=1M
_sudo sync
expect_false timeout 10 \
@ -179,4 +184,19 @@ _sudo rbd-ggate unmap ${DEV}
DEV=
rbd bench -p ${POOL} ${IMAGE} --io-type=write --io-size=1024 --io-total=1024
# unmap by image name test
DEV=`_sudo rbd-ggate map ${POOL}/${IMAGE}`
_sudo rbd-ggate list | grep " ${DEV} *$"
_sudo rbd-ggate unmap "${POOL}/${IMAGE}"
rbd-ggate list-mapped | expect_false grep " ${DEV} *$"
DEV=
# map/unmap snap test
rbd snap create ${POOL}/${IMAGE}@snap
DEV=`_sudo rbd-ggate map ${POOL}/${IMAGE}@snap`
_sudo rbd-ggate list | grep " ${DEV} *$"
_sudo rbd-ggate unmap "${POOL}/${IMAGE}@snap"
rbd-ggate list-mapped | expect_false grep " ${DEV} *$"
DEV=
echo OK

View File

@ -76,9 +76,34 @@ static int call_ggate_cmd(const po::variables_map &vm,
return 0;
}
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 image_name;
std::string snap_name;
int r = utils::get_pool_image_snapshot_names(
vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
&snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED,
utils::SPEC_VALIDATION_NONE);
if (r < 0) {
return r;
}
spec->append(pool_name);
spec->append("/");
spec->append(image_name);
if (!snap_name.empty()) {
spec->append("@");
spec->append(snap_name);
}
return 0;
}
void get_list_arguments(po::options_description *positional,
po::options_description *options)
{ }
po::options_description *options) {
at::add_format_options(options);
}
int execute_list(const po::variables_map &vm)
{
@ -86,6 +111,14 @@ int execute_list(const po::variables_map &vm)
args.push_back("list");
if (vm.count("format")) {
args.push_back("--format");
args.push_back(vm["format"].as<at::Format>().value.c_str());
}
if (vm["pretty-format"].as<bool>()) {
args.push_back("--pretty-format");
}
return call_ggate_cmd(vm, args);
}
@ -102,28 +135,13 @@ void get_map_arguments(po::options_description *positional,
int execute_map(const po::variables_map &vm)
{
size_t arg_index = 0;
std::string pool_name;
std::string image_name;
std::string snap_name;
int r = utils::get_pool_image_snapshot_names(
vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
&snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED,
utils::SPEC_VALIDATION_NONE);
if (r < 0) {
return r;
}
std::vector<const char*> args;
args.push_back("map");
std::string img;
img.append(pool_name);
img.append("/");
img.append(image_name);
if (!snap_name.empty()) {
img.append("@");
img.append(snap_name);
int r = get_image_or_snap_spec(vm, &img);
if (r < 0) {
return r;
}
args.push_back(img.c_str());
@ -145,7 +163,12 @@ void get_unmap_arguments(po::options_description *positional,
po::options_description *options)
{
positional->add_options()
("device-spec", "specify ggate device");
("image-or-snap-or-device-spec",
"image, snapshot, or device specification\n"
"[<pool-name>/]<image-name>[@<snapshot-name>] or <device-path>");
at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE);
at::add_image_option(options, at::ARGUMENT_MODIFIER_NONE);
at::add_snap_option(options, at::ARGUMENT_MODIFIER_NONE);
}
int execute_unmap(const po::variables_map &vm)
@ -155,15 +178,25 @@ int execute_unmap(const po::variables_map &vm)
device_name.clear();
}
std::string image_name;
if (device_name.empty()) {
std::cerr << "rbd: ggate unmap requires device path" << std::endl;
int r = get_image_or_snap_spec(vm, &image_name);
if (r < 0) {
return r;
}
}
if (device_name.empty() && image_name.empty()) {
std::cerr << "rbd: unmap requires either image name or device path"
<< std::endl;
return -EINVAL;
}
std::vector<const char*> args;
args.push_back("unmap");
args.push_back(device_name.c_str());
args.push_back(device_name.empty() ? image_name.c_str() :
device_name.c_str());
return call_ggate_cmd(vm, args);
}

View File

@ -29,32 +29,31 @@ int Driver::kill(const std::string &devname) {
return r;
}
int Driver::list(std::list<std::string> &devs) {
int Driver::list(std::map<std::string, DevInfo> *devices) {
size_t size = 1024;
char **devs_ = nullptr;
ggate_drv_info *devs = nullptr;
int r;
while (size <= 1024 * 1024) {
devs_ = static_cast<char **>(
realloc(static_cast<void *>(devs_), size * sizeof(*devs_)));
r = ggate_drv_list(devs_, &size);
devs = static_cast<ggate_drv_info *>(
realloc(static_cast<void *>(devs), size * sizeof(*devs)));
r = ggate_drv_list(devs, &size);
if (r != -ERANGE) {
break;
}
size *= 2;
}
if (r < 0) {
goto free;
}
devs.clear();
devices->clear();
for (size_t i = 0; i < size; i++) {
devs.push_back(devs_[i]);
auto &dev = devs[i];
(*devices)[dev.id] = {dev.name, dev.info};
}
ggate_drv_list_free(devs_, size);
free:
free(devs_);
free(devs);
return r;
}

View File

@ -4,7 +4,7 @@
#ifndef CEPH_RBD_GGATE_DRIVER_H
#define CEPH_RBD_GGATE_DRIVER_H
#include <list>
#include <map>
#include <string>
#include "ggate_drv.h"
@ -16,9 +16,10 @@ struct Request;
class Driver {
public:
typedef std::pair<std::string, std::string> DevInfo;
static int load();
static int kill(const std::string &devname);
static int list(std::list<std::string> &devs);
static int list(std::map<std::string, DevInfo> *devices);
Driver(const std::string &devname, size_t sectorsize, size_t mediasize,
bool readonly, const std::string &info);

View File

@ -330,7 +330,17 @@ int ggate_drv_send(ggate_drv_t drv_, ggate_drv_req_t req) {
return r;
}
int ggate_drv_list(char **devs, size_t *size) {
static const char * get_conf(struct ggeom *gp, const char *name) {
struct gconfig *conf;
LIST_FOREACH(conf, &gp->lg_config, lg_config) {
if (strcmp(conf->lg_name, name) == 0)
return (conf->lg_val);
}
return "";
}
int ggate_drv_list(struct ggate_drv_info *info, size_t *size) {
struct gmesh mesh;
struct gclass *class;
struct ggeom *gp;
@ -355,8 +365,10 @@ int ggate_drv_list(char **devs, size_t *size) {
goto done;
}
LIST_FOREACH(gp, &class->lg_geom, lg_geom) {
*devs = strdup(gp->lg_name);
devs++;
strlcpy(info->id, get_conf(gp, "unit"), sizeof(info->id));
strlcpy(info->name, gp->lg_name, sizeof(info->name));
strlcpy(info->info, get_conf(gp, "info"), sizeof(info->info));
info++;
}
}
}
@ -365,11 +377,3 @@ done:
geom_deletetree(&mesh);
return r;
}
void ggate_drv_list_free(char **devs, size_t size) {
size_t i;
for (i = 0; i < size; i++) {
free(devs[i]);
}
}

View File

@ -8,6 +8,8 @@
extern "C" {
#endif
#include <sys/param.h>
#include <stdbool.h>
#include <stdint.h>
@ -25,6 +27,12 @@ enum {
GGATE_DRV_CMD_DISCARD = 4,
};
struct ggate_drv_info {
char id[16];
char name[NAME_MAX];
char info[2048]; /* G_GATE_INFOSIZE */
};
uint64_t ggate_drv_req_id(ggate_drv_req_t req);
int ggate_drv_req_cmd(ggate_drv_req_t req);
void *ggate_drv_req_buf(ggate_drv_req_t req);
@ -47,8 +55,7 @@ int ggate_drv_send(ggate_drv_t drv, ggate_drv_req_t req);
int ggate_drv_resize(ggate_drv_t drv, size_t newsize);
int ggate_drv_kill(const char *devname);
int ggate_drv_list(char **devs, size_t *size);
void ggate_drv_list_free(char **devs, size_t size);
int ggate_drv_list(struct ggate_drv_info *info, size_t *size);
#ifdef __cplusplus
}

View File

@ -13,9 +13,13 @@
#include <assert.h>
#include <iostream>
#include <memory>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/regex.hpp>
#include "common/Formatter.h"
#include "common/Preforker.h"
#include "common/TextTable.h"
#include "common/ceph_argparse.h"
#include "common/config.h"
#include "common/debug.h"
@ -25,6 +29,7 @@
#include "include/rados/librados.hpp"
#include "include/rbd/librbd.hpp"
#include "include/stringify.h"
#include "Driver.h"
#include "Server.h"
@ -39,15 +44,20 @@ static void usage() {
std::cout << "Usage: rbd-ggate [options] map <image-or-snap-spec> Map an image to ggate device\n"
<< " unmap <device path> Unmap ggate device\n"
<< " list List mapped ggate devices\n"
<< "Options:\n"
<< "\n"
<< "Map options:\n"
<< " --device <device path> Specify ggate device path\n"
<< " --read-only Map readonly\n"
<< " --exclusive Forbid writes by other clients\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();
}
static std::string devpath, poolname("rbd"), imgname, snapname;
static std::string devpath, poolname, imgname, snapname;
static bool readonly = false;
static bool exclusive = false;
@ -90,7 +100,7 @@ static int do_map(int argc, const char *argv[])
std::string err;
r = forker.prefork(err);
if (r < 0) {
cerr << err << std::endl;
std::cerr << err << std::endl;
return r;
}
@ -106,7 +116,11 @@ static int do_map(int argc, const char *argv[])
common_init_finish(g_ceph_context);
global_init_chdir(g_ceph_context);
std::string devname = (devpath.compare(0, 5, "/dev/") == 0) ?
if (poolname.empty()) {
poolname = g_ceph_context->_conf->get_val<std::string>("rbd_default_pool");
}
std::string devname = boost::starts_with(devpath, "/dev/") ?
devpath.substr(5) : devpath;
std::unique_ptr<rbd::ggate::Watcher> watcher;
uint64_t handle;
@ -118,24 +132,30 @@ static int do_map(int argc, const char *argv[])
r = rados.connect();
if (r < 0) {
std::cerr << "rbd-ggate: failed to connect to cluster: " << cpp_strerror(r)
<< std::endl;
goto done;
}
r = rados.ioctx_create(poolname.c_str(), io_ctx);
if (r < 0) {
std::cerr << "rbd-ggate: failed to acces pool " << poolname << ": "
<< cpp_strerror(r) << std::endl;
goto done;
}
r = rbd.open(io_ctx, image, imgname.c_str());
if (r < 0) {
std::cerr << "rbd-ggate: failed to open image " << imgname << ": "
<< cpp_strerror(r) << std::endl;
goto done;
}
if (exclusive) {
r = image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE);
if (r < 0) {
cerr << "rbd-ggate: failed to acquire exclusive lock: " << cpp_strerror(r)
<< std::endl;
std::cerr << "rbd-ggate: failed to acquire exclusive lock: "
<< cpp_strerror(r) << std::endl;
goto done;
}
}
@ -145,6 +165,8 @@ static int do_map(int argc, const char *argv[])
if (!snapname.empty()) {
r = image.snap_set(snapname.c_str());
if (r < 0) {
std::cerr << "rbd-ggate: failed to set snapshot " << snapname << ": "
<< cpp_strerror(r) << std::endl;
goto done;
}
readonly = true;
@ -153,6 +175,8 @@ static int do_map(int argc, const char *argv[])
r = image.stat(info, sizeof(info));
if (r < 0) {
std::cerr << "rbd-ggate: image stat failed: " << cpp_strerror(r)
<< std::endl;
goto done;
}
@ -161,12 +185,16 @@ static int do_map(int argc, const char *argv[])
r = drv->init();
if (r < 0) {
r = -errno;
std::cerr << "rbd-ggate: failed to create ggate device: " << cpp_strerror(r)
<< std::endl;
goto done;
}
watcher.reset(new rbd::ggate::Watcher(drv.get(), io_ctx, image, info.size));
r = image.update_watch(watcher.get(), &handle);
if (r < 0) {
std::cerr << "rbd-ggate: failed to set watcher: " << cpp_strerror(r)
<< std::endl;
drv->shut_down();
goto done;
}
@ -199,6 +227,10 @@ done:
io_ctx.close();
rados.shutdown();
if (r < 0) {
std::cerr << "rbd-ggate: failed to map: " << cpp_strerror(r) << std::endl;
}
forker.exit(r < 0 ? EXIT_FAILURE : 0);
// Unreachable;
return r;
@ -206,7 +238,7 @@ done:
static int do_unmap()
{
std::string devname = (devpath.compare(0, 5, "/dev/") == 0) ?
std::string devname = boost::starts_with(devpath, "/dev/") ?
devpath.substr(5) : devpath;
int r = rbd::ggate::Driver::kill(devname);
@ -219,8 +251,8 @@ static int do_unmap()
return 0;
}
static int parse_imgpath(const std::string &imgpath)
{
static int parse_imgpath(const std::string &imgpath, std::string *poolname,
std::string *imgname, std::string *snapname) {
boost::regex pattern("^(?:([^/@]+)/)?([^/@]+)(?:@([^/@]+))?$");
boost::smatch match;
if (!boost::regex_match(imgpath, match, pattern)) {
@ -229,31 +261,126 @@ static int parse_imgpath(const std::string &imgpath)
}
if (match[1].matched) {
poolname = match[1];
*poolname = match[1];
}
imgname = match[2];
*imgname = match[2];
if (match[3].matched) {
snapname = match[3];
*snapname = match[3];
}
return 0;
}
static int do_list()
static bool find_mapped_dev_by_spec(const std::string &spec,
std::string *devname) {
std::string poolname, imgname, snapname;
int r = parse_imgpath(spec, &poolname, &imgname, &snapname);
if (r < 0) {
return false;
}
if (poolname.empty()) {
// We could use rbd_default_pool config to set pool name but then
// we would need to initialize the global context. So right now it
// is mandatory for the user to specify a pool. Fortunately the
// preferred way for users to call rbd-ggate is via rbd, which
// cares to set the pool name.
return false;
}
std::map<std::string, rbd::ggate::Driver::DevInfo> devs;
r = rbd::ggate::Driver::list(&devs);
if (r < 0) {
return false;
}
for (auto &it : devs) {
auto &name = it.second.first;
auto &info = it.second.second;
if (!boost::starts_with(info, "RBD ")) {
continue;
}
std::string p, i, s;
parse_imgpath(info.substr(4), &p, &i, &s);
if (p == poolname && i == imgname && s == snapname) {
*devname = name;
return true;
}
}
return false;
}
static int do_list(const std::string &format, bool pretty_format)
{
rbd::ggate::Driver::load();
std::list<std::string> devs;
int r = rbd::ggate::Driver::list(devs);
std::map<std::string, rbd::ggate::Driver::DevInfo> devs;
int r = rbd::ggate::Driver::list(&devs);
if (r < 0) {
return -r;
}
for (auto &devname : devs) {
cout << "/dev/" << devname << std::endl;
std::unique_ptr<ceph::Formatter> f;
TextTable tbl;
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-ggate: invalid output format: " << format << std::endl;
return -EINVAL;
}
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);
}
int count = 0;
for (auto &it : devs) {
auto &id = it.first;
auto &name = it.second.first;
auto &info = it.second.second;
if (!boost::starts_with(info, "RBD ")) {
continue;
}
std::string poolname;
std::string imgname;
std::string snapname(f ? "" : "-");
parse_imgpath(info.substr(4), &poolname, &imgname, &snapname);
if (f) {
f->open_object_section(stringify(id).c_str());
f->dump_string("pool", poolname);
f->dump_string("image", imgname);
f->dump_string("snap", snapname);
f->dump_string("device", "/dev/" + name);
f->close_section();
} else {
tbl << id << poolname << imgname << snapname << "/dev/" + name
<< TextTable::endrow;
}
count++;
}
if (f) {
f->close_section(); // devices
f->flush(std::cout);
} else if (count > 0) {
std::cout << tbl;
}
return 0;
}
@ -271,6 +398,8 @@ int main(int argc, const char *argv[]) {
argv_to_vec(argc, argv, args);
md_config_t().parse_argv(args);
std::string format;
bool pretty_format = false;
std::vector<const char*>::iterator i;
for (i = args.begin(); i != args.end(); ) {
@ -283,6 +412,10 @@ int main(int argc, const char *argv[]) {
readonly = true;
} else if (ceph_argparse_flag(args, i, "--exclusive", (char *)NULL)) {
exclusive = true;
} else if (ceph_argparse_witharg(args, i, &format, "--format",
(char *)NULL)) {
} else if (ceph_argparse_flag(args, i, "--pretty-format", (char *)NULL)) {
pretty_format = true;
} else {
++i;
}
@ -313,16 +446,21 @@ int main(int argc, const char *argv[]) {
cerr << "rbd-ggate: must specify image-or-snap-spec" << std::endl;
return EXIT_FAILURE;
}
if (parse_imgpath(string(*args.begin())) < 0)
if (parse_imgpath(*args.begin(), &poolname, &imgname, &snapname) < 0) {
return EXIT_FAILURE;
}
args.erase(args.begin());
break;
case Disconnect:
if (args.begin() == args.end()) {
cerr << "rbd-ggate: must specify ggate device path" << std::endl;
std::cerr << "rbd-ggate: must specify ggate device or image-or-snap-spec"
<< std::endl;
return EXIT_FAILURE;
}
devpath = *args.begin();
if (boost::starts_with(*args.begin(), "/dev/") ||
!find_mapped_dev_by_spec(*args.begin(), &devpath)) {
devpath = *args.begin();
}
args.erase(args.begin());
break;
default:
@ -351,7 +489,7 @@ int main(int argc, const char *argv[]) {
return EXIT_FAILURE;
break;
case List:
r = do_list();
r = do_list(format, pretty_format);
if (r < 0)
return EXIT_FAILURE;
break;