// which we will transmit to the monitor.
std::vector<MgrMap::ModuleInfo> module_info;
for (const auto &module : modules) {
+ // do not announce always_on modules to the monitor
+ if (module->is_always_on()) {
+ continue;
+ }
MgrMap::ModuleInfo info;
info.name = module->get_name();
info.error_string = module->get_error_string();
mutable Mutex lock{"PyModule::lock"};
private:
const std::string module_name;
+ const bool always_on;
std::string get_site_packages();
int load_subclass_of(const char* class_name, PyObject** py_class);
PyObject *pClass = nullptr;
PyObject *pStandbyClass = nullptr;
- explicit PyModule(const std::string &module_name_)
- : module_name(module_name_)
+ explicit PyModule(const std::string &module_name_, bool always_on)
+ : module_name(module_name_), always_on(always_on)
{
}
error_string = reason;
}
- bool is_enabled() const { Mutex::Locker l(lock) ; return enabled; }
+ bool is_enabled() const {
+ Mutex::Locker l(lock);
+ return enabled || always_on;
+ }
+
bool is_failed() const { Mutex::Locker l(lock) ; return failed; }
bool is_loaded() const { Mutex::Locker l(lock) ; return loaded; }
+ bool is_always_on() const { Mutex::Locker l(lock) ; return always_on; }
const std::string &get_name() const {
Mutex::Locker l(lock) ; return module_name;
#include "BaseMgrStandbyModule.h"
#include "Gil.h"
#include "MgrContext.h"
+#include "mgr/mgr_commands.h"
#include "ActivePyModules.h"
for (const auto& module_name : module_names) {
dout(1) << "Loading python module '" << module_name << "'" << dendl;
+ const bool always_on = always_on_modules.find(module_name) !=
+ always_on_modules.end();
+
// Everything starts disabled, set enabled flag on module
// when we see first MgrMap
- auto mod = std::make_shared<PyModule>(module_name);
+ auto mod = std::make_shared<PyModule>(module_name, always_on);
int r = mod->load(pMainThreadState);
if (r != 0) {
// Don't use handle_pyerror() here; we don't have the GIL
std::set<std::string> failed_modules;
for (const auto &i : modules) {
if (!(i.second->is_enabled() && i.second->get_can_run())) {
+ // report always_on modules with a standby mode that won't run
+ if (i.second->is_always_on() && i.second->pStandbyClass) {
+ failed_modules.insert(i.second->get_name());
+ }
continue;
}
}
}
+ // report failed always_on modules as health errors
+ for (const auto& name : always_on_modules) {
+ if (!active_modules->module_exists(name)) {
+ if (failed_modules.find(name) == failed_modules.end() &&
+ dependency_modules.find(name) == dependency_modules.end()) {
+ failed_modules[name] = "Unknown error";
+ }
+ }
+ }
+
if (!dependency_modules.empty()) {
std::ostringstream ss;
if (dependency_modules.size() == 1) {
#include "mgr_commands.h"
+// an always_on module behaves as if it provides built-in functionality. it's
+// always enabled, and always_on modules do not appear in the list of modules
+// that can be enabled or disabled by the user. a health warning error is
+// reported if a module in this set is cannot be loaded and started.
+//
+// NOTE: if a module is added to this set AND that module _may_ have been
+// enabled in some cluster (e.g. an existing module is being upgraded to
+// always-on status), then additional logic is needed in the monitor to trim the
+// enabled set (or upgrade script to run `disable <module`). At the time of
+// writing, the proposed always-on modules are being added within the same
+// release.
+const std::set<std::string> always_on_modules = {"crash"};
+
/* The set of statically defined (C++-handled) commands. This
* does not include the Python-defined commands, which are loaded
* in PyModules */
#pragma once
#include "mon/MonCommand.h"
+#include <set>
+#include <string>
#include <vector>
+extern const std::set<std::string> always_on_modules;
extern const std::vector<MonCommand> mgr_commands;