Currently job artifacts in CI/CD pipelines on LRZ GitLab never expire. Starting from Wed 26.1.2022 the default expiration time will be 30 days (GitLab default). Currently existing artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit f82f16d1 authored by Micha Mueller's avatar Micha Mueller
Browse files

dcdbpusher RestAPI: Add endpoints to (un)load plugins

parent 125b1fa8
......@@ -308,6 +308,8 @@ std::string RESTHttpsServer::splitUri(const std::string& uri, queries_t& queries
std::string path;
std::string query;
ServerLOG(debug) << "Splitting URI " << uri;
size_t pos = uri.find('?');
path = uri.substr(0, pos);
query = uri.substr(pos+1, uri.length());
......
......@@ -68,7 +68,7 @@ bool Configuration::readPlugins(PluginManager& pluginManager) {
}
}
if(!pluginManager.loadPlugin(pluginSettings, pluginName, pluginPath, pluginConfig)) {
if(!pluginManager.loadPlugin(pluginName, pluginPath, pluginConfig)) {
LOG(error) << "Could not load plugin " << pluginName;
pluginManager.unloadPlugin();
return false;
......
......@@ -13,12 +13,14 @@
using namespace std;
PluginManager::PluginManager(const pluginSettings_t& pluginSettings) :
_pluginSettings(pluginSettings) {}
PluginManager::~PluginManager() {
unloadPlugin();
}
bool PluginManager::loadPlugin(const pluginSettings_t& pluginSettings,
const string& name,
bool PluginManager::loadPlugin(const string& name,
const string& pluginPath,
const string& config) {
......@@ -91,7 +93,7 @@ bool PluginManager::loadPlugin(const pluginSettings_t& pluginSettings,
plugin.configurator = plugin.create();
//set prefix to global prefix (may be overwritten)
plugin.configurator->setGlobalSettings(pluginSettings);
plugin.configurator->setGlobalSettings(_pluginSettings);
//read in config
if (!(plugin.configurator->readConfig(pluginConfig))) {
LOG(error) << "Plugin " << plugin.id << " could not read configuration!";
......@@ -124,6 +126,8 @@ void PluginManager::unloadPlugin(const string& id) {
g->stop();
}
removeTopics(*it);
if (it->configurator) {
it->destroy(it->configurator);
}
......
......@@ -37,8 +37,18 @@ using pusherPluginStorage_t = std::vector<pusherPlugin_t>;
class PluginManager {
public:
PluginManager() {};
/**
* @brief Constructor.
*
* @param pluginSettings Use this plugin settings as default when loading
* a new plugin.
*/
PluginManager(const pluginSettings_t& pluginSettings);
/**
* @brief Destructor.
*/
virtual ~PluginManager();
/**
......@@ -52,8 +62,6 @@ public:
* loading the plugin still has to be initialized (initPlugin())
* before it can be started (startPlugin()).
*
*
* @param pluginSettings Default settings for the plugin.
* @param name Identifying name (ID) of the new plugin.
* @param pluginPath Path where the plugin shared library is located. If
* not specified we will search the default library
......@@ -65,8 +73,7 @@ public:
* @return True on success, false otherwise. In the latter case the plugin
* is not loaded.
*/
bool loadPlugin(const pluginSettings_t& pluginSettings,
const std::string& name,
bool loadPlugin(const std::string& name,
const std::string& pluginPath = "",
const std::string& config = "");
......@@ -171,6 +178,8 @@ private:
void removeTopics(const pusherPlugin_t& p);
pusherPluginStorage_t _plugins; /**< Storage to hold all loaded plugins */
pluginSettings_t _pluginSettings; /**< Default plugin settings to use when
loading a new plugin */
logger_t lg;
};
......
......@@ -179,6 +179,44 @@ Tables with allowed ressources sorted by REST methods can be found below. A quer
</tr>
</table>
<table>
<tr>
<td colspan="2"><b>PUT /load</b></td>
<td colspan="2">Load and intitialize a new plugin but do not start it. Use the /start request to kick off the plugin's data collection.</td>
</tr>
<tr>
<td>plugin</td>
<td>Plugin name.</td>
<td>No</td>
<td>Name of the new plugin. Is used to build the shared library file name which holds the plugin. Shared lib file name is of the form libdcdbplugin_PLUGINNAME.so (or .dylib for Apple).</td>
</tr>
<tr>
<td>path</td>
<td>A file path.</td>
<td>Yes</td>
<td>Path to where the shared library for the plugin is located. If not specified the default library directories (urs/lib and friends) are searched.</td>
</tr>
<tr>
<td>config</td>
<td>A file path including file name.</td>
<td>Yes</td>
<td>Path and name of the plugin configuration file. If not specified we will search for "./PLUGINNAME.conf".</td>
</tr>
</table>
<table>
<tr>
<td colspan="2"><b>PUT /unload</b></td>
<td colspan="2">Unload a plugin, removing it completely from dcdbpusher. To use the plugin again one has to /load it first.</td>
</tr>
<tr>
<td>plugin</td>
<td>All plugin names.</td>
<td>No</td>
<td>Specify the plugin.</td>
</tr>
</table>
<table>
<tr>
<td colspan="2"><b>PUT /start</b></td>
......
......@@ -33,6 +33,8 @@ RestAPI::RestAPI(serverSettings_t settings,
addEndpoint("/sensors", {http::verb::get, stdBind(GET_sensors)});
addEndpoint("/average", {http::verb::get, stdBind(GET_average)});
addEndpoint("/load", {http::verb::put, stdBind(PUT_load)});
addEndpoint("/unload", {http::verb::put, stdBind(PUT_unload)});
addEndpoint("/start", {http::verb::put, stdBind(PUT_start)});
addEndpoint("/stop", {http::verb::put, stdBind(PUT_stop)});
addEndpoint("/reload", {http::verb::put, stdBind(PUT_reload)});
......@@ -184,6 +186,66 @@ void RestAPI::GET_average(endpointArgs) {
}
}
void RestAPI::PUT_load(endpointArgs) {
const std::string plugin = getQuery("plugin", queries);
const std::string path = getQuery("path", queries);
const std::string config = getQuery("config", queries);
if (!hasPlugin(plugin, res)) {
return;
}
//before modifying the plugin we need to ensure that we have exclusive access
//therefore pause the only other concurrent user (MQTTPusher)
if (!_mqttPusher->halt()) {
res.body() = "Could not reload plugin (Timeout while waiting).\n";
res.result(http::status::internal_server_error);
return;
}
if(_pluginManager->loadPlugin(plugin, path, config)) {
res.body() = "Plugin " + plugin + " successfully loaded!\n";
res.result(http::status::ok);
_pluginManager->initPlugin(_io, plugin);
} else {
res.body() = "Failed to load plugin " + plugin + "!\n";
res.result(http::status::internal_server_error);
}
//continue MQTTPusher
_mqttPusher->cont();
//Updating the SensorNavigator on plugin reloads, if analytics plugins are currently running
if (_manager!=NULL && _manager->getPlugins().size()>0) {
QueryEngine &qEngine = QueryEngine::getInstance();
std::shared_ptr <SensorNavigator> navigator = std::make_shared<SensorNavigator>();
std::vector<std::string> names, topics;
for (const auto &p : _pluginManager->getPlugins())
for (const auto &g : p.configurator->getSensorGroups())
for (const auto &s : g->getSensors()) {
names.push_back(s->getName());
topics.push_back(s->getMqtt());
}
navigator->buildTree(qEngine.getSensorHierarchy(), &names, &topics);
qEngine.setNavigator(navigator);
qEngine.triggerUpdate();
}
}
void RestAPI::PUT_unload(endpointArgs) {
const std::string plugin = getQuery("plugin", queries);
if (!hasPlugin(plugin, res)) {
return;
}
_pluginManager->unloadPlugin(plugin);
res.body() = "Plugin " + plugin + " unloaded.\n";
res.result(http::status::ok);
return;
}
void RestAPI::PUT_start(endpointArgs) {
const std::string plugin = getQuery("plugin", queries);
......
......@@ -39,7 +39,12 @@ public:
" Average of last sensor readings from the last\n"
" [interval] seconds or of all cached readings if no\n"
" interval is given.\n"
" -PUT: /start?plugin Start the sensors of the plugin.\n"
" -PUT: /load?plugin;[path];[config]\n"
" Load a new plugin. Optionally specify path to the\n"
" shared library and/or the config file for the "
" plugin.\n"
" /unload?plugin Unload a plugin."
" /start?plugin Start the sensors of the plugin.\n"
" /stop?plugin Stop the sensors of the plugin.\n"
" /reload?plugin Reload the plugin configuration.\n"
"\n";
......@@ -102,6 +107,36 @@ private:
/******************************************************************************/
/**
* PUT "/load"
*
* @brief Load and initialize a plugin but do not start it yet.
*
* Queries | key | possible values | explanation
* -------------------------------------------------------------------------
* Required | plugin | name of the new | specify the plugin
* | | plugin |
* Optional | path | file path | specify a file path where to
* | | | search for the shared lib.
* | | | Defaults to (usr/lib etc.)
* | config | file path + name | specify the config file for
* | | | the plugin. Defaults to
* | | | ./PLUGINNAME.conf
*/
void PUT_load(endpointArgs);
/**
* PUT "/unload"
*
* @brief Unload a plugin.
*
* Queries | key | possible values | explanation
* -------------------------------------------------------------------------
* Required | plugin | all plugin names | specify the plugin
* Optional | - | - | -
*/
void PUT_unload(endpointArgs);
/**
* PUT "/start"
*
......
......@@ -255,7 +255,7 @@ int main(int argc, char** argv) {
LOG(info) << "Logging setup complete";
_pluginManager = new PluginManager();
_pluginManager = new PluginManager(pluginSettings);
//Read in rest of configuration. Also creates all sensors
if(!_configuration->readPlugins(*_pluginManager)) {
LOG(fatal) << "Failed to read configuration!";
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment