From c2b92cd212a84d46a78a26c732129311b89dc379 Mon Sep 17 00:00:00 2001 From: Evgeniy Alekseev Date: Sun, 26 Feb 2017 05:00:48 +0300 Subject: [PATCH] some limit improvements --- sources/queued/include/queued/Queued.h | 1 + sources/queued/include/queued/QueuedCore.h | 56 ++++++++ .../queued/include/queued/QueuedDatabase.h | 34 ++--- .../include/queued/QueuedDatabaseSchema.h | 7 +- sources/queued/include/queued/QueuedLimits.h | 130 +++++++++++++++++ sources/queued/include/queued/QueuedProcess.h | 31 +---- .../include/queued/QueuedProcessManager.h | 27 ++-- sources/queued/include/queued/QueuedUser.h | 88 +++--------- .../queued/include/queued/QueuedUserManager.h | 27 ++++ sources/queued/src/QueuedCore.cpp | 131 +++++++++++++++++- sources/queued/src/QueuedDatabase.cpp | 112 +++++++-------- sources/queued/src/QueuedLimits.cpp | 81 +++++++++++ sources/queued/src/QueuedProcess.cpp | 66 +-------- sources/queued/src/QueuedProcessManager.cpp | 84 ++++++----- sources/queued/src/QueuedUser.cpp | 117 ++++------------ sources/queued/src/QueuedUserManager.cpp | 79 ++++++++--- 16 files changed, 686 insertions(+), 385 deletions(-) create mode 100644 sources/queued/include/queued/QueuedLimits.h create mode 100644 sources/queued/src/QueuedLimits.cpp diff --git a/sources/queued/include/queued/Queued.h b/sources/queued/include/queued/Queued.h index 38b62c7..197f1cc 100644 --- a/sources/queued/include/queued/Queued.h +++ b/sources/queued/include/queued/Queued.h @@ -30,6 +30,7 @@ #include "QueuedDatabase.h" #include "QueuedDebug.h" #include "QueuedEnums.h" +#include "QueuedLimits.h" #include "QueuedProcess.h" #include "QueuedProcessManager.h" #include "QueuedReportManager.h" diff --git a/sources/queued/include/queued/QueuedCore.h b/sources/queued/include/queued/QueuedCore.h index 684612b..95ad4ee 100644 --- a/sources/queued/include/queued/QueuedCore.h +++ b/sources/queued/include/queued/QueuedCore.h @@ -26,6 +26,8 @@ #include +#include "QueuedLimits.h" + class QueuedAdvancedSettings; class QueuedDatabase; @@ -52,16 +54,65 @@ public: * @brief QueuedCore class destructor */ virtual ~QueuedCore(); + /** + * @brief add new task + * @param _command command line + * @param _arguments command arguments + * @param _workingDirectory working directory + * @param _nice nice level + * @param _userId task owner user ID + * @param _limits task defined limits + * @return true on successfully addition + */ + bool addTask(const QString &_command, const QStringList &_arguments, + const QString &_workingDirectory, const unsigned int _nice, + const long long _userId, const QueuedLimits::Limits &_limits); + /** + * @brief add new user + * @param _name user name + * @param _email user email + * @param _password user password + * @param _permissions user permissions + * @param _limits user limits + * @return true on successfully addition + */ + bool addUser(const QString &_name, const QString &_email, + const QString &_password, const unsigned int _permissions, + const QueuedLimits::Limits &_limits); /** * @brief deinit subclasses */ void deinit(); + /** + * @brief edit user + * @param _id user ID to edit + * @param _userData user data to edit + * @remark ref + * @return + */ + bool editUser(const long long _id, const QVariantHash &_userData); /** * @brief init subclasses * @param _configuration path to configuration file */ void init(const QString &_configuration); +private slots: + /** + * @brief update process time + * @param _id task id + * @param _startTime task start time or empty + * @param _endTime task end time or empty + */ + void updateTaskTime(const long long _id, const QDateTime &_startTime, + const QDateTime &_endTime); + /** + * @brief update user login time + * @param _id user ID + * @param _time user login time + */ + void updateUserLoginTime(const long long _id, const QDateTime &_time); + private: /** * @brief pointer to advanced settings object @@ -87,6 +138,11 @@ private: * @brief pointer to user manager */ QueuedUserManager *m_users = nullptr; + // additional properties + /** + * @brief connection list + */ + QList m_connections; }; diff --git a/sources/queued/include/queued/QueuedDatabase.h b/sources/queued/include/queued/QueuedDatabase.h index bbda891..0310d54 100644 --- a/sources/queued/include/queued/QueuedDatabase.h +++ b/sources/queued/include/queued/QueuedDatabase.h @@ -50,13 +50,6 @@ public: * @brief QueuedDatabase class destructor */ virtual ~QueuedDatabase(); - /** - * @brief add record to database - * @param _table table name - * @param _value value to insert - * @return index of inserted record or -1 if no insertion - */ - long long add(const QString &_table, const QVariantHash &_value); /** * @brief check and create database */ @@ -82,15 +75,6 @@ public: * @return variant map from table */ QVariantHash get(const QString &_table, const long long _id); - /** - * @brief modify record in table - * @param _table table name - * @param _id id for search - * @param _value value to update - * @return true on successfully modification - */ - bool modify(const QString &_table, const long long _id, - const QVariantHash &_value); /** * @brief open database * @param _hostname hostname to connect, may be empty @@ -107,6 +91,24 @@ public: */ QString path() const; +public slots: + /** + * @brief add record to database + * @param _table table name + * @param _value value to insert + * @return index of inserted record or -1 if no insertion + */ + long long add(const QString &_table, const QVariantHash &_value); + /** + * @brief modify record in table + * @param _table table name + * @param _id id for search + * @param _value value to update + * @return true on successfully modification + */ + bool modify(const QString &_table, const long long _id, + const QVariantHash &_value); + private: /** * @brief database diff --git a/sources/queued/include/queued/QueuedDatabaseSchema.h b/sources/queued/include/queued/QueuedDatabaseSchema.h index 7dc690e..2ccb999 100644 --- a/sources/queued/include/queued/QueuedDatabaseSchema.h +++ b/sources/queued/include/queued/QueuedDatabaseSchema.h @@ -96,6 +96,7 @@ const QueuedDBSchema DBSchema = { {"nice", {"nice", "INT", QVariant::UInt}}, {"uid", {"uid", "INT", QVariant::UInt}}, {"gid", {"gid", "INT", QVariant::UInt}}, + {"limits", {"limits", "TEXT", QVariant::String}}, {"startTime", {"startTime", "INT", QVariant::LongLong}}, {"endTime", {"endTime", "INT", QVariant::LongLong}}}}, {TOKENS_TABLE, @@ -112,11 +113,7 @@ const QueuedDBSchema DBSchema = { {"passwordSHA512", {"passwordSHA512", "TEXT", QVariant::String}}, {"email", {"email", "TEXT", QVariant::String}}, {"lastLogin", {"lastLogin", "TEXT", QVariant::String}}, - {"cpu", {"cpu", "INT", QVariant::LongLong}}, - {"gpu", {"gpu", "INT", QVariant::LongLong}}, - {"memory", {"memory", "INT", QVariant::LongLong}}, - {"gpumemory", {"gpumemory", "INT", QVariant::LongLong}}, - {"storage", {"storage", "INT", QVariant::LongLong}}, + {"limits", {"limits", "TEXT", QVariant::String}}, {"permissions", {"permissions", "INT", QVariant::UInt}}}}}; }; diff --git a/sources/queued/include/queued/QueuedLimits.h b/sources/queued/include/queued/QueuedLimits.h new file mode 100644 index 0000000..95f0ec7 --- /dev/null +++ b/sources/queued/include/queued/QueuedLimits.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2016 Evgeniy Alekseev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ +/** + * @file QueuedLimits.h + * Header of Queued library + * @author Evgeniy Alekseev + * @copyright MIT + * @bug https://github.com/arcan1s/queued/issues + */ + + +#ifndef QUEUEDLIMITS_H +#define QUEUEDLIMITS_H + +#include + + +/** + * @defgroup QueuedLimits + * @brief Queued limits + */ +namespace QueuedLimits +{ +/** + * @ingroup QueuedLimits + * @struct Limits + * @var cpu + * limit by CPU + * @var gpu + * limit by GPU + * @var memory + * limit by memory + * @var gpumemory + * limit by GPU memory + * @var storage + * limit by storage + * @var valid + * is this permissions default generated or not + */ +struct Limits { + long long cpu; + long long gpu; + long long memory; + long long gpumemory; + long long storage; + bool valid; + // structure methods + /** + * @brief limits to string conversion + * @return string representation of limits + */ + QString toString() const + { + return QString("%1\x01%2\x01%3\x01%4\x01%5") + .arg(cpu) + .arg(gpu) + .arg(memory) + .arg(gpumemory) + .arg(storage); + }; + /** + * @brief default structure constructor + */ + Limits() + : cpu(0) + , gpu(0) + , memory(0) + , gpumemory(0) + , storage(0) + , valid(false){}; + /** + * @brief structure constructor from string representation + * @param _stringLimits limits string representation + */ + Limits(const QString &_stringLimits) + { + QStringList limits = _stringLimits.split(QChar('\x01')); + while (limits.count() < 4) + limits.append(QString("0")); + + cpu = limits.at(0).toLongLong(); + gpu = limits.at(1).toLongLong(); + memory = limits.at(2).toLongLong(); + gpumemory = limits.at(3).toLongLong(); + storage = limits.at(4).toLongLong(); + valid = true; + }; +}; +/** + * @ingroup QueuedLimits + * @brief convert QString memory value to integer + * @param _value value to convert + * @param _status conversion status + * @return converted integer + */ +long long convertMemory(QString _value, bool *_status); +/** + * @ingroup QueuedLimits + * @brief compare two limits + * @param _first first limit + * @param _second second limit + * @return true if first limit is less than second + */ +bool limitCompare(const long long _first, const long long _second); +/** + * @ingroup QueuedLimits + * @brief get minimal limits from given + * @param _task task defined limits + * @param _user user defined limit + * @param _default default limits if anu + * @return minimal limits from given + */ +Limits minimalLimits(const Limits &_task, const Limits &_user, + const Limits &_default); +}; + + +#endif /* QUEUEDLIMITS_H */ diff --git a/sources/queued/include/queued/QueuedProcess.h b/sources/queued/include/queued/QueuedProcess.h index 0ff3348..ef1710e 100644 --- a/sources/queued/include/queued/QueuedProcess.h +++ b/sources/queued/include/queued/QueuedProcess.h @@ -26,9 +26,8 @@ #include #include -#include -#include "QueuedEnums.h" +#include "QueuedLimits.h" /** @@ -62,6 +61,8 @@ public: * process end time * @var user * task owner ID + * @var limits + * task limits */ typedef struct { QString command; @@ -73,6 +74,7 @@ public: QDateTime startTime; QDateTime endTime; long long user; + QueuedLimits::Limits limits; } QueuedProcessDefinitions; /** @@ -98,18 +100,6 @@ public: * @return generated name of process */ QString name() const; - /** - * @brief remove limit - * @param _limitType limit type - */ - virtual void removeLimit(const QueuedEnums::LimitType _limitType); - /** - * @brief set limit - * @param _limitType limit type - * @param _value limit value - */ - virtual void setLimit(const QueuedEnums::LimitType _limitType, - const QVariant &_value = QVariant()); /** * @brief equal operator implementation * @param _other other object @@ -132,7 +122,7 @@ public slots: * @brief set end time * @param _time process end time */ - void setEndTime(const QDateTime &_time); + void setEndTime(const QDateTime &_time); private: /** @@ -143,17 +133,6 @@ private: * @brief index of process */ long long m_index = -1; - /** - * @brief limits array - */ - QHash m_limits; - /** - * @brief convert QString memory value to integer - * @param _value value to convert - * @param _status conversion status - * @return converted integer - */ - long long convertMemory(QString _value, bool *_status) const; }; diff --git a/sources/queued/include/queued/QueuedProcessManager.h b/sources/queued/include/queued/QueuedProcessManager.h index edc5461..b7193a7 100644 --- a/sources/queued/include/queued/QueuedProcessManager.h +++ b/sources/queued/include/queued/QueuedProcessManager.h @@ -74,23 +74,25 @@ public: virtual ~QueuedProcessManager(); /** * @brief add task + * @param _properties task properties from database * @param _index task index + * @return pointer to created task + */ + QueuedProcess *add(const QVariantHash &_properties, const long long _index); + /** + * @brief add task * @param _definitions process definitions + * @param _index task index * @return pointer to created task */ QueuedProcess * - add(const long long _index, - const QueuedProcess::QueuedProcessDefinitions _definitions); + add(const QueuedProcess::QueuedProcessDefinitions _definitions, + const long long _index); /** * @brief add tasks from database * @param _processes database stored tasks */ - void add(const QList &_processes); - /** - * @brief default action on exit - * @return default action from possible ones - */ - OnExitAction onExit() const; + void loadProcesses(const QList &_processes); /** * @brief task * @param _index task index @@ -112,6 +114,11 @@ public: * @param _index task index */ void stop(const long long _index); + /** + * @brief default action on exit + * @return default action from possible ones + */ + OnExitAction onExit() const; signals: /** @@ -119,13 +126,13 @@ signals: * @param _index task index * @param _time task start time */ - void taskStartTimeReceived(const long long _index, const QDateTime _time); + void taskStartTimeReceived(const long long _index, const QDateTime &_time); /** * @brief signal which will be called on task end * @param _index task index * @param _time task stop time */ - void taskStopTimeReceived(const long long _index, const QDateTime _time); + void taskStopTimeReceived(const long long _index, const QDateTime &_time); private slots: /** diff --git a/sources/queued/include/queued/QueuedUser.h b/sources/queued/include/queued/QueuedUser.h index 8632019..2a84f64 100644 --- a/sources/queued/include/queued/QueuedUser.h +++ b/sources/queued/include/queued/QueuedUser.h @@ -28,6 +28,7 @@ #include #include "QueuedEnums.h" +#include "QueuedLimits.h" /** @@ -45,16 +46,8 @@ class QueuedUser : public QObject Q_PROPERTY(unsigned int permissions READ permissions WRITE setPermissions NOTIFY userUpdated) // limits - Q_PROPERTY( - long long cpuLimit READ cpuLimit WRITE setCpuLimit NOTIFY userUpdated) - Q_PROPERTY( - long long gpuLimit READ gpuLimit WRITE setGpuLimit NOTIFY userUpdated) - Q_PROPERTY(long long memoryLimit READ memoryLimit WRITE setMemoryLimit - NOTIFY userUpdated) - Q_PROPERTY(long long gpumemoryLimit READ gpumemoryLimit WRITE - setGpumemoryLimit NOTIFY userUpdated) - Q_PROPERTY(long long storageLimit READ storageLimit WRITE setStorageLimit - NOTIFY userUpdated) + Q_PROPERTY(QueuedLimits::Limits limits READ limits WRITE setLimits NOTIFY + userUpdated) public: /** @@ -68,27 +61,15 @@ public: * password hash, may be empty * @var permissions * user permissions - * @var cpuLimit - * user limit by CPU - * @var gpuLimit - * user limit by GPU - * @var memoryLimit - * user limit by memory - * @var gpumemoryLimit - * user limit by GPU memory - * @var storageLimit - * user limit by storage + * @var limits + * user defined limits */ typedef struct { QString name; QString email; QString passwordSHA512; unsigned int permissions; - long long cpuLimit; - long long gpuLimit; - long long memoryLimit; - long long gpumemoryLimit; - long long storageLimit; + QueuedLimits::Limits limits; } QueuedUserDefinitions; /** @@ -124,6 +105,11 @@ public: * @return true if user has permission otherwise return false */ bool hasPermission(const QueuedEnums::Permission _permission); + /** + * @brief get UID and GID from user ID + * @return pair of {uid, gid} + */ + QPair ids(); /** * @brief check if password is valid * @param _password password as string @@ -165,30 +151,10 @@ public: unsigned int permissions() const; // permissions /** - * @brief cpu limit - * @return cpu limit in cores + * @brief user limits + * @return user limits */ - long long cpuLimit() const; - /** - * @brief gpu limit - * @return gpu limit in cores - */ - long long gpuLimit() const; - /** - * @brief memory limit - * @return memory limit in bytes - */ - long long memoryLimit() const; - /** - * @brief GPU memory limit - * @return GPU memory limit in bytes - */ - long long gpumemoryLimit() const; - /** - * @brief storage limit - * @return storage limit in bytes - */ - long long storageLimit() const; + QueuedLimits::Limits limits() const; // main properties /** * @brief set user email @@ -212,30 +178,10 @@ public: void setPermissions(const unsigned int _permissions); // permissions /** - * @brief set cpu limit - * @param _cpuLimit new cpu limit in cores + * @brief set limits + * @param _limit new user limits */ - void setCpuLimit(const long long _cpuLimit); - /** - * @brief set gpu limit - * @param _gpuLimit new gpu limit in cores - */ - void setGpuLimit(const long long _gpuLimit); - /** - * @brief set memory limit - * @param _memoryLimit new memory limit in bytes - */ - void setMemoryLimit(const long long _memoryLimit); - /** - * @brief set GPU memory limit - * @param _gpumemoryLimit new GPU memory limit in bytes - */ - void setGpumemoryLimit(const long long _gpumemoryLimit); - /** - * @brief set storage limit - * @param _storageLimit new storage limit in bytes - */ - void setStorageLimit(const long long _storageLimit); + void setLimits(const QueuedLimits::Limits &_limits); /** * @brief equal operator implementation * @param _other other object diff --git a/sources/queued/include/queued/QueuedUserManager.h b/sources/queued/include/queued/QueuedUserManager.h index 923befc..25ef50a 100644 --- a/sources/queued/include/queued/QueuedUserManager.h +++ b/sources/queued/include/queued/QueuedUserManager.h @@ -52,6 +52,13 @@ public: * @brief QueuedUserManager class destructor */ virtual ~QueuedUserManager(); + /** + * @brief add user + * @param _properties user properties from database + * @param _id user ID + * @return pointer to created user + */ + QueuedUser *add(const QVariantHash &_properties, const long long _id); /** * @brief add user * @param _definitions user definitions @@ -76,6 +83,12 @@ public: */ bool authorize(const QString &_user, const QString &_token, const QueuedEnums::Permission _service); + /** + * @brief get UID and GID from user ID + * @param _id user id + * @return pair of {uid, gid} + */ + QPair ids(const long long _id); /** * @brief load tokens * @param _tokens tokens list from database @@ -86,6 +99,12 @@ public: * @param _users users list from database */ void loadUsers(const QList &_users); + /** + * @brief user by ID + * @param _id user id for search + * @return user by id or nullptr if no user found + */ + QueuedUser *user(const long long _id); /** * @brief user by name * @param _name user name for search @@ -104,6 +123,14 @@ public: */ void setTokenExpiration(const long long &_expiry); +signals: + /** + * @brief signal which emits on each user successfully login + * @param _id user ID + * @param _time user login time + */ + void userLoggedIn(const long long _id, const QDateTime &_time); + private: /** * @brief token expiration in days diff --git a/sources/queued/src/QueuedCore.cpp b/sources/queued/src/QueuedCore.cpp index 2bee29c..98ea7df 100644 --- a/sources/queued/src/QueuedCore.cpp +++ b/sources/queued/src/QueuedCore.cpp @@ -50,11 +50,89 @@ QueuedCore::~QueuedCore() } +/** + * @addTask + */ +bool QueuedCore::addTask(const QString &_command, const QStringList &_arguments, + const QString &_workingDirectory, + const unsigned int _nice, const long long _userId, + const QueuedLimits::Limits &_limits) +{ + qCDebug(LOG_LIB) << "Add task" << _command << "with arguments" << _arguments + << "from user" << _userId; + + auto ids = m_users->ids(_userId); + auto taskLimits = QueuedLimits::minimalLimits( + _limits, m_users->user(_userId)->limits(), + QueuedLimits::Limits( + m_advancedSettings->get(QString("DefaultLimits")).toString())); + QVariantHash properties + = {{"userId", _userId}, {"command", _command}, + {"arguments", _arguments}, {"workDirectory", _workingDirectory}, + {"nice", _nice}, {"uid", ids.first}, + {"gid", ids.second}, {"limits", taskLimits.toString()}}; + auto id = m_database->add(QueuedDB::TASKS_TABLE, properties); + if (id == -1) { + qCWarning(LOG_LIB) << "Could not add task" << _command; + return false; + } + + m_processes->add(properties, id); + return true; +} + + +/** + * @fn addUser + */ +bool QueuedCore::addUser(const QString &_name, const QString &_email, + const QString &_password, + const unsigned int _permissions, + const QueuedLimits::Limits &_limits) +{ + qCDebug(LOG_LIB) << "Add user" << _name << "with email" << _email + << "and permissions" << _permissions; + + QVariantHash properties + = {{"name", _name}, + {"passwordSHA512", QueuedUser::hashFromPassword(_password)}, + {"email", _email}, + {"permissions", _permissions}, + {"limits", _limits.toString()}}; + auto id = m_database->add(QueuedDB::USERS_TABLE, properties); + if (id == -1) { + qCWarning(LOG_LIB) << "Could not add user" << _name; + return false; + } + + m_users->add(properties, id); + return true; +} + + /** * @fn deinit */ void QueuedCore::deinit() { + // clear connections first + for (auto &connection : m_connections) + disconnect(connection); + m_connections.clear(); + + // delete objects now + if (m_reports) + delete m_reports; + if (m_processes) + delete m_processes; + if (m_users) + delete m_users; + if (m_database) + delete m_database; + if (m_settings) + delete m_settings; + if (m_advancedSettings) + delete m_advancedSettings; } @@ -92,10 +170,61 @@ void QueuedCore::init(const QString &_configuration) m_users->setTokenExpiration(expiry); m_users->loadTokens(m_database->get(QueuedDB::TOKENS_TABLE)); m_users->loadUsers(m_database->get(QueuedDB::USERS_TABLE)); + m_connections += connect( + m_users, SIGNAL(userLoggedIn(const long long, const QDateTime &)), this, + SLOT(updateUserLoginTime(const long long, const QDateTime &))); // and processes finally auto onExitAction = static_cast( m_advancedSettings->get(QString("OnExitAction")).toInt()); m_processes = new QueuedProcessManager(this, onExitAction); - m_processes->add(m_database->get(QueuedDB::TASKS_TABLE)); + m_processes->loadProcesses(m_database->get(QueuedDB::TASKS_TABLE)); + m_connections + += connect(m_processes, &QueuedProcessManager::taskStartTimeReceived, + [this](const long long _index, const QDateTime &_time) { + return updateTaskTime(_index, _time, QDateTime()); + }); + m_connections + += connect(m_processes, &QueuedProcessManager::taskStopTimeReceived, + [this](const long long _index, const QDateTime &_time) { + return updateTaskTime(_index, QDateTime(), _time); + }); +} + + +/** +* @fn updateTaskTime + */ +void QueuedCore::updateTaskTime(const long long _id, + const QDateTime &_startTime, + const QDateTime &_endTime) +{ + qCDebug(LOG_LIB) << "Update task" << _id << "time to" << _startTime + << _endTime; + + QVariantHash record; + if (_startTime.isValid()) + record[QString("startTime")] = _startTime.toString(Qt::ISODate); + if (_endTime.isValid()) + record[QString("endTime")] = _endTime.toString(Qt::ISODate); + + bool status = m_database->modify(QueuedDB::TASKS_TABLE, _id, record); + if (!status) + qCWarning(LOG_LIB) << "Could not modify task record" << _id; +} + + +/** + * @fn updateUserLoginTime + */ +void QueuedCore::updateUserLoginTime(const long long _id, + const QDateTime &_time) +{ + qCDebug(LOG_LIB) << "Update user" << _id << "with login time" << _time; + + QVariantHash record = {{"lastLogin", _time.toString(Qt::ISODate)}}; + + bool status = m_database->modify(QueuedDB::USERS_TABLE, _id, record); + if (!status) + qCWarning(LOG_LIB) << "Could not modify user record" << _id; } diff --git a/sources/queued/src/QueuedDatabase.cpp b/sources/queued/src/QueuedDatabase.cpp index 7dd691f..617796a 100644 --- a/sources/queued/src/QueuedDatabase.cpp +++ b/sources/queued/src/QueuedDatabase.cpp @@ -51,31 +51,8 @@ QueuedDatabase::QueuedDatabase(QObject *parent, const QString path, QueuedDatabase::~QueuedDatabase() { qCDebug(LOG_LIB) << __PRETTY_FUNCTION__; -} - -/** - * @fn add - */ -long long QueuedDatabase::add(const QString &_table, const QVariantHash &_value) -{ - qCDebug(LOG_LIB) << "Add record" << _value << "to table" << _table; - - auto payload = getQueryPayload(_table, _value); - // build query - QSqlQuery query - = m_database.exec(QString("INSERT INTO %1 (%2) VALUES (%3)") - .arg(_table) - .arg(payload.first.join(QChar(','))) - .arg(payload.second.join(QChar(',')))); - QSqlError error = query.lastError(); - if (error.isValid()) { - qCCritical(LOG_LIB) << "Could not add record" << _value << "to table" - << _table << "message" << error.text(); - return -1; - } - - return lastInsertionId(_table); + m_database.close(); } @@ -181,37 +158,6 @@ QVariantHash QueuedDatabase::get(const QString &_table, const long long _id) } -/** - * @fn modify - */ -bool QueuedDatabase::modify(const QString &_table, const long long _id, - const QVariantHash &_value) -{ - qCDebug(LOG_LIB) << "Modify record" << _id << "in table" << _table - << "with value" << _value; - - auto payload = getQueryPayload(_table, _value); - QStringList stringPayload; - for (int i = 0; i < payload.first.count(); i++) - stringPayload.append(QString("%1='%2'") - .arg(payload.first.at(i)) - .arg(payload.second.at(i))); - // build query - QSqlQuery query = m_database.exec(QString("UPDATE %1 SET %2 WHERE _id=%3") - .arg(_table) - .arg(stringPayload.join(QChar(','))) - .arg(_id)); - QSqlError error = query.lastError(); - if (error.isValid()) { - qCCritical(LOG_LIB) << "Could not add record" << _value << "to table" - << _table << "message" << error.text(); - return false; - } - - return true; -} - - /** * @fn open */ @@ -243,6 +189,62 @@ QString QueuedDatabase::path() const } +/** + * @fn add + */ +long long QueuedDatabase::add(const QString &_table, const QVariantHash &_value) +{ + qCDebug(LOG_LIB) << "Add record" << _value << "to table" << _table; + + auto payload = getQueryPayload(_table, _value); + // build query + QSqlQuery query + = m_database.exec(QString("INSERT INTO %1 (%2) VALUES (%3)") + .arg(_table) + .arg(payload.first.join(QChar(','))) + .arg(payload.second.join(QChar(',')))); + QSqlError error = query.lastError(); + if (error.isValid()) { + qCCritical(LOG_LIB) << "Could not add record" << _value << "to table" + << _table << "message" << error.text(); + return -1; + } + + return lastInsertionId(_table); +} + + +/** + * @fn modify + */ +bool QueuedDatabase::modify(const QString &_table, const long long _id, + const QVariantHash &_value) +{ + qCDebug(LOG_LIB) << "Modify record" << _id << "in table" << _table + << "with value" << _value; + + auto payload = getQueryPayload(_table, _value); + QStringList stringPayload; + for (int i = 0; i < payload.first.count(); i++) + stringPayload.append(QString("%1='%2'") + .arg(payload.first.at(i)) + .arg(payload.second.at(i))); + // build query + QSqlQuery query = m_database.exec(QString("UPDATE %1 SET %2 WHERE _id=%3") + .arg(_table) + .arg(stringPayload.join(QChar(','))) + .arg(_id)); + QSqlError error = query.lastError(); + if (error.isValid()) { + qCCritical(LOG_LIB) << "Could not modify record" << _value << "in table" + << _table << "message" << error.text(); + return false; + } + + return true; +} + + /** * @fn createSchema */ diff --git a/sources/queued/src/QueuedLimits.cpp b/sources/queued/src/QueuedLimits.cpp new file mode 100644 index 0000000..d349cde --- /dev/null +++ b/sources/queued/src/QueuedLimits.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016 Evgeniy Alekseev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ +/** + * @file QueuedLimits.cpp + * Source code of queued library + * @author Evgeniy Alekseev + * @copyright GPLv3 + * @bug https://github.com/arcan1s/queued/issues + */ + + +#include "queued/Queued.h" + + +/** + * @fn convertMemory + */ +long long QueuedLimits::convertMemory(QString _value, bool *_status) +{ + long long intValue; + if (_value.endsWith(QString("K"))) + intValue = _value.remove(QString("K")).toLongLong(_status) * 1024; + else if (_value.endsWith(QString("M"))) + intValue + = _value.remove(QString("M")).toLongLong(_status) * 1024 * 1024; + else if (_value.endsWith(QString("G"))) + intValue = _value.remove(QString("G")).toLongLong(_status) * 1024 * 1024 + * 1024; + else + intValue = _value.toInt(_status); + + return intValue; +} + + +/** + * @fn limitCompare + */ +bool QueuedLimits::limitCompare(const long long _first, const long long _second) +{ + if (_first == 0) + return false; + else if (_second == 0) + return true; + else + return _first < _second; +} + + +/** + * @fn minimalLimits + */ +QueuedLimits::Limits +QueuedLimits::minimalLimits(const QueuedLimits::Limits &_task, + const QueuedLimits::Limits &_user, + const QueuedLimits::Limits &_default) +{ + QueuedLimits::Limits limits; + limits.cpu = std::min({_task.cpu, _user.cpu, _default.cpu}, &limitCompare); + limits.gpu = std::min({_task.gpu, _user.gpu, _default.gpu}, &limitCompare); + limits.memory = std::min({_task.memory, _user.memory, _default.memory}, + &limitCompare); + limits.gpumemory = std::min( + {_task.gpumemory, _user.gpumemory, _default.gpumemory}, &limitCompare); + limits.storage = std::min({_task.storage, _user.storage, _default.storage}, + &limitCompare); + + return limits; +} diff --git a/sources/queued/src/QueuedProcess.cpp b/sources/queued/src/QueuedProcess.cpp index 393bf05..d7f8531 100644 --- a/sources/queued/src/QueuedProcess.cpp +++ b/sources/queued/src/QueuedProcess.cpp @@ -23,9 +23,12 @@ #include "queued/Queued.h" -#include #include +extern "C" { +#include +} + /** * @class QueuedProcess @@ -75,41 +78,6 @@ QString QueuedProcess::name() const } -/** - * @fn removeLimit - */ -void QueuedProcess::removeLimit(const QueuedEnums::LimitType _limitType) -{ - qCDebug(LOG_LIB) << "Remove limit" << static_cast(_limitType); - - m_limits.remove(_limitType); -} - - -/** - * @fn setLimit - */ -void QueuedProcess::setLimit(const QueuedEnums::LimitType _limitType, - const QVariant &_value) -{ - qCDebug(LOG_LIB) << "Set limit" << static_cast(_limitType) << "to" - << _value; - - if (!_value.isValid()) - return removeLimit(_limitType); - - bool status = false; - long long intValue = _value.type() == QVariant::String - ? convertMemory(_value.toString(), &status) - : _value.toLongLong(&status); - - if (!status) - removeLimit(_limitType); - else - m_limits[_limitType] = intValue; -} - - /** * @fn operator== */ @@ -142,33 +110,9 @@ void QueuedProcess::run() /** * @fn setEndTime */ - void QueuedProcess::setEndTime(const QDateTime &_time) +void QueuedProcess::setEndTime(const QDateTime &_time) { qCDebug(LOG_LIB) << "Set end time to" << _time; m_definitions.endTime = _time; } - - -/** - * @fn convertMemory - */ -long long QueuedProcess::convertMemory(QString _value, bool *_status) const -{ - qCDebug(LOG_LIB) << "Convert memory value" << _value; - - long long intValue; - if (_value.endsWith(QString("K"))) - intValue = _value.remove(QString("K")).toLongLong(_status) * 1024; - else if (_value.endsWith(QString("M"))) - intValue - = _value.remove(QString("M")).toLongLong(_status) * 1024 * 1024; - else if (_value.endsWith(QString("G"))) - intValue = _value.remove(QString("G")).toLongLong(_status) * 1024 * 1024 - * 1024; - else - intValue = _value.toInt(_status); - - qCInfo(LOG_LIB) << "Converted value" << intValue; - return intValue; -} diff --git a/sources/queued/src/QueuedProcessManager.cpp b/sources/queued/src/QueuedProcessManager.cpp index f4b229f..899bfb6 100644 --- a/sources/queued/src/QueuedProcessManager.cpp +++ b/sources/queued/src/QueuedProcessManager.cpp @@ -50,12 +50,44 @@ QueuedProcessManager::~QueuedProcessManager() } +/** + * @fn add + */ +QueuedProcess *QueuedProcessManager::add(const QVariantHash &_properties, + const long long _index) +{ + qCDebug(LOG_LIB) << "Add new process" << _properties << "with index" + << _index; + + QueuedProcess::QueuedProcessDefinitions defs; + // parameters + defs.command = _properties[QString("command")].toString(); + defs.arguments + = _properties[QString("arguments")].toString().split(QChar('\x01')); + defs.workingDirectory = _properties[QString("workDirectory")].toString(); + defs.nice = _properties[QString("nice")].toUInt(); + defs.limits + = QueuedLimits::Limits(_properties[QString("limits")].toString()); + // user data + defs.uid = _properties[QString("uid")].toUInt(); + defs.gid = _properties[QString("gid")].toUInt(); + defs.user = _properties[QString("user")].toLongLong(); + // metadata + defs.startTime = QDateTime::fromString( + _properties[QString("startTime")].toString(), Qt::ISODate); + defs.endTime = QDateTime::fromString( + _properties[QString("endTime")].toString(), Qt::ISODate); + + return add(defs, _index); +} + + /** * @fn add */ QueuedProcess *QueuedProcessManager::add( - const long long _index, - const QueuedProcess::QueuedProcessDefinitions _definitions) + const QueuedProcess::QueuedProcessDefinitions _definitions, + const long long _index) { qCDebug(LOG_LIB) << "Add new process" << _definitions.command << "with index" << _index; @@ -78,41 +110,14 @@ QueuedProcess *QueuedProcessManager::add( /** - * @fn add + * @fn loadProcesses */ -void QueuedProcessManager::add(const QList &_processes) +void QueuedProcessManager::loadProcesses(const QList &_processes) { qCDebug(LOG_LIB) << "Add tasks from" << _processes; - for (auto &pr : _processes) { - QueuedProcess::QueuedProcessDefinitions defs; - // parameters - defs.command = pr[QString("command")].toString(); - defs.arguments - = pr[QString("arguments")].toString().split(QChar('\x01')); - defs.workingDirectory = pr[QString("workDirectory")].toString(); - defs.nice = pr[QString("nice")].toUInt(); - // user data - defs.uid = pr[QString("uid")].toUInt(); - defs.gid = pr[QString("gid")].toUInt(); - defs.user = pr[QString("user")].toLongLong(); - // metadata - defs.startTime = QDateTime::fromString( - pr[QString("startTime")].toString(), Qt::ISODate); - defs.endTime = QDateTime::fromString(pr[QString("endTime")].toString(), - Qt::ISODate); - - add(pr[QString("_id")].toLongLong(), defs); - } -} - - -/** - * @fn onExit - */ -QueuedProcessManager::OnExitAction QueuedProcessManager::onExit() const -{ - return m_onExit; + for (auto &processData : _processes) + add(processData, processData[QString("_id")].toLongLong()); } @@ -150,7 +155,7 @@ void QueuedProcessManager::remove(const long long _index) auto connection = m_connections.take(_index); disconnect(connection); - switch (m_onExit) { + switch (onExit()) { case OnExitAction::Kill: pr->kill(); break; @@ -177,7 +182,7 @@ void QueuedProcessManager::stop(const long long _index) return; } - switch (m_onExit) { + switch (onExit()) { case OnExitAction::Kill: pr->kill(); break; @@ -189,6 +194,15 @@ void QueuedProcessManager::stop(const long long _index) } +/** + * @fn onExit + */ +QueuedProcessManager::OnExitAction QueuedProcessManager::onExit() const +{ + return m_onExit; +} + + /** * @fn taskFinished */ diff --git a/sources/queued/src/QueuedUser.cpp b/sources/queued/src/QueuedUser.cpp index 723f459..169c56d 100644 --- a/sources/queued/src/QueuedUser.cpp +++ b/sources/queued/src/QueuedUser.cpp @@ -25,6 +25,11 @@ #include +extern "C" { +#include +#include +} + /** * @class QueuedUser @@ -94,6 +99,24 @@ bool QueuedUser::hasPermission(const QueuedEnums::Permission _permission) } +/** + * @fn ids + */ +QPair QueuedUser::ids() +{ + QPair system = {1, 1}; + + auto pwd = getpwnam(name().toLocal8Bit().constData()); + if (!pwd) { + qCWarning(LOG_LIB) << "No user found by name" << name(); + return system; + } + system = {pwd->pw_uid, pwd->pw_gid}; + + return system; +} + + /** * @fn isPasswordValid */ @@ -164,47 +187,11 @@ unsigned int QueuedUser::permissions() const /** - * @fn cpuLimit + * @fn limits */ -long long QueuedUser::cpuLimit() const +QueuedLimits::Limits QueuedUser::limits() const { - return m_definitions.cpuLimit; -} - - -/** - * @fn gpuLimit - */ -long long QueuedUser::gpuLimit() const -{ - return m_definitions.gpuLimit; -} - - -/** - * @fn memoryLimit - */ -long long QueuedUser::memoryLimit() const -{ - return m_definitions.memoryLimit; -} - - -/** - * @fn gpumemoryLimit - */ -long long QueuedUser::gpumemoryLimit() const -{ - return m_definitions.gpumemoryLimit; -} - - -/** - * @fn storageLimit - */ -long long QueuedUser::storageLimit() const -{ - return m_definitions.storageLimit; + return m_definitions.limits; } @@ -253,57 +240,13 @@ void QueuedUser::setPermissions(const unsigned int _permissions) /** - * @fn setCpuLimit + * @fn setLimits */ -void QueuedUser::setCpuLimit(const long long _cpuLimit) +void QueuedUser::setLimits(const QueuedLimits::Limits &_limits) { - qCDebug(LOG_LIB) << "New user cpu limit" << _cpuLimit; + qCDebug(LOG_LIB) << "New user limits" << _limits.toString(); - m_definitions.cpuLimit = _cpuLimit; -} - - -/** - * @fn setGpuLimit - */ -void QueuedUser::setGpuLimit(const long long _gpuLimit) -{ - qCDebug(LOG_LIB) << "New user gpu limit" << _gpuLimit; - - m_definitions.gpuLimit = _gpuLimit; -} - - -/** - * @fn setMemoryLimit - */ -void QueuedUser::setMemoryLimit(const long long _memoryLimit) -{ - qCDebug(LOG_LIB) << "New user memory limit" << _memoryLimit; - - m_definitions.memoryLimit = _memoryLimit; -} - - -/** - * @fn setGpumemoryLimit - */ -void QueuedUser::setGpumemoryLimit(const long long _gpumemoryLimit) -{ - qCDebug(LOG_LIB) << "New user gpu memory limit" << _gpumemoryLimit; - - m_definitions.gpumemoryLimit = _gpumemoryLimit; -} - - -/** - * @fn setStorageLimit - */ -void QueuedUser::setStorageLimit(const long long _storageLimit) -{ - qCDebug(LOG_LIB) << "New user storage limit" << _storageLimit; - - m_definitions.storageLimit = _storageLimit; + m_definitions.limits = _limits; } diff --git a/sources/queued/src/QueuedUserManager.cpp b/sources/queued/src/QueuedUserManager.cpp index 13779fa..1dedd13 100644 --- a/sources/queued/src/QueuedUserManager.cpp +++ b/sources/queued/src/QueuedUserManager.cpp @@ -53,6 +53,26 @@ QueuedUserManager::~QueuedUserManager() } +/** + * @fn add + */ +QueuedUser *QueuedUserManager::add(const QVariantHash &_properties, + const long long _id) +{ + qCDebug(LOG_LIB) << "Add user" << _properties << "with ID" << _id; + + QueuedUser::QueuedUserDefinitions defs; + defs.name = _properties[QString("name")].toString(); + defs.email = _properties[QString("email")].toString(); + defs.passwordSHA512 = _properties[QString("passwordSHA512")].toString(); + defs.permissions = _properties[QString("permissions")].toUInt(); + defs.limits + = QueuedLimits::Limits(_properties[QString("limits")].toString()); + + return add(defs, _id); +} + + /** * @fn add */ @@ -82,19 +102,21 @@ QString QueuedUserManager::authorize(const QString &_user, { qCDebug(LOG_LIB) << "Authorize user" << _user; - if (!m_users.contains(_user)) { + auto userObj = user(_user); + if (!userObj) { qCInfo(LOG_LIB) << "No user found" << _user; return QString(); } - bool status = m_users[_user]->isPasswordValid(_password); + bool status = userObj->isPasswordValid(_password); if (!status) { qCInfo(LOG_LIB) << "User password invalid for" << _user; return QString(); } - QDateTime expiry - = QDateTime::currentDateTimeUtc().addDays(tokenExpiration()); + auto time = QDateTime::currentDateTimeUtc(); + QDateTime expiry = time.addDays(tokenExpiration()); + emit(userLoggedIn(userObj->index(), time)); return m_tokens->registerToken(expiry); } @@ -123,6 +145,23 @@ bool QueuedUserManager::authorize(const QString &_user, const QString &_token, } +/** + * @fn ids + */ +QPair QueuedUserManager::ids(const long long _id) +{ + qCDebug(LOG_LIB) << "Get ids for user" << _id; + + auto userObj = user(_id); + if (!userObj) { + qCWarning(LOG_LIB) << "No user found for ID" << _id; + return {1, 1}; + } + + return userObj->ids(); +} + + /** * @fn loadTokens */ @@ -142,21 +181,25 @@ void QueuedUserManager::loadUsers(const QList &_users) qCDebug(LOG_LIB) << "Set users from" << _users; // load now - for (auto &userdata : _users) { - QueuedUser::QueuedUserDefinitions defs; - defs.name = userdata[QString("name")].toString(); - defs.email = userdata[QString("email")].toString(); - defs.passwordSHA512 = userdata[QString("passwordSHA512")].toString(); - defs.permissions = userdata[QString("permissions")].toUInt(); - // limits - defs.cpuLimit = userdata[QString("cpuLimit")].toLongLong(); - defs.gpuLimit = userdata[QString("gpuLimit")].toLongLong(); - defs.memoryLimit = userdata[QString("memoryLimit")].toLongLong(); - defs.gpumemoryLimit = userdata[QString("gpumemoryLimit")].toLongLong(); - defs.storageLimit = userdata[QString("storageLimit")].toLongLong(); + for (auto &userData : _users) + add(userData, userData[QString("_id")].toLongLong()); +} - add(defs, userdata[QString("_id")].toLongLong()); + +/** + * @fn user + */ +QueuedUser *QueuedUserManager::user(const long long _id) +{ + qCDebug(LOG_LIB) << "Look for user" << _id; + + for (auto &userObj : m_users.values()) { + if (userObj->index() != _id) + continue; + return userObj; } + + return nullptr; } @@ -167,7 +210,7 @@ QueuedUser *QueuedUserManager::user(const QString &_name) { qCDebug(LOG_LIB) << "Look for user" << _name; - return m_users.contains(_name) ? m_users[_name] : nullptr; + return m_users.value(_name, nullptr); }