diff --git a/include/maxscale/monitor.hh b/include/maxscale/monitor.hh index 658232cae..6df10348c 100644 --- a/include/maxscale/monitor.hh +++ b/include/maxscale/monitor.hh @@ -79,6 +79,8 @@ extern const char CN_MONITOR_INTERVAL[]; extern const char CN_SCRIPT[]; extern const char CN_SCRIPT_TIMEOUT[]; +json_t* monitor_json_data(const mxs::Monitor* monitor, const char* host); + /** * The monitor API version number. Any change to the monitor module API * must change these versions using the rules defined in modinfo.h diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index a625999a5..c0b9df974 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -26,6 +26,7 @@ add_library(maxscale-common SHARED modulecmd.cc modutil.cc monitor.cc + monitormanager.cc mysql_binlog.cc mysql_utils.cc paths.cc diff --git a/server/core/internal/monitor.hh b/server/core/internal/monitor.hh index 5e519bfaf..66624d706 100644 --- a/server/core/internal/monitor.hh +++ b/server/core/internal/monitor.hh @@ -19,6 +19,7 @@ #include #include #include "externcmd.hh" +#include "monitormanager.hh" #define MON_ARG_MAX 8192 @@ -49,132 +50,6 @@ static const MXS_ENUM_VALUE mxs_monitor_event_enum_values[] = {NULL} }; -/** - * This class contains internal monitor management functions that should not be exposed in the public - * monitor class. It's a friend of MXS_MONITOR. - */ -class MonitorManager -{ -public: - - /** - * Creates a new monitor. Loads the module, calls constructor and configure, and adds monitor to the - * global list. - * - * @param name The configuration name of the monitor - * @param module The module name to load - * @return The newly created monitor, or NULL on error - */ - static mxs::Monitor* create_monitor(const std::string& name, const std::string& module, - MXS_CONFIG_PARAMETER* params); - - /** - * Mark monitor as deactivated. A deactivated monitor appears not to exist, as if it had been - * destroyed. Any servers the monitor had are removed. The monitor should not be serialized after - * this function. - * - * @param monitor Monitor to deactivate - */ - static void deactivate_monitor(mxs::Monitor* monitor); - - /** - * @brief Destroys all monitors. At this point all monitors should - * have been stopped. - * - * @attn Must only be called in single-thread context at system shutdown. - */ - static void destroy_all_monitors(); - - static void start_monitor(mxs::Monitor* monitor); - - /** - * Stop a given monitor - * - * @param monitor The monitor to stop - */ - static void stop_monitor(mxs::Monitor* monitor); - - static void stop_all_monitors(); - static void start_all_monitors(); - - static mxs::Monitor* find_monitor(const char* name); - - /** - * @brief Populate services with the servers of the monitors. - */ - static void populate_services(); - - /** - * Get links to monitors that relate to a server. - * - * @param server Server to inspect - * @param host Hostname of this server - * @return Array of monitor links or NULL if no relations exist - */ - static json_t* monitor_relations_to_server(const SERVER* server, const char* host); - - /** - * Convert all monitors to JSON. - * - * @param host Hostname of this server - * @return JSON array containing all monitors - */ - static json_t* monitor_list_to_json(const char* host); - - /** - * Check if a server is being monitored and return the monitor. - * @param server Server that is queried - * @return The monitor watching this server, or NULL if not monitored - */ - static mxs::Monitor* server_is_monitored(const SERVER* server); - - static void show_all_monitors(DCB* dcb); - static void monitor_show(DCB* dcb, mxs::Monitor* monitor); - - static void monitor_list(DCB*); - - static std::unique_ptr monitor_get_list(); - - /** - * @brief Serialize a monitor to a file - * - * This converts the static configuration of the monitor into an INI format file. - * - * @param monitor Monitor to serialize - * @return True if serialization was successful - */ - static bool monitor_serialize(const mxs::Monitor* monitor); - - /** - * Attempt to reconfigure a monitor - * - * If the configuration fails, the old parameters are restored. - * - * @param monitor Monitor to reconfigure - * @param parameters New parameters to apply - * - * @return True if reconfiguration was successful - */ - static bool reconfigure_monitor(mxs::Monitor* monitor, const MXS_CONFIG_PARAMETER& parameters); - - /** - * @brief Convert monitor to JSON - * - * @param monitor Monitor to convert - * @param host Hostname of this server - * - * @return JSON representation of the monitor - */ - static json_t* monitor_to_json(const mxs::Monitor* monitor, const char* host); - - static bool create_monitor_config(const mxs::Monitor* monitor, const char* filename); - - /** - * Waits until all running monitors have advanced one tick. - */ - static void debug_wait_one_tick(); -}; - // RAII helper class for temprarily stopping monitors class MonitorStop { diff --git a/server/core/internal/monitormanager.hh b/server/core/internal/monitormanager.hh new file mode 100644 index 000000000..07fcc5734 --- /dev/null +++ b/server/core/internal/monitormanager.hh @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2019 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2023-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ +#pragma once + +#include + +/** + * This class contains internal monitor management functions that should not be exposed in the public + * monitor class. It's a friend of MXS_MONITOR. + */ +class MonitorManager +{ +public: + + /** + * Creates a new monitor. Loads the module, calls constructor and configure, and adds monitor to the + * global list. + * + * @param name The configuration name of the monitor + * @param module The module name to load + * @return The newly created monitor, or NULL on error + */ + static mxs::Monitor* create_monitor(const std::string& name, const std::string& module, + MXS_CONFIG_PARAMETER* params); + + /** + * Mark monitor as deactivated. A deactivated monitor appears not to exist, as if it had been + * destroyed. Any servers the monitor had are removed. The monitor should not be serialized after + * this function. + * + * @param monitor Monitor to deactivate + */ + static void deactivate_monitor(mxs::Monitor* monitor); + + /** + * @brief Destroys all monitors. At this point all monitors should + * have been stopped. + * + * @attn Must only be called in single-thread context at system shutdown. + */ + static void destroy_all_monitors(); + + static void start_monitor(mxs::Monitor* monitor); + + /** + * Stop a given monitor + * + * @param monitor The monitor to stop + */ + static void stop_monitor(mxs::Monitor* monitor); + + static void stop_all_monitors(); + static void start_all_monitors(); + + static mxs::Monitor* find_monitor(const char* name); + + /** + * @brief Populate services with the servers of the monitors. + */ + static void populate_services(); + + /** + * Get links to monitors that relate to a server. + * + * @param server Server to inspect + * @param host Hostname of this server + * @return Array of monitor links or NULL if no relations exist + */ + static json_t* monitor_relations_to_server(const SERVER* server, const char* host); + + /** + * Convert all monitors to JSON. + * + * @param host Hostname of this server + * @return JSON array containing all monitors + */ + static json_t* monitor_list_to_json(const char* host); + + /** + * Check if a server is being monitored and return the monitor. + * @param server Server that is queried + * @return The monitor watching this server, or NULL if not monitored + */ + static mxs::Monitor* server_is_monitored(const SERVER* server); + + static void show_all_monitors(DCB* dcb); + static void monitor_show(DCB* dcb, mxs::Monitor* monitor); + + static void monitor_list(DCB*); + + static std::unique_ptr monitor_get_list(); + + /** + * @brief Serialize a monitor to a file + * + * This converts the static configuration of the monitor into an INI format file. + * + * @param monitor Monitor to serialize + * @return True if serialization was successful + */ + static bool monitor_serialize(const mxs::Monitor* monitor); + + /** + * Attempt to reconfigure a monitor + * + * If the configuration fails, the old parameters are restored. + * + * @param monitor Monitor to reconfigure + * @param parameters New parameters to apply + * + * @return True if reconfiguration was successful + */ + static bool reconfigure_monitor(mxs::Monitor* monitor, const MXS_CONFIG_PARAMETER& parameters); + + /** + * @brief Convert monitor to JSON + * + * @param monitor Monitor to convert + * @param host Hostname of this server + * + * @return JSON representation of the monitor + */ + static json_t* monitor_to_json(const mxs::Monitor* monitor, const char* host); + + static bool create_monitor_config(const mxs::Monitor* monitor, const char* filename); + + /** + * Waits until all running monitors have advanced one tick. + */ + static void debug_wait_one_tick(); +}; diff --git a/server/core/monitor.cc b/server/core/monitor.cc index fc052284f..87f99f05d 100644 --- a/server/core/monitor.cc +++ b/server/core/monitor.cc @@ -87,61 +87,6 @@ const char CN_SCRIPT_TIMEOUT[] = "script_timeout"; namespace { -class ThisUnit -{ -public: - - /** - * Call a function on every monitor in the global monitor list. - * - * @param apply The function to apply. If the function returns false, iteration is discontinued. - */ - void foreach_monitor(std::function apply) - { - Guard guard(m_all_monitors_lock); - for (Monitor* monitor : m_all_monitors) - { - if (!apply(monitor)) - { - break; - } - } - } - - /** - * Clear the internal list and return previous contents. - * - * @return Contents before clearing - */ - std::vector clear() - { - Guard guard(m_all_monitors_lock); - return std::move(m_all_monitors); - } - - void insert_front(Monitor* monitor) - { - Guard guard(m_all_monitors_lock); - m_all_monitors.insert(m_all_monitors.begin(), monitor); - } - - void move_to_deactivated_list(Monitor *monitor) - { - Guard guard(m_all_monitors_lock); - auto iter = std::find(m_all_monitors.begin(), m_all_monitors.end(), monitor); - mxb_assert(iter != m_all_monitors.end()); - m_all_monitors.erase(iter); - m_deact_monitors.push_back(monitor); - } - -private: - std::mutex m_all_monitors_lock; /**< Protects access to arrays */ - std::vector m_all_monitors; /**< Global list of monitors, in configuration file order */ - std::vector m_deact_monitors; /**< Deactivated monitors. TODO: delete monitors */ -}; - -ThisUnit this_unit; - const char* monitor_state_to_string(monitor_state_t state) { switch (state) @@ -415,6 +360,8 @@ json_t* monitor_parameters_to_json(const Monitor* monitor) return rval; } +} + json_t* monitor_json_data(const Monitor* monitor, const char* host) { json_t* rval = json_object(); @@ -459,394 +406,6 @@ json_t* monitor_json_data(const Monitor* monitor, const char* host) return rval; } -} - -Monitor* MonitorManager::create_monitor(const string& name, const string& module, - MXS_CONFIG_PARAMETER* params) -{ - MXS_MONITOR_API* api = (MXS_MONITOR_API*)load_module(module.c_str(), MODULE_MONITOR); - if (!api) - { - MXS_ERROR("Unable to load library file for monitor '%s'.", name.c_str()); - return NULL; - } - - Monitor* mon = api->createInstance(name, module); - if (!mon) - { - MXS_ERROR("Unable to create monitor instance for '%s', using module '%s'.", - name.c_str(), module.c_str()); - return NULL; - } - - if (mon->configure(params)) - { - this_unit.insert_front(mon); - } - else - { - delete mon; - mon = NULL; - } - return mon; -} - -void MonitorManager::debug_wait_one_tick() -{ - using namespace std::chrono; - std::map ticks; - - // Get tick values for all monitors - this_unit.foreach_monitor([&ticks](Monitor* mon) { - ticks[mon] = mxb::atomic::load(&mon->m_ticks); - return true; - }); - - // Wait for all running monitors to advance at least one tick. - this_unit.foreach_monitor([&ticks](Monitor* mon) { - if (mon->state() == MONITOR_STATE_RUNNING) - { - auto start = steady_clock::now(); - // A monitor may have been added in between the two foreach-calls (not if config changes are - // serialized). Check if entry exists. - if (ticks.count(mon) > 0) - { - auto tick = ticks[mon]; - while (mxb::atomic::load(&mon->m_ticks) == tick && (steady_clock::now() - start < seconds(60))) - { - std::this_thread::sleep_for(milliseconds(100)); - } - } - } - return true; - }); -} - -void MonitorManager::destroy_all_monitors() -{ - auto monitors = this_unit.clear(); - for (auto monitor : monitors) - { - mxb_assert(monitor->state() == MONITOR_STATE_STOPPED); - delete monitor; - } -} - -void MonitorManager::start_monitor(Monitor* monitor) -{ - mxb_assert(monitor); - - Guard guard(monitor->m_lock); - - // Only start the monitor if it's stopped. - if (monitor->state() == MONITOR_STATE_STOPPED) - { - if (!monitor->start()) - { - MXS_ERROR("Failed to start monitor '%s'.", monitor->name()); - } - } -} - -void MonitorManager::populate_services() -{ - this_unit.foreach_monitor([](Monitor* pMonitor) -> bool { - pMonitor->populate_services(); - return true; - }); -} - -/** - * Start all monitors - */ -void MonitorManager::start_all_monitors() -{ - this_unit.foreach_monitor([](Monitor* monitor) { - MonitorManager::start_monitor(monitor); - return true; - }); -} - -void MonitorManager::stop_monitor(Monitor* monitor) -{ - mxb_assert(monitor); - - Guard guard(monitor->m_lock); - - /** Only stop the monitor if it is running */ - if (monitor->state() == MONITOR_STATE_RUNNING) - { - monitor->stop(); - } -} - -void MonitorManager::deactivate_monitor(Monitor* monitor) -{ - // This cannot be done with configure(), since other, module-specific config settings may depend on the - // "servers"-setting of the base monitor. Directly manipulate monitor field for now, later use a dtor - // to cleanly "deactivate" inherited objects. - stop_monitor(monitor); - while (!monitor->m_servers.empty()) - { - monitor->remove_server(monitor->m_servers.front()->server); - } - this_unit.move_to_deactivated_list(monitor); -} - -/** - * Shutdown all running monitors - */ -void MonitorManager::stop_all_monitors() -{ - this_unit.foreach_monitor([](Monitor* monitor) { - MonitorManager::stop_monitor(monitor); - return true; - }); -} - -/** - * Show all monitors - * - * @param dcb DCB for printing output - */ -void MonitorManager::show_all_monitors(DCB* dcb) -{ - this_unit.foreach_monitor([dcb](Monitor* monitor) { - monitor_show(dcb, monitor); - return true; - }); -} - -/** - * Show a single monitor - * - * @param dcb DCB for printing output - */ -void MonitorManager::monitor_show(DCB* dcb, Monitor* monitor) -{ - monitor->show(dcb); -} - -/** - * List all the monitors - * - * @param dcb DCB for printing output - */ -void MonitorManager::monitor_list(DCB* dcb) -{ - dcb_printf(dcb, "---------------------+---------------------\n"); - dcb_printf(dcb, "%-20s | Status\n", "Monitor"); - dcb_printf(dcb, "---------------------+---------------------\n"); - - this_unit.foreach_monitor([dcb](Monitor* ptr) { - dcb_printf(dcb, "%-20s | %s\n", - ptr->name(), ptr->state() == MONITOR_STATE_RUNNING ? "Running" : "Stopped"); - return true; - }); - - dcb_printf(dcb, "---------------------+---------------------\n"); -} - -/** - * Find a monitor by name - * - * @param name The name of the monitor - * @return Pointer to the monitor or NULL - */ -Monitor* MonitorManager::find_monitor(const char* name) -{ - Monitor* rval = nullptr; - this_unit.foreach_monitor([&rval, name](Monitor* ptr) { - if (ptr->m_name == name) - { - rval = ptr; - } - return (rval == nullptr); - }); - return rval; -} - -/** - * Return a resultset that has the current set of monitors in it - * - * @return A Result set - */ -std::unique_ptr MonitorManager::monitor_get_list() -{ - std::unique_ptr set = ResultSet::create({"Monitor", "Status"}); - this_unit.foreach_monitor([&set](Monitor* ptr) { - const char* state = ptr->state() == MONITOR_STATE_RUNNING ? "Running" : "Stopped"; - set->add_row({ptr->m_name, state}); - return true; - }); - return set; -} - -Monitor* MonitorManager::server_is_monitored(const SERVER* server) -{ - Monitor* rval = nullptr; - this_unit.foreach_monitor([&rval, server](Monitor* monitor) { - Guard guard(monitor->m_lock); - for (MonitorServer* db : monitor->m_servers) - { - if (db->server == server) - { - rval = monitor; - break; - } - } - return (rval == nullptr); - }); - return rval; -} - -bool MonitorManager::create_monitor_config(const Monitor* monitor, const char* filename) -{ - int file = open(filename, O_EXCL | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (file == -1) - { - MXS_ERROR("Failed to open file '%s' when serializing monitor '%s': %d, %s", - filename, - monitor->name(), - errno, - mxs_strerror(errno)); - return false; - } - - { - Guard guard(monitor->m_lock); - - const MXS_MODULE* mod = get_module(monitor->m_module.c_str(), NULL); - mxb_assert(mod); - - string config = generate_config_string(monitor->m_name, monitor->parameters, - config_monitor_params, mod->parameters); - - if (dprintf(file, "%s", config.c_str()) == -1) - { - MXS_ERROR("Could not write serialized configuration to file '%s': %d, %s", - filename, errno, mxs_strerror(errno)); - } - } - - close(file); - return true; -} - -bool MonitorManager::monitor_serialize(const Monitor* monitor) -{ - bool rval = false; - char filename[PATH_MAX]; - snprintf(filename, - sizeof(filename), - "%s/%s.cnf.tmp", - get_config_persistdir(), - monitor->name()); - - if (unlink(filename) == -1 && errno != ENOENT) - { - MXS_ERROR("Failed to remove temporary monitor configuration at '%s': %d, %s", - filename, - errno, - mxs_strerror(errno)); - } - else if (create_monitor_config(monitor, filename)) - { - char final_filename[PATH_MAX]; - strcpy(final_filename, filename); - - char* dot = strrchr(final_filename, '.'); - mxb_assert(dot); - *dot = '\0'; - - if (rename(filename, final_filename) == 0) - { - rval = true; - } - else - { - MXS_ERROR("Failed to rename temporary monitor configuration at '%s': %d, %s", - filename, - errno, - mxs_strerror(errno)); - } - } - - return rval; -} - -// static -bool MonitorManager::reconfigure_monitor(mxs::Monitor* monitor, const MXS_CONFIG_PARAMETER& parameters) -{ - // Backup monitor parameters in case configure fails. - auto orig = monitor->parameters; - monitor->parameters.clear(); - - bool success = monitor->configure(¶meters); - - if (!success) - { - MXB_AT_DEBUG(bool check = ) monitor->configure(&orig); - mxb_assert(check); - } - - return success; -} - -json_t* MonitorManager::monitor_to_json(const Monitor* monitor, const char* host) -{ - string self = MXS_JSON_API_MONITORS; - self += monitor->m_name; - return mxs_json_resource(host, self.c_str(), monitor_json_data(monitor, host)); -} - -json_t* MonitorManager::monitor_list_to_json(const char* host) -{ - json_t* rval = json_array(); - this_unit.foreach_monitor([rval, host](Monitor* mon) { - json_t* json = monitor_json_data(mon, host); - if (json) - { - json_array_append_new(rval, json); - } - return true; - }); - - return mxs_json_resource(host, MXS_JSON_API_MONITORS, rval); -} - -json_t* MonitorManager::monitor_relations_to_server(const SERVER* server, const char* host) -{ - std::vector names; - this_unit.foreach_monitor([&names, server](Monitor* mon) { - Guard guard(mon->m_lock); - for (MonitorServer* db : mon->m_servers) - { - if (db->server == server) - { - names.push_back(mon->m_name); - break; - } - } - return true; - }); - - json_t* rel = NULL; - if (!names.empty()) - { - rel = mxs_json_relationship(host, MXS_JSON_API_MONITORS); - - for (std::vector::iterator it = names.begin(); - it != names.end(); it++) - { - mxs_json_add_relation(rel, it->c_str(), CN_MONITORS); - } - } - - return rel; -} - - namespace maxscale { diff --git a/server/core/monitormanager.cc b/server/core/monitormanager.cc new file mode 100644 index 000000000..fdc8c0c5a --- /dev/null +++ b/server/core/monitormanager.cc @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2019 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2025-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include + +#include +#include + +#include "internal/config.hh" +#include "internal/monitor.hh" +#include "internal/modules.hh" + +using maxscale::Monitor; +using maxscale::MonitorServer; +using Guard = std::lock_guard; +using std::string; + +namespace +{ + +class ThisUnit +{ +public: + + /** + * Call a function on every monitor in the global monitor list. + * + * @param apply The function to apply. If the function returns false, iteration is discontinued. + */ + void foreach_monitor(std::function apply) + { + Guard guard(m_all_monitors_lock); + for (Monitor* monitor : m_all_monitors) + { + if (!apply(monitor)) + { + break; + } + } + } + + /** + * Clear the internal list and return previous contents. + * + * @return Contents before clearing + */ + std::vector clear() + { + Guard guard(m_all_monitors_lock); + return std::move(m_all_monitors); + } + + void insert_front(Monitor* monitor) + { + Guard guard(m_all_monitors_lock); + m_all_monitors.insert(m_all_monitors.begin(), monitor); + } + + void move_to_deactivated_list(Monitor* monitor) + { + Guard guard(m_all_monitors_lock); + auto iter = std::find(m_all_monitors.begin(), m_all_monitors.end(), monitor); + mxb_assert(iter != m_all_monitors.end()); + m_all_monitors.erase(iter); + m_deact_monitors.push_back(monitor); + } + +private: + std::mutex m_all_monitors_lock; /**< Protects access to arrays */ + std::vector m_all_monitors; /**< Global list of monitors, in configuration file order */ + std::vector m_deact_monitors; /**< Deactivated monitors. TODO: delete monitors */ +}; + +ThisUnit this_unit; + +} + +Monitor* MonitorManager::create_monitor(const string& name, const string& module, + MXS_CONFIG_PARAMETER* params) +{ + MXS_MONITOR_API* api = (MXS_MONITOR_API*)load_module(module.c_str(), MODULE_MONITOR); + if (!api) + { + MXS_ERROR("Unable to load library file for monitor '%s'.", name.c_str()); + return NULL; + } + + Monitor* mon = api->createInstance(name, module); + if (!mon) + { + MXS_ERROR("Unable to create monitor instance for '%s', using module '%s'.", + name.c_str(), module.c_str()); + return NULL; + } + + if (mon->configure(params)) + { + this_unit.insert_front(mon); + } + else + { + delete mon; + mon = NULL; + } + return mon; +} + +void MonitorManager::debug_wait_one_tick() +{ + using namespace std::chrono; + std::map ticks; + + // Get tick values for all monitors + this_unit.foreach_monitor([&ticks](Monitor* mon) { + ticks[mon] = mxb::atomic::load(&mon->m_ticks); + return true; + }); + + // Wait for all running monitors to advance at least one tick. + this_unit.foreach_monitor([&ticks](Monitor* mon) { + if (mon->state() == MONITOR_STATE_RUNNING) + { + auto start = steady_clock::now(); + // A monitor may have been added in between the two foreach-calls (not if config changes are + // serialized). Check if entry exists. + if (ticks.count(mon) > 0) + { + auto tick = ticks[mon]; + while (mxb::atomic::load(&mon->m_ticks) == tick && (steady_clock::now() - start < seconds(60))) + { + std::this_thread::sleep_for(milliseconds(100)); + } + } + } + return true; + }); +} + +void MonitorManager::destroy_all_monitors() +{ + auto monitors = this_unit.clear(); + for (auto monitor : monitors) + { + mxb_assert(monitor->state() == MONITOR_STATE_STOPPED); + delete monitor; + } +} + +void MonitorManager::start_monitor(Monitor* monitor) +{ + mxb_assert(monitor); + + Guard guard(monitor->m_lock); + + // Only start the monitor if it's stopped. + if (monitor->state() == MONITOR_STATE_STOPPED) + { + if (!monitor->start()) + { + MXS_ERROR("Failed to start monitor '%s'.", monitor->name()); + } + } +} + +void MonitorManager::populate_services() +{ + this_unit.foreach_monitor([](Monitor* pMonitor) -> bool { + pMonitor->populate_services(); + return true; + }); +} + +/** + * Start all monitors + */ +void MonitorManager::start_all_monitors() +{ + this_unit.foreach_monitor([](Monitor* monitor) { + MonitorManager::start_monitor(monitor); + return true; + }); +} + +void MonitorManager::stop_monitor(Monitor* monitor) +{ + mxb_assert(monitor); + + Guard guard(monitor->m_lock); + + /** Only stop the monitor if it is running */ + if (monitor->state() == MONITOR_STATE_RUNNING) + { + monitor->stop(); + } +} + +void MonitorManager::deactivate_monitor(Monitor* monitor) +{ + // This cannot be done with configure(), since other, module-specific config settings may depend on the + // "servers"-setting of the base monitor. Directly manipulate monitor field for now, later use a dtor + // to cleanly "deactivate" inherited objects. + stop_monitor(monitor); + while (!monitor->m_servers.empty()) + { + monitor->remove_server(monitor->m_servers.front()->server); + } + this_unit.move_to_deactivated_list(monitor); +} + +/** + * Shutdown all running monitors + */ +void MonitorManager::stop_all_monitors() +{ + this_unit.foreach_monitor([](Monitor* monitor) { + MonitorManager::stop_monitor(monitor); + return true; + }); +} + +/** + * Show all monitors + * + * @param dcb DCB for printing output + */ +void MonitorManager::show_all_monitors(DCB* dcb) +{ + this_unit.foreach_monitor([dcb](Monitor* monitor) { + monitor_show(dcb, monitor); + return true; + }); +} + +/** + * Show a single monitor + * + * @param dcb DCB for printing output + */ +void MonitorManager::monitor_show(DCB* dcb, Monitor* monitor) +{ + monitor->show(dcb); +} + +/** + * List all the monitors + * + * @param dcb DCB for printing output + */ +void MonitorManager::monitor_list(DCB* dcb) +{ + dcb_printf(dcb, "---------------------+---------------------\n"); + dcb_printf(dcb, "%-20s | Status\n", "Monitor"); + dcb_printf(dcb, "---------------------+---------------------\n"); + + this_unit.foreach_monitor([dcb](Monitor* ptr) { + dcb_printf(dcb, "%-20s | %s\n", + ptr->name(), ptr->state() == MONITOR_STATE_RUNNING ? "Running" : "Stopped"); + return true; + }); + + dcb_printf(dcb, "---------------------+---------------------\n"); +} + +/** + * Find a monitor by name + * + * @param name The name of the monitor + * @return Pointer to the monitor or NULL + */ +Monitor* MonitorManager::find_monitor(const char* name) +{ + Monitor* rval = nullptr; + this_unit.foreach_monitor([&rval, name](Monitor* ptr) { + if (ptr->m_name == name) + { + rval = ptr; + } + return (rval == nullptr); + }); + return rval; +} + +/** + * Return a resultset that has the current set of monitors in it + * + * @return A Result set + */ +std::unique_ptr MonitorManager::monitor_get_list() +{ + std::unique_ptr set = ResultSet::create({"Monitor", "Status"}); + this_unit.foreach_monitor([&set](Monitor* ptr) { + const char* state = ptr->state() == MONITOR_STATE_RUNNING ? "Running" : "Stopped"; + set->add_row({ptr->m_name, state}); + return true; + }); + return set; +} + +Monitor* MonitorManager::server_is_monitored(const SERVER* server) +{ + Monitor* rval = nullptr; + this_unit.foreach_monitor([&rval, server](Monitor* monitor) { + Guard guard(monitor->m_lock); + for (MonitorServer* db : monitor->m_servers) + { + if (db->server == server) + { + rval = monitor; + break; + } + } + return (rval == nullptr); + }); + return rval; +} + +bool MonitorManager::create_monitor_config(const Monitor* monitor, const char* filename) +{ + int file = open(filename, O_EXCL | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (file == -1) + { + MXS_ERROR("Failed to open file '%s' when serializing monitor '%s': %d, %s", + filename, + monitor->name(), + errno, + mxs_strerror(errno)); + return false; + } + + { + Guard guard(monitor->m_lock); + + const MXS_MODULE* mod = get_module(monitor->m_module.c_str(), NULL); + mxb_assert(mod); + + string config = generate_config_string(monitor->m_name, monitor->parameters, + config_monitor_params, mod->parameters); + + if (dprintf(file, "%s", config.c_str()) == -1) + { + MXS_ERROR("Could not write serialized configuration to file '%s': %d, %s", + filename, errno, mxs_strerror(errno)); + } + } + + close(file); + return true; +} + +bool MonitorManager::monitor_serialize(const Monitor* monitor) +{ + bool rval = false; + char filename[PATH_MAX]; + snprintf(filename, + sizeof(filename), + "%s/%s.cnf.tmp", + get_config_persistdir(), + monitor->name()); + + if (unlink(filename) == -1 && errno != ENOENT) + { + MXS_ERROR("Failed to remove temporary monitor configuration at '%s': %d, %s", + filename, + errno, + mxs_strerror(errno)); + } + else if (create_monitor_config(monitor, filename)) + { + char final_filename[PATH_MAX]; + strcpy(final_filename, filename); + + char* dot = strrchr(final_filename, '.'); + mxb_assert(dot); + *dot = '\0'; + + if (rename(filename, final_filename) == 0) + { + rval = true; + } + else + { + MXS_ERROR("Failed to rename temporary monitor configuration at '%s': %d, %s", + filename, + errno, + mxs_strerror(errno)); + } + } + + return rval; +} + +// static +bool MonitorManager::reconfigure_monitor(mxs::Monitor* monitor, const MXS_CONFIG_PARAMETER& parameters) +{ + // Backup monitor parameters in case configure fails. + auto orig = monitor->parameters; + monitor->parameters.clear(); + + bool success = monitor->configure(¶meters); + + if (!success) + { + MXB_AT_DEBUG(bool check = ) monitor->configure(&orig); + mxb_assert(check); + } + + return success; +} + +json_t* MonitorManager::monitor_to_json(const Monitor* monitor, const char* host) +{ + string self = MXS_JSON_API_MONITORS; + self += monitor->m_name; + return mxs_json_resource(host, self.c_str(), monitor_json_data(monitor, host)); +} + +json_t* MonitorManager::monitor_list_to_json(const char* host) +{ + json_t* rval = json_array(); + this_unit.foreach_monitor([rval, host](Monitor* mon) { + json_t* json = monitor_json_data(mon, host); + if (json) + { + json_array_append_new(rval, json); + } + return true; + }); + + return mxs_json_resource(host, MXS_JSON_API_MONITORS, rval); +} + +json_t* MonitorManager::monitor_relations_to_server(const SERVER* server, const char* host) +{ + std::vector names; + this_unit.foreach_monitor([&names, server](Monitor* mon) { + Guard guard(mon->m_lock); + for (MonitorServer* db : mon->m_servers) + { + if (db->server == server) + { + names.push_back(mon->m_name); + break; + } + } + return true; + }); + + json_t* rel = NULL; + if (!names.empty()) + { + rel = mxs_json_relationship(host, MXS_JSON_API_MONITORS); + + for (std::vector::iterator it = names.begin(); + it != names.end(); it++) + { + mxs_json_add_relation(rel, it->c_str(), CN_MONITORS); + } + } + + return rel; +}