integrate plugin engine

This commit is contained in:
Evgenii Alekseev 2017-03-10 06:38:54 +03:00
parent d177aa81af
commit 2d8d501f02
11 changed files with 299 additions and 18 deletions

View File

@ -106,12 +106,14 @@ public:
signals:
/**
* @brief emits on each value update
* @param _id
* internal key id
* @param _key
* changed key
* @param _value
* changed value
*/
void valueUpdated(const QueuedCfg::QueuedSettings _key,
void valueUpdated(const QueuedCfg::QueuedSettings _id, const QString &_key,
const QVariant &_value);
private:

View File

@ -97,6 +97,8 @@ typedef struct {
* internal field to control current database version
* @var QueuedSettings::ProcessCommandLine
* control process command line
* @var QueuedSettings::Plugins
* plugin list
*/
enum class QueuedSettings {
Invalid = 1 << 0,
@ -108,6 +110,7 @@ enum class QueuedSettings {
TokenExpiration = 1 << 6,
DatabaseVersion = 1 << 7,
ProcessCommandLine = 1 << 8,
Plugins = 1 << 9,
};
/**
* @ingroup QueuedCfg
@ -148,7 +151,8 @@ const QueuedSettingsDefaultMap QueuedSettingsDefaults = {
{QueuedSettings::ProcessCommandLine,
"systemd-run\x01--scope\x01--unit={name}\x01--uid={uid}\x01--gid={gid}"
"\x01-p\x01CPUQuota={cpu}%\x01-p\x01MemoryHigh={memory}\x01{"
"application}"}}};
"application}"}},
{"Plugins", {QueuedSettings::Plugins, ""}}};
};
#endif /* QUEUEDCONFIGURATION_H */

View File

@ -35,6 +35,7 @@
class QueuedAdvancedSettings;
class QueuedDatabase;
class QueuedDatabaseManager;
class QueuedPluginManager;
class QueuedProcess;
class QueuedProcessManager;
class QueuedReportManager;
@ -58,6 +59,16 @@ public:
* @brief QueuedCore class destructor
*/
virtual ~QueuedCore();
/**
* @brief add plugin to autoload and load it now
* @param _plugin
* plugin name
* @param _auth
* user auth structure
* @return true on successfully addition
*/
bool addPlugin(const QString &_plugin,
const QueuedUserManager::QueuedUserAuthorization &_auth);
/**
* @brief add new task
* @param _command
@ -175,6 +186,23 @@ public:
* @return option value or empty QVariant
*/
QVariant option(const QString &_key);
/**
* @brief get plugin settings
* @param _plugin
* plugin name
* @return hash of plugin settings
*/
QVariantHash pluginSettings(const QString &_plugin);
/**
* @brief remove plugin from autoload and unload it now
* @param _plugin
* plugin name
* @param _auth
* user auth structure
* @return true on successful plugin removal
*/
bool removePlugin(const QString &_plugin,
const QueuedUserManager::QueuedUserAuthorization &_auth);
/**
* @brief force start task
* @param _id
@ -226,13 +254,15 @@ public:
private slots:
/**
* @brief notify clients about settings update
* @param _id
* updated key id
* @param _key
* updated key
* @param _value
* new value
*/
void updateSettings(const QueuedCfg::QueuedSettings _key,
const QVariant &_value);
void updateSettings(const QueuedCfg::QueuedSettings _id,
const QString &_key, const QVariant &_value);
/**
* @brief update process time
* @param _id
@ -266,6 +296,10 @@ private:
* @brief pointer to database manager object
*/
QueuedDatabaseManager *m_databaseManager = nullptr;
/**
* @brief pointer to plugin manager
*/
QueuedPluginManager *m_plugins = nullptr;
/**
* @brief pointer to process manager
*/
@ -302,6 +336,10 @@ private:
* @throw QueuedDBusException
*/
void initDBus();
/**
* @brief init plugins
*/
void initPlugins();
/**
* @brief init processes
*/
@ -364,6 +402,15 @@ private:
* @return true on successful option edition
*/
bool editOptionPrivate(const QString &_key, const QVariant &_value);
/**
* @brief edit plugin list
* @param _plugin
* plugin name
* @param add
* true if it requires add plugin
* @return true on successful action
*/
bool editPluginPrivate(const QString &_plugin, const bool _add);
/**
* @brief edit task
* @param _id

View File

@ -76,6 +76,30 @@ public slots:
*/
bool OptionEdit(const QString &key, const QDBusVariant &value,
const QString &whoAmI, const QString &token);
/**
* @brief add plugin
* @param plugin
* plugin name
* @param whoAmI
* auth user name
* @param token
* auth user token
* @return true on successful plugin addition
*/
bool PluginAdd(const QString &plugin, const QString &whoAmI,
const QString &token);
/**
* @brief remove plugin
* @param plugin
* plugin name
* @param whoAmI
* auth user name
* @param token
* auth user token
* @return true on successful plugin removal
*/
bool PluginRemove(const QString &plugin, const QString &whoAmI,
const QString &token);
/**
* @brief add new task
* @param command

View File

@ -54,6 +54,14 @@ public:
* @remark plugin settings will be stored as "plugin.name.Key"
*/
virtual void init(const QVariantHash &_settings) = 0;
/**
* @brief method which will be called on option update
* @param _key
* option key
* @param _value
* option value
*/
virtual void updateSettings(const QString &_key, const QVariant &_value);
};
Q_DECLARE_INTERFACE(QueuedPluginInterface, PLUGIN_INTERFACE_NAME)

View File

@ -26,6 +26,7 @@
#include <QHash>
#include <QObject>
#include <QPair>
class QueuedPluginInterface;
@ -55,6 +56,13 @@ public:
* @brief QueuedPluginManager class destructor
*/
virtual ~QueuedPluginManager();
/**
* @brief get option name from database format
* @param _key
* option key
* @return plugin name and option key in plugin format
*/
static QPair<QString, QString> convertOptionName(const QString &_key);
/**
* @brief plugin manager interface
* @return pointer to plugin manager interface
@ -82,6 +90,16 @@ public:
*/
bool unloadPlugin(const QString &_name);
public slots:
/**
* @brief notifies plugin about option changes
* @param _key
* database option key
* @param _value
* option value
*/
void optionChanged(const QString &_key, const QVariant &_value);
private:
/**
* @brief loaded plugins

View File

@ -37,11 +37,9 @@ class QueuedPluginManagerInterface : public QObject
public:
/**
* @brief QueuedPluginManagerInterface class constructor
* @param parent
* pointer to parent object
*/
explicit QueuedPluginManagerInterface(QObject *parent)
: QObject(parent){};
explicit QueuedPluginManagerInterface()
: QObject(nullptr){};
/**
* @brief QueuedPluginManagerInterface class destructor
*/

View File

@ -148,7 +148,7 @@ void QueuedAdvancedSettings::set(const QString &_key, const QVariant &_value)
m_values[_key.toLower()] = _value;
auto id = QueuedCfg::QueuedSettingsDefaults[internalId(_key)].id;
emit(valueUpdated(id, _value));
emit(valueUpdated(id, _key, _value));
}

View File

@ -53,6 +53,25 @@ QueuedCore::~QueuedCore()
}
/**
* @fn addPlugin
*/
bool QueuedCore::addPlugin(
const QString &_plugin,
const QueuedUserManager::QueuedUserAuthorization &_auth)
{
qCDebug(LOG_LIB) << "Add plugin" << _plugin;
bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin);
if (!isAdmin) {
qCInfo(LOG_LIB) << "User" << _auth.user << "not allowed to add plugin";
return false;
}
return editPluginPrivate(_plugin, true);
}
/**
* @addTask
*/
@ -309,6 +328,46 @@ QVariant QueuedCore::option(const QString &_key)
}
/**
* @fn pluginSettings
*/
QVariantHash QueuedCore::pluginSettings(const QString &_plugin)
{
qCDebug(LOG_LIB) << "Get plugin settings for" << _plugin;
auto dbSettings
= m_database->get(QueuedDB::SETTINGS_TABLE,
QString("WHERE key LIKE 'Plugin.%1.%'").arg(_plugin));
QVariantHash settings;
std::for_each(dbSettings.cbegin(), dbSettings.cend(),
[&settings](const QVariantHash &value) {
settings[value["key"].toString()] = value["value"];
});
return settings;
}
/**
* @fn removePlugin
*/
bool QueuedCore::removePlugin(
const QString &_plugin,
const QueuedUserManager::QueuedUserAuthorization &_auth)
{
qCDebug(LOG_LIB) << "Remove plugin" << _plugin;
bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin);
if (!isAdmin) {
qCInfo(LOG_LIB) << "User" << _auth.user
<< "not allowed to remove plugin";
return false;
}
return editPluginPrivate(_plugin, false);
}
/**
* @fn startTask
*/
@ -430,6 +489,8 @@ void QueuedCore::deinit()
delete m_databaseManager;
if (m_reports)
delete m_reports;
if (m_plugins)
delete m_plugins;
if (m_processes)
delete m_processes;
if (m_users)
@ -455,15 +516,17 @@ void QueuedCore::init(const QString &_configuration)
// init parts
initSettings(_configuration);
initPlugins();
initUsers();
initProcesses();
// settings update notifier
m_connections += connect(
m_advancedSettings,
SIGNAL(valueUpdated(const QueuedCfg::QueuedSettings, const QVariant &)),
SIGNAL(valueUpdated(const QueuedCfg::QueuedSettings, const QString &,
const QVariant &)),
this, SLOT(updateSettings(const QueuedCfg::QueuedSettings,
const QVariant &)));
const QString &, const QVariant &)));
// dbus session
initDBus();
@ -473,15 +536,19 @@ void QueuedCore::init(const QString &_configuration)
/**
* @fn updateSettings
*/
void QueuedCore::updateSettings(const QueuedCfg::QueuedSettings _key,
const QVariant &_value)
void QueuedCore::updateSettings(const QueuedCfg::QueuedSettings _id,
const QString &_key, const QVariant &_value)
{
qCDebug(LOG_LIB) << "Received update for" << static_cast<int>(_key)
qCDebug(LOG_LIB) << "Received update for" << static_cast<int>(_id) << _key
<< "with value" << _value;
// FIXME propbably there is a better way to change settings
switch (_key) {
switch (_id) {
case QueuedCfg::QueuedSettings::Invalid:
// check if it is plugin settings
if (_key.startsWith("Plugin."))
m_plugins->optionChanged(_key, _value);
// do nothing otherwise
break;
case QueuedCfg::QueuedSettings::DatabaseInterval:
m_databaseManager->setInterval(_value.toLongLong());
@ -500,6 +567,9 @@ void QueuedCore::updateSettings(const QueuedCfg::QueuedSettings _key,
m_processes->setOnExitAction(
static_cast<QueuedProcessManager::OnExitAction>(_value.toInt()));
break;
case QueuedCfg::QueuedSettings::Plugins:
// do nothing here
break;
case QueuedCfg::QueuedSettings::ProcessCommandLine:
m_processes->setProcessLine(_value.toString());
break;
@ -601,6 +671,22 @@ void QueuedCore::initDBus()
}
/**
* @fn initPlugins
*/
void QueuedCore::initPlugins()
{
QStringList pluginList
= m_advancedSettings->get(QueuedCfg::QueuedSettings::Plugins)
.toString()
.split('\x01');
m_plugins = new QueuedPluginManager(this);
for (auto &plugin : pluginList)
m_plugins->loadPlugin(plugin, pluginSettings(plugin));
}
/**
* @fn initProcesses
*/
@ -796,8 +882,35 @@ bool QueuedCore::editOptionPrivate(const QString &_key, const QVariant &_value)
}
// add to child object
if (status)
if (status) {
m_advancedSettings->set(_key, _value);
// notify plugin if required
}
return status;
}
/**
* @fn editPluginPrivate
*/
bool QueuedCore::editPluginPrivate(const QString &_plugin, const bool _add)
{
qCDebug(LOG_LIB) << "Edit plugin" << _plugin << "add" << _add;
QStringList pluginList
= m_advancedSettings->get(QueuedCfg::QueuedSettings::Plugins)
.toString()
.split('\x01');
bool status = false;
if (_add && !pluginList.contains(_plugin))
status = m_plugins->loadPlugin(_plugin, pluginSettings(_plugin));
else if (!_add && pluginList.contains(_plugin))
status = m_plugins->unloadPlugin(_plugin);
else
qCDebug(LOG_LIB) << "Plugin" << _plugin
<< "not loaded or already loaded";
return status;
}

View File

@ -74,6 +74,31 @@ bool QueuedCoreInterface::OptionEdit(const QString &key,
}
/**
* @fn PluginAdd
*/
bool QueuedCoreInterface::PluginAdd(const QString &plugin,
const QString &whoAmI, const QString &token)
{
qCDebug(LOG_DBUS) << "Add plugin" << plugin << "auth by" << whoAmI;
return m_core->addPlugin(plugin, QueuedUserManager::auth(whoAmI, token));
}
/**
* @fn PluginRemove
*/
bool QueuedCoreInterface::PluginRemove(const QString &plugin,
const QString &whoAmI,
const QString &token)
{
qCDebug(LOG_DBUS) << "Remove plugin" << plugin << "auth by" << whoAmI;
return m_core->removePlugin(plugin, QueuedUserManager::auth(whoAmI, token));
}
/**
* @fn TaskAdd
*/

View File

@ -39,7 +39,7 @@ QueuedPluginManager::QueuedPluginManager(QObject *parent)
{
qCDebug(LOG_PL) << __PRETTY_FUNCTION__;
m_interface = new QueuedPluginManagerInterface(this);
m_interface = new QueuedPluginManagerInterface();
}
@ -58,6 +58,25 @@ QueuedPluginManager::~QueuedPluginManager()
}
/**
* @fn convertOptionName
*/
QPair<QString, QString>
QueuedPluginManager::convertOptionName(const QString &_key)
{
qCDebug(LOG_PL) << "Convert option name" << _key;
QStringList fields = _key.split('.');
// Plugin.
fields.takeFirst();
// plugin name
QString plugin = fields.takeFirst();
QString option = fields.join('.');
return {plugin, option};
}
/**
* @fn interface
*/
@ -76,6 +95,10 @@ bool QueuedPluginManager::loadPlugin(const QString &_name,
qCDebug(LOG_PL) << "Load plugin" << _name << "with settings" << _settings;
QString libraryName = QString("lib%2.so").arg(_name);
// init plugin settings with valid keys
QVariantHash pluginSettings;
for (auto &key : _settings.keys())
pluginSettings[convertOptionName(key).first] = _settings[key];
for (auto &dir : pluginLocations()) {
if (!QDir(dir).entryList(QDir::Files).contains(libraryName))
@ -93,7 +116,7 @@ bool QueuedPluginManager::loadPlugin(const QString &_name,
<< "error" << loader.errorString();
if (item) {
m_plugins[_name] = item;
item->init(_settings);
item->init(pluginSettings);
item->connect(interface());
} else {
qCCritical(LOG_PL) << "Could not cast plugin" << _name;
@ -140,3 +163,22 @@ bool QueuedPluginManager::unloadPlugin(const QString &_name)
return true;
}
/**
* @fn optionChanged
*/
void QueuedPluginManager::optionChanged(const QString &_key,
const QVariant &_value)
{
qCDebug(LOG_PL) << "Option" << _key << "changed to" << _value;
auto option = convertOptionName(_key);
if (!m_plugins.contains(option.first)) {
qCWarning(LOG_PL) << "Plugin" << option.first << "not found for"
<< _key;
return;
}
return m_plugins[option.first]->updateSettings(option.second, _value);
}