//------------------------------------------------------------------------------
// @file: SpaceCmd.cc
// @author: Fabio Luchetti - CERN
//------------------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2018 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 "SpaceCmd.hh"
#include "mgm/proc/ProcInterface.hh"
#include "mgm/tgc/Constants.hh"
#include "mgm/http/rest-api/Constants.hh"
#include "mgm/http/rest-api/manager/RestApiManager.hh"
#include "mgm/XrdMgmOfs.hh"
#include "mgm/LRU.hh"
#include "common/Path.hh"
#include "mgm/tracker/ReplicationTracker.hh"
#include "mgm/inspector/FileInspector.hh"
#include "mgm/Egroup.hh"
#include "mgm/config/IConfigEngine.hh"
#include "mgm/GroupBalancer.hh"
#include "mgm/GroupDrainer.hh"
#include "mgm/balancer/FsBalancer.hh"
#include "mgm/placement/FsScheduler.hh"
#include "namespace/interface/IFsView.hh"
#include "namespace/interface/IContainerMDSvc.hh"
#include "namespace/interface/IView.hh"
#include "common/Constants.hh"
#include "common/StringTokenizer.hh"
#include "common/StringUtils.hh"
#include "common/token/EosTok.hh"
EOSMGMNAMESPACE_BEGIN
static const std::string BALANCER_KEY_PREFIX = "balancer";
static const std::string GROUPBALANCER_KEY_PREFIX = "groupbalancer";
static const std::string GROUPDRAINER_KEY_PREFIX = "groupdrainer";
//------------------------------------------------------------------------------
// Method implementing the specific behavior of the command executed by the
// asynchronous thread
//------------------------------------------------------------------------------
eos::console::ReplyProto
SpaceCmd::ProcessRequest() noexcept
{
eos::console::ReplyProto reply;
eos::console::SpaceProto space = mReqProto.space();
switch (mReqProto.space().subcmd_case()) {
case eos::console::SpaceProto::kLs:
LsSubcmd(space.ls(), reply);
break;
case eos::console::SpaceProto::kSet:
SetSubcmd(space.set(), reply);
break;
case eos::console::SpaceProto::kStatus:
StatusSubcmd(space.status(), reply);
break;
case eos::console::SpaceProto::kNodeSet:
NodeSetSubcmd(space.nodeset(), reply);
break;
case eos::console::SpaceProto::kNodeGet:
NodeGetSubcmd(space.nodeget(), reply);
break;
case eos::console::SpaceProto::kReset:
ResetSubcmd(space.reset(), reply);
break;
case eos::console::SpaceProto::kDefine:
DefineSubcmd(space.define(), reply);
break;
case eos::console::SpaceProto::kConfig:
ConfigSubcmd(space.config(), reply);
break;
case eos::console::SpaceProto::kQuota:
QuotaSubcmd(space.quota(), reply);
break;
case eos::console::SpaceProto::kRm:
RmSubcmd(space.rm(), reply);
break;
case eos::console::SpaceProto::kTracker:
TrackerSubcmd(space.tracker(), reply);
break;
case eos::console::SpaceProto::kInspector:
InspectorSubcmd(space.inspector(), reply);
break;
case eos::console::SpaceProto::kGroupbalancer:
GroupBalancerSubCmd(space.groupbalancer(), reply);
break;
case eos::console::SpaceProto::kGroupdrainer:
GroupDrainerSubCmd(space.groupdrainer(), reply);
break;
default:
reply.set_std_err("error: not supported");
reply.set_retc(EINVAL);
}
return reply;
}
//----------------------------------------------------------------------------
// Execute ls subcommand
//----------------------------------------------------------------------------
void SpaceCmd::LsSubcmd(const eos::console::SpaceProto_LsProto& ls,
eos::console::ReplyProto& reply)
{
using eos::console::SpaceProto;
bool json_output = false;
std::string list_format;
std::string format;
auto format_case = ls.outformat();
if ((format_case == SpaceProto::LsProto::NONE) && WantsJsonOutput()) {
format_case = SpaceProto::LsProto::MONITORING;
}
switch (format_case) {
case SpaceProto::LsProto::LISTING:
format = FsView::GetSpaceFormat("l");
list_format = FsView::GetFileSystemFormat("l");
break;
case SpaceProto::LsProto::MONITORING:
format = FsView::GetSpaceFormat("m");
json_output = WantsJsonOutput();
break;
case SpaceProto::LsProto::IO:
format = FsView::GetSpaceFormat("io");
break;
case SpaceProto::LsProto::FSCK:
format = FsView::GetSpaceFormat("fsck");
break;
default : // NONE
format = FsView::GetSpaceFormat("");
break;
}
std::string std_out;
eos::common::RWMutexReadLock lock(FsView::gFsView.ViewMutex);
FsView::gFsView.PrintSpaces(std_out, format, list_format, ls.outdepth(),
ls.selection().c_str(), "", mReqProto.dontcolor());
if (json_output) {
std_out = ResponseToJsonString(std_out);
}
reply.set_std_out(std_out);
reply.set_retc(0);
}
//----------------------------------------------------------------------------
// Execute status subcommand
//----------------------------------------------------------------------------
void SpaceCmd::StatusSubcmd(const eos::console::SpaceProto_StatusProto& status,
eos::console::ReplyProto& reply)
{
std::ostringstream std_out;
bool monitoring = status.outformat_m() || WantsJsonOutput();
const char* fmtstr = (monitoring) ? "%s=%s " : "%-32s := %s\n";
eos::common::RWMutexReadLock lock(FsView::gFsView.ViewMutex);
if (!FsView::gFsView.mSpaceView.count(status.mgmspace())) {
reply.set_std_err("error: cannot find space - no space with name=" +
status.mgmspace());
reply.set_retc(ENOENT);
return;
}
if (!monitoring) {
std_out <<
"# ------------------------------------------------------------------------------------\n";
std_out << "# Space Variables\n";
std_out <<
"# ....................................................................................\n";
}
std::vector keylist;
FsView::gFsView.mSpaceView[status.mgmspace()]->GetConfigKeys(keylist);
std::sort(keylist.begin(), keylist.end());
for (auto& i : keylist) {
char line[32678];
if (((i == "nominalsize") || (i == "headroom")) && !monitoring) {
XrdOucString sizestring;
// size printout
snprintf(line, sizeof(line) - 1, fmtstr, i.c_str(),
eos::common::StringConversion::GetReadableSizeString(
sizestring,
strtoull(FsView::gFsView.mSpaceView[status.mgmspace()]
->GetConfigMember(i).c_str(), nullptr, 10),
"B"));
} else {
snprintf(line, sizeof(line) - 1, fmtstr, i.c_str(),
FsView::gFsView.mSpaceView[status.mgmspace()]
->GetConfigMember(i).c_str());
}
std_out << line;
}
if (WantsJsonOutput()) {
std_out.str(ResponseToJsonString(std_out.str()));
}
reply.set_std_out(std_out.str());
reply.set_retc(0);
}
//----------------------------------------------------------------------------
// Execute set subcommand
//----------------------------------------------------------------------------
void SpaceCmd::SetSubcmd(const eos::console::SpaceProto_SetProto& set,
eos::console::ReplyProto& reply)
{
std::ostringstream std_out, std_err;
int ret_c = 0;
if (mVid.uid != 0) {
reply.set_std_err("error: you have to take role 'root' to execute this command");
reply.set_retc(EPERM);
return;
}
if (set.mgmspace().empty()) {
reply.set_std_err("error: illegal parameters");
reply.set_retc(EINVAL);
return;
}
eos::common::RWMutexReadLock lock(FsView::gFsView.ViewMutex);
if (!FsView::gFsView.mSpaceView.count(set.mgmspace())) {
reply.set_std_err("error: no such space - define one using 'space define' or add a filesystem under that space!");
reply.set_retc(EINVAL);
return;
}
std::string key = "status";
std::string status = (set.state_switch()) ? "on" : "off";
// Loop over all groups within this space
if (FsView::gFsView.mSpaceGroupView.count(set.mgmspace())) {
for (auto& group : FsView::gFsView.mSpaceGroupView.at(set.mgmspace())) {
if (!group->SetConfigMember(key, status)) {
std_err << "error: cannot set status in group <" << group->mName << ">\n";
ret_c = EIO;
}
}
}
// Enable all nodes if 'on' request
if (set.state_switch()) {
for (auto& node : FsView::gFsView.mNodeView) {
if (!node.second->SetConfigMember(key, status)) {
std_err << "error: cannot set status=on in node <"
<< node.second->mName << ">\n";
ret_c = EIO;
}
}
}
reply.set_std_out(std_out.str());
reply.set_std_err(std_err.str());
reply.set_retc(ret_c);
}
//----------------------------------------------------------------------------
// Execute node-set subcommand
//----------------------------------------------------------------------------
void SpaceCmd::NodeSetSubcmd(const eos::console::SpaceProto_NodeSetProto&
nodeset, eos::console::ReplyProto& reply)
{
std::ostringstream std_out, std_err;
int ret_c = 0;
std::string val = nodeset.nodeset_value();
if (mVid.uid != 0) {
reply.set_std_err("error: you have to take role 'root' to execute this command");
reply.set_retc(EPERM);
return;
}
if (nodeset.mgmspace().empty() || nodeset.nodeset_key().empty() ||
nodeset.nodeset_value().empty()) {
reply.set_std_err("error: illegal parameters");
reply.set_retc(EINVAL);
return;
}
eos::common::RWMutexWriteLock lock(FsView::gFsView.ViewMutex);
if (!FsView::gFsView.mSpaceView.count(nodeset.mgmspace())) {
reply.set_std_err("error: no such space - define one using 'space define' or add a filesystem under that space!");
reply.set_retc(EINVAL);
return;
}
{
// loop over all nodes
std::map::const_iterator it;
for (it = FsView::gFsView.mNodeView.begin();
it != FsView::gFsView.mNodeView.end(); it++) {
XrdOucString file = val.c_str();
if (file.beginswith("file:/")) {
// load the file on the MGM
file.erase(0, 5);
eos::common::Path iPath(file.c_str());
XrdOucString fpath = iPath.GetPath();
if (!fpath.beginswith("/var/eos/")) {
std_err.str(("error: cannot load requested file=" + file +
" - only files under /var/eos/ can bo loaded\n").c_str());
ret_c = EINVAL;
} else {
std::ifstream ifs(file.c_str(), std::ios::in | std::ios::binary);
if (!ifs) {
std_err.str(("error: cannot load requested file=" + file).c_str());
ret_c = EINVAL;
} else {
val = std::string((std::istreambuf_iterator(ifs)),
std::istreambuf_iterator());
// store the value b64 encoded
XrdOucString val64;
eos::common::SymKey::Base64Encode((char*) val.c_str(), val.length(), val64);
val = ("base64:" + val64).c_str();
std_out << "success: loaded contents \n" + val;
}
}
}
if (!ret_c && !it->second->SetConfigMember(nodeset.nodeset_key(), val)) {
std_err << "error: cannot set node-set for node <" + it->first + ">\n";
ret_c = EIO;
}
}
}
reply.set_std_out(std_out.str());
reply.set_std_err(std_err.str());
reply.set_retc(ret_c);
}
//----------------------------------------------------------------------------
// Execute node-get subcommand
//----------------------------------------------------------------------------
void SpaceCmd::NodeGetSubcmd(const eos::console::SpaceProto_NodeGetProto&
nodeget, eos::console::ReplyProto& reply)
{
std::ostringstream std_out;
if (mVid.uid != 0) {
reply.set_std_err("error: you have to take role 'root' to execute this command");
reply.set_retc(EPERM);
return;
}
if (nodeget.mgmspace().empty() || nodeget.nodeget_key().empty()) {
reply.set_std_err("error: illegal parameters");
reply.set_retc(EINVAL);
return;
}
eos::common::RWMutexReadLock lock(FsView::gFsView.ViewMutex);
if (!FsView::gFsView.mSpaceView.count(nodeget.mgmspace())) {
reply.set_std_err("error: no such space - define one using 'space define' or add a filesystem under that space!");
reply.set_retc(EINVAL);
return;
}
{
std::string val;
std::string new_val;
bool identical = true;
// loop over all nodes
std::map::const_iterator it;
for (it = FsView::gFsView.mNodeView.begin();
it != FsView::gFsView.mNodeView.end(); it++) {
new_val = it->second->GetConfigMember(nodeget.nodeget_key());
if (val.length() && new_val != val) {
identical = false;
}
val = new_val;
std_out << "# [ " + (it->first).substr(0,
it->first.find(':')) + " ]\n" + new_val + '\n';
}
if (identical) {
std_out.str("*:=" + val + '\n');
}
}
reply.set_std_out(std_out.str());
}
//----------------------------------------------------------------------------
// Execute reset subcommand
//----------------------------------------------------------------------------
void SpaceCmd::ResetSubcmd(const eos::console::SpaceProto_ResetProto& reset,
eos::console::ReplyProto& reply)
{
std::ostringstream std_out, std_err;
int ret_c = 0;
eos::common::RWMutexReadLock fsViewLock(FsView::gFsView.ViewMutex);
switch (reset.option()) {
case eos::console::SpaceProto_ResetProto::DRAIN: {
if (FsView::gFsView.mSpaceView.count(reset.mgmspace())) {
FsView::gFsView.mSpaceView[reset.mgmspace()]->ResetDraining();
std_out << "info: reset draining in space '" + reset.mgmspace() + "'";
} else {
std_err << "error: illegal space name";
ret_c = EINVAL;
}
}
break;
case eos::console::SpaceProto_ResetProto::EGROUP: {
gOFS->EgroupRefresh->Reset();
std_out << "\ninfo: clear cached EGroup information ...";
}
break;
case eos::console::SpaceProto_ResetProto::NSFILESISTEMVIEW: {
eos::common::RWMutexWriteLock lock(gOFS->eosViewRWMutex);
gOFS->eosFsView->shrink();
std_out << "\ninfo: resized namespace filesystem view ...";
}
break;
case eos::console::SpaceProto_ResetProto::NSFILEMAP: {
std_out << "\n info: ns does not support file map resizing";
}
break;
case eos::console::SpaceProto_ResetProto::NSDIRECTORYMAP: {
std_out << "\ninfo: ns does not support directory map resizing";
}
break;
case eos::console::SpaceProto_ResetProto::NS: {
eos::common::RWMutexWriteLock lock(gOFS->eosViewRWMutex);
gOFS->eosFsView->shrink();
std_out << "\ninfo: ns does not support map resizing";
}
break;
case eos::console::SpaceProto_ResetProto::MAPPING: {
eos::common::Mapping::Reset();
std_out << "\ninfo: clear all user/group uid/gid caches ...\n";
}
break;
case eos::console::SpaceProto_ResetProto::SCHEDULEDRAIN: {
gOFS->mFidTracker.Clear(eos::mgm::TrackerType::Drain);
std_out.str("info: reset drain scheduling map in space '" + reset.mgmspace() +
'\'');
}
break;
case eos::console::SpaceProto_ResetProto::SCHEDULEBALANCE: {
gOFS->mFidTracker.Clear(eos::mgm::TrackerType::Balance);
std_out.str("info: reset balance scheduling map in space '" + reset.mgmspace() +
'\'');
}
break;
default: { // NONE - when NONE, do cases DRAIN and EGROUP and MAPPING
if (FsView::gFsView.mSpaceView.count(reset.mgmspace())) {
FsView::gFsView.mSpaceView[reset.mgmspace()]->ResetDraining();
std_out << "info: reset draining in space '" + reset.mgmspace() + "'";
} else {
std_err << "error: illegal space name";
ret_c = EINVAL;
}
gOFS->EgroupRefresh->Reset();
std_out << "\ninfo: clear cached EGroup information ...";
eos::common::Mapping::Reset();
std_out << "\ninfo: clear all user/group uid/gid caches ...\n";
}
break;
}
reply.set_std_out(std_out.str());
reply.set_std_err(std_err.str());
reply.set_retc(ret_c);
}
//----------------------------------------------------------------------------
// Execute define subcommand
//----------------------------------------------------------------------------
void SpaceCmd::DefineSubcmd(const eos::console::SpaceProto_DefineProto& define,
eos::console::ReplyProto& reply)
{
if (mVid.uid != 0) {
reply.set_std_err("error: you have to take role 'root' to execute this command");
reply.set_retc(EPERM);
return;
}
if (define.mgmspace().empty()) {
reply.set_std_err("error: illegal parameters ");
reply.set_retc(EINVAL);
return;
}
if ((define.groupsize() * define.groupmod()) > 65536) {
reply.set_std_err("error: the product of * must be a positive integer (<=65536)!");
reply.set_retc(EINVAL);
return;
}
eos::common::RWMutexWriteLock lock(FsView::gFsView.ViewMutex);
if (!FsView::gFsView.mSpaceView.count(define.mgmspace())) {
reply.set_std_out("info: creating space '" + define.mgmspace() + "'");
if (!FsView::gFsView.RegisterSpace(define.mgmspace().c_str())) {
reply.set_std_err("error: cannot register space <" + define.mgmspace() + ">");
reply.set_retc(EIO);
return;
}
}
// Set the new space parameters
auto space = FsView::gFsView.mSpaceView[define.mgmspace()];
if ((!space->SetConfigMember("groupsize",
std::to_string(define.groupsize()))) ||
(!space->SetConfigMember("groupmod", std::to_string(define.groupmod())))) {
reply.set_std_err("error: cannot set space config value");
reply.set_retc(EIO);
}
}
//----------------------------------------------------------------------------
// Execute config subcommand
//----------------------------------------------------------------------------
void SpaceCmd::ConfigSubcmd(const eos::console::SpaceProto_ConfigProto& config,
eos::console::ReplyProto& reply)
{
if (mVid.uid != 0) {
reply.set_std_err("error: you have to take role 'root' to execute this command");
reply.set_retc(EPERM);
return;
}
int ret_c = 0;
std::ostringstream std_out, std_err;
const std::string space_name = config.mgmspace_name();
std::string key = config.mgmspace_key();
std::string value = config.mgmspace_value();
if (space_name.empty() || key.empty() || value.empty()) {
reply.set_std_err("error: illegal parameters");
reply.set_retc(EINVAL);
return;
}
bool applied = false;
FileSystem* fs = nullptr;
eos::common::RWMutexReadLock lock(FsView::gFsView.ViewMutex);
auto it_space = FsView::gFsView.mSpaceView.find(space_name);
if ((it_space == FsView::gFsView.mSpaceView.end()) ||
(it_space->second == nullptr)) {
ret_c = EINVAL;
std_err.str("error: cannot find space <" + space_name + ">");
reply.set_std_err(std_err.str());
reply.set_retc(ret_c);
return;
}
FsSpace* space = it_space->second;
if (!strcmp(mgm::rest::TAPE_REST_API_SWITCH_ON_OFF, key.c_str())) {
applied = true;
//REST API activation
if ((value != "on") && (value != "off")) {
ret_c = EINVAL;
std_err.str("error: value has to either on or off");
} else {
if (space_name != "default") {
ret_c = EIO;
std_err.str("error: the tape REST API can only be enabled or disabled on the default space");
} else {
if (!space->SetConfigMember(key, value)) {
ret_c = EIO;
std_err.str("error: cannot set space config value");
} else {
auto config = gOFS->mRestApiManager->getTapeRestApiConfig();
if (value == "on") {
if (!config->isActivated()) {
// Stage should be deactivated by default
if (!space->SetConfigMember(rest::TAPE_REST_API_STAGE_SWITCH_ON_OFF, "off")) {
ret_c = EIO;
std_err.str("error: cannot set space config value");
} else {
config->setActivated(true);
config->setStageEnabled(false);
std_out << "success: Tape REST API enabled";
}
} else {
std_out << "The tape REST API is already enabled";
}
} else {
//Switch off the tape REST API
//Also switch off the STAGE resource
if (!space->SetConfigMember(
rest::TAPE_REST_API_STAGE_SWITCH_ON_OFF, "off")) {
ret_c = EIO;
std_err.str("error: cannot set space config value");
} else {
config->setActivated(false);
config->setStageEnabled(false);
std_out << "success: Tape REST API disabled";
}
}
}
}
}
}
if (!strcmp(mgm::rest::TAPE_REST_API_STAGE_SWITCH_ON_OFF, key.c_str())) {
applied = true;
//REST API activation
if ((value != "on") && (value != "off")) {
ret_c = EINVAL;
std_err.str("error: value has to either on or off");
} else {
if (space_name != "default") {
ret_c = EIO;
std_err.str("error: the tape REST API STAGE resource can only be enabled or disabled on the default space");
} else {
if (!space
->SetConfigMember(key, value)) {
ret_c = EIO;
std_err.str("error: cannot set space config value");
} else {
if (value == "on") {
gOFS->mRestApiManager->getTapeRestApiConfig()->setStageEnabled(true);
std_out << "success: Tape REST API STAGE resource enabled";
} else {
gOFS->mRestApiManager->getTapeRestApiConfig()->setStageEnabled(false);
std_out << "success: Tape REST API STAGE resource disabled";
}
}
}
}
}
// set a space related parameter
if (!key.compare(0, 6, "space.")) {
key.erase(0, 6);
if (eos::common::startsWith(key, "policy.") ||
eos::common::startsWith(key, "local.policy.")) {
if (value == "remove") {
applied = true;
if ((key == "policy.recycle")) {
gOFS->enforceRecycleBin = false;
}
if (!space->DeleteConfigMember(key)) {
ret_c = ENOENT;
std_err.str("error: key has not been deleted");
} else {
std_out.str("success: removed space policy '" + key + "'\n");
}
} else {
applied = true;
// set a space policy parameters e.g. default placement attributes
if (!space->SetConfigMember(key, value)) {
std_err.str("error: cannot set space config value");
ret_c = EIO;
} else {
std_out.str("success: configured policy in space='" + space_name +
"' as " + key + "='" + value + "'\n");
ret_c = 0;
}
if ((key == "policy.recycle")) {
if (value == "on") {
gOFS->enforceRecycleBin = true;
} else {
gOFS->enforceRecycleBin = false;
}
}
}
} else if (key == eos::mgm::tgc::TGC_NAME_FREE_BYTES_SCRIPT) {
applied = true;
if (!space->SetConfigMember(key, value)) {
std_err.str("error: cannot set space config value");
ret_c = EIO;
} else {
std_out.str("success: configured policy in space='" + space_name +
"' as " + key + "='" + value + "'\n");
ret_c = 0;
}
} else if (key == "groupbalancer.engine") {
applied = true;
if (GroupBalancer::is_valid_engine(value)) {
if (!space->SetConfigMember(key, value)) {
std_err.str("error: cannot set space config value");
ret_c = EIO;
} else {
std_out.str("success: configured groupbalancer.engine in space='" +
space_name + "' as " + key + "='" + value + "'\n");
ret_c = 0;
}
} else {
std_err.str("error: invalid groupbalancer engine name");
ret_c = EINVAL;
}
} else if (key == "groupbalancer.blocklist") {
if (!space->SetConfigMember(key, value)) {
std_err.str("error: cannot set space config value");
ret_c = EIO;
} else {
space->mGroupBalancer->reconfigure();
applied = true;
std_out.str("success: updated " + key + "in space='" +
space_name + "' as " + value + "'\n");
ret_c = 0;
}
} else if (key == "scheduler.type") {
if (!space->SetConfigMember(key, value)) {
std_err.str("error: cannot set space config value");
ret_c = EIO;
} else {
applied = true;
gOFS->mFsScheduler->setPlacementStrategy(space->mName, value);
std_out.str("success: configured scheduler.type in space='"+
space_name + "' as " + value + "\n");
ret_c = 0;
}
} else {
if ((key == "nominalsize") ||
(key == "headroom") ||
(key == "graceperiod") ||
(key == "drainperiod") ||
(key == "balancer") ||
(key == "balancer.threshold") ||
(key == "balancer.node.rate") ||
(key == "balancer.node.ntx") ||
(key == "balancer.max-queue-jobs") ||
(key == "balancer.max-thread-pool-size") ||
(key == "balancer.update.interval") ||
(key == "drainer.node.rate") ||
(key == "drainer.node.ntx") ||
(key == "drainer.node.nfs") ||
(key == "drainer.retries") ||
(key == "drainer.fs.ntx") ||
(key == "converter") ||
(key == "tracker") ||
(key == "inspector") ||
(key == "inspector.interval") ||
(key == "inspector.price.disk.tbyear") ||
(key == "inspector.price.tape.tbyear") ||
(key == "inspector.price.currency") ||
(key == "lru") ||
(key == "lru.interval") ||
(key == "wfe") ||
(key == "wfe.interval") ||
(key == "wfe.ntx") ||
(key == "converter.ntx") ||
(key == "groupbalancer") ||
(key == "groupbalancer.ntx") ||
(key == "groupbalancer.threshold") ||
(key == "groupbalancer.min_threshold") ||
(key == "groupbalancer.max_threshold") ||
(key == "groupbalancer.min_file_size") ||
(key == "groupbalancer.max_file_size") ||
(key == "groupbalancer.file_attempts") ||
(key == "geobalancer") ||
(key == "geobalancer.ntx") ||
(key == "geobalancer.threshold") ||
(key == "groupdrainer") ||
(key == "groupdrainer.threshold") ||
(key == "groupdrainer.group_refresh_interval") ||
(key == "groupdrainer.retry_interval") ||
(key == "groupdrainer.retry_count") ||
(key == "groupdrainer.ntx") ||
(key == "geo.access.policy.read.exact") ||
(key == "geo.access.policy.write.exact") ||
(key == "filearchivedgc") ||
(key == "max.ropen") ||
(key == "max.wopen") ||
(key == eos::mgm::tgc::TGC_NAME_QRY_PERIOD_SECS) ||
(key == eos::mgm::tgc::TGC_NAME_AVAIL_BYTES) ||
(key == eos::mgm::tgc::TGC_NAME_TOTAL_BYTES) ||
(key == "token.generation") ||
(key == eos::common::SCAN_IO_RATE_NAME) ||
(key == eos::common::SCAN_ENTRY_INTERVAL_NAME) ||
(key == eos::common::SCAN_RAIN_ENTRY_INTERVAL_NAME) ||
(key == eos::common::SCAN_DISK_INTERVAL_NAME) ||
(key == eos::common::SCAN_NS_INTERVAL_NAME) ||
(key == eos::common::SCAN_NS_RATE_NAME) ||
(key == eos::common::FSCK_REFRESH_INTERVAL_NAME)) {
if ((key == "balancer") ||
(key == "converter") ||
(key == "tracker") ||
(key == "inspector") ||
(key == "lru") ||
(key == "groupbalancer") ||
(key == "geobalancer") ||
(key == "geo.access.policy.read.exact") ||
(key == "geo.access.policy.write.exact") ||
(key == "filearchivedgc") ||
(key == "groupdrainer")) {
applied = true;
if ((value != "on") && (value != "off")) {
ret_c = EINVAL;
std_err.str("error: value has to either on or off");
} else {
if (!space->SetConfigMember(key, value)) {
ret_c = EIO;
std_err.str("error: cannot set space config value");
} else {
if (key == "balancer") {
if (space->mFsBalancer) {
if (value == "on") {
std_out << "success: (fs) balancer is enabled!";
} else {
std_out << "success: (fs) balancer is disabled!";
}
if (space->mFsBalancer) {
space->mFsBalancer->SignalConfigUpdate();
}
} else {
std_err.str("error: (fs) balancer not initialized for space");
ret_c = EIO;
}
}
if (key == "converter") {
if (value == "on") {
std_out << "success: converter is enabled!";
} else {
std_out << "success: converter is disabled!";
}
}
if (key == "tracker") {
if (value == "on") {
gOFS->mReplicationTracker->enable();
std_out << "success: tracker is enabled!";
} else {
gOFS->mReplicationTracker->disable();
std_out << "success: tracker is disabled!";
}
}
if (key == "inspector") {
if (space->mFileInspector) {
if (value == "on") {
space->mFileInspector->enable();
std_out << "success: file inspector is enabled!";
} else {
space->mFileInspector->disable();
std_out << "success: file inspector is disabled!";
}
} else {
std_err.str("error: no inspector for space");
ret_c = EINVAL;
}
}
if (key == "groupbalancer") {
if (space->mGroupBalancer) {
if (value == "on") {
std_out << "success: groupbalancer is enabled!";
} else {
std_out << "success: groupbalancer is disabled!";
}
space->mGroupBalancer->reconfigure();
} else {
std_err.str("error: group balancer not initialized for space");
ret_c = EIO;
}
}
if (key == "geobalancer") {
if (space->mGeoBalancer) {
if (value == "on") {
std_out << "success: geobalancer is enabled!";
} else {
std_out << "success: geobalancer is disabled!";
}
} else {
std_err.str("error: geo balancer not initialized for space");
ret_c = EIO;
}
}
if (key == "groupdrainer") {
if (space->mGroupDrainer) {
if (value == "on") {
std_out << "success: groupdrainer is enabled!";
} else {
std_out << "success: groupdrainer is disabled!";
}
space->mGroupDrainer->reconfigure();
} else {
std_err.str("error: group drainer not initialized for space");
ret_c = EIO;
}
}
if (key == "geo.access.policy.read.exact") {
if (value == "on") {
std_out <<
"success: geo access policy prefers the exact geo matching replica for reading!";
} else {
std_out <<
"success: geo access policy prefers with a weight the geo matching replica for reading!";
}
}
if (key == "geo.access.policy.write.exact") {
if (value == "on") {
std_out <<
"success: geo access policy prefers the exact geo matching replica for placements!";
} else {
std_out <<
"success: geo access policy prefers with a weight the geo matching replica for placements!";
}
}
if (key == "scheduler.skip.overloaded") {
if (value == "on") {
std_out << "success: scheduler skips overloaded eth-out nodes!";
} else {
std_out << "success: scheduler does not skip overloaded eth-out nodes!";
}
}
if (key == "filearchivedgc") {
if (value == "on") {
std_out << "success: 'file archived' garbage collector is enabled";
} else {
std_out << "success: 'file archived' garbage collector is disabled";
}
}
if (key == "lru") {
std_out << ((value == "on") ? "success: LRU is enabled" :
"success: LRU is disabled");
gOFS->mLRUEngine->RefreshOptions();
}
}
}
} else if (key == "wfe") {
applied = true;
if ((value != "on") && (value != "off") && (value != "paused")) {
ret_c = EINVAL;
std_err.str("error: value has to either on, paused or off");
} else {
if (!space->SetConfigMember(key, value)) {
ret_c = EIO;
std_err.str("error: cannot set space config value");
} else {
std::string status = (value == "on") ? "enabled" :
(value == "off" ? "disabled" : "paused");
std_out << "success: wfe is " << status << "!";
}
}
} else {
if (value == "remove") {
applied = true;
if (!space->DeleteConfigMember(key)) {
ret_c = ENOENT;
std_err.str("error: key has not been deleted");
} else {
std_out.str("success: deleted space config : " + key);
}
} else {
errno = 0;
applied = true;
unsigned long long size = eos::common::StringConversion::GetSizeFromString(
value.c_str());
if (!errno) {
if ((key != "balancer.threshold") &&
(key != "geobalancer.threshold") &&
(key != "groupbalancer.threshold") &&
(key != "groupbalancer.min_threshold") &&
(key != "groupbalancer.max_threshold") &&
(key != "groupdrainer.threshold")) {
// Threshold is allowed to be decimal!
char ssize[1024];
snprintf(ssize, sizeof(ssize) - 1, "%llu", size);
value = ssize;
}
if (!space->SetConfigMember(key, value)) {
ret_c = EIO;
std_err.str("error: cannot set space config value");
} else {
std_out.str("success: setting " + key + "=" + value);
if ((key == "token.generation")) {
eos::common::EosTok::sTokenGeneration = strtoull(value.c_str(), 0, 0);
}
if (key == "lru.interval") {
gOFS->mLRUEngine->RefreshOptions();
}
if (eos::common::startsWith(key, GROUPBALANCER_KEY_PREFIX)) {
space->mGroupBalancer->reconfigure();
} else if (eos::common::startsWith(key, GROUPDRAINER_KEY_PREFIX)) {
space->mGroupDrainer->reconfigure();
} else if (eos::common::startsWith(key, BALANCER_KEY_PREFIX)) {
if (space->mFsBalancer) {
space->mFsBalancer->SignalConfigUpdate();
}
}
}
} else {
ret_c = EINVAL;
std_err.str("error: value has to be a positive number");
}
}
}
}
}
}
if (!key.compare(0, 5, "atime")) {
applied = true;
if (!space->SetConfigMember(key, value)) {
ret_c = EIO;
std_err.str("error: cannot set space config value");
} else {
std_out.str("success: defining space acces time tracking: " + key + "=" +
value);
}
}
// Set a bandwidth limitation parameter
if (!key.compare(0, 3, "bw.")) {
applied = true;
if (value == "remove") {
if (!space->DeleteConfigMember(key)) {
ret_c = ENOENT;
std_err.str("error: key has not been deleted");
} else {
std_out.str("success: deleted stream bandwidth setting: " + key);
}
} else {
if (!space->SetConfigMember(key, value)) {
ret_c = EIO;
std_err.str("error: cannot set space config value");
} else {
std_out.str("success: defining stream bandwidth limitation: " + key + "=" +
value);
}
}
}
// Set iopriority parameter
if (!key.compare(0, 11, "iopriority.")) {
applied = true;
if (value == "remove") {
if (!space->DeleteConfigMember(key)) {
ret_c = ENOENT;
std_err.str("error: key has not been deleted");
} else {
std_out.str("success: deleted iopriority setting: " + key);
}
} else {
if (!space->SetConfigMember(key, value)) {
ret_c = EIO;
std_err.str("error: cannot set space config value");
} else {
std_out.str("success: defining space iopriority: " + key + "=" + value);
}
}
}
// Set iotype parameter
if (!key.compare(0, 7, "iotype.")) {
applied = true;
if (value == "remove") {
if (!space->DeleteConfigMember(key)) {
ret_c = ENOENT;
std_err.str("error: key has not been deleted");
} else {
std_out.str("success: deleted iotype setting: " + key);
}
} else {
if (!space->SetConfigMember(key, value)) {
ret_c = EIO;
std_err.str("error: cannot set space config value");
} else {
std_out.str("success: defining space iotype: " + key + "=" + value);
}
}
}
// Set schedule parameter
if (!key.compare(0, 9, "schedule.")) {
applied = true;
if (value == "remove") {
if (!space->DeleteConfigMember(key)) {
ret_c = ENOENT;
std_err.str("error: key has not been deleted");
} else {
std_out.str("success: deleted application scheduling setting: " + key);
}
} else {
if (!space->SetConfigMember(key, value)) {
ret_c = EIO;
std_err.str("error: cannot set space config value");
} else {
std_out.str("success: defining space scheduling: " + key + "=" + value);
}
}
}
// Set a filesystem related parameter
if (!key.compare(0, 3, "fs.")) {
applied = true;
key.erase(0, 3);
// we disable the autosave, do all the updates and then switch back
// to autosave and evt. save all changes
gOFS->ConfEngine->SetAutoSave(false);
// Store these as a global parameters of the space
if ((key == "headroom") || (key == "graceperiod") || (key == "drainperiod") ||
(key == "max.ropen") || (key == "max.wopen") ||
(key == eos::common::SCAN_IO_RATE_NAME) ||
(key == eos::common::SCAN_ENTRY_INTERVAL_NAME) ||
(key == eos::common::SCAN_RAIN_ENTRY_INTERVAL_NAME) ||
(key == eos::common::SCAN_DISK_INTERVAL_NAME) ||
(key == eos::common::SCAN_NS_INTERVAL_NAME) ||
(key == eos::common::SCAN_NS_RATE_NAME) ||
(key == eos::common::FSCK_REFRESH_INTERVAL_NAME)) {
unsigned long long size = eos::common::StringConversion::GetSizeFromString(
value.c_str());
char ssize[1024];
snprintf(ssize, sizeof(ssize) - 1, "%llu", size);
if (value == "remove") {
if (!space->DeleteConfigMember(key)) {
ret_c = ENOENT;
} else {
std_out.str("success: deleting " + key);
}
} else {
if ((!space->SetConfigMember(key, ssize))) {
std_err << "error: failed to set space parameter <" + key + ">\n";
ret_c = EINVAL;
} else {
std_out.str("success: setting " + key + "=" + value);
}
}
} else {
if (key != "configstatus") {
std_err << "error: not an allowed parameter <" + key + ">\n";
ret_c = EINVAL;
}
}
for (auto it = space->begin(); it != space->end(); ++it) {
fs = FsView::gFsView.mIdView.lookupByID(*it);
if (fs) {
// check the allowed strings
if (((key == "configstatus") &&
(eos::common::FileSystem::GetConfigStatusFromString(value.c_str()) !=
eos::common::ConfigStatus::kUnknown))) {
fs->SetString(key.c_str(), value.c_str());
FsView::gFsView.StoreFsConfig(fs, false);
} else {
errno = 0;
eos::common::StringConversion::GetSizeFromString(value.c_str());
if (((key == "headroom") || (key == "graceperiod") || (key == "drainperiod") ||
(key == "max.ropen") || (key == "max.wopen") ||
(key == eos::common::SCAN_IO_RATE_NAME) ||
(key == eos::common::SCAN_ENTRY_INTERVAL_NAME) ||
(key == eos::common::SCAN_RAIN_ENTRY_INTERVAL_NAME) ||
(key == eos::common::SCAN_DISK_INTERVAL_NAME) ||
(key == eos::common::SCAN_NS_INTERVAL_NAME) ||
(key == eos::common::SCAN_NS_RATE_NAME) ||
(key == eos::common::FSCK_REFRESH_INTERVAL_NAME)) && (!errno)) {
if (value == "remove") {
fs->RemoveKey(key.c_str());
} else {
fs->SetLongLong(key.c_str(),
eos::common::StringConversion::GetSizeFromString(value.c_str()));
}
FsView::gFsView.StoreFsConfig(fs, false);
} else {
std_err << "error: not an allowed parameter <" + key + ">\n";
ret_c = EINVAL;
break;
}
}
} else {
std_err << "error: cannot identify the filesystem by <" + space_name
+ ">\n";
ret_c = EINVAL;
}
}
gOFS->ConfEngine->SetAutoSave(true);
gOFS->ConfEngine->AutoSave();
}
if (!applied) {
ret_c = EINVAL;
std_err.str("error: unknown parameter <" + key +
"> - probably need to prefix with 'space.' or 'fs.'\n");
}
reply.set_std_out(std_out.str());
reply.set_std_err(std_err.str());
reply.set_retc(ret_c);
}
//----------------------------------------------------------------------------
// Execute quota subcommand
//----------------------------------------------------------------------------
void SpaceCmd::QuotaSubcmd(const eos::console::SpaceProto_QuotaProto& quota,
eos::console::ReplyProto& reply)
{
std::string key = "quota";
std::string onoff = (quota.quota_switch()) ? "on" : "off" ;
if (mVid.uid != 0) {
reply.set_std_err("error: you have to take role 'root' to execute this command");
reply.set_retc(EPERM);
return;
}
if (quota.mgmspace().empty()) {
reply.set_std_err("error: illegal parameters");
reply.set_retc(EINVAL);
return;
}
eos::common::RWMutexReadLock lock(FsView::gFsView.ViewMutex);
if (FsView::gFsView.mSpaceView.count(quota.mgmspace())) {
if (!FsView::gFsView.mSpaceView[quota.mgmspace()]->SetConfigMember(key,
onoff)) {
reply.set_std_err("error: cannot set space config value");
reply.set_retc(EIO);
}
} else {
reply.set_std_err("error: no such space defined");
reply.set_retc(EINVAL);
}
}
//----------------------------------------------------------------------------
// Execute rm subcommand
//----------------------------------------------------------------------------
void SpaceCmd::RmSubcmd(const eos::console::SpaceProto_RmProto& rm,
eos::console::ReplyProto& reply)
{
if (mVid.uid != 0) {
reply.set_std_err("error: you have to take role 'root' to execute this command");
reply.set_retc(EPERM);
return;
}
if (rm.mgmspace().empty()) {
reply.set_std_err("error: illegal parameters");
reply.set_retc(EINVAL);
return;
}
eos::common::RWMutexWriteLock lock(FsView::gFsView.ViewMutex);
if (!FsView::gFsView.mSpaceView.count(rm.mgmspace())) {
reply.set_std_err("error: no such space '" + rm.mgmspace() + "'");
reply.set_retc(ENOENT);
return;
}
for (auto it = FsView::gFsView.mSpaceView[rm.mgmspace()]->begin();
it != FsView::gFsView.mSpaceView[rm.mgmspace()]->end(); it++) {
FileSystem* fs = FsView::gFsView.mIdView.lookupByID(*it);
if (fs) {
// check that filesystems are empty
if ((fs->GetConfigStatus(false) != eos::common::ConfigStatus::kEmpty)) {
reply.set_std_err("error: unable to remove space '" + rm.mgmspace() +
"' - filesystems are not all in empty state - try to drain them or: space config configstatus=empty\n");
reply.set_retc(EBUSY);
return;
}
}
}
common::SharedHashLocator spaceLocator =
common::SharedHashLocator::makeForSpace(rm.mgmspace());
if (!mq::SharedHashWrapper::deleteHash(gOFS->mMessagingRealm.get(),
spaceLocator)) {
reply.set_std_err("error: unable to remove config of space '" + rm.mgmspace() +
"'");
reply.set_retc(EIO);
} else {
if (FsView::gFsView.UnRegisterSpace(rm.mgmspace().c_str())) {
reply.set_std_out("success: removed space '" + rm.mgmspace() + "'");
} else {
reply.set_std_err("error: unable to unregister space '" + rm.mgmspace() + "'");
}
}
}
//----------------------------------------------------------------------------
// Execute tracker subcommand
//----------------------------------------------------------------------------
void SpaceCmd::TrackerSubcmd(const eos::console::SpaceProto_TrackerProto&
tracker, eos::console::ReplyProto& reply)
{
std::ostringstream std_out;
std::string tmp;
gOFS->mReplicationTracker->Scan(2 * 86400, false, &tmp);
std_out <<
"# ------------------------------------------------------------------------------------\n";
std_out << tmp;
std_out <<
"# ------------------------------------------------------------------------------------\n";
reply.set_std_out(std_out.str());
reply.set_retc(0);
}
//----------------------------------------------------------------------------
// Execute inspector subcommand
//----------------------------------------------------------------------------
void SpaceCmd::InspectorSubcmd(const eos::console::SpaceProto_InspectorProto&
inspector, eos::console::ReplyProto& reply)
{
std::string_view options = inspector.options();
std::string std_out;
eos::common::RWMutexReadLock lock(FsView::gFsView.ViewMutex);
auto space_it = FsView::gFsView.mSpaceView.find(inspector.mgmspace());
if (space_it != FsView::gFsView.mSpaceView.end()) {
space_it->second->mFileInspector->Dump(std_out, options,
FileInspector::LockFsView::Off);
reply.set_std_out(std_out);
reply.set_retc(0);
} else {
reply.set_std_err("error: no such space");
reply.set_retc(EINVAL);
}
}
//----------------------------------------------------------------------------
// Execute group balancer subcommand
//----------------------------------------------------------------------------
void
SpaceCmd::GroupBalancerSubCmd(const eos::console::SpaceProto_GroupBalancerProto&
groupbalancer,
eos::console::ReplyProto& reply)
{
if (groupbalancer.mgmspace().empty()) {
reply.set_std_err("error: A spacename is needed for this cmd");
reply.set_retc(EINVAL);
return;
}
eos::common::RWMutexReadLock lock(FsView::gFsView.ViewMutex);
auto space_it = FsView::gFsView.mSpaceView.find(groupbalancer.mgmspace());
if (space_it == FsView::gFsView.mSpaceView.end()) {
reply.set_std_err("error: No such space exists!");
reply.set_retc(EINVAL);
return;
}
const auto fs_space = space_it->second;
switch (groupbalancer.cmd_case()) {
case eos::console::SpaceProto_GroupBalancerProto::kStatus:
GroupBalancerStatusCmd(groupbalancer.status(), reply, fs_space);
break;
default:
reply.set_std_err("error: not supported");
reply.set_retc(EINVAL);
}
}
//----------------------------------------------------------------------------
// Execute group balancer status subcommand
//----------------------------------------------------------------------------
void SpaceCmd::GroupBalancerStatusCmd(const
eos::console::SpaceProto_GroupBalancerStatusProto& status,
eos::console::ReplyProto& reply,
FsSpace* const fs_space)
{
if (fs_space == nullptr || fs_space->mGroupBalancer == nullptr) {
reply.set_std_err("Invalid space/GroupBalancer config");
reply.set_retc(EINVAL);
return;
}
bool monitoring = status.options().find('m') != std::string::npos;
bool detail = status.options().find('d') != std::string::npos;
reply.set_std_out(fs_space->mGroupBalancer->Status(detail, monitoring));
reply.set_retc(0);
}
//----------------------------------------------------------------------------
// Execute group drainer status subcommand
//----------------------------------------------------------------------------
void
SpaceCmd::GroupDrainerSubCmd(const eos::console::SpaceProto_GroupDrainerProto&
groupdrainer,
console::ReplyProto& reply)
{
if (groupdrainer.mgmspace().empty()) {
reply.set_std_err("error: A spacename is needed for this cmd");
reply.set_retc(EINVAL);
return;
}
eos::common::RWMutexReadLock lock(FsView::gFsView.ViewMutex);
auto space_it = FsView::gFsView.mSpaceView.find(groupdrainer.mgmspace());
if (space_it == FsView::gFsView.mSpaceView.end()) {
reply.set_std_err("error: No such space exists!");
reply.set_retc(EINVAL);
return;
}
const auto fs_space = space_it->second;
if (!fs_space->mGroupDrainer) {
reply.set_std_out("GroupDrainer not enabled or is configuring!");
reply.set_retc(EIO);
return;
}
switch (groupdrainer.cmd_case()) {
case eos::console::SpaceProto_GroupDrainerProto::kStatus:
switch (groupdrainer.status().outformat()) {
case eos::console::SpaceProto::GroupDrainerStatusProto::MONITORING:
reply.set_std_out(fs_space->mGroupDrainer->getStatus(
GroupDrainer::StatusFormat::MONITORING));
break;
case eos::console::SpaceProto::GroupDrainerStatusProto::DETAIL:
reply.set_std_out(fs_space->mGroupDrainer->getStatus(
GroupDrainer::StatusFormat::DETAIL));
break;
default:
reply.set_std_out(fs_space->mGroupDrainer->getStatus(
GroupDrainer::StatusFormat::NONE));
}
break;
case eos::console::SpaceProto_GroupDrainerProto::kReset:
switch (groupdrainer.reset().option()) {
case eos::console::SpaceProto::GroupDrainerResetProto::FAILED:
fs_space->mGroupDrainer->resetFailedTransfers();
reply.set_std_out("Done resetting all failed transfers!");
break;
case eos::console::SpaceProto::GroupDrainerResetProto::ALL:
fs_space->mGroupDrainer->resetCaches();
reply.set_std_out("Done clearing all GroupDrainer caches!");
break;
default:
reply.set_std_out("Unknown option!");
reply.set_retc(EINVAL);
return;
}
break;
default:
reply.set_std_err("Unknown option!");
reply.set_retc(EINVAL);
return;
}
reply.set_retc(0);
}
EOSMGMNAMESPACE_END