Commit 7ba5d23f authored by Swann Perarnau's avatar Swann Perarnau
Browse files

Argo Containers V1

Judicael's code, unchanged, extracted from the master branch of pre-ECP
NodeOS repository.

This is not a filter-branch, just a cp -r. We are not losing anything
important in the git history, as it was only 3 commits long, without any
details on the history of the code.
parents
# Object files
*.o
*.ko
*.obj
*.elf
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
1- Back-to-back invocation of argo_nodeos_config for container manipulation
could fail. This is due to the cgroup filesystem (sysfs in general) taking
some time to do its processing. It is recommended to use sync or put some
sleep between the invocations.
2- While altering a container, argo_nodeos_config is not particularly forgiving
of "mindless" user inputs. Here are a few examples:
-If one tries to add to a container a resource that it already has, the
operation will fail (with an explicit error message). The same happens for a
resource being removed. This should be changed to exhibit an idempotent
behavior instead (with respect to the particular hardware resource being
added or removed).
Basically, if a container has the cpus cores {0,1,4,5}; an "alter_container"
command that tries to add {0,1,8,9} will fail because of the attempt to
"re-add" {0,1}. Ideally, the operation should actually succeed and lead the
container to now possess {0,1,4,5,8,9}.
3- alter_service_os is partially implemented (and is not working yet). Instead,
the user can delete and recreate (in the meantime).
4- json files as input was planned from the beginning; but is not implemented
(didn't see this as a high priority feature).
This diff is collapsed.
A-Daemon mode (--daemon, needs a client)
-Beacause the startup might be a bit heavy; the deamon mode is recommended
when the config is expected to keep changing. The daemon is expected to run
on the serviceOS
B- Each new supported resource controller leads to the implementation of two
interfaces:
-IResource_controller: A container aggregates some of this
-IResource_controller_state: the container manager aggregates these and
keeps acocunting and all
1-)Build with make
Be root while building (or make sure that the final binary is owned by root)
2-)Execute ./argo_nodeos_config --help="misc help_sections"; read the message and
start aving fun. The full help can be displayed by simply executing
./argo_nodeos_config --help
-Update process hosting in cgroups with cpuset.procs instead of tasks
-Update so the argo_reserved_mems knob could be manipulated from user-space
-Add support for mask-based mem_exclusive instead of the current ALL or NOTHING situation
create_container
delete_container
alter_container
attach //attach a process or a set of processes
detach //detach a process or a set of processes
#include "acl.hpp"
#include "utils.hpp"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstdlib>
Acl::Acl(string config_file, uid_t uid):
_config_file(config_file)
{
if(uid == INVALID_UID)
_uid = ruid;
else
_uid = uid;
check_config_file();
_syslog_disable_info = DEFAULT_ACL_CONTROL;
_syslog_disable_warning = DEFAULT_ACL_CONTROL;
_syslog_disable_warning = DEFAULT_ACL_CONTROL;
_syslog_enable_debug = DEFAULT_ACL_CONTROL;
}
Acl::~Acl()
{
}
void Acl::check_config_file()
{
struct stat stat_buf;
if(stat(_config_file.c_str(), &stat_buf) == -1)
return;
if(stat_buf.st_uid != 0)
argo_exit(EXIT_FAILURE, "The ACL config file must be owned by root");
if((stat_buf.st_mode&S_IWGRP || stat_buf.st_mode& S_IWOTH))
argo_exit(EXIT_FAILURE,
"The ACL config file must be writable only for root. " +
string(APP_NAME) + " cannot use it! ");
}
bool Acl::is_allowed(Acl_token token)
{
if(_uid == 0)
return true; //always allowed for root
map<Acl_token, bool>::const_iterator it =
_single_value_privileges.find(token);
if(it == _single_value_privileges.end())
return DEFAULT_ACL_CONTROL;
return it->second;
}
#ifndef __ACL_H__
#define __ACL_H__
#include "utils.hpp"
#include "defaults.hpp"
#include <map>
using std::map;
#define INVALID_UID 0xffffffff
#ifndef DEFAULT_ACL_CONTROL
#define DEFAULT_ACL_CONTROL true; //for tests
#endif
enum Acl_token
{
ACL_DUMMY,
ACL_ENABLE_SYSLOG_DEBUG,
ACL_DISABLE_SYSLOG_INFO,
ACL_DISABLE_SYSLOG_WARNING,
ACL_DISABLE_SYSLOG_ERROR,
ACL_REMOVE_PROCESS, //from a container
ACL_ADD_PROCESS, //to a container
};
class Acl
{
private:
string _config_file;
uid_t _uid;
map<Acl_token, bool> _single_value_privileges;
bool _syslog_enable_debug;
bool _syslog_disable_info;
bool _syslog_disable_warning;
bool _syslog_disable_error;
//These below are not meant to be used
Acl(const Acl& orig){}
Acl& operator = (const Acl& orig){ return *this;}
void check_config_file();
public:
/*uid=-1 means the real uid of the process will be used*/
Acl(string config_path=DEFAULT_ACL_CONFIG, uid_t uid=INVALID_UID);
~Acl();
inline uid_t get_uid() const {return _uid;}
bool is_allowed(Acl_token token);
template <typename T>
bool is_allowed_on(Acl_token token, T& args);
};
template <typename T>
bool Acl::is_allowed_on(Acl_token token, T& args)
{
return false;
}
#endif //__ACL_H__
#include "aggregatelogger.hpp"
Aggregatelogger::Aggregatelogger(uid_t ruid):
ILogger(ruid, LOG_DEST_DEV_NULL)
{
}
Aggregatelogger::~Aggregatelogger()
{
}
void Aggregatelogger::log(Log_type type, const string& message, int errnum)
{
for(int i=0; i<(int)_loggers.size(); i++)
_loggers[i]->log(type, message, errnum);
}
void Aggregatelogger::add_logger(ILogger* logger)
{
for(int i=0; i<(int)_loggers.size(); i++)
{
if(_loggers[i]->log_destination() == logger->log_destination())
{
_loggers[i] = logger;
return;
}
}
_log_dest |= logger->log_destination();
_loggers.push_back(logger);
}
#ifndef __AGGREGATELOGGER_HPP__
#define __AGGREGATELOGGER_HPP__
#include "ilogger.hpp"
#include "syslogger.hpp"
#include "stderrlogger.hpp"
#include "filelogger.hpp"
class Aggregatelogger: public ILogger
{
Log_dest _log_dest;
vector<ILogger*> _loggers;
public:
Aggregatelogger(uid_t ruid);
~Aggregatelogger();
virtual void log(Log_type type, const string& message, int errnum = 0);
virtual inline Log_dest log_destination() const {return _log_dest;}
/*A maximum of 1 instance of each type of logger. If a type of logger
* is added that already existed, it is overwritten.
* The added loggers must be deleted outside thsi object*/
void add_logger(ILogger* logger);
inline void clear_loggers() {_loggers.clear(); _log_dest = LOG_DEST_DEV_NULL;}
};
#endif
#!/bin/bash
rm -rf /etc/argo/nodeos/
rm -rf /var/run/argo/nodeos/
rm -rf /var/lock/argo/
rm -f .argo_nodeos_config_exit_message #One of this is created in each
#folder from which argo_nodeos_config has
#been
#executed. So might need to execute the
#script (or manually delete the file)
#from each of the concerned folders
#include "argo_container.hpp"
#include <algorithm>
#include <pwd.h>
#include <stdio.h>
#include <sstream>
#include "utils.hpp"
#include <fstream>
#include "int_list_parser.hpp"
#include <signal.h>
Argo_container::Argo_container(const string& parent_path,
String_parser& sp)
{
_created = false;
_parent_path = parent_path;
if(_parent_path[_parent_path.size() - 1] != '/')
_parent_path += '/';
_commit_type = CCT_NONE;
_owner = ruid;
_cpu_exclusive = true;
_mem_exclusive = false;
_load_balancing = false;
_mem_migrate = true;
sp.get_string("name", _name);
sp.get_int_list("cpus", _cpus);
sp.get_int_list("mems", _mems);
sp.get_bool("cpu_exclusive", _cpu_exclusive);
sp.get_bool("mem_exclusive", _mem_exclusive);
sp.get_bool("load_balancing", _load_balancing);
sp.get_bool("mem_migrate", _mem_migrate);
_older_name = _name;
}
/*This one builds a non-functional (empty) Argo_container that must
*subsequently be reloaded from teh cgroup fs*/
Argo_container::Argo_container(const string& parent_path,
const string& name, uid_t owner)
{
_created = false;
_parent_path = parent_path;
if(_parent_path[_parent_path.size() - 1] != '/')
_parent_path += '/';
_older_name = _name = name;
_owner = owner;
_cpu_exclusive = true;
_mem_exclusive = false;
_load_balancing = false;
_mem_migrate = true;
}
/*This assumes that checks that must be made by the caller
* are all made already*/
void Argo_container::alter(String_parser& sp)
{
}
Argo_container::~Argo_container()
{
}
void Argo_container::remove()
{
ASSERT_MSG(_created, "The container was not commited.");
reload_task_list();
THROW_IF(_tasks.size() != 0,
"Trying to delete a container that still hosts some tasks");
string path = _parent_path + _older_name;
int ret = rmdir(path.c_str());
THROW_WITH_ERRNO_IF(ret,
string("Failed to remove container ") + _older_name);
}
void Argo_container::add_cpu(int cpu)
{
if(get_object_index(_cpus, cpu) == -1)
{
_cpus.push_back(cpu);
_commit_type |= CCT_CPUS;
}
}
void Argo_container::add_cpus(const vector<int>& cpus)
{
size_t old_nb_cpus = _cpus.size();
add_objects<int>(_cpus, cpus);
if(old_nb_cpus != _cpus.size())
_commit_type |= CCT_CPUS;
}
void Argo_container::replace_cpus(const vector<int>& cpus)
{
_cpus.assign(cpus.begin(), cpus.end());
_commit_type |= CCT_CPUS;
}
void Argo_container::remove_cpu(int cpu)
{
size_t old_nb_cpus = _cpus.size();
remove_object<int>(_cpus, cpu);
if(old_nb_cpus != _cpus.size())
_commit_type |= CCT_CPUS;
}
void Argo_container::remove_cpus(const vector<int>& cpus)
{
size_t old_nb_cpus = _cpus.size();
remove_objects<int>(_cpus, cpus);
if(old_nb_cpus != _cpus.size())
_commit_type |= CCT_CPUS;
}
void Argo_container::get_cpus(vector<int>& out) const
{
out.assign(_cpus.begin(), _cpus.end());
}
void Argo_container::add_mem(int mem)
{
if(get_object_index(_mems, mem) == -1)
{
_mems.push_back(mem);
_commit_type |= CCT_MEMS;
}
}
void Argo_container::add_mems(const vector<int>& mems)
{
size_t nb_old_mems = _mems.size();
add_objects<int>(_mems, mems);
if(nb_old_mems != _mems.size())
_commit_type |= CCT_MEMS;
}
void Argo_container::replace_mems(const vector<int>& mems)
{
_mems.assign(mems.begin(), mems.end());
_commit_type |= CCT_MEMS;
}
void Argo_container::remove_mem(int mem)
{
size_t nb_old_mems = _mems.size();
remove_object<int>(_mems, mem);
if(nb_old_mems != _mems.size())
_commit_type |= CCT_MEMS;
}
void Argo_container::remove_mems(const vector<int>& mems)
{
size_t nb_old_mems = _mems.size();
remove_objects<int>(_mems, mems);
if(nb_old_mems != _mems.size())
_commit_type |= CCT_MEMS;
}
void Argo_container::get_mems(vector<int>& out) const
{
out.assign(_mems.begin(), _mems.end());
}
void Argo_container::add_task(int task)
{
if(get_object_index<int>(_tasks, task) == -1)
{
_added_tasks.push_back(task);
_commit_type |= CCT_TASKS;
}
}
void Argo_container::add_tasks(const vector<int>& tasks)
{
size_t nb_old_tasks = _added_tasks.size();
for(int i=0; i<(int)tasks.size(); i++)
{
if(get_object_index<int>(_tasks, tasks[i]) == -1 &&
get_object_index(_added_tasks, tasks[i]) == -1)
_added_tasks.push_back(tasks[i]);
}
if(nb_old_tasks != _added_tasks.size())
_commit_type |= CCT_TASKS;
}
void Argo_container::remove_task(int task)
{
remove_object<int>(_tasks, task);
//No need for commit for removed tasks
}
void Argo_container::remove_tasks(const vector<int>& tasks)
{
remove_objects<int>(_tasks, tasks);
//No need for commit for removed tasks
}
void Argo_container::rename(const string& new_name)
{
if(new_name != _name)
_commit_type |= CCT_NAME;
_name = new_name;
}
void Argo_container::get_tasks(vector<int>& out) const
{
out.assign(_tasks.begin(), _tasks.end());
}
void Argo_container::display() const
{
string str_cpus, str_mems, str_tasks;
struct passwd* pwd;
pwd = getpwuid(_owner);
string str_cpu_exclusive = _cpu_exclusive ? "(exclusive)" : "(non exclusive)";
string str_mem_exclusive = _mem_exclusive ? "(exclusive)" : "(non exclusive)";
format_int_list_for_display(_cpus, str_cpus, "-CPU cores" + str_cpu_exclusive + ": ");
format_int_list_for_display(_mems, str_mems, "-Memory nodes" +
str_mem_exclusive + ": ");
format_int_list_for_display(_tasks, str_tasks, "-Tasks: ");
printf(
"----- COMPUTE CONTAINER -----\n"
"-Name: %s\n"
"-Owner: %s (%d)\n"
"-Hardware threads%s: "
"%s\n"
"-Memory nodes%s: "
"%s\n"
"-Tasks: "
"%s\n"
"-%s"
"-%s"
"\n",
_name.c_str(),
pwd->pw_name, _owner,
str_cpu_exclusive.c_str(),
str_cpus.c_str(),
str_mem_exclusive.c_str(),
str_mems.c_str(),
str_tasks.c_str(),
_load_balancing ? "Balancing load\n" : "Not balancing load\n",
_mem_migrate ? "Memory migration is enabled\n" : "Memory migration is NOT enabled\n"
);
}
void Argo_container::display_resources() const
{
string str_cpus, str_mems;
format_int_list_for_display(_cpus, str_cpus, "-CPU cores: ");
format_int_list_for_display(_mems, str_mems, "-Memory nodes: ");
printf(
"-Hardware threads: "
"%s\n"
"-Memory nodes: "
"%s\n"
"%s\n"
,
str_cpus.c_str(),
str_mems.c_str(),
_mem_migrate ? "Memory migration is enabled" : "Memory migration is NOT enabled"
);
}
void Argo_container::reload_task_list()
{
retrieve_from_file<int>(_parent_path + _older_name + "/tasks",
_tasks);
_added_tasks.clear();
}
void Argo_container::reload(bool reload_tasks)
{
string str;
retrieve_from_file(_parent_path + _older_name + "/cpuset.cpus",
str);
Int_list_parser ilp;
ilp.convert_to_comprehensive(str, _cpus);
retrieve_from_file(_parent_path + _older_name + "/cpuset.mems",
str);
ilp.convert_to_comprehensive(str, _mems);
if(reload_tasks)
{
retrieve_from_file<int>(_parent_path + _older_name + "/tasks",
_tasks);
_added_tasks.clear();
}
retrieve_from_file(_parent_path + _older_name + "/cpuset.cpu_exclusive",
str);
_cpu_exclusive = str == "1" ? true : false;
retrieve_from_file(_parent_path + _older_name + "/cpuset.mem_exclusive",
str);
_mem_exclusive = str == "1" ? true : false;
retrieve_from_file(_parent_path + _older_name + "/cpuset.memory_migrate",
str);
_mem_migrate = str == "1" ? true : false;
retrieve_from_file(_parent_path + _older_name + "/cpuset.sched_load_balance",
str);
_load_balancing = str == "1" ? true : false;
_created = true;
}
void Argo_container::commit_last_changes()
{
int status;
elevate_privileges();
if(_commit_type & CCT_NAME)
{
string older = _parent_path + _older_name;
string newer = _parent_path + _name;
status = ::rename(older.c_str(), newer.c_str());
THROW_WITH_ERRNO_IF(status, "Failed to rename container");
_older_name = _name;
}
if(_commit_type & CCT_CPUS)
{
string cpus_str;
stringify_int_list(_cpus, ',', cpus_str);
commit_in_file<string>(_parent_path + _older_name + "/cpuset.cpus",
cpus_str);