mirror of
https://github.com/ceph/ceph
synced 2025-04-11 04:02:04 +00:00
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:
commit
0193e38b3f
@ -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
|
||||
-------------
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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}")
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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> ¶m_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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
14
src/mgr/mgr_commands.cc
Normal 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
9
src/mgr/mgr_commands.h
Normal 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;
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
127
src/mon/MonCommand.h
Normal 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)
|
@ -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()) {
|
||||
|
@ -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> ¶m_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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user