//------------------------------------------------------------------------------ // File: IConfigEngine.cc // Author: Andreas-Joachim Peters - CERN //------------------------------------------------------------------------------ /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2016 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #include "mgm/config/IConfigEngine.hh" #include "common/Mapping.hh" #include "mgm/Access.hh" #include "mgm/FsView.hh" #include "mgm/Quota.hh" #include "mgm/Vid.hh" #include "mgm/Iostat.hh" #include "mgm/proc/proc_fs.hh" #include "mgm/XrdMgmOfs.hh" #include "mgm/GeoTreeEngine.hh" #include "mgm/RouteEndpoint.hh" #include "mgm/PathRouting.hh" #include "mgm/fsck/Fsck.hh" #include "mgm/XrdMgmOfs.hh" #include "mgm/IMaster.hh" #include "namespace/interface/IContainerMDSvc.hh" #include "namespace/interface/IFileMDSvc.hh" #include "common/StringUtils.hh" #include "common/StringTokenizer.hh" #include "mq/SharedHashWrapper.hh" #include EOSMGMNAMESPACE_BEGIN //------------------------------------------------------------------------------ // **** IConfigEngine **** //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Constructor //------------------------------------------------------------------------------ IConfigEngine::IConfigEngine(): mChangelog(), mAutosave(false), mConfigFile("default") {} //------------------------------------------------------------------------------ // XrdOucHash callback function to apply a configuration value //------------------------------------------------------------------------------ int IConfigEngine::ApplyEachConfig(const char* key, XrdOucString* val, XrdOucString* err) { if (!key || !val) { return 0; } std::ostringstream oss_err; XrdOucString toenv = val->c_str(); while (toenv.replace(" ", "&")) {} XrdOucEnv envdev(toenv.c_str()); XrdOucString skey = key; std::string sval = val->c_str(); eos_static_debug("key=%s val=%s", skey.c_str(), val->c_str()); if (skey.beginswith("fs:")) { // Set a filesystem definition skey.erase(0, 3); if (!FsView::gFsView.ApplyFsConfig(skey.c_str(), sval)) { oss_err << "error: failed to apply config " << key << " => " << val->c_str() << std::endl; } } else if (skey.beginswith("global:")) { // Set a global configuration skey.erase(0, 7); if (!FsView::gFsView.ApplyGlobalConfig(skey.c_str(), sval)) { oss_err << "error: failed to apply config " << key << " => " << val->c_str() << std::endl; } // Apply the access settings but not the redirection rules Access::ApplyAccessConfig(false); } else if (skey.beginswith("map:")) { // Set a mapping skey.erase(0, 4); if (!gOFS->AddPathMap(skey.c_str(), sval.c_str(), false)) { oss_err << "error: failed to apply config " << key << " => " << val->c_str() << std::endl; } } else if (skey.beginswith("route:")) { // Set a routing skey.erase(0, 6); RouteEndpoint endpoint; if (!endpoint.ParseFromString(sval.c_str())) { eos_static_err("failed to parse route config %s => %s", key, val->c_str()); oss_err << "error: failed to parse route config " << key << " => " << val->c_str() << std::endl; } else { if (!gOFS->mRouting->Add(skey.c_str(), std::move(endpoint))) { oss_err << "error: failed to apply config " << key << " => " << val->c_str() << std::endl; } } } else if (skey.beginswith("quota:")) { // Set a quota definition skey.erase(0, 6); int space_offset = 0; int ug_offset = skey.find(':', space_offset + 1); int ug_equal_offset = skey.find('=', ug_offset + 1); int tag_offset = skey.find(':', ug_equal_offset + 1); if ((ug_offset == STR_NPOS) || (ug_equal_offset == STR_NPOS) || (tag_offset == STR_NPOS)) { eos_static_err("cannot parse config line key: |%s|", skey.c_str()); oss_err << "error: cannot parse config line key: " << skey.c_str() << std::endl; *err = oss_err.str().c_str(); return 0; } XrdOucString space(skey, 0, ug_offset - 1); XrdOucString ug(skey, ug_offset + 1, ug_equal_offset - 1); XrdOucString ugid(skey, ug_equal_offset + 1, tag_offset - 1); XrdOucString tag(skey, tag_offset + 1); unsigned long long value = strtoll(val->c_str(), 0, 10); long id = strtol(ugid.c_str(), 0, 10); if (!space.endswith('/')) { space += '/'; } if (id > 0 || (ugid == "0")) { if (Quota::Create(space.c_str())) { if (!Quota::SetQuotaForTag(space.c_str(), tag.c_str(), id, value)) { eos_static_err("failed to set quota for id=%s", ugid.c_str()); oss_err << "error: failed to set quota for id:" << ugid << std::endl; } } else { // This is just ignored ... maybe path is wrong?! eos_static_err("failed to create quota for space=%s", space.c_str()); } } else { eos_static_err("config id is negative"); oss_err << "error: illegal id found: " << ugid << std::endl; } } else if (skey.beginswith("vid:")) { // Set a virutal Identity int envlen; if (!Vid::Set(envdev.Env(envlen), false)) { eos_static_err("failed applying config line key: |%s| => |%s|", skey.c_str(), val->c_str()); oss_err << "error: cannot apply config line key: " << skey.c_str() << std::endl; } } else if (skey.beginswith("geosched:")) { skey.erase(0, 9); if (!gOFS->mGeoTreeEngine->setParameter(skey.c_str(), sval.c_str(), -2)) { eos_static_err("failed applying config line key: |geosched:%s| => |%s|", skey.c_str(), val->c_str()); oss_err << "error: failed applying config line key: geosched:" << skey.c_str() << std::endl; } } else if (skey.beginswith("comment")) { // Ignore comments return 0; } else if (skey.beginswith("policy:")) { // Set a policy - not used return 0; } else if (skey.beginswith("ns:")) { // internal NS configuration option std::map map_cfg; gOFS->mMaster->FillNsCacheConfig(gOFS->ConfEngine, map_cfg); gOFS->eosFileService->configure(map_cfg); gOFS->eosDirectoryService->configure(map_cfg); return 0; } else { oss_err << "error: unsupported configuration line: " << skey.c_str() << " -> " << sval.c_str() << std::endl; } *err += oss_err.str().c_str(); return 0; } //------------------------------------------------------------------------------ // Publish the given configuration change //------------------------------------------------------------------------------ void IConfigEngine::PublishConfigChange(const std::string& key, const std::string& value) { eos_info("msg=\"publish configuration change\" key=\"%s\" val=\"%s\"", key.c_str(), value.c_str()); XrdOucString repval = value.c_str(); while (repval.replace("&", " ")) {} mq::SharedHashWrapper::makeGlobalMgmHash(gOFS->mMessagingRealm.get()).set(key, repval.c_str()); } //------------------------------------------------------------------------------ // Publish the deletion of the given configuration key //------------------------------------------------------------------------------ void IConfigEngine::PublishConfigDeletion(const std::string& key) { eos_info("msg=\"publish deletion of configuration\" key=\"%s\"", key.c_str()); mq::SharedHashWrapper::makeGlobalMgmHash(gOFS->mMessagingRealm.get()).del(key); } //------------------------------------------------------------------------------ // Apply a given configuration definition //------------------------------------------------------------------------------ bool IConfigEngine::ApplyConfig(XrdOucString& err, bool apply_stall_redirect) { err = ""; // Cleanup quota map (void) Quota::CleanUp(); { eos::common::RWMutexWriteLock wr_lock(eos::common::Mapping::gMapMutex); eos::common::Mapping::gUserRoleVector.clear(); eos::common::Mapping::gGroupRoleVector.clear(); eos::common::Mapping::gVirtualUidMap.clear(); eos::common::Mapping::gVirtualGidMap.clear(); eos::common::Mapping::gAllowedTidentMatches.clear(); } Access::Reset(!apply_stall_redirect); { eos::common::RWMutexWriteLock wr_view_lock(eos::mgm::FsView::gFsView.ViewMutex); std::lock_guard lock(mMutex); // Disable the defaults in FsSpace FsSpace::gDisableDefaults = true; for (auto it = sConfigDefinitions.begin(); it != sConfigDefinitions.end(); it++) { XrdOucString val(it->second.c_str()); ApplyEachConfig(it->first.c_str(), &val, &err); } // Enable the defaults in FsSpace FsSpace::gDisableDefaults = false; } Access::ApplyAccessConfig(apply_stall_redirect); gOFS->mFsckEngine->ApplyFsckConfig(); gOFS->IoStats->ApplyIostatConfig(&FsView::gFsView); if (err.length()) { errno = EINVAL; return false; } return true; } //------------------------------------------------------------------------------ // Delete a configuration key from the responsible object //------------------------------------------------------------------------------ void IConfigEngine::ApplyKeyDeletion(const char* key) { XrdOucString skey = key; eos_static_info("key=%s", skey.c_str()); if (skey.beginswith("fs:")) { XrdOucString stdOut; XrdOucString stdErr; std::string id; eos::common::VirtualIdentity rootvid = eos::common::VirtualIdentity::Root(); skey.erase(0, 3); int spos1 = skey.find("/", 1); int spos2 = skey.find("/", spos1 + 1); int spos3 = skey.find("/", spos2 + 1); std::string nodename = skey.c_str(); std::string mountpoint = skey.c_str(); nodename.erase(spos3); mountpoint.erase(0, spos3); eos::common::RWMutexWriteLock lock(FsView::gFsView.ViewMutex); proc_fs_rm(nodename, mountpoint, id, stdOut, stdErr, rootvid); } else if (skey.beginswith("map:")) { skey.erase(0, 4); eos::common::RWMutexWriteLock lock(gOFS->PathMapMutex); if (gOFS->PathMap.count(skey.c_str())) { gOFS->PathMap.erase(skey.c_str()); } } else if (skey.beginswith("route:")) { skey.erase(0, 6); gOFS->mRouting->Remove(skey.c_str()); } else if (skey.beginswith("quota:")) { // Remove quota definition skey.erase(0, 6); int space_offset = 0; int ug_offset = skey.find(':', space_offset + 1); int ug_equal_offset = skey.find('=', ug_offset + 1); int tag_offset = skey.find(':', ug_equal_offset + 1); if ((ug_offset == STR_NPOS) || (ug_equal_offset == STR_NPOS) || (tag_offset == STR_NPOS)) { eos_static_err("failed to remove quota definition %s", skey.c_str()); return; } XrdOucString space(skey, 0, ug_offset - 1); XrdOucString ug(skey, ug_offset + 1, ug_equal_offset - 1); XrdOucString ugid(skey, ug_equal_offset + 1, tag_offset - 1); XrdOucString tag(skey, tag_offset + 1); long id = strtol(ugid.c_str(), 0, 10); if (id > 0 || (ugid == "0")) { if (!Quota::RmQuotaForTag(space.c_str(), tag.c_str(), id)) { eos_static_err("failed to remove quota %s for id=%ll", tag.c_str(), id); } } } else if (skey.beginswith("vid:")) { // Remove vid entry XrdOucString stdOut; XrdOucString stdErr; int retc = 0; XrdOucString vidstr = "mgm.vid.key="; vidstr += skey.c_str(); XrdOucEnv videnv(vidstr.c_str()); Vid::Rm(videnv, retc, stdOut, stdErr, false); if (retc) { eos_static_err("failed to remove vid entry for key=%s", skey.c_str()); } } else if (skey.beginswith("policy:") || (skey.beginswith("global:"))) { // For policy or global tags don't do anything } } //------------------------------------------------------------------------------ // Delete configuration values matching the pattern //------------------------------------------------------------------------------ void IConfigEngine::DeleteConfigValueByMatch(const char* prefix, const char* match) { XrdOucString smatch = prefix; smatch += ":"; smatch += match; std::lock_guard lock(mMutex); auto it = sConfigDefinitions.begin(); while (it != sConfigDefinitions.end()) { if (strncmp(it->first.c_str(), smatch.c_str(), smatch.length()) == 0) { it = sConfigDefinitions.erase(it); } else { it++; } } } //------------------------------------------------------------------------------ // Dump method for selective configuration printing //------------------------------------------------------------------------------ bool IConfigEngine::DumpConfig(XrdOucString& out, const std::string& filename) { if (filename.empty()) { std::lock_guard lock(mMutex); for (auto& sConfigDefinition : sConfigDefinitions) { eos_static_debug("%s => %s", sConfigDefinition.first.c_str(), sConfigDefinition.second.c_str()); out += (sConfigDefinition.first + " => " + sConfigDefinition.second + "\n").c_str(); } while (out.replace("&", " ")) {} } else { std::ostringstream ss; FilterConfig(ss, filename.c_str()); out = ss.str().c_str(); } eos::common::StringConversion::SortLines(out); return true; } //------------------------------------------------------------------------------ // Get a configuration value //------------------------------------------------------------------------------ bool IConfigEngine::Get(const std::string& prefix, const std::string& key, std::string& out) { std::lock_guard lock(mMutex); std::string config_key = FormFullKey(prefix.c_str(), key.c_str()); auto it = sConfigDefinitions.find(config_key); if (it == sConfigDefinitions.end()) { return false; } out = it->second; return true; } //------------------------------------------------------------------------------ // Reset the configuration //------------------------------------------------------------------------------ void IConfigEngine::ResetConfig(bool apply_stall_redirect) { mConfigFile = ""; (void) Quota::CleanUp(); { eos::common::RWMutexWriteLock wr_lock(eos::common::Mapping::gMapMutex); eos::common::Mapping::gUserRoleVector.clear(); eos::common::Mapping::gGroupRoleVector.clear(); eos::common::Mapping::gVirtualUidMap.clear(); eos::common::Mapping::gVirtualGidMap.clear(); eos::common::Mapping::gAllowedTidentMatches.clear(); } Access::Reset(!apply_stall_redirect); gOFS->ResetPathMap(); gOFS->mRouting->Clear(); FsView::gFsView.Reset(); gOFS->ObjectManager.Clear(); { std::lock_guard lock(mMutex); sConfigDefinitions.clear(); } // Load all the quota nodes from the namespace Quota::LoadNodes(); } //------------------------------------------------------------------------------ // Check if configuration key is deprecated //------------------------------------------------------------------------------ bool IConfigEngine::IsDeprecated(const std::string& config_key) const { if (config_key.find("global:") == 0) { if (config_key.find("#drainer.central") != std::string::npos) { return true; } if (config_key.find("#new_balancer") != std::string::npos) { return true; } if (config_key.find("#transfer.schedule") != std::string::npos) { return true; } } if (common::startsWith(config_key, "comment-")) { return true; } return false; } //------------------------------------------------------------------------------ // Filter out deprecated entries from the map //------------------------------------------------------------------------------ void IConfigEngine::FilterDeprecated(std::map& map) { using namespace eos::common; std::set deprecated; for (auto it = map.begin(); it != map.end(); it++) { if (IsDeprecated(it->first)) { deprecated.insert(it->first); continue; } // Filter out deprecated file system attributes if (it->first.find("fs:/eos/") == 0) { std::map fs_map; std::list fs_attrs = StringTokenizer::split> (it->second, ' '); for (const auto& elem : fs_attrs) { std::string key, val; if (StringConversion::SplitKeyValue(elem, key, val, "=")) { fs_map[key] = val; } else { continue; } } if (fs_map.empty()) { continue; } std::string data = FileSystem::SerializeWithFilter(fs_map, {"stat.", "local."}); map[it->first] = data; } } for (auto it = deprecated.begin(); it != deprecated.end(); it++) { map.erase(*it); } } EOSMGMNAMESPACE_END