// ----------------------------------------------------------------------
// File: Policy.cc
// Author: Andreas-Joachim Peters - CERN
// ----------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2011 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 "common/Logging.hh"
#include "common/LayoutId.hh"
#include "common/Mapping.hh"
#include "common/ParseUtils.hh"
#include "common/utils/ContainerUtils.hh"
#include "common/utils/XrdUtils.hh"
#include "mgm/Constants.hh"
#include "mgm/Policy.hh"
#include "mgm/XrdMgmOfs.hh"
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
EOSMGMNAMESPACE_BEGIN
const std::vector Policy::gBasePolicyKeys = {
"policy.space",
"policy.layout",
"policy.nstripes",
"policy.checksum",
"policy.blocksize",
"policy.blockchecksum",
"policy.localredirect"
};
const std::vector Policy::gBasePolicyRWKeys = {
"policy.bandwidth",
"policy.iopriority",
"policy.iotype",
"policy.schedule"
};
/*----------------------------------------------------------------------------*/
double
Policy::GetDefaultSizeFactor(std::shared_ptr cmd)
{
XrdOucEnv env("");
unsigned long forcedfsid;
long forcedgroup;
unsigned long layoutid = 0;
XrdOucString ret_space;
std::string bandwidth;
bool schedule = 0;
std::string iopriority;
std::string iotype;
bool isrw = false; // does not matter
uint64_t atimeage = 0; // does not matter
eos::IContainerMD::XAttrMap attrmap = cmd->getAttributes();
eos::common::VirtualIdentity rootvid = eos::common::VirtualIdentity::Root();
GetLayoutAndSpace("/",
attrmap,
rootvid,
layoutid,
ret_space,
env,
forcedfsid,
forcedgroup,
bandwidth,
schedule,
iopriority,
iotype,
isrw,
false,
&atimeage);
double f = eos::common::LayoutId::GetSizeFactor(layoutid);
return f ? f : 1.0;
}
/*----------------------------------------------------------------------------*/
unsigned long
Policy::GetSpacePolicyLayout(const char* space)
{
std::string space_env = "eos.space=";
space_env += space;
XrdOucEnv env(space_env.c_str());
unsigned long forcedfsid;
long forcedgroup;
unsigned long layoutid = 0;
XrdOucString ret_space;
std::string bandwidth;
bool schedule = 0;
std::string iopriority;
std::string iotype;
bool isrw = false; // does not matter
uint64_t atimeage = 0; // does not matter
eos::IContainerMD::XAttrMap attrmap;
eos::common::VirtualIdentity rootvid = eos::common::VirtualIdentity::Root();
GetLayoutAndSpace("/",
attrmap,
rootvid,
layoutid,
ret_space,
env,
forcedfsid,
forcedgroup,
bandwidth,
schedule,
iopriority,
iotype,
isrw,
true,
&atimeage);
return layoutid;
}
/*----------------------------------------------------------------------------*/
void
Policy::GetLayoutAndSpace(const char* path,
eos::IContainerMD::XAttrMap& attrmap,
const eos::common::VirtualIdentity& vid,
unsigned long& layoutId, XrdOucString& space,
XrdOucEnv& env,
unsigned long& forcedfsid,
long& forcedgroup,
std::string& bandwidth,
bool& schedule,
std::string& iopriority,
std::string& iotype,
bool rw,
bool lockview,
uint64_t* atimeage)
{
eos::common::RWMutexReadLock lock;
// this is for the moment only defaulting or manual selection
unsigned long layout = eos::common::LayoutId::GetLayoutFromEnv(env);
unsigned long xsum = eos::common::LayoutId::GetChecksumFromEnv(env);
unsigned long bxsum = eos::common::LayoutId::GetBlockChecksumFromEnv(env);
unsigned long stripes = eos::common::LayoutId::GetStripeNumberFromEnv(env);
unsigned long blocksize = eos::common::LayoutId::GetBlocksizeFromEnv(env);
bandwidth = eos::common::LayoutId::GetBandwidthFromEnv(env);
iotype = eos::common::LayoutId::GetIotypeFromEnv(env);
bool noforcedchecksum = false;
const char* val = 0;
bool conversion = IsProcConversion(path);
std::map spacepolicies;
std::map spacerwpolicies;
std::string satime;
RWParams rwparams {vid.uid_string, vid.gid_string,
eos::common::XrdUtils::GetEnv(env, "eos.app", "default"),
rw};
auto policy_keys = GetConfigKeys();
auto policy_rw_keys = GetRWConfigKeys(rwparams);
if (!conversion) {
// don't apply space policies to conversion paths
if (lockview) {
lock.Grab(FsView::gFsView.ViewMutex);
}
auto it = FsView::gFsView.mSpaceView.find("default");
if (it != FsView::gFsView.mSpaceView.end()) {
it->second->GetConfigMembers(policy_keys,
spacepolicies);
it->second->GetConfigMembers(policy_rw_keys,
spacerwpolicies);
satime = it->second->GetConfigMember("atime");
} // FSView default
if (lockview) {
lock.Release();
}
} // if !conversion
std::string schedule_str;
GetRWValue(spacerwpolicies, POLICY_SCHEDULE, rwparams, schedule_str);
GetRWValue(spacerwpolicies, POLICY_IOPRIORITY, rwparams, iopriority);
GetRWValue(spacerwpolicies, POLICY_IOTYPE, rwparams, iotype);
GetRWValue(spacerwpolicies, POLICY_BANDWIDTH, rwparams, bandwidth);
schedule = schedule_str.length() ? schedule_str == "1" : schedule;
if ((val = env.Get("eos.space"))) {
space = val;
} else {
space = "default";
if (!conversion) {
std::string space_key = "policy.space";
if (auto kv = spacepolicies.find(space_key);
kv != spacepolicies.end() && (! kv->second.empty())) {
// if there is no explicit space given, we preset with the policy one
space = kv->second.c_str();
}
}
}
// Replace the non empty settings from the default space have been already
// defined before
if (!conversion && space != "default") {
std::map nondefault_policies;
spacerwpolicies.clear();
if (lockview) {
lock.Grab(FsView::gFsView.ViewMutex);
}
auto it = FsView::gFsView.mSpaceView.find(space.c_str());
if (it != FsView::gFsView.mSpaceView.end()) {
it->second->GetConfigMembers(policy_keys,
nondefault_policies);
it->second->GetConfigMembers(policy_rw_keys,
spacerwpolicies);
satime = it->second->GetConfigMember("atime");
} // FsView;
if (lockview) {
lock.Release();
}
// Since this map only contains keys that are already populated, we can be
// sure that we'll be only replacing non empty keys
for (auto && kv : nondefault_policies) {
if (!kv.second.empty()) {
spacepolicies.insert_or_assign(kv.first,
std::move(kv.second));
}
}
std::string schedule_str;
GetRWValue(spacerwpolicies, POLICY_SCHEDULE, rwparams, schedule_str);
GetRWValue(spacerwpolicies, POLICY_IOPRIORITY, rwparams, iopriority);
GetRWValue(spacerwpolicies, POLICY_IOTYPE, rwparams, iotype);
GetRWValue(spacerwpolicies, POLICY_BANDWIDTH, rwparams, bandwidth);
schedule = schedule_str.length() ? schedule_str == "1" : schedule;
} // !conversion && space != default
// look if we have to inject the default space policies
for (const auto& it : spacepolicies) {
std::string key_name = it.first.substr(7);
if (key_name == "space") {
continue;
}
std::string sys_key = "sys.forced.";
std::string user_key = "user.forced.";
sys_key += key_name;
user_key += key_name;
if ((!attrmap.count(sys_key)) &&
(!attrmap.count(user_key)) &&
!it.second.empty()) {
attrmap[sys_key] = it.second;
}
}
forcedgroup = eos::common::XrdUtils::GetEnv(env, "eos.group", (long) - 1);
if ((xsum != eos::common::LayoutId::kNone) &&
(val = env.Get("eos.checksum.noforce"))) {
// we don't force *.forced.checksum settings
// we need this flag to be able to force MD5 checksums for S3 uploads
noforcedchecksum = true;
}
if ((vid.uid == 0) && (val = env.Get("eos.layout.noforce"))) {
// root can request not to apply any forced settings
} else {
if (auto space_kv = attrmap.find(SYS_FORCED_SPACE);
space_kv != attrmap.end()) {
// we force to use a certain space in this directory even if the user wants something else
space = space_kv->second.c_str();
eos_static_debug("sys.forced.space in %s", path);
}
if (auto forcedgroup_kv = attrmap.find(SYS_FORCED_GROUP);
forcedgroup_kv != attrmap.end()) {
// we force to use a certain group in this directory even if the user wants something else
eos::common::StringToNumeric(forcedgroup_kv->second, forcedgroup);
eos_static_debug("sys.forced.group in %s", path);
}
if (auto kv = attrmap.find(SYS_FORCED_LAYOUT);
kv != attrmap.end()) {
// we force to use a specified layout in this directory even if the user wants something else
layout = eos::common::LayoutId::GetLayoutFromString(kv->second);
eos_static_debug("sys.forced.layout in %s", path);
}
if (!noforcedchecksum) {
if (auto kv = attrmap.find(SYS_FORCED_CHECKSUM);
kv != attrmap.end()) {
// we force to use a specified checksumming in this directory even if the user wants something else
xsum = eos::common::LayoutId::GetChecksumFromString(kv->second);
eos_static_debug("sys.forced.checksum in %s", path);
}
}
if (auto kv = attrmap.find(SYS_FORCED_BLOCKCHECKSUM);
kv != attrmap.end()) {
bxsum = eos::common::LayoutId::GetBlockChecksumFromString(kv->second);
eos_static_debug("sys.forced.blockchecksum in %s %x", path, bxsum);
}
if (attrmap.count(SYS_FORCED_NSTRIPES)) {
XrdOucString layoutstring = "eos.layout.nstripes=";
layoutstring += attrmap["sys.forced.nstripes"].c_str();
XrdOucEnv layoutenv(layoutstring.c_str());
// we force to use a specified stripe number in this directory even if the user wants something else
stripes = eos::common::LayoutId::GetStripeNumberFromEnv(layoutenv);
eos_static_debug("sys.forced.nstripes in %s", path);
}
if (attrmap.count(SYS_FORCED_BLOCKSIZE)) {
XrdOucString layoutstring = "eos.layout.blocksize=";
layoutstring += attrmap["sys.forced.blocksize"].c_str();
XrdOucEnv layoutenv(layoutstring.c_str());
// we force to use a specified stripe width in this directory even if the user wants something else
blocksize = eos::common::LayoutId::GetBlocksizeFromEnv(layoutenv);
eos_static_debug("sys.forced.blocksize in %s : %llu", path, blocksize);
}
std::string iotypeattr = rw ? "sys.forced.iotype:w" : "sys.forced.iotype:r";
if (attrmap.count(iotypeattr)) {
iotype = attrmap[iotypeattr];
eos_static_debug("sys.forced.iotype i %s : %s", path, iotype.c_str());
}
std::string iopriorityattr = rw ? "sys.forced.iopriority:w" :
"sys.forced.iopriority:r";
if (attrmap.count(iopriorityattr)) {
iopriority = attrmap[iopriorityattr];
eos_static_debug("sys.forced.iopriority i %s : %s", path, iopriority.c_str());
}
std::string bandwidthattr = rw ? "sys.forced.bandwidth:w" :
"sys.forced.bandwidth:r";
if (attrmap.count(bandwidthattr)) {
bandwidth = attrmap[bandwidthattr];
eos_static_debug("sys.forced.bandwidth i %s : %s", path, bandwidth.c_str());
}
std::string scheduleattr = rw ? "sys.forced.schedule:w" :
"sys.forced.schedule:r";
if (attrmap.count(scheduleattr)) {
schedule = (attrmap[scheduleattr] == "1");
eos_static_debug("sys.forced.schedule i %s : %d", path);
}
if (((!attrmap.count("sys.forced.nouserlayout")) ||
(attrmap["sys.forced.nouserlayout"] != "1")) &&
((!attrmap.count("user.forced.nouserlayout")) ||
(attrmap["user.forced.nouserlayout"] != "1"))) {
if (attrmap.count("user.forced.space")) {
// we force to use a certain space in this directory even if the user wants something else
space = attrmap["user.forced.space"].c_str();
eos_static_debug("user.forced.space in %s", path);
}
if (auto kv = attrmap.find(USER_FORCED_LAYOUT);
kv != attrmap.end()) {
// we force to use a specified layout in this directory even if the user wants something else
layout = eos::common::LayoutId::GetLayoutFromString(kv->second);
eos_static_debug("user.forced.layout in %s", path);
}
if (!noforcedchecksum) {
if (auto kv = attrmap.find(USER_FORCED_CHECKSUM);
kv != attrmap.end()) {
// we force to use a specified checksumming in this directory even if the user wants something else
xsum = eos::common::LayoutId::GetChecksumFromString(kv->second);
eos_static_debug("user.forced.checksum in %s", path);
}
}
if (auto kv = attrmap.find(USER_FORCED_BLOCKCHECKSUM);
kv != attrmap.end()) {
// we force to use a specified checksumming in this directory even if the user wants something else
bxsum = eos::common::LayoutId::GetBlockChecksumFromString(kv->second);
eos_static_debug("user.forced.blockchecksum in %s", path);
}
if (attrmap.count(USER_FORCED_NSTRIPES)) {
XrdOucString layoutstring = "eos.layout.nstripes=";
layoutstring += attrmap["user.forced.nstripes"].c_str();
XrdOucEnv layoutenv(layoutstring.c_str());
// we force to use a specified stripe number in this directory even if the user wants something else
stripes = eos::common::LayoutId::GetStripeNumberFromEnv(layoutenv);
eos_static_debug("user.forced.nstripes in %s", path);
}
if (attrmap.count(USER_FORCED_BLOCKSIZE)) {
XrdOucString layoutstring = "eos.layout.blocksize=";
layoutstring += attrmap["user.forced.blocksize"].c_str();
XrdOucEnv layoutenv(layoutstring.c_str());
// we force to use a specified stripe width in this directory even if the user wants something else
blocksize = eos::common::LayoutId::GetBlocksizeFromEnv(layoutenv);
eos_static_debug("user.forced.blocksize in %s", path);
}
}
if ((attrmap.count("sys.forced.nofsselection") &&
(attrmap["sys.forced.nofsselection"] == "1")) ||
(attrmap.count("user.forced.nofsselection") &&
(attrmap["user.forced.nofsselection"] == "1"))) {
eos_static_debug(".forced.nofsselection in %s", path);
forcedfsid = 0;
} else {
forcedfsid = eos::common::XrdUtils::GetEnv(env, "eos.force.fsid", 0l);
}
}
if (satime.length() && atimeage) {
*atimeage = std::stoull(satime.c_str(), 0, 10);
}
layoutId = eos::common::LayoutId::GetId(layout, xsum, stripes, blocksize,
bxsum);
return;
}
/*----------------------------------------------------------------------------*/
void
Policy::GetPlctPolicy(const char* path,
eos::IContainerMD::XAttrMap& attrmap,
const eos::common::VirtualIdentity& vid,
XrdOucEnv& env,
eos::mgm::Scheduler::tPlctPolicy& plctpol,
std::string& targetgeotag)
{
// default to save
plctpol = eos::mgm::Scheduler::kScattered;
std::string policyString;
const char* val = 0;
if ((val = env.Get("eos.placementpolicy"))) {
// we force an explicit placement policy
policyString = val;
}
if ((vid.uid == 0) && (val = env.Get("eos.placementpolicy.noforce"))) {
// root can request not to apply any forced settings
} else if (attrmap.count("sys.forced.placementpolicy")) {
// we force to use a certain placement policy even if the user wants something else
policyString = attrmap["sys.forced.placementpolicy"].c_str();
eos_static_debug("sys.forced.placementpolicy in %s", path);
} else {
// check there are no user placement restrictions
if (((!attrmap.count("sys.forced.nouserplacementpolicy")) ||
(attrmap["sys.forced.nouserplacementpolicy"] != "1")) &&
((!attrmap.count("user.forced.nouserplacementpolicy")) ||
(attrmap["user.forced.nouserplacementpolicy"] != "1"))) {
if (attrmap.count("user.forced.placementpolicy")) {
// we use the user defined placement policy
policyString = attrmap["user.forced.placementpolicy"].c_str();
eos_static_debug("user.forced.placementpolicy in %s", path);
}
}
}
if (policyString.empty() || policyString == "scattered") {
plctpol = eos::mgm::Scheduler::kScattered;
return;
}
std::string::size_type seppos = policyString.find(':');
// if no target geotag is provided, it's not a valid placement policy
if (seppos == std::string::npos || seppos == policyString.length() - 1) {
eos_static_warning("no geotag given in placement policy for path %s : \"%s\"",
path, policyString.c_str());
return;
}
targetgeotag = policyString.substr(seppos + 1);
// Check if geotag is valid
std::string tmp_geotag = eos::common::SanitizeGeoTag(targetgeotag);
if (tmp_geotag != targetgeotag) {
eos_static_warning("%s", tmp_geotag.c_str());
return;
}
if (!policyString.compare(0, seppos, "hybrid")) {
plctpol = eos::mgm::Scheduler::kHybrid;
} else if (!policyString.compare(0, seppos, "gathered")) {
plctpol = eos::mgm::Scheduler::kGathered;
} else {
eos_static_warning("unknown placement policy for path %s : \"%s\"", path,
policyString.c_str());
}
return;
}
/*----------------------------------------------------------------------------*/
bool
Policy::RedirectLocal(const char* path,
eos::IContainerMD::XAttrMap& map,
const eos::common::VirtualIdentity& vid,
unsigned long& layoutId,
XrdOucString& space,
XrdOucEnv& env
)
{
std::string rkey = "sys.forced.localredirect";
if (map.count(rkey) && ((map[rkey] == "true") || (map[rkey] == "1")) &&
((eos::common::LayoutId::GetLayoutType(layoutId) ==
eos::common::LayoutId::kReplica) ||
(eos::common::LayoutId::GetLayoutType(layoutId) ==
eos::common::LayoutId::kPlain))) {
if (env.Get("eos.localredirect") &&
(std::string(env.Get("eos.localredirect")) == "0")) {
return false;
} else {
return true;
}
}
if (env.Get("eos.localredirect") &&
(std::string(env.Get("eos.localredirect")) == "1")) {
return true;
} else {
return false;
}
}
/*----------------------------------------------------------------------------*/
bool
Policy::Set(const char* value)
{
XrdOucEnv env(value);
XrdOucString policy = env.Get("mgm.policy");
XrdOucString skey = env.Get("mgm.policy.key");
XrdOucString policycmd = env.Get("mgm.policy.cmd");
if (!skey.length()) {
return false;
}
bool set = false;
if (!value) {
return false;
}
// gOFS->ConfigEngine->SetConfigValue("policy",skey.c_str(), svalue.c_str());
return set;
}
/*----------------------------------------------------------------------------*/
bool
Policy::Set(XrdOucEnv& env, int& retc, XrdOucString& stdOut,
XrdOucString& stdErr)
{
int envlen;
// no '&' are allowed into stdOut !
XrdOucString inenv = env.Env(envlen);
while (inenv.replace("&", " ")) {
};
bool rc = Set(env.Env(envlen));
if (rc == true) {
stdOut += "success: set policy [ ";
stdOut += inenv;
stdOut += "]\n";
errno = 0;
retc = 0;
return true;
} else {
stdErr += "error: failed to set policy [ ";
stdErr += inenv;
stdErr += "]\n";
errno = EINVAL;
retc = EINVAL;
return false;
}
}
/*----------------------------------------------------------------------------*/
void
Policy::Ls(XrdOucEnv& env, int& retc, XrdOucString& stdOut,
XrdOucString& stdErr) { }
/*----------------------------------------------------------------------------*/
bool
Policy::Rm(XrdOucEnv& env, int& retc, XrdOucString& stdOut,
XrdOucString& stdErr)
{
return true;
}
/*----------------------------------------------------------------------------*/
const char*
Policy::Get(const char* key)
{
return 0;
}
/*----------------------------------------------------------------------------*/
bool
Policy::IsProcConversion(const char* path)
{
XrdOucString spath = path;
if (spath.beginswith(gOFS->MgmProcConversionPath.c_str())) {
return true;
} else {
return false;
}
}
void
Policy::GetRWValue(const std::map& conf_map,
const std::string& key_name, const RWParams& params,
std::string& value)
{
for (auto && k : params.getKeys(key_name)) {
if (const auto& kv = conf_map.find(k);
kv != conf_map.end() &&
!kv->second.empty()) {
value = kv->second;
}
}
}
std::vector
Policy::GetRWConfigKeys(const RWParams& params)
{
std::vector config_keys;
config_keys.reserve(16);
for (const auto& _key : gBasePolicyRWKeys) {
eos::common::splice(config_keys,
params.getKeys(_key));
}
return config_keys;
}
std::vector
Policy::RWParams::getKeys(const string& key) const
{
auto key_name = getKey(key);
return {
key_name + app_key,
key_name + user_key,
key_name + group_key,
key_name
};
}
EOSMGMNAMESPACE_END