Merge pull request #16028 from jcsp/wip-mgr-commands

mon: load mgr commands at runtime

Reviewed-by: Sage Weil <sage@redhat.com>
Reviewed-by: Kefu Chai <kchai@redhat.com>
This commit is contained in:
Kefu Chai 2017-07-21 18:16:13 +08:00 committed by GitHub
commit 0193e38b3f
26 changed files with 386 additions and 220 deletions

View File

@ -55,17 +55,20 @@ daemon as failed using ``ceph mgr fail <mgr name>``.
Calling module commands
-----------------------
Where a module implements command line hooks, using the Ceph CLI's
``tell`` command to call them like this::
Where a module implements command line hooks, the commands will
be accessible as ordinary Ceph commands::
ceph tell mgr <command | help>
ceph <command | help>
If you would like to see the list of commands handled by the
manager (where normal ``ceph help`` would show all mon and mgr commands),
you can send a command directly to the manager daemon::
ceph tell mgr help
Note that it is not necessary to address a particular mgr instance,
simply ``mgr`` will pick the current active daemon.
Use the ``help`` command to get a list of available commands from all
modules.
Configuration
-------------

View File

@ -8,8 +8,8 @@ tasks:
- (MGR_DOWN)
- exec:
mon.a:
- ceph tell mgr.x restful create-key admin
- ceph tell mgr.x restful create-self-signed-cert
- ceph restful create-key admin
- ceph restful create-self-signed-cert
- ceph.restart: [mgr.x]
- workunit:
clients:

View File

@ -691,7 +691,8 @@ if (WITH_MGR)
mgr/PyState.cc
mgr/MgrPyModule.cc
mgr/MgrStandby.cc
mgr/Mgr.cc)
mgr/Mgr.cc
mgr/mgr_commands.cc)
add_executable(ceph-mgr ${mgr_srcs}
$<TARGET_OBJECTS:heap_profiler_objs>)
target_include_directories(ceph-mgr PRIVATE "${PYTHON_INCLUDE_DIRS}")

View File

@ -197,22 +197,6 @@ inline void encode(const char *s, bufferlist& bl)
}
// array
template<class A>
inline void encode_array_nohead(const A a[], int n, bufferlist &bl)
{
for (int i=0; i<n; i++)
encode(a[i], bl);
}
template<class A>
inline void decode_array_nohead(A a[], int n, bufferlist::iterator &p)
{
for (int i=0; i<n; i++)
decode(a[i], p);
}
// -----------------------------
// buffers

View File

@ -2635,7 +2635,7 @@ namespace {
};
bufferlist inbl, outbl;
string outstring;
int ret = client.mon_command(cmd, inbl, &outbl, &outstring);
int ret = client.mgr_command(cmd, inbl, &outbl, &outstring);
if (ret) {
return ret;
}

View File

@ -16,13 +16,14 @@
#define CEPH_MMGRBEACON_H
#include "messages/PaxosServiceMessage.h"
#include "mon/MonCommand.h"
#include "include/types.h"
class MMgrBeacon : public PaxosServiceMessage {
static const int HEAD_VERSION = 3;
static const int HEAD_VERSION = 4;
static const int COMPAT_VERSION = 1;
protected:
@ -33,6 +34,9 @@ protected:
uuid_d fsid;
std::set<std::string> available_modules;
// Only populated during activation
std::vector<MonCommand> command_descs;
public:
MMgrBeacon()
: PaxosServiceMessage(MSG_MGR_BEACON, 0, HEAD_VERSION, COMPAT_VERSION),
@ -56,6 +60,16 @@ public:
const uuid_d& get_fsid() const { return fsid; }
std::set<std::string>& get_available_modules() { return available_modules; }
void set_command_descs(const std::vector<MonCommand> &cmds)
{
command_descs = cmds;
}
const std::vector<MonCommand> &get_command_descs()
{
return command_descs;
}
private:
~MMgrBeacon() override {}
@ -77,6 +91,7 @@ public:
::encode(name, payload);
::encode(fsid, payload);
::encode(available_modules, payload);
::encode(command_descs, payload);
}
void decode_payload() override {
bufferlist::iterator p = payload.begin();
@ -91,6 +106,9 @@ public:
if (header.version >= 3) {
::decode(available_modules, p);
}
if (header.version >= 4) {
::decode(command_descs, p);
}
}
};

View File

@ -17,6 +17,9 @@
#include "auth/RotatingKeyRing.h"
#include "json_spirit/json_spirit_writer.h"
#include "mgr/mgr_commands.h"
#include "mon/MonCommand.h"
#include "messages/MMgrOpen.h"
#include "messages/MMgrConfigure.h"
#include "messages/MMonMgrReport.h"
@ -31,6 +34,8 @@
#undef dout_prefix
#define dout_prefix *_dout << "mgr.server " << __func__ << " "
DaemonServer::DaemonServer(MonClient *monc_,
Finisher &finisher_,
DaemonStateIndex &daemon_state_,
@ -402,24 +407,6 @@ bool DaemonServer::handle_report(MMgrReport *m)
return true;
}
struct MgrCommand {
string cmdstring;
string helpstring;
string module;
string perm;
string availability;
bool requires_perm(char p) const {
return (perm.find(p) != string::npos);
}
} mgr_commands[] = {
#define COMMAND(parsesig, helptext, module, perm, availability) \
{parsesig, helptext, module, perm, availability},
#include "MgrCommands.h"
#undef COMMAND
};
void DaemonServer::_generate_command_map(
map<string,cmd_vartype>& cmdmap,
@ -444,16 +431,14 @@ void DaemonServer::_generate_command_map(
}
}
const MgrCommand *DaemonServer::_get_mgrcommand(
const MonCommand *DaemonServer::_get_mgrcommand(
const string &cmd_prefix,
MgrCommand *cmds,
int cmds_size)
const std::vector<MonCommand> &cmds)
{
MgrCommand *this_cmd = NULL;
for (MgrCommand *cp = cmds;
cp < &cmds[cmds_size]; cp++) {
if (cp->cmdstring.compare(0, cmd_prefix.size(), cmd_prefix) == 0) {
this_cmd = cp;
const MonCommand *this_cmd = nullptr;
for (const auto &cmd : cmds) {
if (cmd.cmdstring.compare(0, cmd_prefix.size(), cmd_prefix) == 0) {
this_cmd = &cmd;
break;
}
}
@ -466,7 +451,7 @@ bool DaemonServer::_allowed_command(
const string &prefix,
const map<string,cmd_vartype>& cmdmap,
const map<string,string>& param_str_map,
const MgrCommand *this_cmd) {
const MonCommand *this_cmd) {
if (s->entity_name.is_mon()) {
// mon is all-powerful. even when it is forwarding commands on behalf of
@ -593,30 +578,29 @@ bool DaemonServer::handle_command(MCommand *m)
dout(4) << "prefix=" << prefix << dendl;
if (prefix == "get_command_descriptions") {
int cmdnum = 0;
dout(10) << "reading commands from python modules" << dendl;
auto py_commands = py_modules.get_commands();
const auto py_commands = py_modules.get_commands();
int cmdnum = 0;
JSONFormatter f;
f.open_object_section("command_descriptions");
for (const auto &pyc : py_commands) {
auto dump_cmd = [&cmdnum, &f](const MonCommand &mc){
ostringstream secname;
secname << "cmd" << setfill('0') << std::setw(3) << cmdnum;
dout(20) << "Dumping " << pyc.cmdstring << " (" << pyc.helpstring
<< ")" << dendl;
dump_cmddesc_to_json(&f, secname.str(), pyc.cmdstring, pyc.helpstring,
"mgr", pyc.perm, "cli", 0);
dump_cmddesc_to_json(&f, secname.str(), mc.cmdstring, mc.helpstring,
mc.module, mc.req_perms, mc.availability, 0);
cmdnum++;
};
for (const auto &pyc : py_commands) {
dump_cmd(pyc);
}
for (const auto &cp : mgr_commands) {
ostringstream secname;
secname << "cmd" << setfill('0') << std::setw(3) << cmdnum;
dump_cmddesc_to_json(&f, secname.str(), cp.cmdstring, cp.helpstring,
cp.module, cp.perm, cp.availability, 0);
cmdnum++;
for (const auto &mgr_cmd : mgr_commands) {
dump_cmd(mgr_cmd);
}
f.close_section(); // command_descriptions
f.flush(cmdctx->odata);
cmdctx->reply(0, ss);
@ -624,11 +608,10 @@ bool DaemonServer::handle_command(MCommand *m)
}
// lookup command
const MgrCommand *mgr_cmd = _get_mgrcommand(prefix, mgr_commands,
ARRAY_SIZE(mgr_commands));
const MonCommand *mgr_cmd = _get_mgrcommand(prefix, mgr_commands);
_generate_command_map(cmdctx->cmdmap, param_str_map);
if (!mgr_cmd) {
MgrCommand py_command = {"", "", "py", "rw", "cli"};
MonCommand py_command = {"", "", "py", "rw", "cli"};
if (!_allowed_command(session.get(), py_command.module, prefix, cmdctx->cmdmap,
param_str_map, &py_command)) {
dout(1) << " access denied" << dendl;
@ -937,7 +920,7 @@ bool DaemonServer::handle_command(MCommand *m)
// None of the special native commands,
MgrPyModule *handler = nullptr;
auto py_commands = py_modules.get_commands();
auto py_commands = py_modules.get_py_commands();
for (const auto &pyc : py_commands) {
auto pyc_prefix = cmddesc_get_prefix(pyc.cmdstring);
dout(1) << "pyc_prefix: '" << pyc_prefix << "'" << dendl;

View File

@ -35,7 +35,7 @@ class MMgrReport;
class MMgrOpen;
class MMonMgrReport;
class MCommand;
struct MgrCommand;
struct MonCommand;
/**
@ -74,13 +74,13 @@ protected:
static void _generate_command_map(map<string,cmd_vartype>& cmdmap,
map<string,string> &param_str_map);
static const MgrCommand *_get_mgrcommand(const string &cmd_prefix,
MgrCommand *cmds, int cmds_size);
static const MonCommand *_get_mgrcommand(const string &cmd_prefix,
const std::vector<MonCommand> &commands);
bool _allowed_command(
MgrSession *s, const string &module, const string &prefix,
const map<string,cmd_vartype>& cmdmap,
const map<string,string>& param_str_map,
const MgrCommand *this_cmd);
const MonCommand *this_cmd);
private:
friend class ReplyOnFinish;

View File

@ -22,6 +22,7 @@
#include "global/signal_handler.h"
#include "mgr/MgrContext.h"
#include "mgr/mgr_commands.h"
#include "MgrPyModule.h"
#include "DaemonServer.h"
@ -632,3 +633,14 @@ void Mgr::tick()
dout(10) << dendl;
server.send_report();
}
std::vector<MonCommand> Mgr::get_command_set() const
{
Mutex::Locker l(lock);
std::vector<MonCommand> commands = mgr_commands;
std::vector<MonCommand> py_commands = py_modules.get_commands();
commands.insert(commands.end(), py_commands.begin(), py_commands.end());
return commands;
}

View File

@ -53,7 +53,7 @@ protected:
Client *client;
Messenger *client_messenger;
Mutex lock;
mutable Mutex lock;
SafeTimer timer;
Finisher finisher;
@ -98,6 +98,8 @@ public:
void background_init(Context *completion);
void shutdown();
std::vector<MonCommand> get_command_set() const;
};
#endif

View File

@ -48,7 +48,8 @@ MgrStandby::MgrStandby(int argc, const char **argv) :
timer(g_ceph_context, lock),
active_mgr(nullptr),
orig_argc(argc),
orig_argv(argv)
orig_argv(argv),
available_in_map(false)
{
}
@ -151,7 +152,11 @@ void MgrStandby::send_beacon()
set<string> modules;
PyModules::list_modules(&modules);
// Whether I think I am available (request MgrMonitor to set me
// as available in the map)
bool available = active_mgr != nullptr && active_mgr->is_initialized();
auto addr = available ? active_mgr->get_server_addr() : entity_addr_t();
dout(10) << "sending beacon as gid " << monc.get_global_id()
<< " modules " << modules << dendl;
@ -162,6 +167,16 @@ void MgrStandby::send_beacon()
addr,
available,
modules);
if (available && !available_in_map) {
// We are informing the mon that we are done initializing: inform
// it of our command set. This has to happen after init() because
// it needs the python modules to have loaded.
m->set_command_descs(active_mgr->get_command_set());
dout(4) << "going active, including " << m->get_command_descs().size()
<< " commands in beacon" << dendl;
}
monc.send_mon_message(m);
}
@ -278,7 +293,7 @@ void MgrStandby::_update_log_config()
void MgrStandby::handle_mgr_map(MMgrMap* mmap)
{
auto map = mmap->get_map();
auto &map = mmap->get_map();
dout(4) << "received map epoch " << map.get_epoch() << dendl;
const bool active_in_map = map.active_gid == monc.get_global_id();
dout(4) << "active in map: " << active_in_map
@ -303,6 +318,11 @@ void MgrStandby::handle_mgr_map(MMgrMap* mmap)
respawn();
}
}
if (!available_in_map && map.get_available()) {
dout(4) << "Map now says I am available" << dendl;
available_in_map = true;
}
} else {
if (active_mgr != nullptr) {
derr << "I was active but no longer am" << dendl;

View File

@ -59,6 +59,8 @@ protected:
void _update_log_config();
void send_beacon();
bool available_in_map;
public:
MgrStandby(int argc, const char **argv);
~MgrStandby() override;

View File

@ -608,12 +608,12 @@ void PyModules::set_config(const std::string &handle,
}
}
std::vector<ModuleCommand> PyModules::get_commands()
std::vector<ModuleCommand> PyModules::get_py_commands() const
{
Mutex::Locker l(lock);
std::vector<ModuleCommand> result;
for (auto& i : modules) {
for (const auto& i : modules) {
auto module = i.second.get();
auto mod_commands = module->get_commands();
for (auto j : mod_commands) {
@ -624,6 +624,17 @@ std::vector<ModuleCommand> PyModules::get_commands()
return result;
}
std::vector<MonCommand> PyModules::get_commands() const
{
std::vector<ModuleCommand> commands = get_py_commands();
std::vector<MonCommand> result;
for (auto &pyc: commands) {
result.push_back({pyc.cmdstring, pyc.helpstring, "mgr",
pyc.perm, "cli", MonCommand::FLAG_MGR});
}
return result;
}
void PyModules::insert_config(const std::map<std::string,
std::string> &new_config)
{

View File

@ -24,6 +24,7 @@
#include "client/Client.h"
#include "common/LogClient.h"
#include "mon/MgrMap.h"
#include "mon/MonCommand.h"
#include "DaemonState.h"
#include "ClusterState.h"
@ -80,7 +81,11 @@ public:
std::map<std::string, std::string> config_cache;
std::vector<ModuleCommand> get_commands();
// Python command definitions, including callback
std::vector<ModuleCommand> get_py_commands() const;
// Monitor command definitions, suitable for CLI
std::vector<MonCommand> get_commands() const;
void insert_config(const std::map<std::string, std::string> &new_config);

14
src/mgr/mgr_commands.cc Normal file
View File

@ -0,0 +1,14 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
#include "mgr_commands.h"
/* The set of statically defined (C++-handled) commands. This
* does not include the Python-defined commands, which are loaded
* in PyModules */
const std::vector<MonCommand> mgr_commands = {
#define COMMAND(parsesig, helptext, module, perm, availability) \
{parsesig, helptext, module, perm, availability, 0},
#include "MgrCommands.h"
#undef COMMAND
};

9
src/mgr/mgr_commands.h Normal file
View File

@ -0,0 +1,9 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
#pragma once
#include "mon/MonCommand.h"
#include <vector>
extern const std::vector<MonCommand> mgr_commands;

View File

@ -20,7 +20,8 @@ set(lib_mon_srcs
DataHealthService.cc
PGMonitor.cc
PGMap.cc
ConfigKeyService.cc)
ConfigKeyService.cc
../mgr/mgr_commands.cc)
add_library(mon STATIC
${lib_mon_srcs}
$<TARGET_OBJECTS:kv_objs>

View File

@ -20,6 +20,7 @@
#include "PGStatService.h"
#include "include/stringify.h"
#include "mgr/MgrContext.h"
#include "mgr/mgr_commands.h"
#include "OSDMonitor.h"
#include "MgrMonitor.h"
@ -35,13 +36,20 @@ static ostream& _prefix(std::ostream *_dout, Monitor *mon,
}
// Prefix for mon store of active mgr's command descriptions
const static std::string command_descs_prefix = "mgr_command_descs";
void MgrMonitor::create_initial()
{
boost::tokenizer<> tok(g_conf->mgr_initial_modules);
for (auto& m : tok) {
pending_map.modules.insert(m);
}
dout(10) << __func__ << " initial modules " << pending_map.modules << dendl;
pending_command_descs = mgr_commands;
dout(10) << __func__ << " initial modules " << pending_map.modules
<< ", " << pending_command_descs.size() << " commands"
<< dendl;
}
void MgrMonitor::update_from_paxos(bool *need_bootstrap)
@ -54,6 +62,9 @@ void MgrMonitor::update_from_paxos(bool *need_bootstrap)
int err = get_version(version, bl);
assert(err == 0);
bool old_available = map.get_available();
uint64_t old_gid = map.get_active_gid();
bufferlist::iterator p = bl.begin();
map.decode(p);
@ -71,6 +82,21 @@ void MgrMonitor::update_from_paxos(bool *need_bootstrap)
}
check_subs();
if (version == 1
|| (map.get_available()
&& (!old_available || old_gid != map.get_active_gid()))) {
dout(4) << "mkfs or daemon transitioned to available, loading commands"
<< dendl;
bufferlist loaded_commands;
int r = mon->store->get(command_descs_prefix, "", loaded_commands);
if (r < 0) {
derr << "Failed to load mgr commands: " << cpp_strerror(r) << dendl;
} else {
auto p = loaded_commands.begin();
::decode(command_descs, p);
}
}
}
// feed our pet MgrClient
@ -125,6 +151,18 @@ void MgrMonitor::encode_pending(MonitorDBStore::TransactionRef t)
put_value(t, "ever_had_active_mgr", 1);
}
encode_health(next, t);
if (pending_command_descs.size()) {
dout(4) << __func__ << " encoding " << pending_command_descs.size()
<< " command_descs" << dendl;
for (auto& p : pending_command_descs) {
p.set_flag(MonCommand::FLAG_MGR);
}
bufferlist bl;
::encode(pending_command_descs, bl);
t->put(command_descs_prefix, "", bl);
pending_command_descs.clear();
}
}
bool MgrMonitor::check_caps(MonOpRequestRef op, const uuid_d& fsid)
@ -255,6 +293,21 @@ bool MgrMonitor::prepare_beacon(MonOpRequestRef op)
dout(4) << "available " << m->get_gid() << dendl;
mon->clog->info() << "Manager daemon " << pending_map.active_name
<< " is now available";
// This beacon should include command descriptions
pending_command_descs = m->get_command_descs();
if (pending_command_descs.empty()) {
// This should not happen, but it also isn't fatal: we just
// won't successfully update our list of commands.
dout(4) << "First available beacon from " << pending_map.active_name
<< "(" << m->get_gid() << ") does not include command descs"
<< dendl;
} else {
dout(4) << "First available beacon from " << pending_map.active_name
<< "(" << m->get_gid() << ") includes "
<< pending_command_descs.size() << " command descs" << dendl;
}
pending_map.available = m->get_available();
updated = true;
}

View File

@ -17,6 +17,7 @@
#include "include/Context.h"
#include "MgrMap.h"
#include "PaxosService.h"
#include "MonCommand.h"
class MgrMonitor: public PaxosService
{
@ -45,6 +46,10 @@ class MgrMonitor: public PaxosService
health_status_t should_warn_about_mgr_down();
// Command descriptions we've learned from the active mgr
std::vector<MonCommand> command_descs;
std::vector<MonCommand> pending_command_descs;
public:
MgrMonitor(Monitor *mn, Paxos *p, const string& service_name)
: PaxosService(mn, p, service_name)
@ -88,6 +93,10 @@ public:
void print_summary(Formatter *f, std::ostream *ss) const;
const std::vector<MonCommand> &get_command_descs() const {
return command_descs;
}
friend class C_Updated;
};

127
src/mon/MonCommand.h Normal file
View File

@ -0,0 +1,127 @@
// -*- 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) 2017 John Spray <john.spray@redhat.com>
*
* 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.
*/
#pragma once
#include <string>
#include "include/encoding.h"
struct MonCommand {
std::string cmdstring;
std::string helpstring;
std::string module;
std::string req_perms;
std::string availability;
uint64_t flags;
// MonCommand flags
static const uint64_t FLAG_NONE = 0;
static const uint64_t FLAG_NOFORWARD = 1 << 0;
static const uint64_t FLAG_OBSOLETE = 1 << 1;
static const uint64_t FLAG_DEPRECATED = 1 << 2;
static const uint64_t FLAG_MGR = 1 << 3;
bool has_flag(uint64_t flag) const { return (flags & flag) != 0; }
void set_flag(uint64_t flag) { flags |= flag; }
void unset_flag(uint64_t flag) { flags &= ~flag; }
void encode(bufferlist &bl) const {
ENCODE_START(1, 1, bl);
encode_bare(bl);
::encode(flags, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::iterator &bl) {
DECODE_START(1, bl);
decode_bare(bl);
::decode(flags, bl);
DECODE_FINISH(bl);
}
/**
* Unversioned encoding for use within encode_array.
*/
void encode_bare(bufferlist &bl) const {
::encode(cmdstring, bl);
::encode(helpstring, bl);
::encode(module, bl);
::encode(req_perms, bl);
::encode(availability, bl);
}
void decode_bare(bufferlist::iterator &bl) {
::decode(cmdstring, bl);
::decode(helpstring, bl);
::decode(module, bl);
::decode(req_perms, bl);
::decode(availability, bl);
}
bool is_compat(const MonCommand* o) const {
return cmdstring == o->cmdstring &&
module == o->module && req_perms == o->req_perms &&
availability == o->availability;
}
bool is_noforward() const {
return has_flag(MonCommand::FLAG_NOFORWARD);
}
bool is_obsolete() const {
return has_flag(MonCommand::FLAG_OBSOLETE);
}
bool is_deprecated() const {
return has_flag(MonCommand::FLAG_DEPRECATED);
}
bool is_mgr() const {
return has_flag(MonCommand::FLAG_MGR);
}
static void encode_array(const MonCommand *cmds, int size, bufferlist &bl) {
ENCODE_START(2, 1, bl);
uint16_t s = size;
::encode(s, bl);
for (int i = 0; i < size; ++i) {
cmds[i].encode_bare(bl);
}
for (int i = 0; i < size; i++) {
::encode(cmds[i].flags, bl);
}
ENCODE_FINISH(bl);
}
static void decode_array(MonCommand **cmds, int *size,
bufferlist::iterator &bl) {
DECODE_START(2, bl);
uint16_t s = 0;
::decode(s, bl);
*size = s;
*cmds = new MonCommand[*size];
for (int i = 0; i < *size; ++i) {
(*cmds)[i].decode_bare(bl);
}
if (struct_v >= 2) {
for (int i = 0; i < *size; i++)
::decode((*cmds)[i].flags, bl);
} else {
for (int i = 0; i < *size; i++)
(*cmds)[i].flags = 0;
}
DECODE_FINISH(bl);
}
bool requires_perm(char p) const {
return (req_perms.find(p) != std::string::npos);
}
};
WRITE_CLASS_ENCODER(MonCommand)

View File

@ -112,17 +112,6 @@ MonCommand mon_commands[] = {
#include <mon/MonCommands.h>
#undef COMMAND
#undef COMMAND_WITH_FLAG
// FIXME: slurp up the Mgr commands too
#define COMMAND(parsesig, helptext, modulename, req_perms, avail) \
{parsesig, helptext, modulename, req_perms, avail, FLAG(MGR)},
#define COMMAND_WITH_FLAG(parsesig, helptext, modulename, req_perms, avail, flags) \
{parsesig, helptext, modulename, req_perms, avail, flags | FLAG(MGR)},
#include <mgr/MgrCommands.h>
#undef COMMAND
#undef COMMAND_WITH_FLAG
};
@ -2821,11 +2810,13 @@ void Monitor::_generate_command_map(map<string,cmd_vartype>& cmdmap,
}
}
const MonCommand *Monitor::_get_moncommand(const string &cmd_prefix,
MonCommand *cmds, int cmds_size)
const MonCommand *Monitor::_get_moncommand(
const string &cmd_prefix,
const MonCommand *cmds,
int cmds_size)
{
MonCommand *this_cmd = NULL;
for (MonCommand *cp = cmds;
const MonCommand *this_cmd = NULL;
for (const MonCommand *cp = cmds;
cp < &cmds[cmds_size]; cp++) {
if (cp->cmdstring.compare(0, cmd_prefix.size(), cmd_prefix) == 0) {
this_cmd = cp;
@ -2855,26 +2846,23 @@ bool Monitor::_allowed_command(MonSession *s, string &module, string &prefix,
return capable;
}
void Monitor::format_command_descriptions(const MonCommand *commands,
unsigned commands_size,
void Monitor::format_command_descriptions(const std::vector<MonCommand> &commands,
Formatter *f,
bufferlist *rdata,
bool hide_mgr_flag)
{
int cmdnum = 0;
f->open_object_section("command_descriptions");
for (const MonCommand *cp = commands;
cp < &commands[commands_size]; cp++) {
unsigned flags = cp->flags;
for (const auto &cmd : commands) {
unsigned flags = cmd.flags;
if (hide_mgr_flag) {
flags &= ~MonCommand::FLAG_MGR;
}
ostringstream secname;
secname << "cmd" << setfill('0') << std::setw(3) << cmdnum;
dump_cmddesc_to_json(f, secname.str(),
cp->cmdstring, cp->helpstring, cp->module,
cp->req_perms, cp->availability, flags);
cmd.cmdstring, cmd.helpstring, cmd.module,
cmd.req_perms, cmd.availability, flags);
cmdnum++;
}
f->close_section(); // command_descriptions
@ -2985,9 +2973,16 @@ void Monitor::handle_command(MonOpRequestRef op)
// hide mgr commands until luminous upgrade is complete
bool hide_mgr_flag =
osdmon()->osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS;
format_command_descriptions(leader_supported_mon_commands,
leader_supported_mon_commands_size, f, &rdata,
hide_mgr_flag);
std::vector<MonCommand> commands;
commands = static_cast<MgrMonitor*>(
paxos_service[PAXOS_MGR])->get_command_descs();
for (int i = 0 ; i < leader_supported_mon_commands_size; ++i) {
commands.push_back(leader_supported_mon_commands[i]);
}
format_command_descriptions(commands, f, &rdata, hide_mgr_flag);
delete f;
reply_command(op, 0, "", rdata, 0);
return;
@ -3017,17 +3012,26 @@ void Monitor::handle_command(MonOpRequestRef op)
// validate command is in leader map
const MonCommand *leader_cmd;
const auto& mgr_cmds = mgrmon()->get_command_descs();
const MonCommand *mgr_cmd = _get_moncommand(prefix, &mgr_cmds.at(0),
mgr_cmds.size());
leader_cmd = _get_moncommand(prefix,
// the boost underlying this isn't const for some reason
const_cast<MonCommand*>(leader_supported_mon_commands),
leader_supported_mon_commands_size);
if (!leader_cmd) {
leader_cmd = mgr_cmd;
if (!leader_cmd) {
reply_command(op, -EINVAL, "command not known", 0);
return;
}
}
// validate command is in our map & matches, or forward if it is allowed
const MonCommand *mon_cmd = _get_moncommand(prefix, mon_commands,
ARRAY_SIZE(mon_commands));
if (!mon_cmd) {
mon_cmd = mgr_cmd;
}
if (!is_leader()) {
if (!mon_cmd) {
if (leader_cmd->is_noforward()) {

View File

@ -38,6 +38,7 @@
#include "Paxos.h"
#include "Session.h"
#include "PGStatService.h"
#include "MonCommand.h"
#include "common/LogClient.h"
#include "auth/cephx/CephxKeyServer.h"
@ -113,7 +114,6 @@ struct MRoute;
struct MForward;
struct MTimeCheck;
struct MMonHealth;
struct MonCommand;
#define COMPAT_SET_LOC "feature_set"
@ -685,8 +685,10 @@ public:
static void _generate_command_map(map<string,cmd_vartype>& cmdmap,
map<string,string> &param_str_map);
static const MonCommand *_get_moncommand(const string &cmd_prefix,
MonCommand *cmds, int cmds_size);
static const MonCommand *_get_moncommand(
const string &cmd_prefix,
const MonCommand *cmds,
int cmds_size);
bool _allowed_command(MonSession *s, string &module, string &prefix,
const map<string,cmd_vartype>& cmdmap,
const map<string,string>& param_str_map,
@ -960,8 +962,7 @@ private:
Monitor& operator=(const Monitor &rhs);
public:
static void format_command_descriptions(const MonCommand *commands,
unsigned commands_size,
static void format_command_descriptions(const std::vector<MonCommand> &commands,
Formatter *f,
bufferlist *rdata,
bool hide_mgr_flag=false);
@ -981,97 +982,6 @@ public:
#define CEPH_MON_FEATURE_INCOMPAT_KRAKEN CompatSet::Feature(8, "support monmap features")
// make sure you add your feature to Monitor::get_supported_features
struct MonCommand {
string cmdstring;
string helpstring;
string module;
string req_perms;
string availability;
uint64_t flags;
// MonCommand flags
static const uint64_t FLAG_NONE = 0;
static const uint64_t FLAG_NOFORWARD = 1 << 0;
static const uint64_t FLAG_OBSOLETE = 1 << 1;
static const uint64_t FLAG_DEPRECATED = 1 << 2;
static const uint64_t FLAG_MGR = 1 << 3;
bool has_flag(uint64_t flag) const { return (flags & flag) != 0; }
void set_flag(uint64_t flag) { flags |= flag; }
void unset_flag(uint64_t flag) { flags &= ~flag; }
void encode(bufferlist &bl) const {
/*
* very naughty: deliberately unversioned because individual commands
* shouldn't be encoded standalone, only as a full set (which we do
* version, see encode_array() below).
*/
::encode(cmdstring, bl);
::encode(helpstring, bl);
::encode(module, bl);
::encode(req_perms, bl);
::encode(availability, bl);
}
void decode(bufferlist::iterator &bl) {
::decode(cmdstring, bl);
::decode(helpstring, bl);
::decode(module, bl);
::decode(req_perms, bl);
::decode(availability, bl);
}
bool is_compat(const MonCommand* o) const {
return cmdstring == o->cmdstring &&
module == o->module && req_perms == o->req_perms &&
availability == o->availability;
}
bool is_noforward() const {
return has_flag(MonCommand::FLAG_NOFORWARD);
}
bool is_obsolete() const {
return has_flag(MonCommand::FLAG_OBSOLETE);
}
bool is_deprecated() const {
return has_flag(MonCommand::FLAG_DEPRECATED);
}
bool is_mgr() const {
return has_flag(MonCommand::FLAG_MGR);
}
static void encode_array(const MonCommand *cmds, int size, bufferlist &bl) {
ENCODE_START(2, 1, bl);
uint16_t s = size;
::encode(s, bl);
::encode_array_nohead(cmds, size, bl);
for (int i = 0; i < size; i++)
::encode(cmds[i].flags, bl);
ENCODE_FINISH(bl);
}
static void decode_array(MonCommand **cmds, int *size,
bufferlist::iterator &bl) {
DECODE_START(2, bl);
uint16_t s = 0;
::decode(s, bl);
*size = s;
*cmds = new MonCommand[*size];
::decode_array_nohead(*cmds, *size, bl);
if (struct_v >= 2) {
for (int i = 0; i < *size; i++)
::decode((*cmds)[i].flags, bl);
} else {
for (int i = 0; i < *size; i++)
(*cmds)[i].flags = 0;
}
DECODE_FINISH(bl);
}
bool requires_perm(char p) const {
return (req_perms.find(p) != string::npos);
}
};
WRITE_CLASS_ENCODER(MonCommand)
#endif

View File

@ -58,7 +58,7 @@ struct Page {
::encode(offset, bl);
}
void decode(bufferlist::iterator &p, size_t page_size) {
::decode_array_nohead(data, page_size, p);
p.copy(page_size, data);
::decode(offset, p);
}

View File

@ -30,7 +30,7 @@ class Root(RestController):
return {
'api_version': 1,
'auth':
'Use "ceph tell mgr restful create_key <key>" to create a key pair, '
'Use "ceph restful create_key <key>" to create a key pair, '
'pass it as HTTP Basic auth to authenticate',
'doc': 'See /doc endpoint',
'info': "Ceph Manager RESTful API server",

View File

@ -41,11 +41,11 @@ static void usage(ostream &out)
out << " get_command_descriptions --pull585\n";
}
static void json_print(const MonCommand *mon_commands, int size)
static void json_print(const std::vector<MonCommand> &mon_commands)
{
bufferlist rdata;
Formatter *f = Formatter::create("json");
Monitor::format_command_descriptions(mon_commands, size, f, &rdata);
Monitor::format_command_descriptions(mon_commands, f, &rdata);
delete f;
string data(rdata.c_str(), rdata.length());
cout << data << std::endl;
@ -56,7 +56,7 @@ static void all()
#undef FLAG
#undef COMMAND
#undef COMMAND_WITH_FLAG
MonCommand mon_commands[] = {
std::vector<MonCommand> mon_commands = {
#define FLAG(f) (MonCommand::FLAG_##f)
#define COMMAND(parsesig, helptext, modulename, req_perms, avail) \
{parsesig, helptext, modulename, req_perms, avail, 0},
@ -66,8 +66,6 @@ static void all()
#undef COMMAND
#undef COMMAND_WITH_FLAG
// FIXME: slurp up the Mgr commands too
#define COMMAND(parsesig, helptext, modulename, req_perms, avail) \
{parsesig, helptext, modulename, req_perms, avail, FLAG(MGR)},
#define COMMAND_WITH_FLAG(parsesig, helptext, modulename, req_perms, avail, flags) \
@ -77,13 +75,13 @@ static void all()
#undef COMMAND_WITH_FLAG
};
json_print(mon_commands, ARRAY_SIZE(mon_commands));
json_print(mon_commands);
}
// syntax error https://github.com/ceph/ceph/pull/585
static void pull585()
{
MonCommand mon_commands[] = {
std::vector<MonCommand> mon_commands = {
{ "osd pool create "
"name=pool,type=CephPoolname "
"name=pg_num,type=CephInt,range=0 "
@ -92,7 +90,7 @@ static void pull585()
"create pool", "osd", "rw", "cli,rest" }
};
json_print(mon_commands, ARRAY_SIZE(mon_commands));
json_print(mon_commands);
}
int main(int argc, char **argv) {

View File

@ -661,9 +661,9 @@ EOF
run 'mgr' $CEPH_BIN/ceph-mgr -i $name $ARGS
done
if ceph_adm tell mgr restful create-self-signed-cert; then
if ceph_adm restful create-self-signed-cert; then
SF=`mktemp`
ceph_adm tell mgr restful create-key admin -o $SF
ceph_adm restful create-key admin -o $SF
RESTFUL_SECRET=`cat $SF`
rm $SF
else