ceph/src/rados.cc
Samuel Just 637e0eaddc rados: --num-objects will now cause bench to stop after that many objects
Reviewed-by: David Zafman <david.zafman@inktank.com>
Signed-off-by: Samuel Just <sam.just@inktank.com>
2013-06-07 15:59:40 -07:00

2340 lines
68 KiB
C++

// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
* Ceph - scalable distributed file system
*
* Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software
* Foundation. See file COPYING.
*
*/
#include "include/types.h"
#include "include/rados/librados.hpp"
#include "include/rados/rados_types.hpp"
#include "rados_sync.h"
using namespace librados;
#include "common/config.h"
#include "common/ceph_argparse.h"
#include "global/global_init.h"
#include "common/Cond.h"
#include "common/debug.h"
#include "common/errno.h"
#include "common/Formatter.h"
#include "common/obj_bencher.h"
#include "mds/inode_backtrace.h"
#include "auth/Crypto.h"
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <time.h>
#include <sstream>
#include <errno.h>
#include <dirent.h>
#include <stdexcept>
#include <climits>
#include <locale>
#include "cls/lock/cls_lock_client.h"
int rados_tool_sync(const std::map < std::string, std::string > &opts,
std::vector<const char*> &args);
// two steps seem to be necessary to do this right
#define STR(x) _STR(x)
#define _STR(x) #x
void usage(ostream& out)
{
out << \
"usage: rados [options] [commands]\n"
"POOL COMMANDS\n"
" lspools list pools\n"
" mkpool <pool-name> [123[ 4]] create pool <pool-name>'\n"
" [with auid 123[and using crush rule 4]]\n"
" cppool <pool-name> <dest-pool> copy content of a pool\n"
" rmpool <pool-name> [<pool-name> --yes-i-really-really-mean-it]\n"
" remove pool <pool-name>'\n"
" df show per-pool and total usage\n"
" ls list objects in pool\n\n"
" chown 123 change the pool owner to auid 123\n"
"\n"
"OBJECT COMMANDS\n"
" get <obj-name> [outfile] fetch object\n"
" put <obj-name> [infile] write object\n"
" truncate <obj-name> length truncate object\n"
" create <obj-name> [category] create object\n"
" rm <obj-name> ... remove object(s)\n"
" cp <obj-name> [target-obj] copy object\n"
" clonedata <src-obj> <dst-obj> clone object data\n"
" listxattr <obj-name>\n"
" getxattr <obj-name> attr\n"
" setxattr <obj-name> attr val\n"
" rmxattr <obj-name> attr\n"
" stat objname stat the named object\n"
" mapext <obj-name>\n"
" lssnap list snaps\n"
" mksnap <snap-name> create snap <snap-name>\n"
" rmsnap <snap-name> remove snap <snap-name>\n"
" rollback <obj-name> <snap-name> roll back object to snap <snap-name>\n"
"\n"
" listsnaps <obj-name> list the snapshots of this object\n"
" bench <seconds> write|seq|rand [-t concurrent_operations] [--no-cleanup]\n"
" default is 16 concurrent IOs and 4 MB ops\n"
" default is to clean up after write benchmark\n"
" cleanup <prefix> clean up a previous benchmark operation\n"
" load-gen [options] generate load on the cluster\n"
" listomapkeys <obj-name> list the keys in the object map\n"
" listomapvals <obj-name> list the keys and vals in the object map \n"
" getomapval <obj-name> <key> show the value for the specified key\n"
" in the object's object map\n"
" setomapval <obj-name> <key> <val>\n"
" rmomapkey <obj-name> <key>\n"
" getomapheader <obj-name>\n"
" setomapheader <obj-name> <val>\n"
" listwatchers <obj-name> list the watchers of this object\n"
"\n"
"IMPORT AND EXPORT\n"
" import [options] <local-directory> <rados-pool>\n"
" Upload <local-directory> to <rados-pool>\n"
" export [options] rados-pool> <local-directory>\n"
" Download <rados-pool> to <local-directory>\n"
" options:\n"
" -f / --force Copy everything, even if it hasn't changed.\n"
" -d / --delete-after After synchronizing, delete unreferenced\n"
" files or objects from the target bucket\n"
" or directory.\n"
" --workers Number of worker threads to spawn \n"
" (default " STR(DEFAULT_NUM_RADOS_WORKER_THREADS) ")\n"
"\n"
"ADVISORY LOCKS\n"
" lock list <obj-name>\n"
" List all advisory locks on an object\n"
" lock get <obj-name> <lock-name>\n"
" Try to acquire a lock\n"
" lock break <obj-name> <lock-name> <locker-name>\n"
" Try to break a lock acquired by another client\n"
" lock info <obj-name> <lock-name>\n"
" Show lock information\n"
" options:\n"
" --lock-tag Lock tag, all locks operation should use\n"
" the same tag\n"
" --lock-cookie Locker cookie\n"
" --lock-description Description of lock\n"
" --lock-duration Lock duration (in seconds)\n"
" --lock-type Lock type (shared, exclusive)\n"
"\n"
"GLOBAL OPTIONS:\n"
" --object_locator object_locator\n"
" set object_locator for operation\n"
" -p pool\n"
" --pool=pool\n"
" select given pool by name\n"
" --target-pool=pool\n"
" select target pool by name\n"
" -b op_size\n"
" set the size of write ops for put or benchmarking\n"
" -s name\n"
" --snap name\n"
" select given snap name for (read) IO\n"
" -i infile\n"
" -o outfile\n"
" specify input or output file (for certain commands)\n"
" --create\n"
" create the pool or directory that was specified\n"
"\n"
"BENCH OPTIONS:\n"
" -t N\n"
" --concurrent-ios=N\n"
" Set number of concurrent I/O operations\n"
" --show-time\n"
" prefix output with date/time\n"
"\n"
"LOAD GEN OPTIONS:\n"
" --num-objects total number of objects\n"
" --min-object-size min object size\n"
" --max-object-size max object size\n"
" --min-ops min number of operations\n"
" --max-ops max number of operations\n"
" --max-backlog max backlog (in MB)\n"
" --percent percent of operations that are read\n"
" --target-throughput target throughput (in MB)\n"
" --run-length total time (in seconds)\n";
}
static void usage_exit()
{
usage(cerr);
exit(1);
}
static int do_get(IoCtx& io_ctx, const char *objname, const char *outfile, unsigned op_size)
{
string oid(objname);
int fd;
if (strcmp(outfile, "-") == 0) {
fd = 1;
} else {
fd = TEMP_FAILURE_RETRY(::open(outfile, O_WRONLY|O_CREAT|O_TRUNC, 0644));
if (fd < 0) {
int err = errno;
cerr << "failed to open file: " << cpp_strerror(err) << std::endl;
return -err;
}
}
uint64_t offset = 0;
int ret;
while (true) {
bufferlist outdata;
ret = io_ctx.read(oid, outdata, op_size, offset);
if (ret <= 0) {
goto out;
}
ret = outdata.write_fd(fd);
if (ret < 0) {
cerr << "error writing to file: " << cpp_strerror(ret) << std::endl;
goto out;
}
if (outdata.length() < op_size)
break;
offset += outdata.length();
}
ret = 0;
out:
if (fd != 1)
TEMP_FAILURE_RETRY(::close(fd));
return ret;
}
static int do_copy(IoCtx& io_ctx, const char *objname, IoCtx& target_ctx, const char *target_obj)
{
string oid(objname);
bufferlist outdata;
librados::ObjectReadOperation read_op;
string start_after;
#define COPY_CHUNK_SIZE (4 * 1024 * 1024)
read_op.read(0, COPY_CHUNK_SIZE, &outdata, NULL);
map<std::string, bufferlist> attrset;
read_op.getxattrs(&attrset, NULL);
bufferlist omap_header;
read_op.omap_get_header(&omap_header, NULL);
#define OMAP_CHUNK 1000
map<string, bufferlist> omap;
read_op.omap_get_vals(start_after, OMAP_CHUNK, &omap, NULL);
bufferlist opbl;
int ret = io_ctx.operate(oid, &read_op, &opbl);
if (ret < 0) {
return ret;
}
librados::ObjectWriteOperation write_op;
string target_oid(target_obj);
/* reset dest if exists */
write_op.create(false);
write_op.remove();
write_op.write_full(outdata);
write_op.omap_set_header(omap_header);
map<std::string, bufferlist>::iterator iter;
for (iter = attrset.begin(); iter != attrset.end(); ++iter) {
write_op.setxattr(iter->first.c_str(), iter->second);
}
if (!omap.empty()) {
write_op.omap_set(omap);
}
ret = target_ctx.operate(target_oid, &write_op);
if (ret < 0) {
return ret;
}
uint64_t off = 0;
while (outdata.length() == COPY_CHUNK_SIZE) {
off += outdata.length();
outdata.clear();
ret = io_ctx.read(oid, outdata, COPY_CHUNK_SIZE, off);
if (ret < 0)
goto err;
ret = target_ctx.write(target_oid, outdata, outdata.length(), off);
if (ret < 0)
goto err;
}
/* iterate through source omap and update target. This is not atomic */
while (omap.size() == OMAP_CHUNK) {
/* now start_after should point at the last entry */
map<string, bufferlist>::iterator iter = omap.end();
--iter;
start_after = iter->first;
omap.clear();
ret = io_ctx.omap_get_vals(oid, start_after, OMAP_CHUNK, &omap);
if (ret < 0)
goto err;
if (omap.empty())
break;
ret = target_ctx.omap_set(target_oid, omap);
if (ret < 0)
goto err;
}
return 0;
err:
target_ctx.remove(target_oid);
return ret;
}
static int do_clone_data(IoCtx& io_ctx, const char *objname, IoCtx& target_ctx, const char *target_obj)
{
string oid(objname);
// get size
uint64_t size;
int r = target_ctx.stat(oid, &size, NULL);
if (r < 0)
return r;
librados::ObjectWriteOperation write_op;
string target_oid(target_obj);
/* reset data stream only */
write_op.create(false);
write_op.truncate(0);
write_op.clone_range(0, oid, 0, size);
return target_ctx.operate(target_oid, &write_op);
}
static int do_copy_pool(Rados& rados, const char *src_pool, const char *target_pool)
{
IoCtx src_ctx, target_ctx;
int ret = rados.ioctx_create(src_pool, src_ctx);
if (ret < 0) {
cerr << "cannot open source pool: " << src_pool << std::endl;
return ret;
}
ret = rados.ioctx_create(target_pool, target_ctx);
if (ret < 0) {
cerr << "cannot open target pool: " << target_pool << std::endl;
return ret;
}
librados::ObjectIterator i = src_ctx.objects_begin();
librados::ObjectIterator i_end = src_ctx.objects_end();
for (; i != i_end; ++i) {
string oid = i->first;
string locator = i->second;
if (i->second.size())
cout << src_pool << ":" << oid << "(@" << locator << ")" << " => "
<< target_pool << ":" << oid << "(@" << locator << ")" << std::endl;
else
cout << src_pool << ":" << oid << " => "
<< target_pool << ":" << oid << std::endl;
target_ctx.locator_set_key(locator);
ret = do_copy(src_ctx, oid.c_str(), target_ctx, oid.c_str());
if (ret < 0) {
char buf[64];
cerr << "error copying object: " << strerror_r(errno, buf, sizeof(buf)) << std::endl;
return ret;
}
}
return 0;
}
static int do_put(IoCtx& io_ctx, const char *objname, const char *infile, int op_size)
{
string oid(objname);
bufferlist indata;
bool stdio = false;
if (strcmp(infile, "-") == 0)
stdio = true;
int ret;
int fd = 0;
if (!stdio)
fd = open(infile, O_RDONLY);
if (fd < 0) {
char buf[80];
cerr << "error reading input file " << infile << ": " << strerror_r(errno, buf, sizeof(buf)) << std::endl;
return 1;
}
char *buf = new char[op_size];
int count = op_size;
uint64_t offset = 0;
while (count != 0) {
count = read(fd, buf, op_size);
if (count < 0) {
ret = -errno;
cerr << "error reading input file " << infile << ": " << cpp_strerror(ret) << std::endl;
goto out;
}
if (count == 0) {
if (!offset) {
ret = io_ctx.create(oid, true);
if (ret < 0) {
cerr << "WARNING: could not create object: " << oid << std::endl;
goto out;
}
}
continue;
}
indata.append(buf, count);
if (offset == 0)
ret = io_ctx.write_full(oid, indata);
else
ret = io_ctx.write(oid, indata, count, offset);
indata.clear();
if (ret < 0) {
goto out;
}
offset += count;
}
ret = 0;
out:
TEMP_FAILURE_RETRY(close(fd));
delete[] buf;
return ret;
}
class RadosWatchCtx : public librados::WatchCtx {
string name;
public:
RadosWatchCtx(const char *imgname) : name(imgname) {}
virtual ~RadosWatchCtx() {}
virtual void notify(uint8_t opcode, uint64_t ver, bufferlist& bl) {
string s;
try {
bufferlist::iterator iter = bl.begin();
::decode(s, iter);
} catch (buffer::error *err) {
cout << "could not decode bufferlist, buffer length=" << bl.length() << std::endl;
}
cout << name << " got notification opcode=" << (int)opcode << " ver=" << ver << " msg='" << s << "'" << std::endl;
}
};
static const char alphanum_table[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
int gen_rand_alphanumeric(char *dest, int size) /* size should be the required string size + 1 */
{
int ret = get_random_bytes(dest, size);
if (ret < 0) {
cerr << "cannot get random bytes: " << cpp_strerror(-ret) << std::endl;
return -1;
}
int i;
for (i=0; i<size - 1; i++) {
int pos = (unsigned)dest[i];
dest[i] = alphanum_table[pos & 63];
}
dest[i] = '\0';
return 0;
}
struct obj_info {
string name;
size_t len;
};
class LoadGen {
size_t total_sent;
size_t total_completed;
IoCtx io_ctx;
Rados *rados;
map<int, obj_info> objs;
utime_t start_time;
bool going_down;
public:
int read_percent;
int num_objs;
size_t min_obj_len;
uint64_t max_obj_len;
size_t min_op_len;
size_t max_op_len;
size_t max_ops;
size_t max_backlog;
size_t target_throughput;
int run_length;
enum {
OP_READ,
OP_WRITE,
};
struct LoadGenOp {
int id;
int type;
string oid;
size_t off;
size_t len;
bufferlist bl;
LoadGen *lg;
librados::AioCompletion *completion;
LoadGenOp() {}
LoadGenOp(LoadGen *_lg) : lg(_lg), completion(NULL) {}
};
int max_op;
map<int, LoadGenOp *> pending_ops;
void gen_op(LoadGenOp *op);
uint64_t gen_next_op();
void run_op(LoadGenOp *op);
uint64_t cur_sent_rate() {
return total_sent / time_passed();
}
uint64_t cur_completed_rate() {
return total_completed / time_passed();
}
uint64_t total_expected() {
return target_throughput * time_passed();
}
float time_passed() {
utime_t now = ceph_clock_now(g_ceph_context);
now -= start_time;
uint64_t ns = now.nsec();
float total = ns / 1000000000;
total += now.sec();
return total;
}
Mutex lock;
Cond cond;
LoadGen(Rados *_rados) : rados(_rados), going_down(false), lock("LoadGen") {
read_percent = 80;
min_obj_len = 1024;
max_obj_len = 5ull * 1024ull * 1024ull * 1024ull;
min_op_len = 1024;
target_throughput = 5 * 1024 * 1024; // B/sec
max_op_len = 2 * 1024 * 1024;
max_backlog = target_throughput * 2;
run_length = 60;
total_sent = 0;
total_completed = 0;
num_objs = 200;
max_op = 16;
}
int bootstrap(const char *pool);
int run();
void cleanup();
void io_cb(completion_t c, LoadGenOp *op) {
total_completed += op->len;
Mutex::Locker l(lock);
double rate = (double)cur_completed_rate() / (1024 * 1024);
cout.precision(3);
cout << "op " << op->id << " completed, throughput=" << rate << "MB/sec" << std::endl;
map<int, LoadGenOp *>::iterator iter = pending_ops.find(op->id);
if (iter != pending_ops.end())
pending_ops.erase(iter);
if (!going_down)
op->completion->release();
delete op;
cond.Signal();
}
};
static void _load_gen_cb(completion_t c, void *param)
{
LoadGen::LoadGenOp *op = (LoadGen::LoadGenOp *)param;
op->lg->io_cb(c, op);
}
int LoadGen::bootstrap(const char *pool)
{
char buf[128];
int i;
if (!pool) {
cerr << "ERROR: pool name was not specified" << std::endl;
return -EINVAL;
}
int ret = rados->ioctx_create(pool, io_ctx);
if (ret < 0) {
cerr << "error opening pool " << pool << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
return ret;
}
int buf_len = 1;
bufferptr p = buffer::create(buf_len);
bufferlist bl;
memset(p.c_str(), 0, buf_len);
bl.push_back(p);
list<librados::AioCompletion *> completions;
for (i = 0; i < num_objs; i++) {
obj_info info;
gen_rand_alphanumeric(buf, 16);
info.name = "obj-";
info.name.append(buf);
info.len = get_random(min_obj_len, max_obj_len);
// throttle...
while (completions.size() > max_ops) {
AioCompletion *c = completions.front();
c->wait_for_complete();
ret = c->get_return_value();
c->release();
completions.pop_front();
if (ret < 0) {
cerr << "aio_write failed" << std::endl;
return ret;
}
}
librados::AioCompletion *c = rados->aio_create_completion(NULL, NULL, NULL);
completions.push_back(c);
// generate object
ret = io_ctx.aio_write(info.name, c, bl, buf_len, info.len - buf_len);
if (ret < 0) {
cerr << "couldn't write obj: " << info.name << " ret=" << ret << std::endl;
return ret;
}
objs[i] = info;
}
list<librados::AioCompletion *>::iterator iter;
for (iter = completions.begin(); iter != completions.end(); ++iter) {
AioCompletion *c = *iter;
c->wait_for_complete();
ret = c->get_return_value();
c->release();
if (ret < 0) { // yes, we leak.
cerr << "aio_write failed" << std::endl;
return ret;
}
}
return 0;
}
void LoadGen::run_op(LoadGenOp *op)
{
op->completion = rados->aio_create_completion(op, _load_gen_cb, NULL);
switch (op->type) {
case OP_READ:
io_ctx.aio_read(op->oid, op->completion, &op->bl, op->len, op->off);
break;
case OP_WRITE:
bufferptr p = buffer::create(op->len);
memset(p.c_str(), 0, op->len);
op->bl.push_back(p);
io_ctx.aio_write(op->oid, op->completion, op->bl, op->len, op->off);
break;
}
total_sent += op->len;
}
void LoadGen::gen_op(LoadGenOp *op)
{
int i = get_random(0, objs.size() - 1);
obj_info& info = objs[i];
op->oid = info.name;
size_t len = get_random(min_op_len, max_op_len);
if (len > info.len)
len = info.len;
size_t off = get_random(0, info.len);
if (off + len > info.len)
off = info.len - len;
op->off = off;
op->len = len;
i = get_random(1, 100);
if (i > read_percent)
op->type = OP_WRITE;
else
op->type = OP_READ;
cout << (op->type == OP_READ ? "READ" : "WRITE") << " : oid=" << op->oid << " off=" << op->off << " len=" << op->len << std::endl;
}
uint64_t LoadGen::gen_next_op()
{
lock.Lock();
LoadGenOp *op = new LoadGenOp(this);
gen_op(op);
op->id = max_op++;
pending_ops[op->id] = op;
lock.Unlock();
run_op(op);
return op->len;
}
int LoadGen::run()
{
start_time = ceph_clock_now(g_ceph_context);
utime_t end_time = start_time;
end_time += run_length;
utime_t stamp_time = start_time;
uint32_t total_sec = 0;
while (1) {
lock.Lock();
utime_t one_second(1, 0);
cond.WaitInterval(g_ceph_context, lock, one_second);
lock.Unlock();
utime_t now = ceph_clock_now(g_ceph_context);
if (now > end_time)
break;
uint64_t expected = total_expected();
lock.Lock();
uint64_t sent = total_sent;
uint64_t completed = total_completed;
lock.Unlock();
if (now - stamp_time >= utime_t(1, 0)) {
double rate = (double)cur_completed_rate() / (1024 * 1024);
++total_sec;
cout.precision(3);
cout << setw(5) << total_sec << ": throughput=" << rate << "MB/sec" << " pending data=" << sent - completed << std::endl;
stamp_time = now;
}
while (sent < expected &&
sent - completed < max_backlog &&
pending_ops.size() < max_ops) {
sent += gen_next_op();
}
}
// get a reference to all pending requests
vector<librados::AioCompletion *> completions;
lock.Lock();
going_down = true;
map<int, LoadGenOp *>::iterator iter;
for (iter = pending_ops.begin(); iter != pending_ops.end(); ++iter) {
LoadGenOp *op = iter->second;
completions.push_back(op->completion);
}
lock.Unlock();
cout << "waiting for all operations to complete" << std::endl;
// now wait on all the pending requests
for (vector<librados::AioCompletion *>::iterator citer = completions.begin(); citer != completions.end(); ++citer) {
librados::AioCompletion *c = *citer;
c->wait_for_complete();
c->release();
}
return 0;
}
void LoadGen::cleanup()
{
cout << "cleaning up objects" << std::endl;
map<int, obj_info>::iterator iter;
for (iter = objs.begin(); iter != objs.end(); ++iter) {
obj_info& info = iter->second;
int ret = io_ctx.remove(info.name);
if (ret < 0)
cerr << "couldn't remove obj: " << info.name << " ret=" << ret << std::endl;
}
}
class RadosBencher : public ObjBencher {
librados::AioCompletion **completions;
librados::Rados& rados;
librados::IoCtx& io_ctx;
librados::ObjectIterator oi;
bool iterator_valid;
protected:
int completions_init(int concurrentios) {
completions = new librados::AioCompletion *[concurrentios];
return 0;
}
void completions_done() {
delete[] completions;
completions = NULL;
}
int create_completion(int slot, void (*cb)(void *, void*), void *arg) {
completions[slot] = rados.aio_create_completion((void *) arg, 0, cb);
if (!completions[slot])
return -EINVAL;
return 0;
}
void release_completion(int slot) {
completions[slot]->release();
completions[slot] = 0;
}
int aio_read(const std::string& oid, int slot, bufferlist *pbl, size_t len) {
return io_ctx.aio_read(oid, completions[slot], pbl, len, 0);
}
int aio_write(const std::string& oid, int slot, bufferlist& bl, size_t len) {
return io_ctx.aio_write(oid, completions[slot], bl, len, 0);
}
int aio_remove(const std::string& oid, int slot) {
return io_ctx.aio_remove(oid, completions[slot]);
}
int sync_read(const std::string& oid, bufferlist& bl, size_t len) {
return io_ctx.read(oid, bl, len, 0);
}
int sync_write(const std::string& oid, bufferlist& bl, size_t len) {
return io_ctx.write(oid, bl, len, 0);
}
int sync_remove(const std::string& oid) {
return io_ctx.remove(oid);
}
bool completion_is_done(int slot) {
return completions[slot]->is_safe();
}
int completion_wait(int slot) {
return completions[slot]->wait_for_safe_and_cb();
}
int completion_ret(int slot) {
return completions[slot]->get_return_value();
}
bool get_objects(std::list<std::string>* objects, int num) {
int count = 0;
if (!iterator_valid) {
oi = io_ctx.objects_begin();
iterator_valid = true;
}
librados::ObjectIterator ei = io_ctx.objects_end();
if (oi == ei) {
iterator_valid = false;
return false;
}
objects->clear();
for ( ; oi != ei && count < num; ++oi) {
objects->push_back(oi->first);
++count;
}
return true;
}
public:
RadosBencher(librados::Rados& _r, librados::IoCtx& _i) : completions(NULL), rados(_r), io_ctx(_i), iterator_valid(false) {}
~RadosBencher() { }
};
static int do_lock_cmd(std::vector<const char*> &nargs,
const std::map < std::string, std::string > &opts,
IoCtx *ioctx,
Formatter *formatter)
{
char buf[128];
if (nargs.size() < 3)
usage_exit();
string cmd(nargs[1]);
string oid(nargs[2]);
string lock_tag;
string lock_cookie;
string lock_description;
int lock_duration = 0;
ClsLockType lock_type = LOCK_EXCLUSIVE;
map<string, string>::const_iterator i;
i = opts.find("lock-tag");
if (i != opts.end()) {
lock_tag = i->second;
}
i = opts.find("lock-cookie");
if (i != opts.end()) {
lock_cookie = i->second;
}
i = opts.find("lock-description");
if (i != opts.end()) {
lock_description = i->second;
}
i = opts.find("lock-duration");
if (i != opts.end()) {
lock_duration = strtol(i->second.c_str(), NULL, 10);
}
i = opts.find("lock-type");
if (i != opts.end()) {
const string& type_str = i->second;
if (type_str.compare("exclusive") == 0) {
lock_type = LOCK_EXCLUSIVE;
} else if (type_str.compare("shared") == 0) {
lock_type = LOCK_SHARED;
} else {
cerr << "unknown lock type was specified, aborting" << std::endl;
return -EINVAL;
}
}
if (cmd.compare("list") == 0) {
list<string> locks;
int ret = rados::cls::lock::list_locks(ioctx, oid, &locks);
if (ret < 0) {
cerr << "ERROR: rados_list_locks(): " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
return ret;
}
formatter->open_object_section("object");
formatter->dump_string("objname", oid);
formatter->open_array_section("locks");
list<string>::iterator iter;
for (iter = locks.begin(); iter != locks.end(); ++iter) {
formatter->open_object_section("lock");
formatter->dump_string("name", *iter);
formatter->close_section();
}
formatter->close_section();
formatter->close_section();
formatter->flush(cout);
return 0;
}
if (nargs.size() < 4)
usage_exit();
string lock_name(nargs[3]);
if (cmd.compare("info") == 0) {
map<rados::cls::lock::locker_id_t, rados::cls::lock::locker_info_t> lockers;
ClsLockType type = LOCK_NONE;
string tag;
int ret = rados::cls::lock::get_lock_info(ioctx, oid, lock_name, &lockers, &type, &tag);
if (ret < 0) {
cerr << "ERROR: rados_lock_get_lock_info(): " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
return ret;
}
formatter->open_object_section("lock");
formatter->dump_string("name", lock_name);
formatter->dump_string("type", cls_lock_type_str(type));
formatter->dump_string("tag", tag);
formatter->open_array_section("lockers");
map<rados::cls::lock::locker_id_t, rados::cls::lock::locker_info_t>::iterator iter;
for (iter = lockers.begin(); iter != lockers.end(); ++iter) {
const rados::cls::lock::locker_id_t& id = iter->first;
const rados::cls::lock::locker_info_t& info = iter->second;
formatter->open_object_section("locker");
formatter->dump_stream("name") << id.locker;
formatter->dump_string("cookie", id.cookie);
formatter->dump_string("description", info.description);
formatter->dump_stream("expiration") << info.expiration;
formatter->dump_stream("addr") << info.addr;
formatter->close_section();
}
formatter->close_section();
formatter->close_section();
formatter->flush(cout);
return ret;
} else if (cmd.compare("get") == 0) {
rados::cls::lock::Lock l(lock_name);
l.set_cookie(lock_cookie);
l.set_tag(lock_tag);
l.set_duration(utime_t(lock_duration, 0));
l.set_description(lock_description);
int ret;
switch (lock_type) {
case LOCK_SHARED:
ret = l.lock_shared(ioctx, oid);
break;
default:
ret = l.lock_exclusive(ioctx, oid);
}
if (ret < 0) {
cerr << "ERROR: failed locking: " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
return ret;
}
return ret;
}
if (nargs.size() < 5)
usage_exit();
if (cmd.compare("break") == 0) {
string locker(nargs[4]);
rados::cls::lock::Lock l(lock_name);
l.set_cookie(lock_cookie);
l.set_tag(lock_tag);
entity_name_t name;
if (!name.parse(locker)) {
cerr << "ERROR: failed to parse locker name (" << locker << ")" << std::endl;
return -EINVAL;
}
int ret = l.break_lock(ioctx, oid, name);
if (ret < 0) {
cerr << "ERROR: failed breaking lock: " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
return ret;
}
} else {
usage_exit();
}
return 0;
}
/**********************************************
**********************************************/
static int rados_tool_common(const std::map < std::string, std::string > &opts,
std::vector<const char*> &nargs)
{
int ret;
bool create_pool = false;
const char *pool_name = NULL;
const char *target_pool_name = NULL;
string oloc, target_oloc;
int concurrent_ios = 16;
int op_size = 1 << 22;
bool cleanup = true;
const char *snapname = NULL;
snap_t snapid = CEPH_NOSNAP;
std::map<std::string, std::string>::const_iterator i;
std::string category;
uint64_t min_obj_len = 0;
uint64_t max_obj_len = 0;
uint64_t min_op_len = 0;
uint64_t max_op_len = 0;
uint64_t max_ops = 0;
uint64_t max_backlog = 0;
uint64_t target_throughput = 0;
int64_t read_percent = -1;
uint64_t num_objs = 0;
int run_length = 0;
bool show_time = false;
Formatter *formatter = NULL;
bool pretty_format = false;
Rados rados;
IoCtx io_ctx;
i = opts.find("create");
if (i != opts.end()) {
create_pool = true;
}
i = opts.find("pool");
if (i != opts.end()) {
pool_name = i->second.c_str();
}
i = opts.find("target_pool");
if (i != opts.end()) {
target_pool_name = i->second.c_str();
}
i = opts.find("object_locator");
if (i != opts.end()) {
oloc = i->second;
}
i = opts.find("target_locator");
if (i != opts.end()) {
target_oloc = i->second;
}
i = opts.find("category");
if (i != opts.end()) {
category = i->second;
}
i = opts.find("concurrent-ios");
if (i != opts.end()) {
concurrent_ios = strtol(i->second.c_str(), NULL, 10);
}
i = opts.find("block-size");
if (i != opts.end()) {
op_size = strtol(i->second.c_str(), NULL, 10);
}
i = opts.find("snap");
if (i != opts.end()) {
snapname = i->second.c_str();
}
i = opts.find("snapid");
if (i != opts.end()) {
snapid = strtoll(i->second.c_str(), NULL, 10);
}
i = opts.find("min-object-size");
if (i != opts.end()) {
min_obj_len = strtoll(i->second.c_str(), NULL, 10);
}
i = opts.find("max-object-size");
if (i != opts.end()) {
max_obj_len = strtoll(i->second.c_str(), NULL, 10);
}
i = opts.find("min-op-len");
if (i != opts.end()) {
min_op_len = strtoll(i->second.c_str(), NULL, 10);
}
i = opts.find("max-op-len");
if (i != opts.end()) {
max_op_len = strtoll(i->second.c_str(), NULL, 10);
}
i = opts.find("max-ops");
if (i != opts.end()) {
max_ops = strtoll(i->second.c_str(), NULL, 10);
}
i = opts.find("max-backlog");
if (i != opts.end()) {
max_backlog = strtoll(i->second.c_str(), NULL, 10);
}
i = opts.find("target-throughput");
if (i != opts.end()) {
target_throughput = strtoll(i->second.c_str(), NULL, 10);
}
i = opts.find("read-percent");
if (i != opts.end()) {
read_percent = strtoll(i->second.c_str(), NULL, 10);
}
i = opts.find("num-objects");
if (i != opts.end()) {
num_objs = strtoll(i->second.c_str(), NULL, 10);
}
i = opts.find("run-length");
if (i != opts.end()) {
run_length = strtol(i->second.c_str(), NULL, 10);
}
i = opts.find("show-time");
if (i != opts.end()) {
show_time = true;
}
i = opts.find("no-cleanup");
if (i != opts.end()) {
cleanup = false;
}
i = opts.find("pretty-format");
if (i != opts.end()) {
pretty_format = true;
}
i = opts.find("format");
if (i != opts.end()) {
const char *format = i->second.c_str();
if (strcmp(format, "xml") == 0)
formatter = new XMLFormatter(pretty_format);
else if (strcmp(format, "json") == 0)
formatter = new JSONFormatter(pretty_format);
else {
cerr << "unrecognized format: " << format << std::endl;
return -EINVAL;
}
}
// open rados
ret = rados.init_with_context(g_ceph_context);
if (ret) {
cerr << "couldn't initialize rados! error " << ret << std::endl;
ret = -1;
goto out;
}
ret = rados.connect();
if (ret) {
cerr << "couldn't connect to cluster! error " << ret << std::endl;
ret = -1;
goto out;
}
char buf[80];
if (create_pool && !pool_name) {
cerr << "--create-pool requested but pool_name was not specified!" << std::endl;
usage_exit();
}
if (create_pool) {
ret = rados.pool_create(pool_name, 0, 0);
if (ret < 0) {
cerr << "error creating pool " << pool_name << ": "
<< strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
}
// open io context.
if (pool_name) {
ret = rados.ioctx_create(pool_name, io_ctx);
if (ret < 0) {
cerr << "error opening pool " << pool_name << ": "
<< strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
}
// snapname?
if (snapname) {
ret = io_ctx.snap_lookup(snapname, &snapid);
if (ret < 0) {
cerr << "error looking up snap '" << snapname << "': " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
}
if (oloc.size()) {
io_ctx.locator_set_key(oloc);
}
if (snapid != CEPH_NOSNAP) {
string name;
ret = io_ctx.snap_get_name(snapid, &name);
if (ret < 0) {
cerr << "snapid " << snapid << " doesn't exist in pool "
<< io_ctx.get_pool_name() << std::endl;
goto out;
}
io_ctx.snap_set_read(snapid);
cout << "selected snap " << snapid << " '" << snapname << "'" << std::endl;
}
assert(!nargs.empty());
// list pools?
if (strcmp(nargs[0], "lspools") == 0) {
list<string> vec;
rados.pool_list(vec);
for (list<string>::iterator i = vec.begin(); i != vec.end(); ++i)
cout << *i << std::endl;
}
else if (strcmp(nargs[0], "df") == 0) {
// pools
list<string> vec;
if (!pool_name)
rados.pool_list(vec);
else
vec.push_back(pool_name);
map<string, map<string, pool_stat_t> > stats;
rados.get_pool_stats(vec, category, stats);
if (!formatter) {
printf("%-15s %-15s"
"%12s %12s %12s %12s "
"%12s %12s %12s %12s %12s\n",
"pool name",
"category",
"KB", "objects", "clones", "degraded",
"unfound", "rd", "rd KB", "wr", "wr KB");
} else {
formatter->open_object_section("stats");
formatter->open_array_section("pools");
}
for (map<string, librados::stats_map>::iterator c = stats.begin(); c != stats.end(); ++c) {
const char *pool_name = c->first.c_str();
stats_map& m = c->second;
if (formatter) {
formatter->open_object_section("pool");
int64_t pool_id = rados.pool_lookup(pool_name);
formatter->dump_string("name", pool_name);
if (pool_id >= 0)
formatter->dump_format("id", "%lld", pool_id);
else
cerr << "ERROR: lookup_pg_pool_name for name=" << pool_name << " returned " << pool_id << std::endl;
formatter->open_array_section("categories");
}
for (stats_map::iterator i = m.begin(); i != m.end(); ++i) {
const char *category = (i->first.size() ? i->first.c_str() : "");
pool_stat_t& s = i->second;
if (!formatter) {
if (!*category)
category = "-";
printf("%-15s "
"%-15s "
"%12lld %12lld %12lld %12lld"
"%12lld %12lld %12lld %12lld %12lld\n",
pool_name,
category,
(long long)s.num_kb,
(long long)s.num_objects,
(long long)s.num_object_clones,
(long long)s.num_objects_degraded,
(long long)s.num_objects_unfound,
(long long)s.num_rd, (long long)s.num_rd_kb,
(long long)s.num_wr, (long long)s.num_wr_kb);
} else {
formatter->open_object_section("category");
if (category)
formatter->dump_string("name", category);
formatter->dump_format("size_bytes", "%lld", s.num_bytes);
formatter->dump_format("size_kb", "%lld", s.num_kb);
formatter->dump_format("num_objects", "%lld", s.num_objects);
formatter->dump_format("num_object_clones", "%lld", s.num_object_clones);
formatter->dump_format("num_object_copies", "%lld", s.num_object_copies);
formatter->dump_format("num_objects_missing_on_primary", "%lld", s.num_objects_missing_on_primary);
formatter->dump_format("num_objects_unfound", "%lld", s.num_objects_unfound);
formatter->dump_format("num_objects_degraded", "%lld", s.num_objects_degraded);
formatter->dump_format("read_bytes", "%lld", s.num_rd);
formatter->dump_format("read_kb", "%lld", s.num_rd_kb);
formatter->dump_format("write_bytes", "%lld", s.num_wr);
formatter->dump_format("write_kb", "%lld", s.num_wr_kb);
formatter->flush(cout);
}
if (formatter) {
formatter->close_section();
}
}
if (formatter) {
formatter->close_section();
formatter->close_section();
formatter->flush(cout);
}
}
// total
cluster_stat_t tstats;
rados.cluster_stat(tstats);
if (!formatter) {
printf(" total used %12lld %12lld\n", (long long unsigned)tstats.kb_used,
(long long unsigned)tstats.num_objects);
printf(" total avail %12lld\n", (long long unsigned)tstats.kb_avail);
printf(" total space %12lld\n", (long long unsigned)tstats.kb);
} else {
formatter->close_section();
formatter->dump_format("total_objects", "%lld", (long long unsigned)tstats.num_objects);
formatter->dump_format("total_used", "%lld", (long long unsigned)tstats.kb_used);
formatter->dump_format("total_avail", "%lld", (long long unsigned)tstats.kb_avail);
formatter->dump_format("total_space", "%lld", (long long unsigned)tstats.kb);
formatter->close_section();
formatter->flush(cout);
}
}
else if (strcmp(nargs[0], "ls") == 0) {
if (!pool_name) {
cerr << "pool name was not specified" << std::endl;
ret = -1;
goto out;
}
bool stdout = (nargs.size() < 2) || (strcmp(nargs[1], "-") == 0);
ostream *outstream;
if(stdout)
outstream = &cout;
else
outstream = new ofstream(nargs[1]);
{
try {
librados::ObjectIterator i = io_ctx.objects_begin();
librados::ObjectIterator i_end = io_ctx.objects_end();
for (; i != i_end; ++i) {
if (i->second.size())
*outstream << i->first << "\t" << i->second << std::endl;
else
*outstream << i->first << std::endl;
}
}
catch (const std::runtime_error& e) {
cerr << e.what() << std::endl;
ret = -1;
goto out;
}
}
if (!stdout)
delete outstream;
}
else if (strcmp(nargs[0], "chown") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
uint64_t new_auid = strtol(nargs[1], 0, 10);
ret = io_ctx.set_auid(new_auid);
if (ret < 0) {
cerr << "error changing auid on pool " << io_ctx.get_pool_name() << ':'
<< strerror_r(-ret, buf, sizeof(buf)) << std::endl;
} else cerr << "changed auid on pool " << io_ctx.get_pool_name()
<< " to " << new_auid << std::endl;
}
else if (strcmp(nargs[0], "mapext") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
string oid(nargs[1]);
std::map<uint64_t,uint64_t> m;
ret = io_ctx.mapext(oid, 0, -1, m);
if (ret < 0) {
cerr << "mapext error on " << pool_name << "/" << oid << ": " << cpp_strerror(ret) << std::endl;
goto out;
}
std::map<uint64_t,uint64_t>::iterator iter;
for (iter = m.begin(); iter != m.end(); ++iter) {
cout << hex << iter->first << "\t" << iter->second << dec << std::endl;
}
}
else if (strcmp(nargs[0], "stat") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
string oid(nargs[1]);
uint64_t size;
time_t mtime;
ret = io_ctx.stat(oid, &size, &mtime);
if (ret < 0) {
cerr << " error stat-ing " << pool_name << "/" << oid << ": "
<< strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
} else {
cout << pool_name << "/" << oid
<< " mtime " << mtime << ", size " << size << std::endl;
}
}
else if (strcmp(nargs[0], "get") == 0) {
if (!pool_name || nargs.size() < 3)
usage_exit();
ret = do_get(io_ctx, nargs[1], nargs[2], op_size);
if (ret < 0) {
cerr << "error getting " << pool_name << "/" << nargs[1] << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
}
else if (strcmp(nargs[0], "put") == 0) {
if (!pool_name || nargs.size() < 3)
usage_exit();
ret = do_put(io_ctx, nargs[1], nargs[2], op_size);
if (ret < 0) {
cerr << "error putting " << pool_name << "/" << nargs[1] << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
}
else if (strcmp(nargs[0], "truncate") == 0) {
if (!pool_name || nargs.size() < 3)
usage_exit();
string oid(nargs[1]);
long size = atol(nargs[2]);
if (size < 0) {
cerr << "error, cannot truncate to negative value" << std::endl;
usage_exit();
}
ret = io_ctx.trunc(oid, size);
if (ret < 0) {
cerr << "error truncating oid "
<< oid << " to " << size << ": "
<< strerror_r(-ret, buf, sizeof(buf)) << std::endl;
} else {
ret = 0;
}
}
else if (strcmp(nargs[0], "setxattr") == 0) {
if (!pool_name || nargs.size() < 4)
usage_exit();
string oid(nargs[1]);
string attr_name(nargs[2]);
string attr_val(nargs[3]);
bufferlist bl;
bl.append(attr_val.c_str(), attr_val.length());
ret = io_ctx.setxattr(oid, attr_name.c_str(), bl);
if (ret < 0) {
cerr << "error setting xattr " << pool_name << "/" << oid << "/" << attr_name << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
else
ret = 0;
}
else if (strcmp(nargs[0], "getxattr") == 0) {
if (!pool_name || nargs.size() < 3)
usage_exit();
string oid(nargs[1]);
string attr_name(nargs[2]);
bufferlist bl;
ret = io_ctx.getxattr(oid, attr_name.c_str(), bl);
if (ret < 0) {
cerr << "error getting xattr " << pool_name << "/" << oid << "/" << attr_name << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
else
ret = 0;
string s(bl.c_str(), bl.length());
cout << s << std::endl;
} else if (strcmp(nargs[0], "rmxattr") == 0) {
if (!pool_name || nargs.size() < 3)
usage_exit();
string oid(nargs[1]);
string attr_name(nargs[2]);
ret = io_ctx.rmxattr(oid, attr_name.c_str());
if (ret < 0) {
cerr << "error removing xattr " << pool_name << "/" << oid << "/" << attr_name << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
} else if (strcmp(nargs[0], "listxattr") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
string oid(nargs[1]);
map<std::string, bufferlist> attrset;
bufferlist bl;
ret = io_ctx.getxattrs(oid, attrset);
if (ret < 0) {
cerr << "error getting xattr set " << pool_name << "/" << oid << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
for (map<std::string, bufferlist>::iterator iter = attrset.begin();
iter != attrset.end(); ++iter) {
cout << iter->first << std::endl;
}
} else if (strcmp(nargs[0], "getomapheader") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
string oid(nargs[1]);
bufferlist header;
ret = io_ctx.omap_get_header(oid, &header);
if (ret < 0) {
cerr << "error getting omap header " << pool_name << "/" << oid
<< ": " << cpp_strerror(ret) << std::endl;
goto out;
} else {
cout << "header (" << header.length() << " bytes) :\n";
header.hexdump(cout);
cout << std::endl;
ret = 0;
}
} else if (strcmp(nargs[0], "setomapheader") == 0) {
if (!pool_name || nargs.size() < 3)
usage_exit();
string oid(nargs[1]);
string val(nargs[2]);
bufferlist bl;
bl.append(val);
ret = io_ctx.omap_set_header(oid, bl);
if (ret < 0) {
cerr << "error setting omap value " << pool_name << "/" << oid
<< ": " << cpp_strerror(ret) << std::endl;
goto out;
} else {
ret = 0;
}
} else if (strcmp(nargs[0], "setomapval") == 0) {
if (!pool_name || nargs.size() < 4)
usage_exit();
string oid(nargs[1]);
string key(nargs[2]);
string val(nargs[3]);
map<string, bufferlist> values;
bufferlist bl;
bl.append(val);
values[key] = bl;
ret = io_ctx.omap_set(oid, values);
if (ret < 0) {
cerr << "error setting omap value " << pool_name << "/" << oid << "/"
<< key << ": " << cpp_strerror(ret) << std::endl;
goto out;
} else {
ret = 0;
}
} else if (strcmp(nargs[0], "getomapval") == 0) {
if (!pool_name || nargs.size() < 3)
usage_exit();
string oid(nargs[1]);
string key(nargs[2]);
set<string> keys;
keys.insert(key);
map<string, bufferlist> values;
ret = io_ctx.omap_get_vals_by_keys(oid, keys, &values);
if (ret < 0) {
cerr << "error getting omap value " << pool_name << "/" << oid << "/"
<< key << ": " << cpp_strerror(ret) << std::endl;
goto out;
} else {
ret = 0;
}
if (values.size() && values.begin()->first == key) {
cout << " (length " << values.begin()->second.length() << ") : ";
values.begin()->second.hexdump(cout);
cout << std::endl;
} else {
cout << "No such key: " << pool_name << "/" << oid << "/" << key
<< std::endl;
ret = -1;
goto out;
}
} else if (strcmp(nargs[0], "rmomapkey") == 0) {
if (!pool_name || nargs.size() < 3)
usage_exit();
string oid(nargs[1]);
string key(nargs[2]);
set<string> keys;
keys.insert(key);
ret = io_ctx.omap_rm_keys(oid, keys);
if (ret < 0) {
cerr << "error removing omap key " << pool_name << "/" << oid << "/"
<< key << ": " << cpp_strerror(ret) << std::endl;
goto out;
} else {
ret = 0;
}
} else if (strcmp(nargs[0], "listomapvals") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
string oid(nargs[1]);
string last_read = "";
int MAX_READ = 512;
do {
map<string, bufferlist> values;
ret = io_ctx.omap_get_vals(oid, last_read, MAX_READ, &values);
if (ret < 0) {
cerr << "error getting omap keys " << pool_name << "/" << oid << ": "
<< cpp_strerror(ret) << std::endl;
return 1;
}
for (map<string, bufferlist>::const_iterator it = values.begin();
it != values.end(); ++it) {
// dump key in hex if it contains nonprintable characters
if (std::count_if(it->first.begin(), it->first.end(),
(int (*)(int))isprint) < (int)it->first.length()) {
cout << "key: (" << it->first.length() << " bytes):\n";
bufferlist keybl;
keybl.append(it->first);
keybl.hexdump(cout);
} else {
cout << it->first;
}
cout << std::endl;
cout << "value: (" << it->second.length() << " bytes) :\n";
it->second.hexdump(cout);
cout << std::endl;
}
} while (ret == MAX_READ);
ret = 0;
}
else if (strcmp(nargs[0], "cp") == 0) {
if (!pool_name)
usage_exit();
if (nargs.size() < 2 || nargs.size() > 3)
usage_exit();
const char *target = target_pool_name;
if (!target)
target = pool_name;
const char *target_obj;
if (nargs.size() < 3) {
if (strcmp(target, pool_name) == 0) {
cerr << "cannot copy object into itself" << std::endl;
ret = -1;
goto out;
}
target_obj = nargs[1];
} else {
target_obj = nargs[2];
}
// open io context.
IoCtx target_ctx;
ret = rados.ioctx_create(target, target_ctx);
if (ret < 0) {
cerr << "error opening target pool " << target << ": "
<< strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
if (target_oloc.size()) {
target_ctx.locator_set_key(target_oloc);
}
ret = do_copy(io_ctx, nargs[1], target_ctx, target_obj);
if (ret < 0) {
cerr << "error copying " << pool_name << "/" << nargs[1] << " => " << target << "/" << target_obj << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
}
else if (strcmp(nargs[0], "clonedata") == 0) {
if (!pool_name)
usage_exit();
if (nargs.size() < 2 || nargs.size() > 3)
usage_exit();
const char *target = target_pool_name;
if (!target)
target = pool_name;
const char *target_obj;
if (nargs.size() < 3) {
if (strcmp(target, pool_name) == 0) {
cerr << "cannot copy object into itself" << std::endl;
ret = -1;
goto out;
}
target_obj = nargs[1];
} else {
target_obj = nargs[2];
}
// open io context.
IoCtx target_ctx;
ret = rados.ioctx_create(target, target_ctx);
if (ret < 0) {
cerr << "error opening target pool " << target << ": "
<< strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
if (oloc.size()) {
target_ctx.locator_set_key(oloc);
} else {
cerr << "must specify locator for clone" << std::endl;
ret = -1;
goto out;
}
ret = do_clone_data(io_ctx, nargs[1], target_ctx, target_obj);
if (ret < 0) {
cerr << "error cloning " << pool_name << "/" << nargs[1] << " => " << target << "/" << target_obj << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
} else if (strcmp(nargs[0], "rm") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
vector<const char *>::iterator iter = nargs.begin();
++iter;
for (; iter != nargs.end(); ++iter) {
const string & oid = *iter;
ret = io_ctx.remove(oid);
if (ret < 0) {
cerr << "error removing " << pool_name << "/" << oid << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
}
}
else if (strcmp(nargs[0], "create") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
string oid(nargs[1]);
if (nargs.size() > 2) {
string category(nargs[2]);
ret = io_ctx.create(oid, true, category);
} else {
ret = io_ctx.create(oid, true);
}
if (ret < 0) {
cerr << "error creating " << pool_name << "/" << oid << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
}
else if (strcmp(nargs[0], "tmap") == 0) {
if (nargs.size() < 3)
usage_exit();
if (strcmp(nargs[1], "dump") == 0) {
bufferlist outdata;
string oid(nargs[2]);
ret = io_ctx.read(oid, outdata, 0, 0);
if (ret < 0) {
cerr << "error reading " << pool_name << "/" << oid << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
bufferlist::iterator p = outdata.begin();
bufferlist header;
map<string, bufferlist> kv;
::decode(header, p);
::decode(kv, p);
cout << "header (" << header.length() << " bytes):\n";
header.hexdump(cout);
cout << "\n";
cout << kv.size() << " keys\n";
for (map<string,bufferlist>::iterator q = kv.begin(); q != kv.end(); ++q) {
cout << "key '" << q->first << "' (" << q->second.length() << " bytes):\n";
q->second.hexdump(cout);
cout << "\n";
}
}
else if (strcmp(nargs[1], "set") == 0 ||
strcmp(nargs[1], "create") == 0) {
if (nargs.size() < 5)
usage_exit();
string oid(nargs[2]);
string k(nargs[3]);
string v(nargs[4]);
bufferlist bl;
char c = (strcmp(nargs[1], "set") == 0) ? CEPH_OSD_TMAP_SET : CEPH_OSD_TMAP_CREATE;
::encode(c, bl);
::encode(k, bl);
::encode(v, bl);
ret = io_ctx.tmap_update(oid, bl);
}
}
else if (strcmp(nargs[0], "mkpool") == 0) {
int auid = 0;
__u8 crush_rule = 0;
if (nargs.size() < 2)
usage_exit();
if (nargs.size() > 2) {
auid = strtol(nargs[2], 0, 10);
cerr << "setting auid:" << auid << std::endl;
if (nargs.size() > 3) {
crush_rule = (__u8)strtol(nargs[3], 0, 10);
cerr << "using crush rule " << (int)crush_rule << std::endl;
}
}
ret = rados.pool_create(nargs[1], auid, crush_rule);
if (ret < 0) {
cerr << "error creating pool " << nargs[1] << ": "
<< strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
cout << "successfully created pool " << nargs[1] << std::endl;
}
else if (strcmp(nargs[0], "cppool") == 0) {
if (nargs.size() != 3)
usage_exit();
const char *src_pool = nargs[1];
const char *target_pool = nargs[2];
if (strcmp(src_pool, target_pool) == 0) {
cerr << "cannot copy pool into itself" << std::endl;
ret = -1;
goto out;
}
ret = do_copy_pool(rados, src_pool, target_pool);
if (ret < 0) {
cerr << "error copying pool " << src_pool << " => " << target_pool << ": "
<< strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
cout << "successfully copied pool " << nargs[1] << std::endl;
}
else if (strcmp(nargs[0], "rmpool") == 0) {
if (nargs.size() < 2)
usage_exit();
if (nargs.size() < 4 ||
strcmp(nargs[1], nargs[2]) != 0 ||
strcmp(nargs[3], "--yes-i-really-really-mean-it") != 0) {
cerr << "WARNING:\n"
<< " This will PERMANENTLY DESTROY an entire pool of objects with no way back.\n"
<< " To confirm, pass the pool to remove twice, followed by\n"
<< " --yes-i-really-really-mean-it" << std::endl;
ret = -1;
goto out;
}
ret = rados.pool_delete(nargs[1]);
if (ret >= 0) {
cout << "successfully deleted pool " << nargs[1] << std::endl;
} else { //error
cerr << "pool " << nargs[1] << " does not exist" << std::endl;
}
}
else if (strcmp(nargs[0], "lssnap") == 0) {
if (!pool_name || nargs.size() != 1)
usage_exit();
vector<snap_t> snaps;
io_ctx.snap_list(&snaps);
for (vector<snap_t>::iterator i = snaps.begin();
i != snaps.end();
++i) {
string s;
time_t t;
if (io_ctx.snap_get_name(*i, &s) < 0)
continue;
if (io_ctx.snap_get_stamp(*i, &t) < 0)
continue;
struct tm bdt;
localtime_r(&t, &bdt);
cout << *i << "\t" << s << "\t";
cout.setf(std::ios::right);
cout.fill('0');
cout << std::setw(4) << (bdt.tm_year+1900)
<< '.' << std::setw(2) << (bdt.tm_mon+1)
<< '.' << std::setw(2) << bdt.tm_mday
<< ' '
<< std::setw(2) << bdt.tm_hour
<< ':' << std::setw(2) << bdt.tm_min
<< ':' << std::setw(2) << bdt.tm_sec
<< std::endl;
cout.unsetf(std::ios::right);
}
cout << snaps.size() << " snaps" << std::endl;
}
else if (strcmp(nargs[0], "mksnap") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
ret = io_ctx.snap_create(nargs[1]);
if (ret < 0) {
cerr << "error creating pool " << pool_name << " snapshot " << nargs[1]
<< ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
cout << "created pool " << pool_name << " snap " << nargs[1] << std::endl;
}
else if (strcmp(nargs[0], "rmsnap") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
ret = io_ctx.snap_remove(nargs[1]);
if (ret < 0) {
cerr << "error removing pool " << pool_name << " snapshot " << nargs[1]
<< ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
cout << "removed pool " << pool_name << " snap " << nargs[1] << std::endl;
}
else if (strcmp(nargs[0], "rollback") == 0) {
if (!pool_name || nargs.size() < 3)
usage_exit();
ret = io_ctx.rollback(nargs[1], nargs[2]);
if (ret < 0) {
cerr << "error rolling back pool " << pool_name << " to snapshot " << nargs[1]
<< strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
cout << "rolled back pool " << pool_name
<< " to snapshot " << nargs[2] << std::endl;
}
else if (strcmp(nargs[0], "bench") == 0) {
if (!pool_name || nargs.size() < 3)
usage_exit();
int seconds = atoi(nargs[1]);
int operation = 0;
if (strcmp(nargs[2], "write") == 0)
operation = OP_WRITE;
else if (strcmp(nargs[2], "seq") == 0)
operation = OP_SEQ_READ;
else if (strcmp(nargs[2], "rand") == 0)
operation = OP_RAND_READ;
else
usage_exit();
RadosBencher bencher(rados, io_ctx);
bencher.set_show_time(show_time);
ret = bencher.aio_bench(operation, seconds, num_objs,
concurrent_ios, op_size, cleanup);
if (ret != 0)
cerr << "error during benchmark: " << ret << std::endl;
}
else if (strcmp(nargs[0], "cleanup") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
const char *prefix = nargs[1];
RadosBencher bencher(rados, io_ctx);
ret = bencher.clean_up(prefix, concurrent_ios);
if (ret != 0)
cerr << "error during cleanup: " << ret << std::endl;
}
else if (strcmp(nargs[0], "watch") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
string oid(nargs[1]);
RadosWatchCtx ctx(oid.c_str());
uint64_t cookie;
ret = io_ctx.watch(oid, 0, &cookie, &ctx);
if (ret != 0)
cerr << "error calling watch: " << ret << std::endl;
else {
cout << "press enter to exit..." << std::endl;
getchar();
}
}
else if (strcmp(nargs[0], "notify") == 0) {
if (!pool_name || nargs.size() < 3)
usage_exit();
string oid(nargs[1]);
string msg(nargs[2]);
bufferlist bl;
::encode(msg, bl);
ret = io_ctx.notify(oid, 0, bl);
if (ret != 0)
cerr << "error calling notify: " << ret << std::endl;
} else if (strcmp(nargs[0], "load-gen") == 0) {
if (!pool_name) {
cerr << "error: must specify pool" << std::endl;
usage_exit();
}
LoadGen lg(&rados);
if (min_obj_len)
lg.min_obj_len = min_obj_len;
if (max_obj_len)
lg.max_obj_len = max_obj_len;
if (min_op_len)
lg.min_op_len = min_op_len;
if (max_op_len)
lg.max_op_len = max_op_len;
if (max_ops)
lg.max_ops = max_ops;
if (max_backlog)
lg.max_backlog = max_backlog;
if (target_throughput)
lg.target_throughput = target_throughput << 20;
if (read_percent >= 0)
lg.read_percent = read_percent;
if (num_objs)
lg.num_objs = num_objs;
if (run_length)
lg.run_length = run_length;
cout << "run length " << run_length << " seconds" << std::endl;
cout << "preparing " << lg.num_objs << " objects" << std::endl;
ret = lg.bootstrap(pool_name);
if (ret < 0) {
cerr << "load-gen bootstrap failed" << std::endl;
exit(1);
}
cout << "load-gen will run " << lg.run_length << " seconds" << std::endl;
lg.run();
lg.cleanup();
} else if (strcmp(nargs[0], "listomapkeys") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
librados::ObjectReadOperation read;
set<string> out_keys;
read.omap_get_keys("", LONG_MAX, &out_keys, &ret);
io_ctx.operate(nargs[1], &read, NULL);
if (ret < 0) {
cerr << "error getting omap key set " << pool_name << "/"
<< nargs[1] << ": " << cpp_strerror(ret) << std::endl;
goto out;
}
for (set<string>::iterator iter = out_keys.begin();
iter != out_keys.end(); ++iter) {
cout << *iter << std::endl;
}
} else if (strcmp(nargs[0], "lock") == 0) {
if (!pool_name)
usage_exit();
if (!formatter) {
formatter = new JSONFormatter(pretty_format);
}
ret = do_lock_cmd(nargs, opts, &io_ctx, formatter);
} else if (strcmp(nargs[0], "listwatchers") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
string oid(nargs[1]);
std::list<obj_watch_t> lw;
ret = io_ctx.list_watchers(oid, &lw);
if (ret < 0) {
cerr << "error listing watchers " << pool_name << "/" << oid << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
else
ret = 0;
for (std::list<obj_watch_t>::iterator i = lw.begin(); i != lw.end(); ++i) {
cout << "watcher=" << i->addr << " client." << i->watcher_id << " cookie=" << i->cookie << std::endl;
}
} else if (strcmp(nargs[0], "listsnaps") == 0) {
if (!pool_name || nargs.size() < 2)
usage_exit();
string oid(nargs[1]);
snap_set_t ls;
io_ctx.snap_set_read(LIBRADOS_SNAP_DIR);
ret = io_ctx.list_snaps(oid, &ls);
if (ret < 0) {
cerr << "error listing snap shots " << pool_name << "/" << oid << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
goto out;
}
else
ret = 0;
map<snap_t,string> snamemap;
if (formatter || pretty_format) {
vector<snap_t> snaps;
io_ctx.snap_list(&snaps);
for (vector<snap_t>::iterator i = snaps.begin();
i != snaps.end(); ++i) {
string s;
if (io_ctx.snap_get_name(*i, &s) < 0)
continue;
snamemap.insert(pair<snap_t,string>(*i, s));
}
}
if (formatter) {
formatter->open_object_section("object");
formatter->dump_string("name", oid);
formatter->open_array_section("clones");
} else {
cout << oid << ":" << std::endl;
cout << "cloneid snaps size overlap" << std::endl;
}
for (std::vector<clone_info_t>::iterator ci = ls.clones.begin();
ci != ls.clones.end(); ++ci) {
if (formatter) formatter->open_object_section("clone");
if (ci->cloneid == librados::SNAP_HEAD) {
if (formatter)
formatter->dump_string("id", "head");
else
cout << "head";
} else {
if (formatter)
formatter->dump_unsigned("id", ci->cloneid);
else
cout << ci->cloneid;
}
if (formatter)
formatter->open_array_section("snapshots");
else
cout << "\t";
if (!formatter && ci->snaps.empty()) {
cout << "-";
}
for (std::vector<snap_t>::const_iterator snapindex = ci->snaps.begin();
snapindex != ci->snaps.end(); ++snapindex) {
map<snap_t,string>::iterator si;
if (formatter || pretty_format) si = snamemap.find(*snapindex);
if (formatter) {
formatter->open_object_section("snapshot");
formatter->dump_unsigned("id", *snapindex);
if (si != snamemap.end())
formatter->dump_string("name", si->second);
formatter->close_section(); //snapshot
} else {
if (snapindex != ci->snaps.begin()) cout << ",";
if (!pretty_format || (si == snamemap.end()))
cout << *snapindex;
else
cout << si->second << "(" << *snapindex << ")";
}
}
if (formatter) {
formatter->close_section(); //Snapshots
formatter->dump_unsigned("size", ci->size);
} else {
cout << "\t" << ci->size;
}
if (ci->cloneid != librados::SNAP_HEAD) {
if (formatter)
formatter->open_array_section("overlaps");
else
cout << "\t[";
for (std::vector< std::pair<uint64_t,uint64_t> >::iterator ovi = ci->overlap.begin();
ovi != ci->overlap.end(); ++ovi) {
if (formatter) {
formatter->open_object_section("section");
formatter->dump_unsigned("start", ovi->first);
formatter->dump_unsigned("length", ovi->second);
formatter->close_section(); //section
} else {
if (ovi != ci->overlap.begin()) cout << ",";
cout << ovi->first << "~" << ovi->second;
}
}
if (formatter)
formatter->close_section(); //overlaps
else
cout << "]" << std::endl;
}
if (formatter) formatter->close_section(); //clone
}
if (formatter) {
formatter->close_section(); //clones
formatter->close_section(); //object
formatter->flush(cout);
} else {
cout << std::endl;
}
} else {
cerr << "unrecognized command " << nargs[0] << std::endl;
usage_exit();
}
if (ret < 0)
cerr << "error " << (-ret) << ": " << cpp_strerror(ret) << std::endl;
out:
delete formatter;
return (ret < 0) ? 1 : 0;
}
int main(int argc, const char **argv)
{
vector<const char*> args;
argv_to_vec(argc, argv, args);
env_to_vec(args);
global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0);
common_init_finish(g_ceph_context);
std::map < std::string, std::string > opts;
std::vector<const char*>::iterator i;
std::string val;
for (i = args.begin(); i != args.end(); ) {
if (ceph_argparse_double_dash(args, i)) {
break;
} else if (ceph_argparse_flag(args, i, "-h", "--help", (char*)NULL)) {
usage(cout);
exit(0);
} else if (ceph_argparse_flag(args, i, "-f", "--force", (char*)NULL)) {
opts["force"] = "true";
} else if (ceph_argparse_flag(args, i, "-d", "--delete-after", (char*)NULL)) {
opts["delete-after"] = "true";
} else if (ceph_argparse_flag(args, i, "-C", "--create", "--create-pool",
(char*)NULL)) {
opts["create"] = "true";
} else if (ceph_argparse_flag(args, i, "--pretty-format", (char*)NULL)) {
opts["pretty-format"] = "true";
} else if (ceph_argparse_flag(args, i, "--show-time", (char*)NULL)) {
opts["show-time"] = "true";
} else if (ceph_argparse_flag(args, i, "--no-cleanup", (char*)NULL)) {
opts["no-cleanup"] = "true";
} else if (ceph_argparse_witharg(args, i, &val, "-p", "--pool", (char*)NULL)) {
opts["pool"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--target-pool", (char*)NULL)) {
opts["target_pool"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--object-locator" , (char *)NULL)) {
opts["object_locator"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--target-locator" , (char *)NULL)) {
opts["target_locator"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--category", (char*)NULL)) {
opts["category"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "-t", "--concurrent-ios", (char*)NULL)) {
opts["concurrent-ios"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--block-size", (char*)NULL)) {
opts["block-size"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "-b", (char*)NULL)) {
opts["block-size"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "-s", "--snap", (char*)NULL)) {
opts["snap"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "-S", "--snapid", (char*)NULL)) {
opts["snapid"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--min-object-size", (char*)NULL)) {
opts["min-object-size"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--max-object-size", (char*)NULL)) {
opts["max-object-size"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--min-op-len", (char*)NULL)) {
opts["min-op-len"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--max-op-len", (char*)NULL)) {
opts["max-op-len"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--max-ops", (char*)NULL)) {
opts["max-ops"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--max-backlog", (char*)NULL)) {
opts["max-backlog"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--target-throughput", (char*)NULL)) {
opts["target-throughput"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--read-percent", (char*)NULL)) {
opts["read-percent"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--num-objects", (char*)NULL)) {
opts["num-objects"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--run-length", (char*)NULL)) {
opts["run-length"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--workers", (char*)NULL)) {
opts["workers"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--format", (char*)NULL)) {
opts["format"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--lock-tag", (char*)NULL)) {
opts["lock-tag"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--lock-cookie", (char*)NULL)) {
opts["lock-cookie"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--lock-description", (char*)NULL)) {
opts["lock-description"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--lock-duration", (char*)NULL)) {
opts["lock-duration"] = val;
} else if (ceph_argparse_witharg(args, i, &val, "--lock-type", (char*)NULL)) {
opts["lock-type"] = val;
} else {
if (val[0] == '-')
usage_exit();
++i;
}
}
if (args.empty()) {
cerr << "rados: you must give an action. Try --help" << std::endl;
return 1;
}
if ((strcmp(args[0], "import") == 0) || (strcmp(args[0], "export") == 0))
return rados_tool_sync(opts, args);
else
return rados_tool_common(opts, args);
}