r = load_commands();
if (r != 0) {
- derr << "Missing COMMAND attr in module '" << module_name << "'" << dendl;
- error_string = "Missing COMMAND attribute";
+ derr << "Missing or invalid COMMANDS attribute in module '"
+ << module_name << "'" << dendl;
+ error_string = "Missing or invalid COMMANDS attribute";
+ return r;
+ }
+
+ r = load_options();
+ if (r != 0) {
+ derr << "Missing or invalid OPTIONS attribute in module '"
+ << module_name << "'" << dendl;
+ error_string = "Missing or invalid OPTIONS attribute";
return r;
}
return 0;
}
-int PyModule::load_commands()
+int PyModule::walk_dict_list(
+ const std::string &attr_name,
+ std::function<int(PyObject*)> fn)
{
- // Don't need a Gil here -- this is called from load(),
- // which already has one.
- PyObject *command_list = PyObject_GetAttrString(pClass, "COMMANDS");
+ PyObject *command_list = PyObject_GetAttrString(pClass, attr_name.c_str());
if (command_list == nullptr) {
- // Even modules that don't define command should still have the COMMANDS
- // from the MgrModule definition. Something is wrong!
- derr << "Module " << get_name() << " has missing COMMANDS member" << dendl;
+ derr << "Module " << get_name() << " has missing " << attr_name
+ << " member" << dendl;
return -EINVAL;
}
if (!PyObject_TypeCheck(command_list, &PyList_Type)) {
// Relatively easy mistake for human to make, e.g. defining COMMANDS
// as a {} instead of a []
- derr << "Module " << get_name() << " has COMMANDS member of wrong type ("
- "should be a list)" << dendl;
+ derr << "Module " << get_name() << " has " << attr_name
+ << " member of wrong type (should be a list)" << dendl;
return -EINVAL;
}
+
+ // Invoke fn on each item in the list
+ int r = 0;
const size_t list_size = PyList_Size(command_list);
for (size_t i = 0; i < list_size; ++i) {
PyObject *command = PyList_GetItem(command_list, i);
assert(command != nullptr);
- ModuleCommand item;
+ if (!PyDict_Check(command)) {
+ derr << "Module " << get_name() << " has non-dict entry "
+ << "in " << attr_name << " list" << dendl;
+ return -EINVAL;
+ }
- PyObject *pCmd = PyDict_GetItemString(command, "cmd");
+ r = fn(command);
+ if (r != 0) {
+ break;
+ }
+ }
+ Py_DECREF(command_list);
+
+ return r;
+}
+
+int PyModule::load_commands()
+{
+ int r = walk_dict_list("COMMANDS", [this](PyObject *pCommand) -> int {
+ ModuleCommand command;
+
+ PyObject *pCmd = PyDict_GetItemString(pCommand, "cmd");
assert(pCmd != nullptr);
- item.cmdstring = PyString_AsString(pCmd);
+ command.cmdstring = PyString_AsString(pCmd);
- dout(20) << "loaded command " << item.cmdstring << dendl;
+ dout(20) << "loaded command " << command.cmdstring << dendl;
- PyObject *pDesc = PyDict_GetItemString(command, "desc");
+ PyObject *pDesc = PyDict_GetItemString(pCommand, "desc");
assert(pDesc != nullptr);
- item.helpstring = PyString_AsString(pDesc);
+ command.helpstring = PyString_AsString(pDesc);
- PyObject *pPerm = PyDict_GetItemString(command, "perm");
+ PyObject *pPerm = PyDict_GetItemString(pCommand, "perm");
assert(pPerm != nullptr);
- item.perm = PyString_AsString(pPerm);
+ command.perm = PyString_AsString(pPerm);
- item.polling = false;
- PyObject *pPoll = PyDict_GetItemString(command, "poll");
+ command.polling = false;
+ PyObject *pPoll = PyDict_GetItemString(pCommand, "poll");
if (pPoll) {
std::string polling = PyString_AsString(pPoll);
if (boost::iequals(polling, "true")) {
- item.polling = true;
+ command.polling = true;
}
}
- item.module_name = module_name;
+ command.module_name = module_name;
- commands.push_back(item);
- }
- Py_DECREF(command_list);
+ commands.push_back(std::move(command));
+
+ return 0;
+ });
dout(10) << "loaded " << commands.size() << " commands" << dendl;
- return 0;
+ return r;
+}
+
+int PyModule::load_options()
+{
+ int r = walk_dict_list("OPTIONS", [this](PyObject *pOption) -> int {
+ PyObject *pName = PyDict_GetItemString(pOption, "name");
+ assert(pName != nullptr);
+
+ ModuleOption option;
+ option.name = PyString_AsString(pName);
+ dout(20) << "loaded option " << option.name << dendl;
+
+ options[option.name] = std::move(option);
+
+ return 0;
+ });
+
+ dout(10) << "loaded " << options.size() << " options" << dendl;
+
+ return r;
+}
+
+bool PyModule::is_option(const std::string &option_name)
+{
+ Mutex::Locker l(lock);
+ return options.count(option_name) > 0;
}
int PyModule::load_subclass_of(const char* base_class, PyObject** py_class)
continue;
}
- module_config.set_config(monc, module_name, key, i.second);
- dout(4) << "Rewrote configuration module:key "
- << module_name << ":" << key << dendl;
+ // Check that the named module exists
+ auto module_iter = modules.find(module_name);
+ if (module_iter == modules.end()) {
+ dout(1) << "KV store contains data for unknown module '"
+ << module_name << "'" << dendl;
+ continue;
+ }
+ PyModuleRef module = module_iter->second;
+
+ // Parse option name out of key
+ std::string option_name;
+ auto slash_loc = key.find("/");
+ if (slash_loc != std::string::npos) {
+ if (key.size() > slash_loc + 1) {
+ // Localized option
+ option_name = key.substr(slash_loc + 1);
+ } else {
+ // Trailing slash: garbage.
+ derr << "Invalid mgr store key: '" << key << "'" << dendl;
+ continue;
+ }
+ } else {
+ option_name = key;
+ }
+
+ // Consult module schema to see if this is really
+ // a configuration value
+ if (!option_name.empty() && module->is_option(option_name)) {
+ module_config.set_config(monc, module_name, key, i.second);
+ dout(4) << "Rewrote configuration module:key "
+ << module_name << ":" << key << dendl;
+ } else {
+ dout(4) << "Leaving store module:key " << module_name
+ << ":" << key << " in store, not config" << dendl;
+ }
}
} else {
dout(10) << "Module configuration contains "