summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSage Weil <sage@inktank.com>2013-07-26 16:12:19 -0700
committerSage Weil <sage@inktank.com>2013-07-26 16:12:19 -0700
commit0757c60b666cf923ce9a29502dfc2e47c57d1e29 (patch)
treed04f022eeca81ec1033ae70ee2dcfec77b48a4d3
parent6ac8aed040049358aaf8aee5cfb16791c4bb54a2 (diff)
parentaa00ace1d806526e02dbd65fddaccba2efa94163 (diff)
downloadceph-0757c60b666cf923ce9a29502dfc2e47c57d1e29.tar.gz
Merge remote-tracking branch 'gh/wip-tell-unified' into next
Reviewed-by: Sage Weil <sage@inktank.com>
-rwxr-xr-xsrc/ceph.in134
-rw-r--r--src/client/Client.cc14
-rw-r--r--src/client/Client.h3
-rw-r--r--src/common/Formatter.cc14
-rw-r--r--src/common/Formatter.h2
-rw-r--r--src/common/admin_socket.cc32
-rw-r--r--src/common/admin_socket.h3
-rw-r--r--src/common/ceph_context.cc35
-rw-r--r--src/common/ceph_context.h3
-rw-r--r--src/common/perf_counters.cc98
-rw-r--r--src/common/perf_counters.h4
-rw-r--r--src/mon/Monitor.cc11
-rw-r--r--src/mon/Monitor.h3
-rw-r--r--src/osd/OSD.cc211
-rw-r--r--src/osd/OSD.h2
-rw-r--r--src/osd/OpRequest.cc26
-rw-r--r--src/osd/OpRequest.h4
-rw-r--r--src/osd/PG.h32
-rw-r--r--src/osd/ReplicatedPG.cc111
-rw-r--r--src/osd/ReplicatedPG.h4
-rw-r--r--src/osdc/Objecter.cc164
-rw-r--r--src/osdc/Objecter.h17
-rw-r--r--src/pybind/ceph_argparse.py37
-rwxr-xr-xsrc/pybind/ceph_rest_api.py366
-rw-r--r--src/test/bench/small_io_bench_fs.cc5
25 files changed, 805 insertions, 530 deletions
diff --git a/src/ceph.in b/src/ceph.in
index 0d361e1c76c..63c41343f9a 100755
--- a/src/ceph.in
+++ b/src/ceph.in
@@ -100,8 +100,6 @@ def parse_cmdargs(args=None, target=''):
parser.add_argument('-h', '--help', help='request mon help',
action='store_true')
- parser.add_argument('--help-all', help='request help for all daemons',
- action='store_true')
parser.add_argument('-c', '--conf', dest='cephconf',
help='ceph configuration file')
@@ -150,14 +148,16 @@ def parse_cmdargs(args=None, target=''):
return parser, parsed_args, extras
-def do_help(parser, args, help_all = False):
+def do_help(parser, args):
"""
Print basic parser help
- If the cluster is available:
- get and print monitor help;
- if help_all, print help for daemon commands as well
+ If the cluster is available, get and print monitor help
"""
+ def help_for_sigs(sigs, partial=None):
+ sys.stdout.write(format_help(parse_json_funcsigs(sigs, 'cli'),
+ partial=partial))
+
def help_for_target(target, partial=None):
ret, outbuf, outs = json_command(cluster_handle, target=target,
prefix='get_command_descriptions',
@@ -167,40 +167,19 @@ def do_help(parser, args, help_all = False):
"couldn't get command descriptions for {0}: {1}".\
format(target, outs)
else:
- sys.stdout.write(format_help(parse_json_funcsigs(outbuf, 'cli'),
- partial))
+ help_for_sigs(outbuf, partial)
- parser.print_help()
- print '\n'
- if (cluster_handle):
- help_for_target(target=('mon', ''), partial=' '.join(args))
-
- if help_all and cluster_handle:
- # try/except in case there are no daemons of that type
- try:
- firstosd = osdids()[0]
- print '\nOSD.{0} tell commands and pg pgid commands:\n\n'.\
- format(firstosd)
- help_for_target(target=('osd', osdids()[0]))
- print '\nOSD daemon commands:\n\n'
- sys.stdout.write(format_help(parse_json_funcsigs(admin_socket(ceph_conf('admin_socket', 'osd.' + firstosd), ['get_command_descriptions']), 'cli')))
- except:
- pass
+ def hdr(s):
+ print '\n', s, '\n', '=' * len(s)
- try:
- firstmon = monids()[0]
- print '\nmon.{0} daemon commands:\n\n'.format(firstmon)
- sys.stdout.write(format_help(parse_json_funcsigs(admin_socket(ceph_conf('admin_socket', 'mon.' + firstmon), ['get_command_descriptions']), 'cli')))
- except:
- pass
+ hdr('Monitor commands:')
+ partial = ' '.join(args)
+ parser.print_help()
+ print '\n'
- try:
- firstmds = mdsids()[0]
- print '\nmds.{0} daemon commands:\n\n'.format(firstmds)
- sys.stdout.write(format_help(parse_json_funcsigs(admin_socket(ceph_conf('admin_socket', 'mds.' + firstmds), ['get_command_descriptions']), 'cli')))
- except:
- pass
+ if (cluster_handle):
+ help_for_target(target=('mon', ''), partial=partial)
return 0
@@ -285,26 +264,57 @@ def format_help(cmddict, partial=None):
return fullusage
-def admin_socket(asok_path, cmd):
- sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+def admin_socket(asok_path, cmd, format=''):
+ """
+ Send a daemon (--admin-daemon) command 'cmd'. asok_path is the
+ path to the admin socket; cmd is a list of strings; format may be
+ set to one of the formatted forms to get output in that form
+ (daemon commands don't support 'plain' output).
+ """
+
+ def do_sockio(path, cmd):
+ """ helper: do all the actual low-level stream I/O """
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.connect(path)
+ try:
+ sock.sendall(cmd + '\0')
+ len_str = sock.recv(4)
+ if len(len_str) < 4:
+ raise RuntimeError("no data returned from admin socket")
+ l, = struct.unpack(">I", len_str)
+ ret = ''
+
+ got = 0
+ while got < l:
+ bit = sock.recv(l - got)
+ ret += bit
+ got += len(bit)
+
+ except Exception as e:
+ raise RuntimeError('exception: ' + str(e))
+ return ret
+
try:
- sock.connect(asok_path)
- sock.sendall(' '.join(cmd) + '\0')
+ cmd_json = do_sockio(asok_path,
+ json.dumps({"prefix":"get_command_descriptions"}))
+ except Exception as e:
+ raise RuntimeError('exception getting command descriptions: ' + str(e))
+
+ if cmd == 'get_command_descriptions':
+ return cmd_json
- len_str = sock.recv(4)
- if len(len_str) < 4:
- raise RuntimeError("no data returned from admin socket")
- l, = struct.unpack(">I", len_str)
- ret = ''
+ sigdict = parse_json_funcsigs(cmd_json, 'cli')
+ valid_dict = validate_command(sigdict, cmd)
+ if not valid_dict:
+ return -errno.EINVAL
- got = 0
- while got < l:
- bit = sock.recv(l - got)
- ret += bit
- got += len(bit)
+ if format:
+ valid_dict['format'] = format
+ try:
+ ret = do_sockio(asok_path, json.dumps(valid_dict))
except Exception as e:
- raise RuntimeError('exception: {0}'.format(e))
+ raise RuntimeError('exception: ' + str(e))
return ret
@@ -344,10 +354,11 @@ def new_style_command(parsed_args, cmdargs, target, sigdict, inbuf, verbose):
if not got_command:
if cmdargs:
# Validate input args against list of sigs
- valid_dict = validate_command(parsed_args, sigdict, cmdargs,
- verbose)
+ valid_dict = validate_command(sigdict, cmdargs, verbose)
if valid_dict:
got_command = True
+ if parsed_args.output_format:
+ valid_dict['format'] = parsed_args.output_format
else:
return -errno.EINVAL, '', 'invalid command'
else:
@@ -360,8 +371,10 @@ def new_style_command(parsed_args, cmdargs, target, sigdict, inbuf, verbose):
return 0, '', ''
cmdargs = parse_cmdargs(interactive_input.split())[2]
target = find_cmd_target(cmdargs)
- valid_dict = validate_command(parsed_args, sigdict, cmdargs)
+ valid_dict = validate_command(sigdict, cmdargs, verbose)
if valid_dict:
+ if parsed_args.output_format:
+ valid_dict['format'] = parsed_args.output_format
if verbose:
print >> sys.stderr, "Submitting command ", valid_dict
ret, outbuf, outs = json_command(cluster_handle,
@@ -470,9 +483,12 @@ def main():
conffile = parsed_args.cephconf
# For now, --admin-daemon is handled as usual. Try it
# first in case we can't connect() to the cluster
+
+ format = parsed_args.output_format
+
if parsed_args.admin_socket:
try:
- print admin_socket(parsed_args.admin_socket, childargs)
+ print admin_socket(parsed_args.admin_socket, childargs, format)
except Exception as e:
print >> sys.stderr, 'admin_socket: {0}'.format(e)
return 0
@@ -481,7 +497,7 @@ def main():
if len(childargs) > 2:
if childargs[1].find('/') >= 0:
try:
- print admin_socket(childargs[1], childargs[2:])
+ print admin_socket(childargs[1], childargs[2:], format)
except Exception as e:
print >> sys.stderr, 'admin_socket: {0}'.format(e)
return 0
@@ -489,7 +505,7 @@ def main():
# try resolve daemon name
path = ceph_conf('admin_socket', childargs[1])
try:
- print admin_socket(path, childargs[2:])
+ print admin_socket(path, childargs[2:], format)
except Exception as e:
print >> sys.stderr, 'admin_socket: {0}'.format(e)
return 0
@@ -544,8 +560,8 @@ def main():
format(e.__class__.__name__)
return 1
- if parsed_args.help or parsed_args.help_all:
- return do_help(parser, childargs, parsed_args.help_all)
+ if parsed_args.help:
+ return do_help(parser, childargs)
# implement -w/--watch_*
# This is ugly, but Namespace() isn't quite rich enough.
diff --git a/src/client/Client.cc b/src/client/Client.cc
index ba036ad9980..5a9c5fdafcc 100644
--- a/src/client/Client.cc
+++ b/src/client/Client.cc
@@ -102,22 +102,24 @@ Client::CommandHook::CommandHook(Client *client) :
{
}
-bool Client::CommandHook::call(std::string command, std::string args, bufferlist& out)
+bool Client::CommandHook::call(std::string command, std::string args,
+ std::string format, bufferlist& out)
{
stringstream ss;
- JSONFormatter formatter(true);
+ Formatter *f = new_formatter(format);
m_client->client_lock.Lock();
if (command == "mds_requests")
- m_client->dump_mds_requests(&formatter);
+ m_client->dump_mds_requests(f);
else if (command == "mds_sessions")
- m_client->dump_mds_sessions(&formatter);
+ m_client->dump_mds_sessions(f);
else if (command == "dump_cache")
- m_client->dump_cache(&formatter);
+ m_client->dump_cache(f);
else
assert(0 == "bad command registered");
m_client->client_lock.Unlock();
- formatter.flush(ss);
+ f->flush(ss);
out.append(ss);
+ delete f;
return true;
}
diff --git a/src/client/Client.h b/src/client/Client.h
index ade0b8f29c8..bc1fbc0401b 100644
--- a/src/client/Client.h
+++ b/src/client/Client.h
@@ -196,7 +196,8 @@ class Client : public Dispatcher {
Client *m_client;
public:
CommandHook(Client *client);
- bool call(std::string command, std::string args, bufferlist& out);
+ bool call(std::string command, std::string args, std::string format,
+ bufferlist& out);
};
CommandHook m_command_hook;
diff --git a/src/common/Formatter.cc b/src/common/Formatter.cc
index 357b287fe32..7362684c070 100644
--- a/src/common/Formatter.cc
+++ b/src/common/Formatter.cc
@@ -62,15 +62,19 @@ Formatter::~Formatter()
}
Formatter *
-new_formatter(const std::string &type)
+new_formatter(const std::string type)
{
- if (type == "json")
+ std::string mytype = type;
+ if (mytype == "")
+ mytype = "json-pretty";
+
+ if (mytype == "json")
return new JSONFormatter(false);
- else if (type == "json-pretty")
+ else if (mytype == "json-pretty")
return new JSONFormatter(true);
- else if (type == "xml")
+ else if (mytype == "xml")
return new XMLFormatter(false);
- else if (type == "xml-pretty")
+ else if (mytype == "xml-pretty")
return new XMLFormatter(true);
else
return (Formatter *)NULL;
diff --git a/src/common/Formatter.h b/src/common/Formatter.h
index 8775c0cf9df..c62a8303ce1 100644
--- a/src/common/Formatter.h
+++ b/src/common/Formatter.h
@@ -60,7 +60,7 @@ class Formatter {
}
};
-Formatter *new_formatter(const std::string &type);
+Formatter *new_formatter(const std::string type);
class JSONFormatter : public Formatter {
public:
diff --git a/src/common/admin_socket.cc b/src/common/admin_socket.cc
index f7ab3501dff..4afd685b72a 100644
--- a/src/common/admin_socket.cc
+++ b/src/common/admin_socket.cc
@@ -310,6 +310,18 @@ bool AdminSocket::do_accept()
bool rval = false;
+ map<string, cmd_vartype> cmdmap;
+ string format;
+ vector<string> cmdvec;
+ stringstream errss;
+ cmdvec.push_back(cmd);
+ if (!cmdmap_from_json(cmdvec, &cmdmap, errss)) {
+ ldout(m_cct, 0) << "AdminSocket: " << errss << dendl;
+ return false;
+ }
+ cmd_getval(m_cct, cmdmap, "format", format);
+ cmd_getval(m_cct, cmdmap, "prefix", c);
+
string firstword;
if (c.find(" ") == string::npos)
firstword = c;
@@ -341,7 +353,7 @@ bool AdminSocket::do_accept()
string args;
if (match != c)
args = c.substr(match.length() + 1);
- bool success = p->second->call(match, args, out);
+ bool success = p->second->call(match, args, format, out);
if (!success) {
ldout(m_cct, 0) << "AdminSocket: request '" << match << "' args '" << args
<< "' to " << p->second << " failed" << dendl;
@@ -406,7 +418,8 @@ int AdminSocket::unregister_command(std::string command)
class VersionHook : public AdminSocketHook {
public:
- virtual bool call(std::string command, std::string args, bufferlist& out) {
+ virtual bool call(std::string command, std::string args, std::string format,
+ bufferlist& out) {
if (command == "0") {
out.append(CEPH_ADMIN_SOCK_VERSION);
} else {
@@ -429,18 +442,19 @@ class HelpHook : public AdminSocketHook {
AdminSocket *m_as;
public:
HelpHook(AdminSocket *as) : m_as(as) {}
- bool call(string command, string args, bufferlist& out) {
- JSONFormatter jf(true);
- jf.open_object_section("help");
+ bool call(string command, string args, string format, bufferlist& out) {
+ Formatter *f = new_formatter(format);
+ f->open_object_section("help");
for (map<string,string>::iterator p = m_as->m_help.begin();
p != m_as->m_help.end();
++p) {
- jf.dump_string(p->first.c_str(), p->second);
+ f->dump_string(p->first.c_str(), p->second);
}
- jf.close_section();
+ f->close_section();
ostringstream ss;
- jf.flush(ss);
+ f->flush(ss);
out.append(ss.str());
+ delete f;
return true;
}
};
@@ -449,7 +463,7 @@ class GetdescsHook : public AdminSocketHook {
AdminSocket *m_as;
public:
GetdescsHook(AdminSocket *as) : m_as(as) {}
- bool call(string command, string args, bufferlist& out) {
+ bool call(string command, string args, string format, bufferlist& out) {
int cmdnum = 0;
JSONFormatter jf(false);
jf.open_object_section("command_descriptions");
diff --git a/src/common/admin_socket.h b/src/common/admin_socket.h
index c390bca0382..30c5eb96ab8 100644
--- a/src/common/admin_socket.h
+++ b/src/common/admin_socket.h
@@ -29,7 +29,8 @@ class CephContext;
class AdminSocketHook {
public:
- virtual bool call(std::string command, std::string args, bufferlist& out) = 0;
+ virtual bool call(std::string command, std::string args, std::string format,
+ bufferlist& out) = 0;
virtual ~AdminSocketHook() {};
};
diff --git a/src/common/ceph_context.cc b/src/common/ceph_context.cc
index cad980bb2a6..6b227d8689e 100644
--- a/src/common/ceph_context.cc
+++ b/src/common/ceph_context.cc
@@ -156,44 +156,46 @@ class CephContextHook : public AdminSocketHook {
public:
CephContextHook(CephContext *cct) : m_cct(cct) {}
- bool call(std::string command, std::string args, bufferlist& out) {
- m_cct->do_command(command, args, &out);
+ bool call(std::string command, std::string args, std::string format,
+ bufferlist& out) {
+ m_cct->do_command(command, args, format, &out);
return true;
}
};
-void CephContext::do_command(std::string command, std::string args, bufferlist *out)
+void CephContext::do_command(std::string command, std::string args,
+ std::string format, bufferlist *out)
{
+ Formatter *f = new_formatter(format);
lgeneric_dout(this, 1) << "do_command '" << command << "' '" << args << "'" << dendl;
if (command == "perfcounters_dump" || command == "1" ||
command == "perf dump") {
- _perf_counters_collection->write_json_to_buf(*out, false);
+ _perf_counters_collection->dump_formatted(f, false);
}
else if (command == "perfcounters_schema" || command == "2" ||
command == "perf schema") {
- _perf_counters_collection->write_json_to_buf(*out, true);
+ _perf_counters_collection->dump_formatted(f, true);
}
else {
- JSONFormatter jf(true);
- jf.open_object_section(command.c_str());
+ f->open_object_section(command.c_str());
if (command == "config show") {
- _conf->show_config(&jf);
+ _conf->show_config(f);
}
else if (command == "config set") {
std::string var = args;
size_t pos = var.find(' ');
if (pos == string::npos) {
- jf.dump_string("error", "syntax error: 'config set <var> <value>'");
+ f->dump_string("error", "syntax error: 'config set <var> <value>'");
} else {
std::string val = var.substr(pos+1);
var.resize(pos);
int r = _conf->set_val(var.c_str(), val.c_str());
if (r < 0) {
- jf.dump_stream("error") << "error setting '" << var << "' to '" << val << "': " << cpp_strerror(r);
+ f->dump_stream("error") << "error setting '" << var << "' to '" << val << "': " << cpp_strerror(r);
} else {
ostringstream ss;
_conf->apply_changes(&ss);
- jf.dump_string("success", ss.str());
+ f->dump_string("success", ss.str());
}
}
} else if (command == "config get") {
@@ -202,9 +204,9 @@ void CephContext::do_command(std::string command, std::string args, bufferlist *
char *tmp = buf;
int r = _conf->get_val(args.c_str(), &tmp, sizeof(buf));
if (r < 0) {
- jf.dump_stream("error") << "error getting '" << args << "': " << cpp_strerror(r);
+ f->dump_stream("error") << "error getting '" << args << "': " << cpp_strerror(r);
} else {
- jf.dump_string(args.c_str(), buf);
+ f->dump_string(args.c_str(), buf);
}
} else if (command == "log flush") {
_log->flush();
@@ -218,11 +220,10 @@ void CephContext::do_command(std::string command, std::string args, bufferlist *
else {
assert(0 == "registered under wrong command?");
}
- ostringstream ss;
- jf.close_section();
- jf.flush(ss);
- out->append(ss.str());
+ f->close_section();
}
+ f->flush(*out);
+ delete f;
lgeneric_dout(this, 1) << "do_command '" << command << "' '" << args << "' result is " << out->length() << " bytes" << dendl;
};
diff --git a/src/common/ceph_context.h b/src/common/ceph_context.h
index 1678680fa6d..85618e35219 100644
--- a/src/common/ceph_context.h
+++ b/src/common/ceph_context.h
@@ -97,7 +97,8 @@ public:
/**
* process an admin socket command
*/
- void do_command(std::string command, std::string args, bufferlist *out);
+ void do_command(std::string command, std::string args, std::string foramt,
+ bufferlist *out);
/**
* get a crypto handler
diff --git a/src/common/perf_counters.cc b/src/common/perf_counters.cc
index 67a777497b3..86fb531f737 100644
--- a/src/common/perf_counters.cc
+++ b/src/common/perf_counters.cc
@@ -15,6 +15,7 @@
#include "common/perf_counters.h"
#include "common/dout.h"
#include "common/errno.h"
+#include "common/Formatter.h"
#include <errno.h>
#include <inttypes.h>
@@ -72,21 +73,20 @@ void PerfCountersCollection::clear()
}
}
-void PerfCountersCollection::write_json_to_buf(bufferlist& bl, bool schema)
+void PerfCountersCollection::dump_formatted(Formatter *f, bool schema)
{
Mutex::Locker lck(m_lock);
- bl.append('{');
+ f->open_object_section("perfcounter_collection");
perf_counters_set_t::iterator l = m_loggers.begin();
perf_counters_set_t::iterator l_end = m_loggers.end();
if (l != l_end) {
while (true) {
- (*l)->write_json_to_buf(bl, schema);
+ (*l)->dump_formatted(f, schema);
if (++l == l_end)
break;
- bl.append(',');
}
}
- bl.append('}');
+ f->close_section();
}
// ---------------------------
@@ -203,34 +203,54 @@ utime_t PerfCounters::tget(int idx) const
return utime_t(data.u64 / 1000000000ull, data.u64 % 1000000000ull);
}
-void PerfCounters::write_json_to_buf(bufferlist& bl, bool schema)
+void PerfCounters::dump_formatted(Formatter *f, bool schema)
{
- char buf[512];
Mutex::Locker lck(m_lock);
- snprintf(buf, sizeof(buf), "\"%s\":{", m_name.c_str());
- bl.append(buf);
-
+ f->open_object_section(m_name.c_str());
perf_counter_data_vec_t::const_iterator d = m_data.begin();
perf_counter_data_vec_t::const_iterator d_end = m_data.end();
if (d == d_end) {
- bl.append('}');
+ f->close_section();
return;
}
while (true) {
- const perf_counter_data_any_d &data(*d);
- buf[0] = '\0';
- if (schema)
- data.write_schema_json(buf, sizeof(buf));
- else
- data.write_json(buf, sizeof(buf));
-
- bl.append(buf);
+ if (schema) {
+ f->open_object_section(d->name);
+ f->dump_int("type", d->type);
+ f->close_section();
+ } else {
+ if (d->type & PERFCOUNTER_LONGRUNAVG) {
+ f->open_object_section(d->name);
+ if (d->type & PERFCOUNTER_U64) {
+ f->dump_format("avgcount", "%"PRId64, d->avgcount);
+ f->dump_format("sum", "%"PRId64, d->u64);
+ } else if (d->type & PERFCOUNTER_TIME) {
+ f->dump_format("avgcount", "%"PRId64, d->avgcount);
+ f->dump_format("sum", "%"PRId64"%09"PRId64,
+ d->u64 / 1000000000ull,
+ d->u64 % 1000000000ull);
+ } else {
+ assert(0);
+ }
+ f->close_section();
+ } else {
+ if (d->type & PERFCOUNTER_U64) {
+ f->dump_format(d->name, "%"PRId64, d->u64);
+ } else if (d->type & PERFCOUNTER_TIME) {
+ f->dump_format(d->name, "%"PRId64"%09"PRId64,
+ d->u64 / 1000000000ull,
+ d->u64 % 1000000000ull);
+ } else {
+ assert(0);
+ }
+ }
+ }
+
if (++d == d_end)
break;
- bl.append(',');
}
- bl.append('}');
+ f->close_section();
}
const std::string &PerfCounters::get_name() const
@@ -258,42 +278,6 @@ PerfCounters::perf_counter_data_any_d::perf_counter_data_any_d()
{
}
-void PerfCounters::perf_counter_data_any_d::write_schema_json(char *buf, size_t buf_sz) const
-{
- snprintf(buf, buf_sz, "\"%s\":{\"type\":%d}", name, type);
-}
-
-void PerfCounters::perf_counter_data_any_d::write_json(char *buf, size_t buf_sz) const
-{
- if (type & PERFCOUNTER_LONGRUNAVG) {
- if (type & PERFCOUNTER_U64) {
- snprintf(buf, buf_sz, "\"%s\":{\"avgcount\":%" PRId64 ","
- "\"sum\":%" PRId64 "}",
- name, avgcount, u64);
- }
- else if (type & PERFCOUNTER_TIME) {
- snprintf(buf, buf_sz, "\"%s\":{\"avgcount\":%" PRId64 ","
- "\"sum\":%llu.%09llu}",
- name, avgcount, u64 / 1000000000ull, u64 % 1000000000ull);
- }
- else {
- assert(0);
- }
- }
- else {
- if (type & PERFCOUNTER_U64) {
- snprintf(buf, buf_sz, "\"%s\":%" PRId64,
- name, u64);
- }
- else if (type & PERFCOUNTER_TIME) {
- snprintf(buf, buf_sz, "\"%s\":%llu.%09llu", name, u64 / 1000000000ull, u64 % 1000000000ull);
- }
- else {
- assert(0);
- }
- }
-}
-
PerfCountersBuilder::PerfCountersBuilder(CephContext *cct, const std::string &name,
int first, int last)
: m_perf_counters(new PerfCounters(cct, name, first, last))
diff --git a/src/common/perf_counters.h b/src/common/perf_counters.h
index 269a32f2c46..ec10f9a9282 100644
--- a/src/common/perf_counters.h
+++ b/src/common/perf_counters.h
@@ -76,7 +76,7 @@ public:
void tinc(int idx, utime_t v);
utime_t tget(int idx) const;
- void write_json_to_buf(ceph::bufferlist& bl, bool schema);
+ void dump_formatted(ceph::Formatter *f, bool schema);
const std::string& get_name() const;
void set_name(std::string s) {
@@ -136,7 +136,7 @@ public:
void add(class PerfCounters *l);
void remove(class PerfCounters *l);
void clear();
- void write_json_to_buf(ceph::bufferlist& bl, bool schema);
+ void dump_formatted(ceph::Formatter *f, bool schema);
private:
CephContext *m_cct;
diff --git a/src/mon/Monitor.cc b/src/mon/Monitor.cc
index bf500dff218..119ef740aa8 100644
--- a/src/mon/Monitor.cc
+++ b/src/mon/Monitor.cc
@@ -225,21 +225,20 @@ class AdminHook : public AdminSocketHook {
Monitor *mon;
public:
AdminHook(Monitor *m) : mon(m) {}
- bool call(std::string command, std::string args, bufferlist& out) {
+ bool call(std::string command, std::string args, std::string format,
+ bufferlist& out) {
stringstream ss;
- mon->do_admin_command(command, args, ss);
+ mon->do_admin_command(command, args, format, ss);
out.append(ss);
return true;
}
};
-void Monitor::do_admin_command(string command, string args, ostream& ss)
+void Monitor::do_admin_command(string command, string args, string format,
+ ostream& ss)
{
Mutex::Locker l(lock);
- map<string, cmd_vartype> cmdmap;
- string format;
- cmd_getval(g_ceph_context, cmdmap, "format", format, string("plain"));
boost::scoped_ptr<Formatter> f(new_formatter(format));
if (command == "mon_status")
diff --git a/src/mon/Monitor.h b/src/mon/Monitor.h
index 82b08816702..bed48ecee34 100644
--- a/src/mon/Monitor.h
+++ b/src/mon/Monitor.h
@@ -745,7 +745,8 @@ public:
int write_fsid();
int write_fsid(MonitorDBStore::Transaction &t);
- void do_admin_command(std::string command, std::string args, ostream& ss);
+ void do_admin_command(std::string command, std::string args,
+ std::string format, ostream& ss);
private:
// don't allow copying
diff --git a/src/osd/OSD.cc b/src/osd/OSD.cc
index 58020d0c322..a2cc0ed229e 100644
--- a/src/osd/OSD.cc
+++ b/src/osd/OSD.cc
@@ -997,44 +997,44 @@ class OSDSocketHook : public AdminSocketHook {
OSD *osd;
public:
OSDSocketHook(OSD *o) : osd(o) {}
- bool call(std::string command, std::string args, bufferlist& out) {
+ bool call(std::string command, std::string args, std::string format,
+ bufferlist& out) {
stringstream ss;
- bool r = osd->asok_command(command, args, ss);
+ bool r = osd->asok_command(command, args, format, ss);
out.append(ss);
return r;
}
};
-bool OSD::asok_command(string command, string args, ostream& ss)
+bool OSD::asok_command(string command, string args, string format, ostream& ss)
{
+ if (format == "")
+ format = "json-pretty";
+ Formatter *f = new_formatter(format);
if (command == "dump_ops_in_flight") {
- op_tracker.dump_ops_in_flight(ss);
+ op_tracker.dump_ops_in_flight(f);
} else if (command == "dump_historic_ops") {
- op_tracker.dump_historic_ops(ss);
+ op_tracker.dump_historic_ops(f);
} else if (command == "dump_op_pq_state") {
- JSONFormatter f(true);
- f.open_object_section("pq");
- op_wq.dump(&f);
- f.close_section();
- f.flush(ss);
+ f->open_object_section("pq");
+ op_wq.dump(f);
+ f->close_section();
} else if (command == "dump_blacklist") {
list<pair<entity_addr_t,utime_t> > bl;
OSDMapRef curmap = service.get_osdmap();
- JSONFormatter f(true);
- f.open_array_section("blacklist");
+ f->open_array_section("blacklist");
curmap->get_blacklist(&bl);
for (list<pair<entity_addr_t,utime_t> >::iterator it = bl.begin();
it != bl.end(); ++it) {
- f.open_array_section("entry");
- f.open_object_section("entity_addr_t");
- it->first.dump(&f);
- f.close_section(); //entity_addr_t
- it->second.localtime(f.dump_stream("expire_time"));
- f.close_section(); //entry
- }
- f.close_section(); //blacklist
- f.flush(ss);
+ f->open_array_section("entry");
+ f->open_object_section("entity_addr_t");
+ it->first.dump(f);
+ f->close_section(); //entity_addr_t
+ it->second.localtime(f->dump_stream("expire_time"));
+ f->close_section(); //entry
+ }
+ f->close_section(); //blacklist
} else if (command == "dump_watchers") {
list<obj_watch_item_t> watchers;
osd_lock.Lock();
@@ -1052,35 +1052,35 @@ bool OSD::asok_command(string command, string args, ostream& ss)
}
osd_lock.Unlock();
- JSONFormatter f(true);
- f.open_array_section("watchers");
+ f->open_array_section("watchers");
for (list<obj_watch_item_t>::iterator it = watchers.begin();
it != watchers.end(); ++it) {
- f.open_array_section("watch");
+ f->open_array_section("watch");
- f.dump_string("namespace", it->obj.nspace);
- f.dump_string("object", it->obj.oid.name);
+ f->dump_string("namespace", it->obj.nspace);
+ f->dump_string("object", it->obj.oid.name);
- f.open_object_section("entity_name");
- it->wi.name.dump(&f);
- f.close_section(); //entity_name_t
+ f->open_object_section("entity_name");
+ it->wi.name.dump(f);
+ f->close_section(); //entity_name_t
- f.dump_int("cookie", it->wi.cookie);
- f.dump_int("timeout", it->wi.timeout_seconds);
+ f->dump_int("cookie", it->wi.cookie);
+ f->dump_int("timeout", it->wi.timeout_seconds);
- f.open_object_section("entity_addr_t");
- it->wi.addr.dump(&f);
- f.close_section(); //entity_addr_t
+ f->open_object_section("entity_addr_t");
+ it->wi.addr.dump(f);
+ f->close_section(); //entity_addr_t
- f.close_section(); //watch
+ f->close_section(); //watch
}
- f.close_section(); //watches
- f.flush(ss);
+ f->close_section(); //watches
} else {
assert(0 == "broken asok registration");
}
+ f->flush(ss);
+ delete f;
return true;
}
@@ -1089,7 +1089,8 @@ class TestOpsSocketHook : public AdminSocketHook {
ObjectStore *store;
public:
TestOpsSocketHook(OSDService *s, ObjectStore *st) : service(s), store(st) {}
- bool call(std::string command, std::string args, bufferlist& out) {
+ bool call(std::string command, std::string args, std::string format,
+ bufferlist& out) {
stringstream ss;
test_ops(service, store, command, args, ss);
out.append(ss);
@@ -3815,51 +3816,90 @@ void OSD::handle_command(MCommand *m)
struct OSDCommand {
string cmdstring;
string helpstring;
+ string module;
+ string perm;
+ string availability;
} osd_commands[] = {
-#define COMMAND(parsesig, helptext) \
- {parsesig, helptext},
+#define COMMAND(parsesig, helptext, module, perm, availability) \
+ {parsesig, helptext, module, perm, availability},
// yes, these are really pg commands, but there's a limit to how
-// much work it's worth. The OSD returns all of them.
+// much work it's worth. The OSD returns all of them. Make this
+// form (pg <pgid> <cmd>) valid only for the cli.
+// Rest uses "tell <pgid> <cmd>"
COMMAND("pg " \
"name=pgid,type=CephPgid " \
"name=cmd,type=CephChoices,strings=query", \
- "show details of a specific pg")
+ "show details of a specific pg", "osd", "r", "cli")
COMMAND("pg " \
"name=pgid,type=CephPgid " \
"name=cmd,type=CephChoices,strings=mark_unfound_lost " \
"name=mulcmd,type=CephChoices,strings=revert", \
- "mark all unfound objects in this pg as lost, either removing or reverting to a prior version if one is available")
+ "mark all unfound objects in this pg as lost, either removing or reverting to a prior version if one is available",
+ "osd", "rw", "cli")
COMMAND("pg " \
"name=pgid,type=CephPgid " \
"name=cmd,type=CephChoices,strings=list_missing " \
"name=offset,type=CephString,req=false",
- "list missing objects on this pg, perhaps starting at an offset given in JSON")
+ "list missing objects on this pg, perhaps starting at an offset given in JSON",
+ "osd", "r", "cli")
-COMMAND("version", "report version of OSD")
+// new form: tell <pgid> <cmd> for both cli and rest
+
+COMMAND("query",
+ "show details of a specific pg", "osd", "r", "cli,rest")
+COMMAND("mark_unfound_lost " \
+ "name=mulcmd,type=CephChoices,strings=revert", \
+ "mark all unfound objects in this pg as lost, either removing or reverting to a prior version if one is available",
+ "osd", "rw", "cli,rest")
+COMMAND("list_missing " \
+ "name=offset,type=CephString,req=false",
+ "list missing objects on this pg, perhaps starting at an offset given in JSON",
+ "osd", "r", "cli,rest")
+
+// tell <osd.n> commands. Validation of osd.n must be special-cased in client
+
+// tell <osd.n> commands. Validation of osd.n must be special-cased in client
+COMMAND("version", "report version of OSD", "osd", "r", "cli,rest")
COMMAND("injectargs " \
"name=injected_args,type=CephString,n=N",
- "inject configuration arguments into running OSD")
+ "inject configuration arguments into running OSD",
+ "osd", "rw", "cli,rest")
COMMAND("bench " \
"name=count,type=CephInt,req=false " \
"name=size,type=CephInt,req=false ", \
"OSD benchmark: write <count> <size>-byte objects, " \
- "(default 1G size 4MB). Results in log.")
-COMMAND("flush_pg_stats", "flush pg stats")
-COMMAND("debug dump_missing " \
+ "(default 1G size 4MB). Results in log.",
+ "osd", "rw", "cli,rest")
+COMMAND("flush_pg_stats", "flush pg stats", "osd", "rw", "cli,rest")
+COMMAND("debug_dump_missing " \
"name=filename,type=CephFilepath",
- "dump missing objects to a named file")
+ "dump missing objects to a named file", "osd", "r", "cli,rest")
COMMAND("debug kick_recovery_wq " \
"name=delay,type=CephInt,range=0",
- "set osd_recovery_delay_start to <val>")
+ "set osd_recovery_delay_start to <val>", "osd", "rw", "cli,rest")
COMMAND("cpu_profiler " \
"name=arg,type=CephChoices,strings=status|flush",
- "run cpu profiling on daemon")
-COMMAND("dump_pg_recovery_stats", "dump pg recovery statistics")
-COMMAND("reset_pg_recovery_stats", "reset pg recovery statistics")
-
+ "run cpu profiling on daemon", "osd", "rw", "cli,rest")
+COMMAND("dump_pg_recovery_stats", "dump pg recovery statistics",
+ "osd", "r", "cli,rest")
+COMMAND("reset_pg_recovery_stats", "reset pg recovery statistics",
+ "osd", "rw", "cli,rest")
+
+// experiment: restate pg commands as "tell <pgid>". Validation of
+// pgid must be special-cased in client.
+COMMAND("query",
+ "show details of a specific pg", "osd", "r", "cli,rest")
+COMMAND("mark_unfound_lost revert " \
+ "name=mulcmd,type=CephChoices,strings=revert", \
+ "mark all unfound objects in this pg as lost, either removing or reverting to a prior version if one is available",
+ "osd", "rw", "cli,rest")
+COMMAND("list_missing " \
+ "name=offset,type=CephString,req=false",
+ "list missing objects on this pg, perhaps starting at an offset given in JSON",
+ "osd", "rw", "cli,rest")
};
void OSD::do_command(Connection *con, tid_t tid, vector<string>& cmd, bufferlist& data)
@@ -3873,6 +3913,9 @@ void OSD::do_command(Connection *con, tid_t tid, vector<string>& cmd, bufferlist
map<string, cmd_vartype> cmdmap;
string prefix;
+ string format;
+ string pgidstr;
+ boost::scoped_ptr<Formatter> f;
if (cmd.empty()) {
ss << "no command given";
@@ -3895,8 +3938,8 @@ void OSD::do_command(Connection *con, tid_t tid, vector<string>& cmd, bufferlist
ostringstream secname;
secname << "cmd" << setfill('0') << std::setw(3) << cmdnum;
- dump_cmd_and_help_to_json(f, secname.str(),
- cp->cmdstring, cp->helpstring);
+ dump_cmddesc_to_json(f, secname.str(), cp->cmdstring, cp->helpstring,
+ cp->module, cp->perm, cp->availability);
cmdnum++;
}
f->close_section(); // command_descriptions
@@ -3907,8 +3950,18 @@ void OSD::do_command(Connection *con, tid_t tid, vector<string>& cmd, bufferlist
goto out;
}
+ cmd_getval(g_ceph_context, cmdmap, "format", format);
+ f.reset(new_formatter(format));
+
if (prefix == "version") {
- ds << pretty_version_to_str();
+ if (f) {
+ f->open_object_section("version");
+ f->dump_string("version", pretty_version_to_str());
+ f->close_section();
+ f->flush(ds);
+ } else {
+ ds << pretty_version_to_str();
+ }
goto out;
}
else if (prefix == "injectargs") {
@@ -3928,9 +3981,16 @@ void OSD::do_command(Connection *con, tid_t tid, vector<string>& cmd, bufferlist
osd_lock.Lock();
}
- else if (prefix == "pg") {
+ // either 'pg <pgid> <command>' or
+ // 'tell <pgid>' (which comes in without any of that prefix)?
+
+ else if (prefix == "pg" ||
+ (cmd_getval(g_ceph_context, cmdmap, "pgid", pgidstr) &&
+ (prefix == "query" ||
+ prefix == "mark_unfound_lost" ||
+ prefix == "list_missing")
+ )) {
pg_t pgid;
- string pgidstr;
if (!cmd_getval(g_ceph_context, cmdmap, "pgid", pgidstr)) {
ss << "no pgid specified";
@@ -3939,14 +3999,15 @@ void OSD::do_command(Connection *con, tid_t tid, vector<string>& cmd, bufferlist
ss << "couldn't parse pgid '" << pgidstr << "'";
r = -EINVAL;
} else {
- vector<string> args;
- cmd_getval(g_ceph_context, cmdmap, "args", args);
PG *pg = _lookup_lock_pg(pgid);
if (!pg) {
ss << "i don't have pgid " << pgid;
r = -ENOENT;
} else {
- r = pg->do_command(cmd, ss, data, odata);
+ // simulate pg <pgid> cmd= for pg->do-command
+ if (prefix != "pg")
+ cmd_putval(g_ceph_context, cmdmap, "cmd", prefix);
+ r = pg->do_command(cmdmap, ss, data, odata);
pg->unlock();
}
}
@@ -3985,9 +4046,18 @@ void OSD::do_command(Connection *con, tid_t tid, vector<string>& cmd, bufferlist
store->queue_transaction(NULL, cleanupt);
uint64_t rate = (double)count / (end - start);
- ss << "bench: wrote " << prettybyte_t(count)
- << " in blocks of " << prettybyte_t(bsize) << " in "
- << (end-start) << " sec at " << prettybyte_t(rate) << "/sec";
+ if (f) {
+ f->open_object_section("osd_bench_results");
+ f->dump_int("bytes_written", count);
+ f->dump_int("blocksize", bsize);
+ f->dump_float("bytes_per_sec", rate);
+ f->close_section();
+ f->flush(ss);
+ } else {
+ ss << "bench: wrote " << prettybyte_t(count)
+ << " in blocks of " << prettybyte_t(bsize) << " in "
+ << (end-start) << " sec at " << prettybyte_t(rate) << "/sec";
+ }
}
else if (prefix == "flush_pg_stats") {
@@ -4084,8 +4154,13 @@ void OSD::do_command(Connection *con, tid_t tid, vector<string>& cmd, bufferlist
else if (prefix == "dump_pg_recovery_stats") {
stringstream s;
- pg_recovery_stats.dump(s);
- ds << "dump pg recovery stats: " << s.str();
+ if (f) {
+ pg_recovery_stats.dump_formatted(f.get());
+ f->flush(ds);
+ } else {
+ pg_recovery_stats.dump(s);
+ ds << "dump pg recovery stats: " << s.str();
+ }
}
else if (prefix == "reset_pg_recovery_stats") {
diff --git a/src/osd/OSD.h b/src/osd/OSD.h
index f9ceaf81bf3..5bcff7442d7 100644
--- a/src/osd/OSD.h
+++ b/src/osd/OSD.h
@@ -622,7 +622,7 @@ protected:
// asok
friend class OSDSocketHook;
class OSDSocketHook *asok_hook;
- bool asok_command(string command, string args, ostream& ss);
+ bool asok_command(string command, string args, string format, ostream& ss);
public:
ClassHandler *class_handler;
diff --git a/src/osd/OpRequest.cc b/src/osd/OpRequest.cc
index 3b8a8714d92..a6cdc9ecffb 100644
--- a/src/osd/OpRequest.cc
+++ b/src/osd/OpRequest.cc
@@ -76,31 +76,27 @@ void OpHistory::dump_ops(utime_t now, Formatter *f)
f->close_section();
}
-void OpTracker::dump_historic_ops(ostream &ss)
+void OpTracker::dump_historic_ops(Formatter *f)
{
- JSONFormatter jf(true);
Mutex::Locker locker(ops_in_flight_lock);
utime_t now = ceph_clock_now(g_ceph_context);
- history.dump_ops(now, &jf);
- jf.flush(ss);
+ history.dump_ops(now, f);
}
-void OpTracker::dump_ops_in_flight(ostream &ss)
+void OpTracker::dump_ops_in_flight(Formatter *f)
{
- JSONFormatter jf(true);
Mutex::Locker locker(ops_in_flight_lock);
- jf.open_object_section("ops_in_flight"); // overall dump
- jf.dump_int("num_ops", ops_in_flight.size());
- jf.open_array_section("ops"); // list of OpRequests
+ f->open_object_section("ops_in_flight"); // overall dump
+ f->dump_int("num_ops", ops_in_flight.size());
+ f->open_array_section("ops"); // list of OpRequests
utime_t now = ceph_clock_now(g_ceph_context);
for (xlist<OpRequest*>::iterator p = ops_in_flight.begin(); !p.end(); ++p) {
- jf.open_object_section("op");
- (*p)->dump(now, &jf);
- jf.close_section(); // this OpRequest
+ f->open_object_section("op");
+ (*p)->dump(now, f);
+ f->close_section(); // this OpRequest
}
- jf.close_section(); // list of OpRequests
- jf.close_section(); // overall dump
- jf.flush(ss);
+ f->close_section(); // list of OpRequests
+ f->close_section(); // overall dump
}
void OpTracker::register_inflight_op(xlist<OpRequest*>::item *i)
diff --git a/src/osd/OpRequest.h b/src/osd/OpRequest.h
index 47b050b8538..a2014472432 100644
--- a/src/osd/OpRequest.h
+++ b/src/osd/OpRequest.h
@@ -59,8 +59,8 @@ class OpTracker {
public:
OpTracker() : seq(0), ops_in_flight_lock("OpTracker mutex") {}
- void dump_ops_in_flight(std::ostream& ss);
- void dump_historic_ops(std::ostream& ss);
+ void dump_ops_in_flight(Formatter *f);
+ void dump_historic_ops(Formatter *f);
void register_inflight_op(xlist<OpRequest*>::item *i);
void unregister_inflight_op(OpRequest *i);
diff --git a/src/osd/PG.h b/src/osd/PG.h
index 819c9c62f62..10e9a2544a9 100644
--- a/src/osd/PG.h
+++ b/src/osd/PG.h
@@ -43,8 +43,10 @@
#include "msg/Messenger.h"
#include "messages/MOSDRepScrub.h"
#include "messages/MOSDPGLog.h"
+#include "common/cmdparse.h"
#include "common/tracked_int_ptr.hpp"
#include "common/WorkQueue.h"
+#include "include/str_list.h"
#include <list>
#include <memory>
@@ -108,10 +110,36 @@ struct PGRecoveryStats {
<< i.total_time << "\t"
<< i.min_time << "\t" << i.max_time << "\t"
<< p->first << "\n";
-
}
}
+ void dump_formatted(Formatter *f) {
+ Mutex::Locker l(lock);
+ f->open_array_section("pg_recovery_stats");
+ for (map<const char *,per_state_info>::iterator p = info.begin();
+ p != info.end(); ++p) {
+ per_state_info& i = p->second;
+ f->open_object_section("recovery_state");
+ f->dump_int("enter", i.enter);
+ f->dump_int("exit", i.exit);
+ f->dump_int("events", i.events);
+ f->dump_stream("event_time") << i.event_time;
+ f->dump_stream("total_time") << i.total_time;
+ f->dump_stream("min_time") << i.min_time;
+ f->dump_stream("max_time") << i.max_time;
+ vector<string> states;
+ get_str_vec(p->first, "/", states);
+ f->open_array_section("nested_states");
+ for (vector<string>::iterator st = states.begin();
+ st != states.end(); ++st) {
+ f->dump_string("state", *st);
+ }
+ f->close_section();
+ f->close_section();
+ }
+ f->close_section();
+ }
+
void log_enter(const char *s) {
Mutex::Locker l(lock);
info[s].enter++;
@@ -1786,7 +1814,7 @@ public:
virtual void do_push_reply(OpRequestRef op) = 0;
virtual void snap_trimmer() = 0;
- virtual int do_command(vector<string>& cmd, ostream& ss,
+ virtual int do_command(cmdmap_t cmdmap, ostream& ss,
bufferlist& idata, bufferlist& odata) = 0;
virtual bool same_for_read_since(epoch_t e) = 0;
diff --git a/src/osd/ReplicatedPG.cc b/src/osd/ReplicatedPG.cc
index 298d38d6ace..658ea7cb746 100644
--- a/src/osd/ReplicatedPG.cc
+++ b/src/osd/ReplicatedPG.cc
@@ -268,57 +268,43 @@ int ReplicatedPG::get_pgls_filter(bufferlist::iterator& iter, PGLSFilter **pfilt
// ==========================================================
-int ReplicatedPG::do_command(vector<string>& cmd, ostream& ss,
+int ReplicatedPG::do_command(cmdmap_t cmdmap, ostream& ss,
bufferlist& idata, bufferlist& odata)
{
const pg_missing_t &missing = pg_log.get_missing();
- map<string, cmd_vartype> cmdmap;
string prefix;
+ string format;
- if (cmd.empty()) {
- ss << "no command given";
- return -EINVAL;
- }
-
- stringstream ss2;
- if (!cmdmap_from_json(cmd, &cmdmap, ss2)) {
- ss << ss2.str();
- return -EINVAL;
- }
-
- cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
- if (prefix != "pg") {
- ss << "ReplicatedPG::do_command: not pg command";
- return -EINVAL;
- }
+ cmd_getval(g_ceph_context, cmdmap, "format", format);
+ boost::scoped_ptr<Formatter> f(new_formatter(format));
+ // demand that we have a formatter
+ if (!f)
+ f.reset(new_formatter("json"));
string command;
cmd_getval(g_ceph_context, cmdmap, "cmd", command);
if (command == "query") {
- JSONFormatter jsf(true);
- jsf.open_object_section("pg");
- jsf.dump_string("state", pg_state_string(get_state()));
- jsf.dump_unsigned("epoch", get_osdmap()->get_epoch());
- jsf.open_array_section("up");
+ f->open_object_section("pg");
+ f->dump_string("state", pg_state_string(get_state()));
+ f->dump_unsigned("epoch", get_osdmap()->get_epoch());
+ f->open_array_section("up");
for (vector<int>::iterator p = up.begin(); p != up.end(); ++p)
- jsf.dump_unsigned("osd", *p);
- jsf.close_section();
- jsf.open_array_section("acting");
+ f->dump_unsigned("osd", *p);
+ f->close_section();
+ f->open_array_section("acting");
for (vector<int>::iterator p = acting.begin(); p != acting.end(); ++p)
- jsf.dump_unsigned("osd", *p);
- jsf.close_section();
- jsf.open_object_section("info");
- info.dump(&jsf);
- jsf.close_section();
-
- jsf.open_array_section("recovery_state");
- handle_query_state(&jsf);
- jsf.close_section();
-
- jsf.close_section();
- stringstream dss;
- jsf.flush(dss);
- odata.append(dss);
+ f->dump_unsigned("osd", *p);
+ f->close_section();
+ f->open_object_section("info");
+ info.dump(f.get());
+ f->close_section();
+
+ f->open_array_section("recovery_state");
+ handle_query_state(f.get());
+ f->close_section();
+
+ f->close_section();
+ f->flush(odata);
return 0;
}
else if (command == "mark_unfound_lost") {
@@ -352,7 +338,6 @@ int ReplicatedPG::do_command(vector<string>& cmd, ostream& ss,
return 0;
}
else if (command == "list_missing") {
- JSONFormatter jf(true);
hobject_t offset;
string offset_json;
if (cmd_getval(g_ceph_context, cmdmap, "offset", offset_json)) {
@@ -366,50 +351,48 @@ int ReplicatedPG::do_command(vector<string>& cmd, ostream& ss,
return -EINVAL;
}
}
- jf.open_object_section("missing");
+ f->open_object_section("missing");
{
- jf.open_object_section("offset");
- offset.dump(&jf);
- jf.close_section();
+ f->open_object_section("offset");
+ offset.dump(f.get());
+ f->close_section();
}
- jf.dump_int("num_missing", missing.num_missing());
- jf.dump_int("num_unfound", get_num_unfound());
+ f->dump_int("num_missing", missing.num_missing());
+ f->dump_int("num_unfound", get_num_unfound());
map<hobject_t,pg_missing_t::item>::const_iterator p = missing.missing.upper_bound(offset);
{
- jf.open_array_section("objects");
+ f->open_array_section("objects");
int32_t num = 0;
bufferlist bl;
while (p != missing.missing.end() && num < g_conf->osd_command_max_records) {
- jf.open_object_section("object");
+ f->open_object_section("object");
{
- jf.open_object_section("oid");
- p->first.dump(&jf);
- jf.close_section();
+ f->open_object_section("oid");
+ p->first.dump(f.get());
+ f->close_section();
}
- p->second.dump(&jf); // have, need keys
+ p->second.dump(f.get()); // have, need keys
{
- jf.open_array_section("locations");
+ f->open_array_section("locations");
map<hobject_t,set<int> >::iterator q = missing_loc.find(p->first);
if (q != missing_loc.end())
for (set<int>::iterator r = q->second.begin(); r != q->second.end(); ++r)
- jf.dump_int("osd", *r);
- jf.close_section();
+ f->dump_int("osd", *r);
+ f->close_section();
}
- jf.close_section();
+ f->close_section();
++p;
num++;
}
- jf.close_section();
+ f->close_section();
}
- jf.dump_int("more", p != missing.missing.end());
- jf.close_section();
- stringstream jss;
- jf.flush(jss);
- odata.append(jss);
+ f->dump_int("more", p != missing.missing.end());
+ f->close_section();
+ f->flush(odata);
return 0;
};
- ss << "unknown command " << cmd;
+ ss << "unknown pg command " << prefix;
return -EINVAL;
}
diff --git a/src/osd/ReplicatedPG.h b/src/osd/ReplicatedPG.h
index 9dafe23faa1..7b70b4381ea 100644
--- a/src/osd/ReplicatedPG.h
+++ b/src/osd/ReplicatedPG.h
@@ -17,6 +17,7 @@
#include <boost/optional.hpp>
#include "include/assert.h"
+#include "common/cmdparse.h"
#include "PG.h"
#include "OSD.h"
@@ -930,7 +931,8 @@ public:
const hobject_t& ioid);
~ReplicatedPG() {}
- int do_command(vector<string>& cmd, ostream& ss, bufferlist& idata, bufferlist& odata);
+ int do_command(cmdmap_t cmdmap, ostream& ss, bufferlist& idata,
+ bufferlist& odata);
void do_op(OpRequestRef op);
bool pg_op_must_wait(MOSDOp *op);
diff --git a/src/osdc/Objecter.cc b/src/osdc/Objecter.cc
index a5a023cb33e..e07d0626d21 100644
--- a/src/osdc/Objecter.cc
+++ b/src/osdc/Objecter.cc
@@ -2180,154 +2180,154 @@ void Objecter::dump_active()
}
}
-void Objecter::dump_requests(Formatter& fmt) const
+void Objecter::dump_requests(Formatter *fmt) const
{
assert(client_lock.is_locked());
- fmt.open_object_section("requests");
+ fmt->open_object_section("requests");
dump_ops(fmt);
dump_linger_ops(fmt);
dump_pool_ops(fmt);
dump_pool_stat_ops(fmt);
dump_statfs_ops(fmt);
dump_command_ops(fmt);
- fmt.close_section(); // requests object
+ fmt->close_section(); // requests object
}
-void Objecter::dump_ops(Formatter& fmt) const
+void Objecter::dump_ops(Formatter *fmt) const
{
- fmt.open_array_section("ops");
+ fmt->open_array_section("ops");
for (map<tid_t,Op*>::const_iterator p = ops.begin();
p != ops.end();
++p) {
Op *op = p->second;
- fmt.open_object_section("op");
- fmt.dump_unsigned("tid", op->tid);
- fmt.dump_stream("pg") << op->pgid;
- fmt.dump_int("osd", op->session ? op->session->osd : -1);
- fmt.dump_stream("last_sent") << op->stamp;
- fmt.dump_int("attempts", op->attempts);
- fmt.dump_stream("object_id") << op->oid;
- fmt.dump_stream("object_locator") << op->oloc;
- fmt.dump_stream("snapid") << op->snapid;
- fmt.dump_stream("snap_context") << op->snapc;
- fmt.dump_stream("mtime") << op->mtime;
-
- fmt.open_array_section("osd_ops");
+ fmt->open_object_section("op");
+ fmt->dump_unsigned("tid", op->tid);
+ fmt->dump_stream("pg") << op->pgid;
+ fmt->dump_int("osd", op->session ? op->session->osd : -1);
+ fmt->dump_stream("last_sent") << op->stamp;
+ fmt->dump_int("attempts", op->attempts);
+ fmt->dump_stream("object_id") << op->oid;
+ fmt->dump_stream("object_locator") << op->oloc;
+ fmt->dump_stream("snapid") << op->snapid;
+ fmt->dump_stream("snap_context") << op->snapc;
+ fmt->dump_stream("mtime") << op->mtime;
+
+ fmt->open_array_section("osd_ops");
for (vector<OSDOp>::const_iterator it = op->ops.begin();
it != op->ops.end();
++it) {
- fmt.dump_stream("osd_op") << *it;
+ fmt->dump_stream("osd_op") << *it;
}
- fmt.close_section(); // osd_ops array
+ fmt->close_section(); // osd_ops array
- fmt.close_section(); // op object
+ fmt->close_section(); // op object
}
- fmt.close_section(); // ops array
+ fmt->close_section(); // ops array
}
-void Objecter::dump_linger_ops(Formatter& fmt) const
+void Objecter::dump_linger_ops(Formatter *fmt) const
{
- fmt.open_array_section("linger_ops");
+ fmt->open_array_section("linger_ops");
for (map<uint64_t, LingerOp*>::const_iterator p = linger_ops.begin();
p != linger_ops.end();
++p) {
LingerOp *op = p->second;
- fmt.open_object_section("linger_op");
- fmt.dump_unsigned("linger_id", op->linger_id);
- fmt.dump_stream("pg") << op->pgid;
- fmt.dump_int("osd", op->session ? op->session->osd : -1);
- fmt.dump_stream("object_id") << op->oid;
- fmt.dump_stream("object_locator") << op->oloc;
- fmt.dump_stream("snapid") << op->snap;
- fmt.dump_stream("registering") << op->snap;
- fmt.dump_stream("registered") << op->snap;
- fmt.close_section(); // linger_op object
+ fmt->open_object_section("linger_op");
+ fmt->dump_unsigned("linger_id", op->linger_id);
+ fmt->dump_stream("pg") << op->pgid;
+ fmt->dump_int("osd", op->session ? op->session->osd : -1);
+ fmt->dump_stream("object_id") << op->oid;
+ fmt->dump_stream("object_locator") << op->oloc;
+ fmt->dump_stream("snapid") << op->snap;
+ fmt->dump_stream("registering") << op->snap;
+ fmt->dump_stream("registered") << op->snap;
+ fmt->close_section(); // linger_op object
}
- fmt.close_section(); // linger_ops array
+ fmt->close_section(); // linger_ops array
}
-void Objecter::dump_command_ops(Formatter& fmt) const
+void Objecter::dump_command_ops(Formatter *fmt) const
{
- fmt.open_array_section("command_ops");
+ fmt->open_array_section("command_ops");
for (map<uint64_t, CommandOp*>::const_iterator p = command_ops.begin();
p != command_ops.end();
++p) {
CommandOp *op = p->second;
- fmt.open_object_section("command_op");
- fmt.dump_unsigned("command_id", op->tid);
- fmt.dump_int("osd", op->session ? op->session->osd : -1);
- fmt.open_array_section("command");
+ fmt->open_object_section("command_op");
+ fmt->dump_unsigned("command_id", op->tid);
+ fmt->dump_int("osd", op->session ? op->session->osd : -1);
+ fmt->open_array_section("command");
for (vector<string>::const_iterator q = op->cmd.begin(); q != op->cmd.end(); ++q)
- fmt.dump_string("word", *q);
- fmt.close_section();
+ fmt->dump_string("word", *q);
+ fmt->close_section();
if (op->target_osd >= 0)
- fmt.dump_int("target_osd", op->target_osd);
+ fmt->dump_int("target_osd", op->target_osd);
else
- fmt.dump_stream("target_pg") << op->target_pg;
- fmt.close_section(); // command_op object
+ fmt->dump_stream("target_pg") << op->target_pg;
+ fmt->close_section(); // command_op object
}
- fmt.close_section(); // command_ops array
+ fmt->close_section(); // command_ops array
}
-void Objecter::dump_pool_ops(Formatter& fmt) const
+void Objecter::dump_pool_ops(Formatter *fmt) const
{
- fmt.open_array_section("pool_ops");
+ fmt->open_array_section("pool_ops");
for (map<tid_t, PoolOp*>::const_iterator p = pool_ops.begin();
p != pool_ops.end();
++p) {
PoolOp *op = p->second;
- fmt.open_object_section("pool_op");
- fmt.dump_unsigned("tid", op->tid);
- fmt.dump_int("pool", op->pool);
- fmt.dump_string("name", op->name);
- fmt.dump_int("operation_type", op->pool_op);
- fmt.dump_unsigned("auid", op->auid);
- fmt.dump_unsigned("crush_rule", op->crush_rule);
- fmt.dump_stream("snapid") << op->snapid;
- fmt.dump_stream("last_sent") << op->last_submit;
- fmt.close_section(); // pool_op object
+ fmt->open_object_section("pool_op");
+ fmt->dump_unsigned("tid", op->tid);
+ fmt->dump_int("pool", op->pool);
+ fmt->dump_string("name", op->name);
+ fmt->dump_int("operation_type", op->pool_op);
+ fmt->dump_unsigned("auid", op->auid);
+ fmt->dump_unsigned("crush_rule", op->crush_rule);
+ fmt->dump_stream("snapid") << op->snapid;
+ fmt->dump_stream("last_sent") << op->last_submit;
+ fmt->close_section(); // pool_op object
}
- fmt.close_section(); // pool_ops array
+ fmt->close_section(); // pool_ops array
}
-void Objecter::dump_pool_stat_ops(Formatter& fmt) const
+void Objecter::dump_pool_stat_ops(Formatter *fmt) const
{
- fmt.open_array_section("pool_stat_ops");
+ fmt->open_array_section("pool_stat_ops");
for (map<tid_t, PoolStatOp*>::const_iterator p = poolstat_ops.begin();
p != poolstat_ops.end();
++p) {
PoolStatOp *op = p->second;
- fmt.open_object_section("pool_stat_op");
- fmt.dump_unsigned("tid", op->tid);
- fmt.dump_stream("last_sent") << op->last_submit;
+ fmt->open_object_section("pool_stat_op");
+ fmt->dump_unsigned("tid", op->tid);
+ fmt->dump_stream("last_sent") << op->last_submit;
- fmt.open_array_section("pools");
+ fmt->open_array_section("pools");
for (list<string>::const_iterator it = op->pools.begin();
it != op->pools.end();
++it) {
- fmt.dump_string("pool", *it);
+ fmt->dump_string("pool", *it);
}
- fmt.close_section(); // pool_op object
+ fmt->close_section(); // pool_op object
- fmt.close_section(); // pool_stat_op object
+ fmt->close_section(); // pool_stat_op object
}
- fmt.close_section(); // pool_stat_ops array
+ fmt->close_section(); // pool_stat_ops array
}
-void Objecter::dump_statfs_ops(Formatter& fmt) const
+void Objecter::dump_statfs_ops(Formatter *fmt) const
{
- fmt.open_array_section("statfs_ops");
+ fmt->open_array_section("statfs_ops");
for (map<tid_t, StatfsOp*>::const_iterator p = statfs_ops.begin();
p != statfs_ops.end();
++p) {
StatfsOp *op = p->second;
- fmt.open_object_section("statfs_op");
- fmt.dump_unsigned("tid", op->tid);
- fmt.dump_stream("last_sent") << op->last_submit;
- fmt.close_section(); // pool_stat_op object
+ fmt->open_object_section("statfs_op");
+ fmt->dump_unsigned("tid", op->tid);
+ fmt->dump_stream("last_sent") << op->last_submit;
+ fmt->close_section(); // pool_stat_op object
}
- fmt.close_section(); // pool_stat_ops array
+ fmt->close_section(); // pool_stat_ops array
}
Objecter::RequestStateHook::RequestStateHook(Objecter *objecter) :
@@ -2335,14 +2335,16 @@ Objecter::RequestStateHook::RequestStateHook(Objecter *objecter) :
{
}
-bool Objecter::RequestStateHook::call(std::string command, std::string args, bufferlist& out)
+bool Objecter::RequestStateHook::call(std::string command, std::string args,
+ std::string format, bufferlist& out)
{
stringstream ss;
- JSONFormatter formatter(true);
+ Formatter *f = new_formatter(format);
m_objecter->client_lock.Lock();
- m_objecter->dump_requests(formatter);
+ m_objecter->dump_requests(f);
m_objecter->client_lock.Unlock();
- formatter.flush(ss);
+ f->flush(ss);
+ delete f;
out.append(ss);
return true;
}
diff --git a/src/osdc/Objecter.h b/src/osdc/Objecter.h
index c1cac88b60e..aa4a20d8b0b 100644
--- a/src/osdc/Objecter.h
+++ b/src/osdc/Objecter.h
@@ -726,7 +726,8 @@ class Objecter {
Objecter *m_objecter;
public:
RequestStateHook(Objecter *objecter);
- bool call(std::string command, std::string args, bufferlist& out);
+ bool call(std::string command, std::string args, std::string format,
+ bufferlist& out);
};
RequestStateHook *m_request_state_hook;
@@ -1236,13 +1237,13 @@ private:
* Output in-flight requests
*/
void dump_active();
- void dump_requests(Formatter& fmt) const;
- void dump_ops(Formatter& fmt) const;
- void dump_linger_ops(Formatter& fmt) const;
- void dump_command_ops(Formatter& fmt) const;
- void dump_pool_ops(Formatter& fmt) const;
- void dump_pool_stat_ops(Formatter& fmt) const;
- void dump_statfs_ops(Formatter& fmt) const;
+ void dump_requests(Formatter *fmt) const;
+ void dump_ops(Formatter *fmt) const;
+ void dump_linger_ops(Formatter *fmt) const;
+ void dump_command_ops(Formatter *fmt) const;
+ void dump_pool_ops(Formatter *fmt) const;
+ void dump_pool_stat_ops(Formatter *fmt) const;
+ void dump_statfs_ops(Formatter *fmt) const;
int get_client_incarnation() const { return client_inc; }
void set_client_incarnation(int inc) { client_inc = inc; }
diff --git a/src/pybind/ceph_argparse.py b/src/pybind/ceph_argparse.py
index b82cc833ba6..ee71b76d6a1 100644
--- a/src/pybind/ceph_argparse.py
+++ b/src/pybind/ceph_argparse.py
@@ -263,6 +263,8 @@ class CephIPAddr(CephArgtype):
if p is not None and long(p) > 65535:
raise ArgumentValid("{0} not a valid port number".format(p))
self.val = s
+ self.addr = a
+ self.port = p
def __str__(self):
return '<IPaddr[:port]>'
@@ -274,6 +276,7 @@ class CephEntityAddr(CephIPAddr):
def valid(self, s, partial=False):
ip, nonce = s.split('/')
super(self.__class__, self).valid(ip)
+ self.nonce = nonce
self.val = s
def __str__(self):
@@ -820,11 +823,10 @@ def validate(args, signature, partial=False):
raise ArgumentError("unused arguments: " + str(myargs))
return d
-def validate_command(parsed_args, sigdict, args, verbose=False):
+def validate_command(sigdict, args, verbose=False):
"""
turn args into a valid dictionary ready to be sent off as JSON,
validated against sigdict.
- parsed_args is the namespace back from argparse
"""
found = []
valid_dict = {}
@@ -882,9 +884,6 @@ def validate_command(parsed_args, sigdict, args, verbose=False):
print >> sys.stderr, concise_sig(cmd['sig'])
return None
- if parsed_args.output_format:
- valid_dict['format'] = parsed_args.output_format
-
return valid_dict
def find_cmd_target(childargs):
@@ -898,20 +897,35 @@ def find_cmd_target(childargs):
sig = parse_funcsig(['tell', {'name':'target','type':'CephName'}])
try:
valid_dict = validate(childargs, sig, partial=True);
+ except ArgumentError:
+ pass
+ else:
if len(valid_dict) == 2:
+ # revalidate to isolate type and id
name = CephName()
+ # if this fails, something is horribly wrong, as it just
+ # validated successfully above
name.valid(valid_dict['target'])
return name.nametype, name.nameid
+
+ sig = parse_funcsig(['tell', {'name':'pgid','type':'CephPgid'}])
+ try:
+ valid_dict = validate(childargs, sig, partial=True);
except ArgumentError:
pass
+ else:
+ if len(valid_dict) == 2:
+ # pg doesn't need revalidation; the string is fine
+ return 'pg', valid_dict['pgid']
sig = parse_funcsig(['pg', {'name':'pgid','type':'CephPgid'}])
try:
valid_dict = validate(childargs, sig, partial=True);
- if len(valid_dict) == 2:
- return 'pg', valid_dict['pgid']
except ArgumentError:
pass
+ else:
+ if len(valid_dict) == 2:
+ return 'pg', valid_dict['pgid']
return 'mon', ''
@@ -939,8 +953,15 @@ def send_command(cluster, target=('mon', ''), cmd=[], inbuf='', timeout=0,
cluster.osd_command(osdid, cmd, inbuf, timeout)
elif target[0] == 'pg':
- # leave it in cmddict for the OSD to use too
pgid = target[1]
+ # pgid will already be in the command for the pg <pgid>
+ # form, but for tell <pgid>, we need to put it in
+ if cmd:
+ cmddict = json.loads(cmd[0])
+ cmddict['pgid'] = pgid
+ else:
+ cmddict = dict(pgid=pgid)
+ cmd = [json.dumps(cmddict)]
if verbose:
print >> sys.stderr, 'submit {0} for pgid {1}'.\
format(cmd, pgid)
diff --git a/src/pybind/ceph_rest_api.py b/src/pybind/ceph_rest_api.py
index 28a0419c33c..830fb2249ea 100755
--- a/src/pybind/ceph_rest_api.py
+++ b/src/pybind/ceph_rest_api.py
@@ -1,12 +1,14 @@
#!/usr/bin/python
# vim: ts=4 sw=4 smarttab expandtab
-import os
import collections
import ConfigParser
+import contextlib
+import errno
import json
import logging
import logging.handlers
+import os
import rados
import textwrap
import xml.etree.ElementTree
@@ -19,13 +21,13 @@ from ceph_argparse import *
# Globals
#
-APPNAME = '__main__'
DEFAULT_BASEURL = '/api/v0.1'
DEFAULT_ADDR = '0.0.0.0:5000'
DEFAULT_LOG_LEVEL = 'warning'
DEFAULT_CLIENTNAME = 'client.restapi'
DEFAULT_LOG_FILE = '/var/log/ceph/' + DEFAULT_CLIENTNAME + '.log'
+APPNAME = '__main__'
app = flask.Flask(APPNAME)
LOGLEVELS = {
@@ -36,7 +38,9 @@ LOGLEVELS = {
'debug':logging.DEBUG,
}
-# my globals, in a named tuple for usage clarity
+# my globals, in a named tuple for usage clarity. I promise
+# these are never written once initialized, and are global
+# to every thread.
glob = collections.namedtuple('gvars', 'cluster urls sigdict baseurl')
glob.cluster = None
@@ -45,8 +49,15 @@ glob.sigdict = {}
glob.baseurl = ''
def load_conf(clustername='ceph', conffile=None):
- import contextlib
+ '''
+ Load the ceph conf file using ConfigParser. Use the standard
+ fallback order:
+ 1) the passed in arg (from CEPH_CONF)
+ 2) /etc/ceph/{cluster}.conf
+ 3) ~/.ceph/{cluster}.conf
+ 4) {cluster}.conf
+ '''
class _TrimIndentFile(object):
def __init__(self, fp):
@@ -91,6 +102,10 @@ def load_conf(clustername='ceph', conffile=None):
raise EnvironmentError('No conf file found for "{0}"'.format(clustername))
def get_conf(cfg, clientname, key):
+ '''
+ Get config entry from conf file, first in [clientname], then [client],
+ then [global].
+ '''
fullkey = 'restapi_' + key
for sectionname in clientname, 'client', 'global':
try:
@@ -99,16 +114,53 @@ def get_conf(cfg, clientname, key):
pass
return None
-# XXX this is done globally, and cluster connection kept open; there
-# are facilities to pass around global info to requests and to
-# tear down connections between requests if it becomes important
+def find_up_osd():
+ '''
+ Find an up OSD. Return the last one that's up.
+ Returns id as an int.
+ '''
+ ret, outbuf, outs = json_command(glob.cluster, prefix="osd dump",
+ argdict=dict(format='json'))
+ if ret:
+ raise EnvironmentError(ret, 'Can\'t get osd dump output')
+ try:
+ osddump = json.loads(outbuf)
+ except:
+ raise EnvironmentError(errno.EINVAL, 'Invalid JSON back from osd dump')
+ osds = [osd['osd'] for osd in osddump['osds'] if osd['up']]
+ if not osds:
+ raise EnvironmentError(errno.ENOENT, 'No up OSDs found')
+ return int(osds[-1])
+
+
+METHOD_DICT = {'r':['GET'], 'w':['PUT', 'DELETE']}
def api_setup():
- """
+ '''
+ This is done globally, and cluster connection kept open for
+ the lifetime of the daemon. librados should assure that even
+ if the cluster goes away and comes back, our connection remains.
+
Initialize the running instance. Open the cluster, get the command
signatures, module, perms, and help; stuff them away in the glob.urls
- dict.
- """
+ dict. Also save glob.sigdict for help() handling.
+ '''
+ def get_command_descriptions(target=('mon','')):
+ ret, outbuf, outs = json_command(glob.cluster, target,
+ prefix='get_command_descriptions',
+ timeout=30)
+ if ret:
+ err = "Can't get command descriptions: {0}".format(outs)
+ app.logger.error(err)
+ raise EnvironmentError(ret, err)
+
+ try:
+ sigdict = parse_json_funcsigs(outbuf, 'rest')
+ except Exception as e:
+ err = "Can't parse command descriptions: {}".format(e)
+ app.logger.error(err)
+ raise EnvironmentError(err)
+ return sigdict
conffile = os.environ.get('CEPH_CONF', '')
clustername = os.environ.get('CEPH_CLUSTER_NAME', 'ceph')
@@ -148,19 +200,22 @@ def api_setup():
h.setFormatter(logging.Formatter(
'%(asctime)s %(name)s %(levelname)s: %(message)s'))
- ret, outbuf, outs = json_command(glob.cluster,
- prefix='get_command_descriptions')
- if ret:
- err = "Can't contact cluster for command descriptions: {0}".format(outs)
- app.logger.error(err)
- raise EnvironmentError(ret, err)
+ glob.sigdict = get_command_descriptions()
- try:
- glob.sigdict = parse_json_funcsigs(outbuf, 'rest')
- except Exception as e:
- err = "Can't parse command descriptions: {}".format(e)
- app.logger.error(err)
- raise EnvironmentError(err)
+ osdid = find_up_osd()
+ if osdid:
+ osd_sigdict = get_command_descriptions(target=('osd', int(osdid)))
+
+ # shift osd_sigdict keys up to fit at the end of the mon's glob.sigdict
+ maxkey = sorted(glob.sigdict.keys())[-1]
+ maxkey = int(maxkey.replace('cmd', ''))
+ osdkey = maxkey + 1
+ for k, v in osd_sigdict.iteritems():
+ newv = v
+ newv['flavor'] = 'tell'
+ globk = 'cmd' + str(osdkey)
+ glob.sigdict[globk] = newv
+ osdkey += 1
# glob.sigdict maps "cmdNNN" to a dict containing:
# 'sig', an array of argdescs
@@ -173,27 +228,37 @@ def api_setup():
glob.urls = {}
for cmdnum, cmddict in glob.sigdict.iteritems():
cmdsig = cmddict['sig']
- url, params = generate_url_and_params(cmdsig)
- if url in glob.urls:
- continue
+ flavor = cmddict.get('flavor', 'mon')
+ url, params = generate_url_and_params(cmdsig, flavor)
+ perm = cmddict['perm']
+ for k in METHOD_DICT.iterkeys():
+ if k in perm:
+ methods = METHOD_DICT[k]
+ urldict = {'paramsig':params,
+ 'help':cmddict['help'],
+ 'module':cmddict['module'],
+ 'perm':perm,
+ 'flavor':flavor,
+ 'methods':methods,
+ }
+
+ # glob.urls contains a list of urldicts (usually only one long)
+ if url not in glob.urls:
+ glob.urls[url] = [urldict]
else:
- perm = cmddict['perm']
- urldict = {'paramsig':params,
- 'help':cmddict['help'],
- 'module':cmddict['module'],
- 'perm':perm,
- }
- method_dict = {'r':['GET'],
- 'w':['PUT', 'DELETE']}
- for k in method_dict.iterkeys():
- if k in perm:
- methods = method_dict[k]
- app.add_url_rule(url, url, handler, methods=methods)
- glob.urls[url] = urldict
-
- url += '.<fmt>'
- app.add_url_rule(url, url, handler, methods=methods)
- glob.urls[url] = urldict
+ # If more than one, need to make union of methods of all.
+ # Method must be checked in handler
+ methodset = set(methods)
+ for old_urldict in glob.urls[url]:
+ methodset |= set(old_urldict['methods'])
+ methods = list(methodset)
+ glob.urls[url].append(urldict)
+
+ # add, or re-add, rule with all methods and urldicts
+ app.add_url_rule(url, url, handler, methods=methods)
+ url += '.<fmt>'
+ app.add_url_rule(url, url, handler, methods=methods)
+
app.logger.debug("urls added: %d", len(glob.urls))
app.add_url_rule('/<path:catchall_path>', '/<path:catchall_path>',
@@ -201,63 +266,89 @@ def api_setup():
return addr, port
-def generate_url_and_params(sig):
- """
+def generate_url_and_params(sig, flavor):
+ '''
Digest command signature from cluster; generate an absolute
(including glob.baseurl) endpoint from all the prefix words,
- and a dictionary of non-prefix parameters
- """
+ and a list of non-prefix param descs
+ '''
url = ''
params = []
+ # the OSD command descriptors don't include the 'tell <target>', so
+ # tack it onto the front of sig
+ if flavor == 'tell':
+ tellsig = parse_funcsig(['tell',
+ {'name':'target', 'type':'CephOsdName'}])
+ sig = tellsig + sig
+
for desc in sig:
+ # prefixes go in the URL path
if desc.t == CephPrefix:
url += '/' + desc.instance.prefix
+ # CephChoices with 1 required string (not --) do too, unless
+ # we've already started collecting params, in which case they
+ # too are params
elif desc.t == CephChoices and \
len(desc.instance.strings) == 1 and \
desc.req and \
- not str(desc.instance).startswith('--'):
+ not str(desc.instance).startswith('--') and \
+ not params:
url += '/' + str(desc.instance)
else:
- params.append(desc)
+ # tell/<target> is a weird case; the URL includes what
+ # would everywhere else be a parameter
+ if flavor == 'tell' and \
+ (desc.t, desc.name) == (CephOsdName, 'target'):
+ url += '/<target>'
+ else:
+ params.append(desc)
+
return glob.baseurl + url, params
-def concise_sig_for_uri(sig):
- """
+#
+# end setup (import-time) functions, begin request-time functions
+#
+
+def concise_sig_for_uri(sig, flavor):
+ '''
Return a generic description of how one would send a REST request for sig
- """
+ '''
prefix = []
args = []
+ ret = ''
+ if flavor == 'tell':
+ ret = 'tell/<osdid-or-pgid>/'
for d in sig:
if d.t == CephPrefix:
prefix.append(d.instance.prefix)
else:
args.append(d.name + '=' + str(d))
- sig = '/'.join(prefix)
+ ret += '/'.join(prefix)
if args:
- sig += '?' + '&'.join(args)
- return sig
+ ret += '?' + '&'.join(args)
+ return ret
def show_human_help(prefix):
- """
+ '''
Dump table showing commands matching prefix
- """
- # XXX this really needs to be a template
- #s = '<html><body><style>.colhalf { width: 50%;} body{word-wrap:break-word;}</style>'
- #s += '<table border=1><col class=colhalf /><col class=colhalf />'
- #s += '<th>Possible commands:</th>'
- # XXX the above mucking with css doesn't cause sensible columns.
+ '''
+ # XXX There ought to be a better discovery mechanism than an HTML table
s = '<html><body><table border=1><th>Possible commands:</th><th>Method</th><th>Description</th>'
- possible = []
permmap = {'r':'GET', 'rw':'PUT'}
line = ''
for cmdsig in sorted(glob.sigdict.itervalues(), cmp=descsort):
concise = concise_sig(cmdsig['sig'])
+ flavor = cmdsig.get('flavor', 'mon')
+ if flavor == 'tell':
+ concise = 'tell/<target>/' + concise
if concise.startswith(prefix):
line = ['<tr><td>']
- wrapped_sig = textwrap.wrap(concise_sig_for_uri(cmdsig['sig']), 40)
+ wrapped_sig = textwrap.wrap(
+ concise_sig_for_uri(cmdsig['sig'], flavor), 40
+ )
for sigline in wrapped_sig:
line.append(flask.escape(sigline) + '\n')
line.append('</td><td>')
@@ -275,23 +366,22 @@ def show_human_help(prefix):
@app.before_request
def log_request():
- """
+ '''
For every request, log it. XXX Probably overkill for production
- """
+ '''
app.logger.info(flask.request.url + " from " + flask.request.remote_addr + " " + flask.request.user_agent.string)
app.logger.debug("Accept: %s", flask.request.accept_mimetypes.values())
-
@app.route('/')
def root_redir():
return flask.redirect(glob.baseurl)
def make_response(fmt, output, statusmsg, errorcode):
- """
+ '''
If formatted output, cobble up a response object that contains the
output and status wrapped in enclosing objects; if nonformatted, just
- use output. Return HTTP status errorcode in any event.
- """
+ use output+status. Return HTTP status errorcode in any event.
+ '''
response = output
if fmt:
if 'json' in fmt:
@@ -303,6 +393,7 @@ def make_response(fmt, output, statusmsg, errorcode):
return flask.make_response("Error decoding JSON from " +
output, 500)
elif 'xml' in fmt:
+ # XXX
# one is tempted to do this with xml.etree, but figuring out how
# to 'un-XML' the XML-dumped output so it can be reassembled into
# a piece of the tree here is beyond me right now.
@@ -328,19 +419,26 @@ def make_response(fmt, output, statusmsg, errorcode):
return flask.make_response(response, errorcode)
-def handler(catchall_path=None, fmt=None):
- """
- Main endpoint handler; generic for every endpoint
- """
+def handler(catchall_path=None, fmt=None, target=None):
+ '''
+ Main endpoint handler; generic for every endpoint, including catchall.
+ Handles the catchall, anything with <.fmt>, anything with embedded
+ <target>. Partial match or ?help cause the HTML-table
+ "show_human_help" output.
+ '''
- if (catchall_path):
- ep = catchall_path.replace('.<fmt>', '')
- else:
- ep = flask.request.endpoint.replace('.<fmt>', '')
+ ep = catchall_path or flask.request.endpoint
+ ep = ep.replace('.<fmt>', '')
if ep[0] != '/':
ep = '/' + ep
+ # demand that endpoint begin with glob.baseurl
+ if not ep.startswith(glob.baseurl):
+ return make_response(fmt, '', 'Page not found', 404)
+
+ rel_ep = ep[len(glob.baseurl)+1:]
+
# Extensions override Accept: headers override defaults
if not fmt:
if 'application/json' in flask.request.accept_mimetypes.values():
@@ -348,12 +446,35 @@ def handler(catchall_path=None, fmt=None):
elif 'application/xml' in flask.request.accept_mimetypes.values():
fmt = 'xml'
- # demand that endpoint begin with glob.baseurl
- if not ep.startswith(glob.baseurl):
- return make_response(fmt, '', 'Page not found', 404)
+ prefix = ''
+ pgid = None
+ cmdtarget = 'mon', ''
- relative_endpoint = ep[len(glob.baseurl)+1:]
- prefix = ' '.join(relative_endpoint.split('/')).strip()
+ if target:
+ # got tell/<target>; validate osdid or pgid
+ name = CephOsdName()
+ pgidobj = CephPgid()
+ try:
+ name.valid(target)
+ except ArgumentError:
+ # try pgid
+ try:
+ pgidobj.valid(target)
+ except ArgumentError:
+ return flask.make_response("invalid osdid or pgid", 400)
+ else:
+ # it's a pgid
+ pgid = pgidobj.val
+ cmdtarget = 'pg', pgid
+ else:
+ # it's an osd
+ cmdtarget = name.nametype, name.nameid
+
+ # prefix does not include tell/<target>/
+ prefix = ' '.join(rel_ep.split('/')[2:]).strip()
+ else:
+ # non-target command: prefix is entire path
+ prefix = ' '.join(rel_ep.split('/')).strip()
# show "match as much as you gave me" help for unknown endpoints
if not ep in glob.urls:
@@ -365,43 +486,59 @@ def handler(catchall_path=None, fmt=None):
else:
return make_response(fmt, '', 'Invalid endpoint ' + ep, 400)
- urldict = glob.urls[ep]
- paramsig = urldict['paramsig']
-
- # allow '?help' for any specifically-known endpoint
- if 'help' in flask.request.args:
- response = flask.make_response('{0}: {1}'.\
- format(prefix + concise_sig(paramsig), urldict['help']))
- response.headers['Content-Type'] = 'text/plain'
- return response
-
- # if there are parameters for this endpoint, process them
- if paramsig:
- args = {}
- for k, l in flask.request.args.iterlists():
- if len(l) == 1:
- args[k] = l[0]
- else:
- args[k] = l
-
- # is this a valid set of params?
- try:
- argdict = validate(args, paramsig)
- except Exception as e:
- return make_response(fmt, '', str(e) + '\n', 400)
- else:
- # no parameters for this endpoint; complain if args are supplied
- if flask.request.args:
- return make_response(fmt, '', ep + 'takes no params', 400)
- argdict = {}
+ found = None
+ exc = ''
+ for urldict in glob.urls[ep]:
+ if flask.request.method not in urldict['methods']:
+ continue
+ paramsig = urldict['paramsig']
+
+ # allow '?help' for any specifically-known endpoint
+ if 'help' in flask.request.args:
+ response = flask.make_response('{0}: {1}'.\
+ format(prefix + concise_sig(paramsig), urldict['help']))
+ response.headers['Content-Type'] = 'text/plain'
+ return response
+
+ # if there are parameters for this endpoint, process them
+ if paramsig:
+ args = {}
+ for k, l in flask.request.args.iterlists():
+ if len(l) == 1:
+ args[k] = l[0]
+ else:
+ args[k] = l
+
+ # is this a valid set of params?
+ try:
+ argdict = validate(args, paramsig)
+ found = urldict
+ break
+ except Exception as e:
+ exc += str(e)
+ continue
+ else:
+ if flask.request.args:
+ continue
+ found = urldict
+ argdict = {}
+ break
+ if not found:
+ return make_response(fmt, '', exc + '\n', 400)
argdict['format'] = fmt or 'plain'
- argdict['module'] = urldict['module']
- argdict['perm'] = urldict['perm']
+ argdict['module'] = found['module']
+ argdict['perm'] = found['perm']
+ if pgid:
+ argdict['pgid'] = pgid
+
+ if not cmdtarget:
+ cmdtarget = ('mon', '')
app.logger.debug('sending command prefix %s argdict %s', prefix, argdict)
ret, outbuf, outs = json_command(glob.cluster, prefix=prefix,
+ target=cmdtarget,
inbuf=flask.request.data, argdict=argdict)
if ret:
return make_response(fmt, '', 'Error: {0} ({1})'.format(outs, ret), 400)
@@ -414,4 +551,7 @@ def handler(catchall_path=None, fmt=None):
response.headers['Content-Type'] = contenttype
return response
+#
+# Last module-level (import-time) ask: set up the cluster connection
+#
addr, port = api_setup()
diff --git a/src/test/bench/small_io_bench_fs.cc b/src/test/bench/small_io_bench_fs.cc
index 61fbacc5570..a37a7e71153 100644
--- a/src/test/bench/small_io_bench_fs.cc
+++ b/src/test/bench/small_io_bench_fs.cc
@@ -32,7 +32,10 @@ struct MorePrinting : public DetailedStatCollector::AdditionalPrinting {
MorePrinting(CephContext *cct) : cct(cct) {}
void operator()(std::ostream *out) {
bufferlist bl;
- cct->get_perfcounters_collection()->write_json_to_buf(bl, 0);
+ Formatter *f = new_formatter("json-pretty");
+ cct->get_perfcounters_collection()->dump_formatted(f, 0);
+ f->flush(bl);
+ delete f;
bl.append('\0');
*out << bl.c_str() << std::endl;
}