mirror of
https://github.com/ceph/ceph
synced 2025-02-23 02:57:21 +00:00
473 lines
14 KiB
C++
473 lines
14 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/librados.hpp"
|
|
using namespace librados;
|
|
|
|
#include "osdc/rados_bencher.h"
|
|
|
|
#include "config.h"
|
|
#include "common/common_init.h"
|
|
#include "common/Cond.h"
|
|
#include <iostream>
|
|
#include <fstream>
|
|
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <sstream>
|
|
|
|
|
|
void usage()
|
|
{
|
|
cerr << "usage: radostool [options] [commands]" << std::endl;
|
|
/* cerr << "If no commands are specified, enter interactive mode.\n";
|
|
cerr << "Commands:" << std::endl;
|
|
cerr << " stop -- cleanly shut down file system" << std::endl
|
|
<< " (osd|pg|mds) stat -- get monitor subsystem status" << std::endl
|
|
<< " ..." << std::endl;
|
|
*/
|
|
cerr << "Commands:\n";
|
|
cerr << " lspools -- list pools\n";
|
|
cerr << " df -- show per-pool and total usage\n\n";
|
|
|
|
cerr << "Pool commands:\n";
|
|
cerr << " get objname [outfile] -- fetch object\n";
|
|
cerr << " put objname [infile] -- write object\n";
|
|
cerr << " rm objname -- remove object\n";
|
|
cerr << " ls -- list objects in pool\n\n";
|
|
cerr << " chown 123 -- change the pool owner to auid 123\n";
|
|
|
|
cerr << " mkpool foo [123[ 4]] -- create pool 'foo'\n"
|
|
<< " [with auid 123[and using crush rule 4]]\n";
|
|
cerr << " rmpool foo -- remove pool 'foo'\n";
|
|
cerr << " mkpool foo -- create the pool 'foo'\n";
|
|
cerr << " lssnap -- list snaps\n";
|
|
cerr << " mksnap foo -- create snap 'foo'\n";
|
|
cerr << " rmsnap foo -- remove snap 'foo'\n";
|
|
cerr << " rollback foo bar -- roll back object foo to snap 'bar'\n\n";
|
|
|
|
cerr << " bench <seconds> write|seq|rand [-t concurrent_operations] [-b op_size]\n";
|
|
cerr << " default is 16 concurrent IOs and 4 MB op size\n\n";
|
|
|
|
cerr << "Options:\n";
|
|
cerr << " -p pool\n";
|
|
cerr << " --pool=pool\n";
|
|
cerr << " select given pool by name\n";
|
|
cerr << " -s name\n";
|
|
cerr << " --snap name\n";
|
|
cerr << " select given snap name for (read) IO\n";
|
|
cerr << " -i infile\n";
|
|
cerr << " -o outfile\n";
|
|
cerr << " specify input or output file (for certain commands)\n";
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/**********************************************
|
|
|
|
**********************************************/
|
|
|
|
int main(int argc, const char **argv)
|
|
{
|
|
DEFINE_CONF_VARS(usage);
|
|
vector<const char*> args;
|
|
argv_to_vec(argc, argv, args);
|
|
env_to_vec(args);
|
|
|
|
common_set_defaults(false);
|
|
common_init(args, "rados", true);
|
|
|
|
vector<const char*> nargs;
|
|
bufferlist indata, outdata;
|
|
|
|
const char *pool = 0;
|
|
|
|
int concurrent_ios = 16;
|
|
int op_size = 1 << 22;
|
|
|
|
const char *snapname = 0;
|
|
snap_t snapid = CEPH_NOSNAP;
|
|
|
|
FOR_EACH_ARG(args) {
|
|
if (CONF_ARG_EQ("pool", 'p')) {
|
|
CONF_SAFE_SET_ARG_VAL(&pool, OPT_STR);
|
|
} else if (CONF_ARG_EQ("snapid", 'S')) {
|
|
CONF_SAFE_SET_ARG_VAL(&snapid, OPT_LONGLONG);
|
|
} else if (CONF_ARG_EQ("snap", 's')) {
|
|
CONF_SAFE_SET_ARG_VAL(&snapname, OPT_STR);
|
|
} else if (CONF_ARG_EQ("help", 'h')) {
|
|
usage();
|
|
} else if (CONF_ARG_EQ("concurrent-ios", 't')) {
|
|
CONF_SAFE_SET_ARG_VAL(&concurrent_ios, OPT_INT);
|
|
} else if (CONF_ARG_EQ("block-size", 'b')) {
|
|
CONF_SAFE_SET_ARG_VAL(&op_size, OPT_INT);
|
|
} else if (args[i][0] == '-' && nargs.empty()) {
|
|
cerr << "unrecognized option " << args[i] << std::endl;
|
|
usage();
|
|
} else
|
|
nargs.push_back(args[i]);
|
|
}
|
|
|
|
if (nargs.empty())
|
|
usage();
|
|
|
|
// open rados
|
|
Rados rados;
|
|
if (rados.initialize(0, NULL) < 0) {
|
|
cerr << "couldn't initialize rados!" << std::endl;
|
|
exit(1);
|
|
}
|
|
|
|
int ret = 0;
|
|
char buf[80];
|
|
|
|
// open pool?
|
|
pool_t p;
|
|
if (pool) {
|
|
ret = rados.open_pool(pool, &p);
|
|
if (ret < 0) {
|
|
cerr << "error opening pool " << pool << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
|
|
goto no_pool_out;
|
|
}
|
|
}
|
|
|
|
// snapname?
|
|
if (snapname) {
|
|
ret = rados.snap_lookup(p, snapname, &snapid);
|
|
if (ret < 0) {
|
|
cerr << "error looking up snap '" << snapname << "': " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
|
|
goto out;
|
|
}
|
|
}
|
|
if (snapid != CEPH_NOSNAP) {
|
|
string name;
|
|
ret = rados.snap_get_name(p, snapid, &name);
|
|
if (ret < 0) {
|
|
cerr << "snapid " << snapid << " doesn't exist in pool " << pool << std::endl;
|
|
goto out;
|
|
}
|
|
rados.set_snap(p, snapid);
|
|
cout << "selected snap " << snapid << " '" << snapname << "'" << std::endl;
|
|
}
|
|
|
|
// list pools?
|
|
if (strcmp(nargs[0], "lspools") == 0) {
|
|
list<string> vec;
|
|
rados.list_pools(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;
|
|
rados.list_pools(vec);
|
|
|
|
map<string,pool_stat_t> stats;
|
|
rados.get_pool_stats(vec, stats);
|
|
|
|
printf("%-15s %12s %12s %12s %12s %12s %12s %12s %12s\n",
|
|
"pool name", "KB", "objects", "clones", "degraded", "rd", "rd KB", "wr", "wr KB");
|
|
for (map<string,pool_stat_t>::iterator i = stats.begin(); i != stats.end(); ++i) {
|
|
printf("%-15s %12lld %12lld %12lld %12lld %12lld %12lld %12lld %12lld\n",
|
|
i->first.c_str(),
|
|
(long long)i->second.num_kb,
|
|
(long long)i->second.num_objects,
|
|
(long long)i->second.num_object_clones,
|
|
(long long)i->second.num_objects_degraded,
|
|
(long long)i->second.num_rd, (long long)i->second.num_rd_kb,
|
|
(long long)i->second.num_wr, (long long)i->second.num_wr_kb);
|
|
}
|
|
|
|
// total
|
|
statfs_t tstats;
|
|
rados.get_fs_stats(tstats);
|
|
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 if (strcmp(nargs[0], "ls") == 0) {
|
|
if (!pool) {
|
|
cerr << "pool name was not specified" << std::endl;
|
|
goto out;
|
|
}
|
|
|
|
bool stdout = (nargs.size() < 2) || (strcmp(nargs[1], "-") == 0);
|
|
ostream *outstream;
|
|
if(stdout)
|
|
outstream = &cout;
|
|
else
|
|
outstream = new ofstream(nargs[1]);
|
|
|
|
Rados::ListCtx ctx;
|
|
rados.list_objects_open(p, &ctx);
|
|
while (1) {
|
|
list<string> vec;
|
|
ret = rados.list_objects_more(ctx, 1 << 10, vec);
|
|
if (ret < 0) {
|
|
cerr << "got error: " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
|
|
goto out;
|
|
}
|
|
if (vec.empty())
|
|
break;
|
|
|
|
for (list<string>::iterator iter = vec.begin(); iter != vec.end(); ++iter)
|
|
*outstream << *iter << std::endl;
|
|
}
|
|
rados.list_objects_close(ctx);
|
|
if (!stdout)
|
|
delete outstream;
|
|
}
|
|
else if (strcmp(nargs[0], "chown") == 0) {
|
|
if (!pool || nargs.size() < 2)
|
|
usage();
|
|
|
|
uint64_t new_auid = strtol(nargs[1], 0, 10);
|
|
ret = rados.change_pool_auid(p, new_auid);
|
|
if (ret < 0) {
|
|
cerr << "error changing auid on pool " << pool << ':'
|
|
<< strerror_r(-ret, buf, sizeof(buf)) << std::endl;
|
|
} else cerr << "changed auid on pool " << pool
|
|
<< " to " << new_auid << std::endl;
|
|
}
|
|
else if (strcmp(nargs[0], "get") == 0) {
|
|
if (!pool || nargs.size() < 3)
|
|
usage();
|
|
string oid(nargs[1]);
|
|
ret = rados.read(p, oid, 0, outdata, 0);
|
|
if (ret < 0) {
|
|
cerr << "error reading " << pool << "/" << oid << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
|
|
goto out;
|
|
}
|
|
|
|
if (strcmp(nargs[2], "-") == 0) {
|
|
::write(1, outdata.c_str(), outdata.length());
|
|
} else {
|
|
outdata.write_file(nargs[2]);
|
|
generic_dout(0) << "wrote " << outdata.length() << " byte payload to " << nargs[2] << dendl;
|
|
}
|
|
}
|
|
else if (strcmp(nargs[0], "put") == 0) {
|
|
if (!pool || nargs.size() < 3)
|
|
usage();
|
|
|
|
string oid(nargs[1]);
|
|
|
|
if (strcmp(nargs[2], "-") == 0) {
|
|
char buf[256];
|
|
while(!cin.eof()) {
|
|
cin.getline(buf, 256);
|
|
indata.append(buf);
|
|
indata.append('\n');
|
|
}
|
|
} else {
|
|
ret = indata.read_file(nargs[2]);
|
|
if (ret) {
|
|
cerr << "error reading input file " << nargs[2] << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
ret = rados.write_full(p, oid, indata);
|
|
if (ret < 0) {
|
|
cerr << "error writing " << pool << "/" << oid << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
|
|
goto out;
|
|
}
|
|
}
|
|
else if (strcmp(nargs[0], "rm") == 0) {
|
|
if (!pool || nargs.size() < 2)
|
|
usage();
|
|
string oid(nargs[1]);
|
|
ret = rados.remove(p, oid);
|
|
if (ret < 0) {
|
|
cerr << "error removing " << pool << "/" << oid << ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
else if (strcmp(nargs[0], "tmap") == 0) {
|
|
if (nargs.size() < 3)
|
|
usage();
|
|
if (strcmp(nargs[1], "dump") == 0) {
|
|
string oid(nargs[2]);
|
|
ret = rados.read(p, oid, 0, outdata, 0);
|
|
if (ret < 0) {
|
|
cerr << "error reading " << pool << "/" << 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[0], "mkpool") == 0) {
|
|
int auid = 0;
|
|
__u8 crush_rule = 0;
|
|
if (nargs.size() < 2)
|
|
usage();
|
|
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.create_pool(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], "rmpool") == 0) {
|
|
if (nargs.size() < 2)
|
|
usage();
|
|
rados_pool_t rm_me;
|
|
ret = rados.open_pool(nargs[1], &rm_me);
|
|
if (ret >= 0) {
|
|
ret = rados.delete_pool(rm_me);
|
|
if (ret < 0) {
|
|
cerr << "error deleting pool " << nargs[1] << ": "
|
|
<< strerror_r(-ret, buf, sizeof(buf)) << std::endl;
|
|
}
|
|
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 || nargs.size() != 1)
|
|
usage();
|
|
|
|
vector<snap_t> snaps;
|
|
rados.snap_list(p, &snaps);
|
|
for (vector<snap_t>::iterator i = snaps.begin();
|
|
i != snaps.end();
|
|
i++) {
|
|
string s;
|
|
time_t t;
|
|
if (rados.snap_get_name(p, *i, &s) < 0)
|
|
continue;
|
|
if (rados.snap_get_stamp(p, *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 || nargs.size() < 2)
|
|
usage();
|
|
|
|
ret = rados.snap_create(p, nargs[1]);
|
|
if (ret < 0) {
|
|
cerr << "error creating pool " << pool << " snapshot " << nargs[1]
|
|
<< ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
|
|
goto out;
|
|
}
|
|
cout << "created pool " << pool << " snap " << nargs[1] << std::endl;
|
|
}
|
|
|
|
else if (strcmp(nargs[0], "rmsnap") == 0) {
|
|
if (!pool || nargs.size() < 2)
|
|
usage();
|
|
|
|
ret = rados.snap_remove(p, nargs[1]);
|
|
if (ret < 0) {
|
|
cerr << "error removing pool " << pool << " snapshot " << nargs[1]
|
|
<< ": " << strerror_r(-ret, buf, sizeof(buf)) << std::endl;
|
|
goto out;
|
|
}
|
|
cout << "removed pool " << pool << " snap " << nargs[1] << std::endl;
|
|
}
|
|
|
|
else if (strcmp(nargs[0], "rollback") == 0) {
|
|
if (!pool || nargs.size() < 3)
|
|
usage();
|
|
|
|
ret = rados.snap_rollback_object(p, nargs[1], nargs[2]);
|
|
if (ret < 0) {
|
|
cerr << "error rolling back pool " << pool << " to snapshot " << nargs[1]
|
|
<< strerror_r(-ret, buf, sizeof(buf)) << std::endl;
|
|
goto out;
|
|
}
|
|
cout << "rolled back pool " << pool
|
|
<< " to snapshot " << nargs[2] << std::endl;
|
|
}
|
|
|
|
else if (strcmp(nargs[0], "bench") == 0) {
|
|
if (!pool || nargs.size() < 3)
|
|
usage();
|
|
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();
|
|
ret = aio_bench(rados, p, operation, seconds, concurrent_ios, op_size);
|
|
if (ret != 0)
|
|
cerr << "error during benchmark: " << ret << std::endl;
|
|
}
|
|
else {
|
|
cerr << "unrecognized command " << nargs[0] << std::endl;
|
|
usage();
|
|
}
|
|
|
|
out:
|
|
if (pool)
|
|
rados.close_pool(p);
|
|
|
|
no_pool_out:
|
|
rados.shutdown();
|
|
if (ret < 0)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|