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 Calling module commands
----------------------- -----------------------
Where a module implements command line hooks, using the Ceph CLI's Where a module implements command line hooks, the commands will
``tell`` command to call them like this:: 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, Note that it is not necessary to address a particular mgr instance,
simply ``mgr`` will pick the current active daemon. simply ``mgr`` will pick the current active daemon.
Use the ``help`` command to get a list of available commands from all
modules.
Configuration Configuration
------------- -------------

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,6 +22,7 @@
#include "global/signal_handler.h" #include "global/signal_handler.h"
#include "mgr/MgrContext.h" #include "mgr/MgrContext.h"
#include "mgr/mgr_commands.h"
#include "MgrPyModule.h" #include "MgrPyModule.h"
#include "DaemonServer.h" #include "DaemonServer.h"
@ -632,3 +633,14 @@ void Mgr::tick()
dout(10) << dendl; dout(10) << dendl;
server.send_report(); 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; Client *client;
Messenger *client_messenger; Messenger *client_messenger;
Mutex lock; mutable Mutex lock;
SafeTimer timer; SafeTimer timer;
Finisher finisher; Finisher finisher;
@ -98,6 +98,8 @@ public:
void background_init(Context *completion); void background_init(Context *completion);
void shutdown(); void shutdown();
std::vector<MonCommand> get_command_set() const;
}; };
#endif #endif

View File

@ -48,7 +48,8 @@ MgrStandby::MgrStandby(int argc, const char **argv) :
timer(g_ceph_context, lock), timer(g_ceph_context, lock),
active_mgr(nullptr), active_mgr(nullptr),
orig_argc(argc), orig_argc(argc),
orig_argv(argv) orig_argv(argv),
available_in_map(false)
{ {
} }
@ -151,7 +152,11 @@ void MgrStandby::send_beacon()
set<string> modules; set<string> modules;
PyModules::list_modules(&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(); bool available = active_mgr != nullptr && active_mgr->is_initialized();
auto addr = available ? active_mgr->get_server_addr() : entity_addr_t(); auto addr = available ? active_mgr->get_server_addr() : entity_addr_t();
dout(10) << "sending beacon as gid " << monc.get_global_id() dout(10) << "sending beacon as gid " << monc.get_global_id()
<< " modules " << modules << dendl; << " modules " << modules << dendl;
@ -162,6 +167,16 @@ void MgrStandby::send_beacon()
addr, addr,
available, available,
modules); 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); monc.send_mon_message(m);
} }
@ -278,7 +293,7 @@ void MgrStandby::_update_log_config()
void MgrStandby::handle_mgr_map(MMgrMap* mmap) 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; dout(4) << "received map epoch " << map.get_epoch() << dendl;
const bool active_in_map = map.active_gid == monc.get_global_id(); const bool active_in_map = map.active_gid == monc.get_global_id();
dout(4) << "active in map: " << active_in_map dout(4) << "active in map: " << active_in_map
@ -303,6 +318,11 @@ void MgrStandby::handle_mgr_map(MMgrMap* mmap)
respawn(); respawn();
} }
} }
if (!available_in_map && map.get_available()) {
dout(4) << "Map now says I am available" << dendl;
available_in_map = true;
}
} else { } else {
if (active_mgr != nullptr) { if (active_mgr != nullptr) {
derr << "I was active but no longer am" << dendl; derr << "I was active but no longer am" << dendl;

View File

@ -59,6 +59,8 @@ protected:
void _update_log_config(); void _update_log_config();
void send_beacon(); void send_beacon();
bool available_in_map;
public: public:
MgrStandby(int argc, const char **argv); MgrStandby(int argc, const char **argv);
~MgrStandby() override; ~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); Mutex::Locker l(lock);
std::vector<ModuleCommand> result; std::vector<ModuleCommand> result;
for (auto& i : modules) { for (const auto& i : modules) {
auto module = i.second.get(); auto module = i.second.get();
auto mod_commands = module->get_commands(); auto mod_commands = module->get_commands();
for (auto j : mod_commands) { for (auto j : mod_commands) {
@ -624,6 +624,17 @@ std::vector<ModuleCommand> PyModules::get_commands()
return result; 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, void PyModules::insert_config(const std::map<std::string,
std::string> &new_config) std::string> &new_config)
{ {

View File

@ -24,6 +24,7 @@
#include "client/Client.h" #include "client/Client.h"
#include "common/LogClient.h" #include "common/LogClient.h"
#include "mon/MgrMap.h" #include "mon/MgrMap.h"
#include "mon/MonCommand.h"
#include "DaemonState.h" #include "DaemonState.h"
#include "ClusterState.h" #include "ClusterState.h"
@ -80,7 +81,11 @@ public:
std::map<std::string, std::string> config_cache; 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); 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 DataHealthService.cc
PGMonitor.cc PGMonitor.cc
PGMap.cc PGMap.cc
ConfigKeyService.cc) ConfigKeyService.cc
../mgr/mgr_commands.cc)
add_library(mon STATIC add_library(mon STATIC
${lib_mon_srcs} ${lib_mon_srcs}
$<TARGET_OBJECTS:kv_objs> $<TARGET_OBJECTS:kv_objs>

View File

@ -20,6 +20,7 @@
#include "PGStatService.h" #include "PGStatService.h"
#include "include/stringify.h" #include "include/stringify.h"
#include "mgr/MgrContext.h" #include "mgr/MgrContext.h"
#include "mgr/mgr_commands.h"
#include "OSDMonitor.h" #include "OSDMonitor.h"
#include "MgrMonitor.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() void MgrMonitor::create_initial()
{ {
boost::tokenizer<> tok(g_conf->mgr_initial_modules); boost::tokenizer<> tok(g_conf->mgr_initial_modules);
for (auto& m : tok) { for (auto& m : tok) {
pending_map.modules.insert(m); 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) 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); int err = get_version(version, bl);
assert(err == 0); assert(err == 0);
bool old_available = map.get_available();
uint64_t old_gid = map.get_active_gid();
bufferlist::iterator p = bl.begin(); bufferlist::iterator p = bl.begin();
map.decode(p); map.decode(p);
@ -71,6 +82,21 @@ void MgrMonitor::update_from_paxos(bool *need_bootstrap)
} }
check_subs(); 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 // feed our pet MgrClient
@ -125,6 +151,18 @@ void MgrMonitor::encode_pending(MonitorDBStore::TransactionRef t)
put_value(t, "ever_had_active_mgr", 1); put_value(t, "ever_had_active_mgr", 1);
} }
encode_health(next, t); 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) 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; dout(4) << "available " << m->get_gid() << dendl;
mon->clog->info() << "Manager daemon " << pending_map.active_name mon->clog->info() << "Manager daemon " << pending_map.active_name
<< " is now available"; << " 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(); pending_map.available = m->get_available();
updated = true; updated = true;
} }

View File

@ -17,6 +17,7 @@
#include "include/Context.h" #include "include/Context.h"
#include "MgrMap.h" #include "MgrMap.h"
#include "PaxosService.h" #include "PaxosService.h"
#include "MonCommand.h"
class MgrMonitor: public PaxosService class MgrMonitor: public PaxosService
{ {
@ -45,6 +46,10 @@ class MgrMonitor: public PaxosService
health_status_t should_warn_about_mgr_down(); 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: public:
MgrMonitor(Monitor *mn, Paxos *p, const string& service_name) MgrMonitor(Monitor *mn, Paxos *p, const string& service_name)
: PaxosService(mn, p, service_name) : PaxosService(mn, p, service_name)
@ -88,6 +93,10 @@ public:
void print_summary(Formatter *f, std::ostream *ss) const; void print_summary(Formatter *f, std::ostream *ss) const;
const std::vector<MonCommand> &get_command_descs() const {
return command_descs;
}
friend class C_Updated; 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> #include <mon/MonCommands.h>
#undef COMMAND #undef COMMAND
#undef COMMAND_WITH_FLAG #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, const MonCommand *Monitor::_get_moncommand(
MonCommand *cmds, int cmds_size) const string &cmd_prefix,
const MonCommand *cmds,
int cmds_size)
{ {
MonCommand *this_cmd = NULL; const MonCommand *this_cmd = NULL;
for (MonCommand *cp = cmds; for (const MonCommand *cp = cmds;
cp < &cmds[cmds_size]; cp++) { cp < &cmds[cmds_size]; cp++) {
if (cp->cmdstring.compare(0, cmd_prefix.size(), cmd_prefix) == 0) { if (cp->cmdstring.compare(0, cmd_prefix.size(), cmd_prefix) == 0) {
this_cmd = cp; this_cmd = cp;
@ -2855,26 +2846,23 @@ bool Monitor::_allowed_command(MonSession *s, string &module, string &prefix,
return capable; return capable;
} }
void Monitor::format_command_descriptions(const MonCommand *commands, void Monitor::format_command_descriptions(const std::vector<MonCommand> &commands,
unsigned commands_size,
Formatter *f, Formatter *f,
bufferlist *rdata, bufferlist *rdata,
bool hide_mgr_flag) bool hide_mgr_flag)
{ {
int cmdnum = 0; int cmdnum = 0;
f->open_object_section("command_descriptions"); f->open_object_section("command_descriptions");
for (const MonCommand *cp = commands; for (const auto &cmd : commands) {
cp < &commands[commands_size]; cp++) { unsigned flags = cmd.flags;
unsigned flags = cp->flags;
if (hide_mgr_flag) { if (hide_mgr_flag) {
flags &= ~MonCommand::FLAG_MGR; flags &= ~MonCommand::FLAG_MGR;
} }
ostringstream secname; ostringstream secname;
secname << "cmd" << setfill('0') << std::setw(3) << cmdnum; secname << "cmd" << setfill('0') << std::setw(3) << cmdnum;
dump_cmddesc_to_json(f, secname.str(), dump_cmddesc_to_json(f, secname.str(),
cp->cmdstring, cp->helpstring, cp->module, cmd.cmdstring, cmd.helpstring, cmd.module,
cp->req_perms, cp->availability, flags); cmd.req_perms, cmd.availability, flags);
cmdnum++; cmdnum++;
} }
f->close_section(); // command_descriptions f->close_section(); // command_descriptions
@ -2985,9 +2973,16 @@ void Monitor::handle_command(MonOpRequestRef op)
// hide mgr commands until luminous upgrade is complete // hide mgr commands until luminous upgrade is complete
bool hide_mgr_flag = bool hide_mgr_flag =
osdmon()->osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS; osdmon()->osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS;
format_command_descriptions(leader_supported_mon_commands,
leader_supported_mon_commands_size, f, &rdata, std::vector<MonCommand> commands;
hide_mgr_flag); 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; delete f;
reply_command(op, 0, "", rdata, 0); reply_command(op, 0, "", rdata, 0);
return; return;
@ -3017,17 +3012,26 @@ void Monitor::handle_command(MonOpRequestRef op)
// validate command is in leader map // validate command is in leader map
const MonCommand *leader_cmd; 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, leader_cmd = _get_moncommand(prefix,
// the boost underlying this isn't const for some reason // the boost underlying this isn't const for some reason
const_cast<MonCommand*>(leader_supported_mon_commands), const_cast<MonCommand*>(leader_supported_mon_commands),
leader_supported_mon_commands_size); leader_supported_mon_commands_size);
if (!leader_cmd) { if (!leader_cmd) {
reply_command(op, -EINVAL, "command not known", 0); leader_cmd = mgr_cmd;
return; 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 // validate command is in our map & matches, or forward if it is allowed
const MonCommand *mon_cmd = _get_moncommand(prefix, mon_commands, const MonCommand *mon_cmd = _get_moncommand(prefix, mon_commands,
ARRAY_SIZE(mon_commands)); ARRAY_SIZE(mon_commands));
if (!mon_cmd) {
mon_cmd = mgr_cmd;
}
if (!is_leader()) { if (!is_leader()) {
if (!mon_cmd) { if (!mon_cmd) {
if (leader_cmd->is_noforward()) { if (leader_cmd->is_noforward()) {

View File

@ -38,6 +38,7 @@
#include "Paxos.h" #include "Paxos.h"
#include "Session.h" #include "Session.h"
#include "PGStatService.h" #include "PGStatService.h"
#include "MonCommand.h"
#include "common/LogClient.h" #include "common/LogClient.h"
#include "auth/cephx/CephxKeyServer.h" #include "auth/cephx/CephxKeyServer.h"
@ -113,7 +114,6 @@ struct MRoute;
struct MForward; struct MForward;
struct MTimeCheck; struct MTimeCheck;
struct MMonHealth; struct MMonHealth;
struct MonCommand;
#define COMPAT_SET_LOC "feature_set" #define COMPAT_SET_LOC "feature_set"
@ -685,8 +685,10 @@ public:
static void _generate_command_map(map<string,cmd_vartype>& cmdmap, static void _generate_command_map(map<string,cmd_vartype>& cmdmap,
map<string,string> &param_str_map); map<string,string> &param_str_map);
static const MonCommand *_get_moncommand(const string &cmd_prefix, static const MonCommand *_get_moncommand(
MonCommand *cmds, int cmds_size); const string &cmd_prefix,
const MonCommand *cmds,
int cmds_size);
bool _allowed_command(MonSession *s, string &module, string &prefix, bool _allowed_command(MonSession *s, string &module, string &prefix,
const map<string,cmd_vartype>& cmdmap, const map<string,cmd_vartype>& cmdmap,
const map<string,string>& param_str_map, const map<string,string>& param_str_map,
@ -960,8 +962,7 @@ private:
Monitor& operator=(const Monitor &rhs); Monitor& operator=(const Monitor &rhs);
public: public:
static void format_command_descriptions(const MonCommand *commands, static void format_command_descriptions(const std::vector<MonCommand> &commands,
unsigned commands_size,
Formatter *f, Formatter *f,
bufferlist *rdata, bufferlist *rdata,
bool hide_mgr_flag=false); bool hide_mgr_flag=false);
@ -981,97 +982,6 @@ public:
#define CEPH_MON_FEATURE_INCOMPAT_KRAKEN CompatSet::Feature(8, "support monmap features") #define CEPH_MON_FEATURE_INCOMPAT_KRAKEN CompatSet::Feature(8, "support monmap features")
// make sure you add your feature to Monitor::get_supported_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 #endif

View File

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

View File

@ -30,7 +30,7 @@ class Root(RestController):
return { return {
'api_version': 1, 'api_version': 1,
'auth': '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', 'pass it as HTTP Basic auth to authenticate',
'doc': 'See /doc endpoint', 'doc': 'See /doc endpoint',
'info': "Ceph Manager RESTful API server", 'info': "Ceph Manager RESTful API server",

View File

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

View File

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