#include "container_manager.hpp" #include "common.hpp" #include #include #include "utils.hpp" #include "ilogger.hpp" #include #include #include "int_list_parser.hpp" #include #include #include #define OWNERSHIP_MAP_FINALIZED "MAP_FINALIZED" //Means that the map is in a sane finalized state #define OWNERSHIP_MAP_TRANSITIONNING "MAP_TRANSITIONNING" /*Means that the container manager has not properly finalized yet */ const char* default_cgroup_root = "/sys/fs/cgroup/"; #define CGROUUP_FS "cgroup_root" Container_manager::Container_manager(ILogger* logger, Error_behavior error_behavior, string cgroup_root): _no_separate_service_os(true), _error_behavior(error_behavior), _service_os_root(NULL), _cgroup_root(cgroup_root) { _logger = logger; if(_cgroup_root[_cgroup_root.size() - 1] != '/') _cgroup_root += '/'; elevate_privileges(); init_cgroup_fs(); _argo_argo_container_root = _cgroup_root + "cpuset/" + ARGO_ROOT_CONTAINER_NAME + "/" + ARGO_COMPUTE_CONTAINERS_ROOT_NAME; /*Create cgroup roots if not existing yet*/ struct stat sb; if(stat(_argo_argo_container_root.c_str(), &sb) == -1 && errno == ENOENT) { create_root_cgroups(); if(!_no_separate_service_os) migrate_service_os(); } else load_root_cgroups(); load_existing_containers_as_root(); apply_ownership_map(); lower_privileges(); } void Container_manager::get_cpus_breakdown(vector& all_cpus, vector& service_cpus, vector& compute_cpus) { all_cpus.clear(); service_cpus.clear(); compute_cpus.clear(); string str; Int_list_parser ilp; retrieve_from_file(_cgroup_root + "cpuset/cpuset.cpus", str); ilp.convert_to_comprehensive(str, all_cpus); compute_cpus.assign(all_cpus.begin(), all_cpus.end()); return; //Not caring about isolcpus anymore #if 0 string cmdline; retrieve_from_file("/proc/cmdline", cmdline); if(!str_contains(cmdline, "isolcpus")) { _no_separate_service_os = true; compute_cpus.assign(all_cpus.begin(), all_cpus.end()); return; } string isolcpus; str_extract_after(cmdline, "isolcpus=", isolcpus, true); str_extract_before(isolcpus, " ", isolcpus); assert(isolcpus != ""); THROW_ON_BAD_INPUT_IF(isolcpus == "", "Ill-formed isolcpus found!"); ilp.convert_to_comprehensive(isolcpus, compute_cpus); THROW_ON_BAD_INPUT_IF(!is_superset(all_cpus, compute_cpus), "Isolcpus are not a subset of the online cpus"); get_complement(all_cpus, compute_cpus, service_cpus); THROW_ON_BAD_INPUT_IF(service_cpus.empty(), "Attempt to build a ServiceOS with no CPU core"); #endif } void Container_manager::get_mems_breakdown(vector& all_mems, vector& service_mems, vector& compute_mems) { all_mems.clear(); service_mems.clear(); compute_mems.clear(); string str; Int_list_parser ilp; retrieve_from_file(_cgroup_root + "cpuset/cpuset.mems", str); ilp.convert_to_comprehensive(str, all_mems); compute_mems.assign(all_mems.begin(), all_mems.end()); return; //ignoring argo_sos_mems for now #if 0 string cmdline; retrieve_from_file("/proc/cmdline", cmdline); if(!str_contains(cmdline, "argo_sos_mems")) { _no_separate_service_os = true; compute_mems.assign(all_mems.begin(), all_mems.end()); return; } string sos_mems; str_extract_after(cmdline, "argo_sos_mems=", sos_mems, true); str_extract_before(sos_mems, " ", sos_mems); assert(sos_mems != ""); THROW_ON_BAD_INPUT_IF(sos_mems == "", "Ill-formed argo_sos_mems!"); ilp.convert_to_comprehensive(sos_mems, service_mems); THROW_ON_BAD_INPUT_IF(!is_superset(all_mems, service_mems), "argo_sos_mems are not a subset of the existing memory nodes"); get_complement(all_mems, service_mems, compute_mems); THROW_ON_BAD_INPUT_IF(compute_mems.empty(), "Attempt to build a NodeOs without any memory node for compute containers"); #endif } void Container_manager::init_cgroup_fs() { struct stat buf; string cpuset = _cgroup_root + "cpuset/"; string cpuset_tasks = cpuset + "tasks"; if(stat(cpuset_tasks.c_str(), &buf) == 0) return; //we're all good int ret = mkdir(_cgroup_root.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); if(ret && errno != EEXIST) throw std::runtime_error(strerror(errno)); //mount -t tmpfs cgroup_root /sys/fs/cgroup; ret = mount("cgroup_root", _cgroup_root.c_str(), "tmpfs", 0 , NULL); if(ret && errno != EBUSY) throw std::runtime_error(strerror(errno)); ret = mkdir(cpuset.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); if(ret && errno != EEXIST) throw std::runtime_error(strerror(errno)); ret = mount("cpuset", cpuset.c_str(), "cgroup", 0, "cpuset"); if(ret && errno != EBUSY) throw std::runtime_error(strerror(errno)); } /*Scan the filesystem and load the containers*/ void Container_manager::apply_ownership_map() { std::ifstream ownership_map_file_in; ownership_map_file_in.open(OWNERSHIP_MAP); if(ownership_map_file_in.fail()) { if(_containers.size() != 0) { logger->log(LOG_TYPE_INFO, string("The ownership map does not exist.\n") + string("Root will take ownership of the existing Compute Containers\n")+ string("and reinitialize the map.")); } } else { string name; uid_t owner; string map_state; ownership_map_file_in>>map_state; if(map_state != OWNERSHIP_MAP_FINALIZED) { ownership_map_file_in.close(); logger->log(LOG_TYPE_INFO, string("The ownership map is in a potentially corrupted stated.\n") + string("Root will take ownership of the existing Compute Containers\n")+ string("and reinitialize the map.")); } else { while(!ownership_map_file_in.eof()) { ownership_map_file_in>>name; ownership_map_file_in>>owner; Argo_container *c = find_by_name(name); if(c) c->set_owner(owner); } ownership_map_file_in.close(); } } std::ofstream ownership_map_file_out; ownership_map_file_out.open(OWNERSHIP_MAP); THROW_WITH_ERRNO_IF(ownership_map_file_out.fail(), "WTF"); ownership_map_file_out< all_cpus; vector service_cpus; vector compute_cpus; vector all_mems; vector service_mems; vector compute_mems; get_cpus_breakdown(all_cpus, service_cpus, compute_cpus); get_mems_breakdown(all_mems, service_mems, compute_mems); _root = new Argo_container(_cgroup_root + "cpuset", ARGO_ROOT_CONTAINER_NAME, 0); _root->set_cpu_exclusive(true); _root->set_mem_exclusive(true); _root->set_load_balancing(true); _root->add_cpus(all_cpus); _root->add_mems(all_mems); _root->create(); _root->commit_all(); _argo_containers_root = new Argo_container( _root->get_parent_path() + _root->get_name(), ARGO_COMPUTE_CONTAINERS_ROOT_NAME, 0); _argo_containers_root->set_cpu_exclusive(true); _argo_containers_root->set_mem_exclusive(true); _argo_containers_root->set_load_balancing(true); _argo_containers_root->add_cpus(compute_cpus); _argo_containers_root->add_mems(compute_mems); _argo_containers_root->create(); _argo_containers_root->commit_all(); _cpu_ownerships.build_from(compute_cpus); _mem_ownerships.build_from(compute_mems); if(_no_separate_service_os) return; _service_os_root = new Argo_container( _root->get_parent_path() + _root->get_name(), ARGO_SERVICE_OS_CONTAINER_NAME, 0); _service_os_root->set_cpu_exclusive(true); _service_os_root->set_mem_exclusive(true); _service_os_root->set_load_balancing(true); _service_os_root->add_cpus(service_cpus); _service_os_root->add_mems(service_mems); _service_os_root->create(); _service_os_root->commit_all(); _sos_cpu_ownerships.build_from(service_cpus); _sos_mem_ownerships.build_from(service_mems); } void Container_manager::migrate_service_os() { int n = system("for t in `cat /sys/fs/cgroup/cpuset/tasks`; do /bin/echo $t > /sys/fs/cgroup/cpuset/argo/service_os/tasks; done 2>/dev/null"); return; vector sos_tasks; retrieve_from_file( _cgroup_root + "cpuset/tasks", sos_tasks); for(int i=0; i<(int)sos_tasks.size(); i++) _service_os_root->add_tasks(sos_tasks); _service_os_root->commit_last_changes(); } vector container_names; //sorry for global ... but can't invest time //in software engineering neatness now -_- int gather_container_names(const char *fpath, const struct stat *sb, int typeflag) { if(typeflag == FTW_D) container_names.push_back(fpath); return 0; } void Container_manager::load_root_cgroups() { _root = new Argo_container(_cgroup_root + "cpuset", ARGO_ROOT_CONTAINER_NAME, 0); _root->reload(false); _argo_containers_root = new Argo_container( _root->get_parent_path() + _root->get_name(), ARGO_COMPUTE_CONTAINERS_ROOT_NAME, 0); _argo_containers_root->reload(false); vector compute_cpus; vector compute_mems; string sos_cgroup_path = _root->get_parent_path() + _root->get_name() + "/" + ARGO_SERVICE_OS_CONTAINER_NAME; struct stat sb; if(stat(sos_cgroup_path.c_str(), &sb) == 0) { _no_separate_service_os = false; _service_os_root = new Argo_container( _root->get_parent_path() + _root->get_name(), ARGO_SERVICE_OS_CONTAINER_NAME, 0); //_service_os_root->reload(false); _service_os_root->reload(); vector service_cpus; vector service_mems; _service_os_root->get_cpus(service_cpus); _service_os_root->get_mems(service_mems); _sos_cpu_ownerships.build_from(service_cpus); _sos_mem_ownerships.build_from(service_mems); _argo_containers_root->get_cpus(compute_cpus); _argo_containers_root->get_mems(compute_mems); } else { _no_separate_service_os = true; //Load all resources for potential compute container use then. string str; Int_list_parser ilp; retrieve_from_file(_cgroup_root + "cpuset/cpuset.cpus", str); ilp.convert_to_comprehensive(str, compute_cpus); retrieve_from_file(_cgroup_root + "cpuset/cpuset.mems", str); ilp.convert_to_comprehensive(str, compute_mems); } _cpu_ownerships.build_from(compute_cpus); _mem_ownerships.build_from(compute_mems); } void Container_manager::load_existing_containers_as_root() { container_names.clear(); char buf[MAX_BUF_SIZE]; //HAZARD: (JZ) ftw(_argo_argo_container_root.c_str(), gather_container_names, 10); for(int i=0; i<(int)container_names.size(); i++) { if(container_names[i] == _argo_argo_container_root) continue; strcpy(buf, container_names[i].c_str()); string name = basename(buf); Argo_container* c = new Argo_container( _argo_argo_container_root, name, 0); c->reload(); grant_resources(c); _containers.push_back(c); } } Container_manager::~Container_manager() #if __cplusplus >= 201103L noexcept(false) #endif { std::ofstream ownership_map_file; ownership_map_file.open(OWNERSHIP_MAP); THROW_WITH_ERRNO_IF(ownership_map_file.fail(), "Failed to open ownership map"); ownership_map_file<get_name()<<'\t' <<_containers[i]->get_owner()<get_name() == name) return _containers[i]; return NULL; } void Container_manager::grant_resources(Argo_container* cc) { vector v; cc->get_mems(v); _mem_ownerships.grant(v, cc->has_exclusive_mem()); cc->get_cpus(v); _cpu_ownerships.grant(v, cc->has_exclusive_cpu()); } void Container_manager::reclaim_resources(Argo_container* cc) { vector v; cc->get_mems(v); _mem_ownerships.reclaim(v); cc->get_cpus(v); _cpu_ownerships.reclaim(v); } bool Container_manager::is_name_available(const string& name) { for(int i=0; i<(int)_containers.size(); i++) if(_containers[i]->get_name() == name) return false; return true; } #define HANDLE_CM_ERROR_IF(cond,cm,msg)\ do{if((cond)){\ if(cm->get_error_behaviour() == ON_ERROR_CONTINUE)\ {(cm)->get_logger()->log(LOG_TYPE_ERROR, (msg));return false;}\ else THROW_ON_FORBIDDEN_ACTION((msg) + string(". Aborting!"));\ }}while(0) bool Container_manager::is_legal_create_command(String_parser& sp) { sp.parse(); string strval; string name; bool bval; vector intvect; if(sp.get_string("name", name)) HANDLE_CM_ERROR_IF(!is_name_available(name), this, string("Another container already has the name ") + strval); bval = false; sp.get_bool("cpu_exclusive", bval); if(sp.get_int_list("cpus", intvect)) HANDLE_CM_ERROR_IF(!are_resources_available(_cpu_ownerships, intvect, bval), this, string("At least part of the requested CPU cores is unavailable") + (bval? string(" exclusively.") : string("."))); bval = false; sp.get_bool("cpu_exclusive", bval); if(sp.get_added_int_list("cpus", intvect)) HANDLE_CM_ERROR_IF(!are_resources_available(_cpu_ownerships, intvect, bval), this, string("At least part of the requested CPU cores is unavailable") + (bval? string(" exclusively.") : string("."))); bval = false; sp.get_bool("mem_exclusive", bval); if(sp.get_int_list("mems", intvect)) HANDLE_CM_ERROR_IF(!are_resources_available(_mem_ownerships, intvect, bval), this, string("At least part of the requested memory nodes is unavailable") + (bval? string(" exclusively.") : string("."))); bval = false; sp.get_bool("mem_exclusive", bval); if(sp.get_added_int_list("mems", intvect)) HANDLE_CM_ERROR_IF(!are_resources_available(_mem_ownerships, intvect, bval), this, string("At least part of the requested memory nodes is unavailable") + (bval? string(" exclusively.") : string("."))); return true; } Argo_container* Container_manager::find_host_of_task(int pid) { for(int i=0; i<(int)_containers.size(); i++) if(_containers[i]->has_task(pid)) return _containers[i]; return NULL; } void Container_manager::show_available_resources(string command) { bool shared = true; vector cpus; vectormems; String_parser sp(command); sp.parse(); THROW_ON_BAD_INPUT_IF(!(sp.get_nb_keys() == 0 || (sp.get_nb_keys() == 1 && sp.has_key("shared"))), "Ill-formed show_available_resource_command"); sp.get_bool("shared", shared); for(int i=0; i<(int)_cpu_ownerships.size(); i++) { if((shared && _cpu_ownerships[i].can_increment_owners()) || (!shared && _cpu_ownerships[i].can_own_exclusively())) cpus.push_back(_cpu_ownerships[i].get_resource()); } for(int i=0; i<(int)_mem_ownerships.size(); i++) { if((shared && _mem_ownerships[i].can_increment_owners()) || (!shared && _mem_ownerships[i].can_own_exclusively())) mems.push_back(_mem_ownerships[i].get_resource()); } string str_cpus, str_mems; printf( "------------Hardware threads------------\n" "%s\n\n" "------------Memory nodes------------\n" "%s\n\n", format_int_list_for_display(cpus, str_cpus).c_str(), format_int_list_for_display(mems, str_mems).c_str() ); } void Container_manager::create_container(string command) { String_parser sp(command); if(!is_legal_create_command(sp)) return; Argo_container* cc = new Argo_container(_argo_argo_container_root, sp); cc->create(); cc->commit_all(); grant_resources(cc); _containers.push_back(cc); _logger->log(LOG_TYPE_INFO, "Container " + cc->get_name() + " created."); } void Container_manager::delete_container(string command) { String_parser sp(command); sp.parse(); THROW_ON_BAD_INPUT_IF(!((sp.get_nb_keys() == 1 && sp.has_key("name"))|| (sp.get_nb_keys() == 2 && sp.has_key("name") && sp.has_key("kill_content"))), "Ill-formed container deletion command"); string name; sp.get_string("name", name); Argo_container *cc = find_by_name(name); THROW_ON_ERRONEOUS_ACTION_IF(!cc, "Container " + name + " does not exists"); bool kill_content = false; sp.get_bool("kill_content", kill_content); THROW_ON_FORBIDDEN_ACTION_IF(ruid != 0 && cc->get_owner() != ruid, "Container " + name + " belongs to another user"); THROW_ON_FORBIDDEN_ACTION_IF(cc->has_any_task() && !kill_content, "Container " + name + " still hosts processes"); if(kill_content) { cc->check_task_ownership(true, true); cc->kill_content(false); } if(kill_content) sleep(1); cc->remove(); vector::iterator it; for(it = _containers.begin(); it != _containers.end(); it++) { if(*it == cc) { _containers.erase(it); reclaim_resources(cc); _logger->log(LOG_TYPE_INFO, "Container " + cc->get_name() + " deleted."); delete cc; break; } } assert(_containers.empty() || it != _containers.end()); } void Container_manager::alter_container(string command) { string name; String_parser sp(command); sp.parse(); THROW_ON_BAD_INPUT_IF(sp.get_nb_keys() <= 1 || !sp.has_key("name"), "Incomplete container altering command"); if(sp.get_string("rename", name)) THROW_ON_FORBIDDEN_ACTION_IF(!is_name_available(name), string("Another container already has the name ") + name); sp.get_string("name", name); Argo_container *cc = find_by_name(name); vector current; vector overwrite; vector added; vector removed; vector final_cpu_states; vector final_mem_states; bool bval = false; sp.get_bool("cpu_exclusive", bval); cc->get_cpus(current); sp.get_int_list("cpus", overwrite); sp.get_added_int_list("cpus", added); sp.get_removed_int_list("cpus", removed); Resource_ownership_set simulated_cpu_ownership_state(_cpu_ownerships); simulate_containers_final_resource_state(_cpu_ownerships, current, overwrite, added, removed, bval, final_cpu_states, simulated_cpu_ownership_state); //This will throw if any issue bool cpus_changed = !overwrite.empty() || !added.empty() || !removed.empty(); bval = false; sp.get_bool("mem_exclusive", bval); cc->get_mems(current); sp.get_int_list("mems", overwrite); sp.get_added_int_list("mems", added); sp.get_removed_int_list("mems", removed); Resource_ownership_set simulated_mem_ownership_state(_mem_ownerships); simulate_containers_final_resource_state(_cpu_ownerships, current, overwrite, added, removed, bval, final_mem_states, simulated_mem_ownership_state); //This will throw if any issue bool mems_changed = !overwrite.empty() || !added.empty() || !removed.empty(); _cpu_ownerships = simulated_cpu_ownership_state; _mem_ownerships = simulated_mem_ownership_state; //Nothing threw. We can alter the container from here on if(sp.get_string("rename", name)) cc->rename(name); if(sp.get_bool("cpu_exclusive", bval)) cc->set_cpu_exclusive(bval); if(sp.get_bool("mem_exclusive", bval)) cc->set_mem_exclusive(bval); if(sp.get_bool("mem_migrate", bval)) cc->set_mem_migrate(bval); if(cpus_changed) cc->replace_cpus(final_cpu_states); if(mems_changed) cc->replace_mems(final_mem_states); cc->commit_last_changes(); _logger->log(LOG_TYPE_INFO, "Container " + cc->get_name() + " altered."); } void Container_manager::alter_service_os(string command) { THROW_NOT_IMPLEMENTED_EXCEPTION(); //Fix the TODO below before removing this THROW_ON_FORBIDDEN_ACTION_IF(ruid != 0, "Only root can alter the service_os config."); #if 0 THROW_ON_ERRONEOUS_ACTION_IF(!_service_os_root, "The node has no service_os"); #else if(!_service_os_root) return; #endif String_parser sp(command); sp.parse(); const char* initkeys[] = {"cpus", "+cpus", "-cpus", "mems", "+mems", "-mems", "mem_migrate"}; vector allowed_keys(initkeys, initkeys + sizeof(initkeys) / sizeof(initkeys[0])); THROW_ON_BAD_INPUT_IF(sp.has_forbidden_keys(allowed_keys), "Invalid service_os alteration command"); vector current; vector overwrite; vector added; vector removed; vector final_cpu_states; vector final_mem_states; Resource_ownership_set simulated_final_cpu_sos; Resource_ownership_set simulated_final_mem_sos; Resource_ownership_set simulated_final_cpu_cc; Resource_ownership_set simulated_final_mem_cc; _service_os_root->get_cpus(current); sp.get_int_list("cpus", overwrite); sp.get_added_int_list("cpus", added); sp.get_removed_int_list("cpus", removed); simulate_sos_final_resource_state(_sos_cpu_ownerships, _cpu_ownerships, current, overwrite, added, removed, final_cpu_states, simulated_final_cpu_sos, simulated_final_cpu_cc); bool cpus_changed = !overwrite.empty() || !added.empty() || !removed.empty(); _service_os_root->get_mems(current); sp.get_int_list("mems", overwrite); sp.get_added_int_list("mems", added); sp.get_removed_int_list("mems", removed); simulate_sos_final_resource_state(_sos_mem_ownerships, _mem_ownerships, current, overwrite, added, removed, final_mem_states, simulated_final_mem_sos, simulated_final_mem_cc); bool mems_changed = !overwrite.empty() || !added.empty() || !removed.empty(); //Nothing threw; we're good! //TODO: There is a potential conflict here. If a resource is swapped between //both _service_os_root and _argo_containers_root, then there will be some transcient //exclusivity violation and things will silently fail. Cater to this later on if(cpus_changed) { _service_os_root->replace_cpus(final_cpu_states); _cpu_ownerships = simulated_final_cpu_cc; _sos_cpu_ownerships = simulated_final_cpu_sos; vector cpus; _cpu_ownerships.get_resources(cpus); _argo_containers_root->replace_cpus(cpus); } if(mems_changed) { _service_os_root->replace_mems(final_mem_states); _mem_ownerships = simulated_final_mem_cc; _sos_mem_ownerships = simulated_final_mem_sos; vector mems; _mem_ownerships.get_resources(mems); _argo_containers_root->replace_mems(mems); } bool bval; if(sp.get_bool("mem_migrate", bval)) _service_os_root->set_mem_migrate(bval); _service_os_root->commit_last_changes(); _logger->log(LOG_TYPE_INFO, "Service_os altered"); if(cpus_changed || mems_changed) _argo_containers_root->commit_last_changes(); } void Container_manager::create_service_os(string command) { THROW_ON_FORBIDDEN_ACTION_IF(ruid != 0, "Only root can create the service_os config."); THROW_ON_ERRONEOUS_ACTION_IF(_service_os_root, "The node already has a service_os"); String_parser sp(command); sp.parse(); const char* initkeys[] = {"cpus", "mems"}; vector allowed_keys(initkeys, initkeys + sizeof(initkeys) / sizeof(initkeys[0])); THROW_ON_BAD_INPUT_IF(sp.has_forbidden_keys(allowed_keys), "Invalid service_os creation command"); vector cpus; sp.get_int_list("cpus", cpus); THROW_ON_FORBIDDEN_ACTION_IF(!are_resources_available(_cpu_ownerships, cpus, true), string("At least part of the requested CPU cores is unavailable")); THROW_ON_FORBIDDEN_ACTION_IF(!_cpu_ownerships.are_unused(cpus), "Some of the CPU cores requested are already in use"); vector mems; sp.get_int_list("mems", mems); THROW_ON_FORBIDDEN_ACTION_IF(!are_resources_available(_mem_ownerships, mems, true), string("At least part of the requested memory nodes is unavailable")); THROW_ON_FORBIDDEN_ACTION_IF(!_cpu_ownerships.are_unused(cpus), "Some of the CPU cores requested are already in use"); _cpu_ownerships.transfer_to(cpus, _sos_cpu_ownerships); _mem_ownerships.transfer_to(mems, _sos_mem_ownerships); _argo_containers_root->remove_cpus(cpus); _argo_containers_root->remove_mems(mems); _argo_containers_root->commit_last_changes(); _service_os_root = new Argo_container( _root->get_parent_path() + _root->get_name(), ARGO_SERVICE_OS_CONTAINER_NAME, 0); _service_os_root->set_cpu_exclusive(true); _service_os_root->set_mem_exclusive(true); _service_os_root->set_load_balancing(true); _service_os_root->add_cpus(cpus); _service_os_root->add_mems(mems); bool bval; if(sp.get_bool("mem_migrate", bval)) _service_os_root->set_mem_migrate(bval); _service_os_root->create(); _service_os_root->commit_all(); migrate_service_os(); _logger->log(LOG_TYPE_INFO, "Service_os created"); } void Container_manager::delete_service_os() { THROW_ON_FORBIDDEN_ACTION_IF(ruid != 0, "Only root can delete the service_os config."); #if 0 THROW_ON_ERRONEOUS_ACTION_IF(!_service_os_root, "The node has no service_os"); #else if(!_service_os_root) return; #endif //First migrate the tasks Argo_container very_root(_cgroup_root, "cpuset", 0); vector tasks; _service_os_root->get_tasks(tasks); very_root.add_tasks(tasks); very_root.commit_last_changes(); vector cpus; vector mems; _service_os_root->get_cpus(cpus); _service_os_root->get_mems(mems); _service_os_root->remove(); _sos_cpu_ownerships.transfer_to(cpus, _cpu_ownerships); _sos_mem_ownerships.transfer_to(mems, _mem_ownerships); _argo_containers_root->add_cpus(cpus); _argo_containers_root->add_mems(mems); _argo_containers_root->commit_last_changes(); _logger->log(LOG_TYPE_INFO, "Service_os deleted"); } void Container_manager::reset() { THROW_ON_FORBIDDEN_ACTION_IF(ruid != 0, "Only root can reset the node."); if(_service_os_root) delete_service_os(); clean_config(""); /*Temporary ugly brutal hack [JZ] (\_/)*/ int n = system("rmdir /sys/fs/cgroup/cpuset/argo/argo_containers/ && rmdir /sys/fs/cgroup/cpuset/argo/"); } void Container_manager::attach_to_container(string command) { String_parser sp(command); sp.parse(); THROW_ON_BAD_INPUT_IF(!sp.has_key("name") || !sp.has_key("pids") || sp.get_int_list_size("pids") < 1, "Bad attach process command"); string name; sp.get_string("name", name); Argo_container *cc = find_by_name(name); THROW_ON_ERRONEOUS_ACTION_IF(!cc, "Container " + name + " does not exists"); vector tasks; sp.get_int_list("pids", tasks); THROW_ON_ERRONEOUS_ACTION_IF(cc->has_any_of_these_tasks(tasks), "At least one of the processes is already hosted in " + name + "."); #ifndef LOOSE_DEMO THROW_ON_FORBIDDEN_ACTION_IF(ruid != 0 && (ruid != cc->get_owner()), "Can't attach to container " + cc->get_name() + " because it belongs to another user! Alternatively, the action can be performed as root"); #endif //LOOSE_DEMO for(vector::iterator it = _containers.begin(); it != _containers.end(); it++) { if((*it)->get_name() == cc->get_name()) continue; THROW_ON_FORBIDDEN_ACTION_IF((*it)->has_any_of_these_tasks(tasks), "At least one of the tasks is already attached to a container. Please use --migrate_to_container instead "); } if(ruid != 0) { uid_t uid; for(int i=0; i<(int)tasks.size(); i++) { uid = get_task_owner(tasks[i]); if(uid != UID_INVALID && uid != ruid) { std::ostringstream ss; ss<<"The task of pid "<add_tasks(tasks); cc->commit_last_changes(); } void Container_manager::detach_from_container(string command) { logger->log(LOG_TYPE_INFO, "Not implemented yet"); return; String_parser sp(command); sp.parse(); THROW_ON_BAD_INPUT_IF(!sp.has_key("name") || !sp.has_key("pids") || sp.get_int_list_size("pids") < 1, "Bad detach process command"); string name; sp.get_string("name", name); Argo_container *cc = find_by_name(name); THROW_ON_ERRONEOUS_ACTION_IF(!cc, "Container " + name + " does not exists"); vector tasks; sp.get_int_list("pids", tasks); THROW_ON_ERRONEOUS_ACTION_IF(!cc->has_all_these_tasks(tasks), "At least one of the processes is not or no longer hosted in " + name + "."); THROW_ON_FORBIDDEN_ACTION_IF(ruid != 0 && (ruid != cc->get_owner()), "Can't detach from container " + cc->get_name() + " because it belongs to another user! Alternatively, the action can be performed as root"); cc->remove_tasks(tasks); commit_in_file(_cgroup_root + "cpuset/tasks", tasks); } void Container_manager::migrate_to_container(string command) { String_parser sp(command); sp.parse(); THROW_ON_BAD_INPUT_IF(!sp.has_key("name") || !sp.has_key("pids") || sp.get_int_list_size("pids") < 1, "Bad migrate process command"); string name; sp.get_string("name", name); Argo_container *cc = find_by_name(name); THROW_ON_ERRONEOUS_ACTION_IF(!cc, "Container " + name + " does not exists"); vector tasks; sp.get_int_list("pids", tasks); #if 0 THROW_ON_ERRONEOUS_ACTION_IF(cc->has_any_of_these_tasks(tasks), "At least one of the processes is already hosted in " + name + "."); #endif THROW_ON_FORBIDDEN_ACTION_IF(ruid != 0 && (ruid != cc->get_owner()), "Can't migrate to container " + cc->get_name() + " because it belongs to another user! Alternatively, the action can be performed as root"); std::ostringstream oss; for(vector::iterator task = tasks.begin(); task != tasks.end(); task++) { bool is_task_contained = false; for(vector::iterator cont = _containers.begin(); cont != _containers.end(); cont++) { if((*cont)->has_task(*task)) { is_task_contained = true; break; } } if(!is_task_contained) { oss<<"Task "<<*task<<" is not in any Argo container; it cannot be migrated"; THROW_ON_ERRONEOUS_ACTION(oss.str()); } } if(ruid != 0) { for(vector::iterator it = _containers.begin(); it != _containers.end(); it++) { if((*it)->get_name() == cc->get_name()) continue; THROW_ON_FORBIDDEN_ACTION_IF((*it)->has_any_of_these_tasks(tasks) && (*it)->get_owner() != ruid, string("At least one of the tasks belongs to container ") + (*it)->get_name() + string(" which belongs to another user. Please retry as root!")); } } cc->add_tasks(tasks); cc->commit_last_changes(); } bool compare_names(Argo_container* c0, Argo_container* c1) { return c0->get_name() < c1->get_name(); } void Container_manager::show_config() { if(_no_separate_service_os) printf("\nNO SEPARATE SERVICE_OS!\n\n"); else { printf("\n======SERVICE_OS======\n"); _service_os_root->display_resources(); printf("- %d tasks\n", _service_os_root->get_nb_tasks()); printf("======================\n\n"); } vector sorted; sorted.assign(_containers.begin(), _containers.end()); std::sort(sorted.begin(), sorted.end(), compare_names); for(int i=0; i<(int)sorted.size(); i++) { #ifndef DEBUG sorted[i]->reload(); #endif sorted[i]->display(); } } void Container_manager::clean_config(string command) { String_parser sp(command); sp.parse(); THROW_ON_BAD_INPUT_IF(!(sp.get_nb_keys() == 0 || (sp.get_nb_keys() == 1 && sp.has_key("kill_content"))), "Ill-formed clean_config command"); bool kill_content = false; sp.get_bool("kill_content", kill_content); for(int i=0; i<(int)_containers.size(); i++) { THROW_ON_FORBIDDEN_ACTION_IF(ruid != 0 && (ruid != _containers[i]->get_owner()), "At least one of the existing containers belongs to another user. Please retry as root"); THROW_ON_FORBIDDEN_ACTION_IF(_containers[i]->has_any_task() && !kill_content, string("Config cleaning failed! Container " + _containers[i]->get_name() + " still hosts processes")); if(kill_content) _containers[i]->check_task_ownership(true, true); } if(kill_content) sleep(1); vector::iterator it; for(it = _containers.begin(); it != _containers.end(); it++) { if(kill_content) (*it)->kill_content(false); (*it)->remove(); delete *it; } _containers.clear(); _logger->log(LOG_TYPE_INFO, "Config has been cleaned."); } void Container_manager::exec(string command) { String_parser sp(command); sp.parse(); THROW_ON_BAD_INPUT_IF(!sp.has_key("name") || !sp.has_key("argv"), "Bad exec command"); string name; sp.get_string("name", name); Argo_container *cc = find_by_name(name); THROW_ON_ERRONEOUS_ACTION_IF(!cc, "Container " + name + " does not exists"); THROW_ON_FORBIDDEN_ACTION_IF(ruid != 0 && (ruid != cc->get_owner()), "Can't execute in container " + cc->get_name() + " because it belongs to another user! Alternatively, the action can be performed as root"); string argv; sp.get_string("argv", argv); if (argv[0] == '\'') /* Strip ' from both ends. */ argv = argv.substr(1, argv.size() - 2); vector argvs; split(argv, ' ', argvs); /* Remove a single layer of backslashes. */ for (vector::iterator i = argvs.begin(); i != argvs.end(); ++i) for (string::iterator j = i->begin(); j != i->end(); ++j) if (*j == '\\') i->erase(j); vector tasks(1, getpid()); cc->add_tasks(tasks); cc->commit_last_changes(); std::ostringstream ss; ss << "About to execute"; /* We use global allocators so that _exec_argv survives after the container manager is destroyed. */ _exec_argv = new char*[argvs.size() + 1]; for (int i = 0; i < argvs.size(); i++) { _exec_argv[i] = new char[argvs[i].size() + 1]; strcpy(_exec_argv[i], argvs[i].c_str()); ss << " '" << argvs[i] << "'"; } _exec_argv[argvs.size()] = NULL; _logger->log(LOG_TYPE_INFO, ss.str()); }