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!";
......
Supports Markdown
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