//------------------------------------------------------------------------------
// File: FsView.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
#include
#include "common/config/ConfigParsing.hh"
#include "mgm/XrdMgmOfs.hh"
#include "mgm/FsView.hh"
#include "mgm/GeoBalancer.hh"
#include "mgm/GroupBalancer.hh"
#include "mgm/GroupDrainer.hh"
#include "mgm/GeoTreeEngine.hh"
#include "mgm/balancer/FsBalancer.hh"
#include "mgm/inspector/FileInspector.hh"
#include "mgm/config/IConfigEngine.hh"
#include "mgm/tgc/Constants.hh"
#include "mgm/http/rest-api/Constants.hh"
#include "mgm/Policy.hh"
#include "mgm/placement/FsScheduler.hh"
#include "mgm/ZMQ.hh"
#include "common/table_formatter/TableFormatterBase.hh"
#include "common/StringConversion.hh"
#include "common/Assert.hh"
#include "common/InstanceName.hh"
#include "common/LayoutId.hh"
#include "mq/SharedHashWrapper.hh"
#include "common/Constants.hh"
#include "common/token/EosTok.hh"
#include "namespace/Prefetcher.hh"
#include "namespace/interface/IContainerMDSvc.hh"
using eos::common::RWMutexReadLock;
EOSMGMNAMESPACE_BEGIN
FsView FsView::gFsView;
std::atomic FsSpace::gDisableDefaults {false};
std::string FsNode::msRefreshTag {"stat.refresh_fs"};
//------------------------------------------------------------------------------
// Check if given heartbeat timestamp is recent enough
//------------------------------------------------------------------------------
inline bool isHeartbeatRecent(time_t heartbeatTime)
{
time_t now = time(NULL);
if ((now - heartbeatTime) < 60) {
return true;
}
return false;
}
//------------------------------------------------------------------------------
// Destructor - destructs all the branches starting at this node
//------------------------------------------------------------------------------
GeoTreeElement::~GeoTreeElement()
{
for (auto it = mSons.begin(); it != mSons.end(); it++) {
delete it->second;
}
}
//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
GeoTree::GeoTree() : pLevels(8)
{
pLevels.resize(1);
pRoot = new GeoTreeElement;
pLevels[0].insert(pRoot);
pRoot->mTagToken = "";
pRoot->mFullTag = "";
pRoot->mFather = NULL;
}
//------------------------------------------------------------------------------
// Destructor
//------------------------------------------------------------------------------
GeoTree::~GeoTree()
{
delete pRoot;
}
//------------------------------------------------------------------------------
// Insert a FileSystem into the tree
//------------------------------------------------------------------------------
bool GeoTree::insert(const fsid_t& fs)
{
if (pLeaves.count(fs)) {
return false;
}
std::string geotag = getGeoTag(fs);
// Tokenize the geotag (geo tag is like adas::acsd::csdw::fee)
std::vector geotokens;
eos::common::StringConversion::EmptyTokenize(geotag, geotokens, ":");
size_t s;
s = geotokens.size();
for (size_t i = 0; i < s; i++) {
if (geotokens[i].size()) {
geotokens.push_back(geotokens[i]);
}
}
geotokens.erase(geotokens.begin(), geotokens.begin() + s);
if (geotokens.empty()) {
geotokens.push_back(""); // geotag is not provided
}
GeoTreeElement* father = pRoot;
std::string fulltag = pRoot->mFullTag;
// Insert all the geotokens in the tree
GeoTreeElement* currentnode = pRoot;
GeoTreeElement* currentleaf = NULL;
for (int i = 0; i < (int)geotokens.size() - 1; i++) {
const std::string& geotoken = geotokens[i];
if (currentnode->mSons.count(geotoken)) {
currentnode = father->mSons[geotoken];
if (!fulltag.empty()) {
fulltag += "::";
}
fulltag += geotoken;
} else {
currentnode = new GeoTreeElement;
currentnode->mTagToken = geotoken;
if (!fulltag.empty()) {
fulltag += "::";
}
fulltag += geotoken;
currentnode->mFullTag = fulltag;
currentnode->mFather = father;
father->mSons[geotoken] = currentnode;
if ((int)pLevels.size() < i + 2) {
pLevels.resize(i + 2);
}
pLevels[i + 1].insert(currentnode);
}
father = currentnode;
}
// Finally, insert the fs
if (!father->mSons.count(geotokens.back())) {
currentleaf = new GeoTreeElement;
currentleaf->mFather = father;
currentleaf->mTagToken = geotokens.back();
if (!fulltag.empty()) {
fulltag += "::";
}
fulltag += geotokens.back();
currentleaf->mFullTag = fulltag;
father->mSons[geotokens.back()] = currentleaf;
if (pLevels.size() < geotokens.size() + 1) {
pLevels.resize(geotokens.size() + 1);
}
pLevels[geotokens.size()].insert(currentleaf);
} else {
// assert(father->mSons[geotokens.back()]->mIsLeaf);
currentleaf = father->mSons[geotokens.back()];
}
if (!currentleaf->mFsIds.count(fs)) {
currentleaf->mFsIds.insert(fs);
pLeaves[fs] = currentleaf;
} else {
return false;
}
return true;
}
//------------------------------------------------------------------------------
// Get number of file systems in the tree
//------------------------------------------------------------------------------
size_t GeoTree::size() const
{
return pLeaves.size();
}
//------------------------------------------------------------------------------
// Remove a file system from the tree
//------------------------------------------------------------------------------
bool GeoTree::erase(const fsid_t& fs)
{
GeoTreeElement* leaf;
if (!pLeaves.count(fs)) {
return false;
} else {
leaf = pLeaves[fs];
}
pLeaves.erase(fs);
leaf->mFsIds.erase(fs);
GeoTreeElement* father = leaf;
if (leaf->mFsIds.empty() && leaf->mSons.empty()) {
// Compute the depth for the current father
int depth = -1;
for (int i = (int)pLevels.size() - 1; i >= 0; i--) {
if (pLevels[i].count(father)) {
depth = i;
break;
}
}
assert(depth >= 0); // consistency check
if (depth < 0) {
return false;
}
// Go uproot until there is more than one branch
while (father->mFather && father->mFather->mSons.size() == 1 &&
father->mFather->mFsIds.empty()) {
if (father->mFather == pRoot) {
break;
}
pLevels[depth--].erase(father);
// We don't update the father's sons list on purpose in order to keep
// the reference for the destruction
father = father->mFather;
}
// Erase the full branch
if (father->mFather) {
father->mFather->mSons.erase(father->mTagToken);
}
pLevels[depth].erase(father);
delete father;
// Update the pLevels size if needed
int count = 0;
for (auto it = pLevels.rbegin(); it != pLevels.rend(); it++) {
if (!it->empty()) {
if (count) {
pLevels.resize(pLevels.size() - count);
}
break;
}
count++;
}
}
return true;
}
//------------------------------------------------------------------------------
// Get the geotag at which the fs is stored if found
//------------------------------------------------------------------------------
bool GeoTree::getGeoTagInTree(const fsid_t& fs, std::string& geoTag)
{
if (!pLeaves.count(fs)) {
return false;
} else {
geoTag = pLeaves[fs]->mFullTag;
}
return true;
}
//------------------------------------------------------------------------------
// Get file system geotag
//------------------------------------------------------------------------------
std::string GeoTree::getGeoTag(const fsid_t& fs) const
{
FileSystem* entry = FsView::gFsView.mIdView.lookupByID(fs);
if (!entry) {
return "";
}
return entry->GetString("stat.geotag");
}
//------------------------------------------------------------------------------
// * * * Class GeoTree::const_iterator * * *
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Copy assignment operator
//------------------------------------------------------------------------------
GeoTree::const_iterator&
GeoTree::const_iterator::operator= (const const_iterator& it)
{
if (this != &it) {
mIt = it.mIt;
mCont = it.mCont;
}
return *this;
}
//------------------------------------------------------------------------------
// ++ operator pre-increment
//------------------------------------------------------------------------------
GeoTree::const_iterator&
GeoTree::const_iterator::operator++()
{
if (mIt != mCont->end()) {
++mIt;
}
return *this;
}
//------------------------------------------------------------------------------
// ++ operator post-increment
//------------------------------------------------------------------------------
GeoTree::const_iterator
GeoTree::const_iterator::operator++(int)
{
GeoTree::const_iterator it(*this);
if (mIt != mCont->end()) {
++mIt;
}
return it;
}
//------------------------------------------------------------------------------
// -- operator pre-decrement
//------------------------------------------------------------------------------
GeoTree::const_iterator&
GeoTree::const_iterator::operator--()
{
if (mIt != mCont->begin()) {
--mIt;
}
return *this;
}
//------------------------------------------------------------------------------
// -- operator post-decrement
//------------------------------------------------------------------------------
GeoTree::const_iterator
GeoTree::const_iterator::operator--(int)
{
GeoTree::const_iterator it(*this);
if (mIt != mCont->begin()) {
--mIt;
}
return it;
}
//------------------------------------------------------------------------------
// Indirection operator
//------------------------------------------------------------------------------
const eos::common::FileSystem::fsid_t&
GeoTree::const_iterator::operator*() const
{
return mIt->first;
}
//------------------------------------------------------------------------------
// fsid_iterator: Iterate either over a given subset, or the pLeaves map.
// Yes this is weird, but we need it for certain BaseView functions.
//------------------------------------------------------------------------------
class fsid_iterator
{
public:
//----------------------------------------------------------------------------
// Constructor. Iterate through subset: if subset is nullptr, iterate through
// tree instead.
//----------------------------------------------------------------------------
fsid_iterator(const std::set* subset,
GeoTree* tree)
{
subsetValid = subset != nullptr;
if (subsetValid) {
subsetIter = subset->begin();
subsetEnd = subset->end();
} else {
geotreeIter = tree->begin();
geotreeEnd = tree->end();
}
}
//----------------------------------------------------------------------------
// Is the iterator still valid?
//----------------------------------------------------------------------------
bool valid() const
{
if (subsetValid) {
return subsetIter != subsetEnd;
}
return geotreeIter != geotreeEnd;
}
//----------------------------------------------------------------------------
// Advance
//----------------------------------------------------------------------------
void next()
{
if (!valid()) {
return;
}
if (subsetValid) {
subsetIter++;
} else {
geotreeIter++;
}
}
eos::common::FileSystem::fsid_t operator*() const
{
if (subsetValid) {
return *subsetIter;
}
return *geotreeIter;
}
private:
bool subsetValid;
std::set::const_iterator subsetIter;
std::set::const_iterator subsetEnd;
GeoTree::const_iterator geotreeIter;
GeoTree::const_iterator geotreeEnd;
};
//------------------------------------------------------------------------------
// Run an aggregator through the tree
//------------------------------------------------------------------------------
bool GeoTree::runAggregator(GeoTreeAggregator* aggregator) const
{
if (pLevels.empty()) {
return false;
}
// Build the GeoTags and the depth indexes
size_t elemCount = 0;
std::vector geotags;
std::vector depthlevelsendindexes;
for (auto itl = pLevels.begin(); itl != pLevels.end(); itl++) {
geotags.resize(geotags.size() + itl->size());
for (auto ite = itl->rbegin(); ite != itl->rend(); ite++) {
// could be made faster and more complex but probably not necessary for the moment
geotags[elemCount] = (*ite)->mTagToken;
GeoTreeElement* element = *ite;
while (element->mFather) {
element = element->mFather;
geotags[elemCount] = element->mTagToken + "::" + geotags[elemCount];
}
elemCount++;
}
depthlevelsendindexes.push_back(elemCount);
}
aggregator->init(geotags, depthlevelsendindexes);
elemCount--;
for (auto itl = pLevels.rbegin(); itl != pLevels.rend(); itl++) {
for (auto ite = itl->begin(); ite != itl->end(); ite++) {
(*ite)->mId = elemCount;
if (!aggregator->aggregateLeavesAndNodes((*ite)->mFsIds, (*ite)->mSons,
elemCount--)) {
return false;
}
}
}
return true;
}
//------------------------------------------------------------------------------
// @brief Get the sums at each tree element
//------------------------------------------------------------------------------
const std::vector* DoubleAggregator::getSums() const
{
return &pSums;
}
//------------------------------------------------------------------------------
// @brief Get the averages at each tree element
//------------------------------------------------------------------------------
const std::vector* DoubleAggregator::getMeans() const
{
return &pMeans;
}
//------------------------------------------------------------------------------
// @brief Get the maximum deviations at each tree element
//------------------------------------------------------------------------------
const std::vector* DoubleAggregator::getMaxAbsDevs() const
{
return &pMaxAbsDevs;
}
//------------------------------------------------------------------------------
// @brief Get the standard deviations at each tree element
//------------------------------------------------------------------------------
const std::vector* DoubleAggregator::getStdDevs() const
{
return &pStdDevs;
}
//------------------------------------------------------------------------------
// Get the geotags at each tree element
//------------------------------------------------------------------------------
const std::vector* DoubleAggregator::getGeoTags() const
{
return &pGeoTags;
}
//------------------------------------------------------------------------------
// @brief Get the end index (excluded) for a given depth level
// @param depth the maximum depth to be reached (-1 for unlimited)
// @return the index of the first element in the vectors being deeper that depth
//------------------------------------------------------------------------------
size_t DoubleAggregator::getEndIndex(int depth) const
{
if (depth < 0 || depth > (int)pDepthLevelsIndexes.size() - 1) {
depth = pDepthLevelsIndexes.size() - 1;
}
return pDepthLevelsIndexes[depth];
};
//------------------------------------------------------------------------------
// @brief Constructor
// @param param Name of the parameter statistics have to be computed for
//------------------------------------------------------------------------------
DoubleAggregator::DoubleAggregator(const char* param):
pParam(param), pView(NULL)
{}
//------------------------------------------------------------------------------
// Destructor
//------------------------------------------------------------------------------
DoubleAggregator::~DoubleAggregator()
{}
//------------------------------------------------------------------------------
// @brief Set the view ordering the statistics. Needs to be set before running
// the aggregator.
// @param view Pointer to the view ordering the statistics
//------------------------------------------------------------------------------
void DoubleAggregator::setView(BaseView* view)
{
pView = view;
}
//------------------------------------------------------------------------------
// Initialize
//------------------------------------------------------------------------------
bool
DoubleAggregator::init(const std::vector& geotags,
const std::vector& depthLevelsIndexes)
{
// Check that the view is defined, this is necessary for the subsequent calls
// to AggregateXXX.
assert(pView);
pGeoTags = geotags;
pDepthLevelsIndexes = depthLevelsIndexes;
return true;
}
//------------------------------------------------------------------------------
// Aggregate leaves
//------------------------------------------------------------------------------
bool
DoubleAggregator::aggregateLeaves(
const std::set& leaves, const size_t& idx)
{
// The following should happen only at the first call
if ((int)idx > (int)pMeans.size() - 1) {
pSums.resize(idx + 1);
pMeans.resize(idx + 1);
pMaxDevs.resize(idx + 1);
pMinDevs.resize(idx + 1);
pMaxAbsDevs.resize(idx + 1);
pStdDevs.resize(idx + 1);
pNb.resize(idx + 1);
}
pNb[idx] = pView->ConsiderCount(false, &leaves);
if (pNb[idx]) {
pSums[idx] = pView->SumDouble(pParam.c_str(), false, &leaves);
pMeans[idx] = pView->AverageDouble(pParam.c_str(), false, &leaves);
pMaxDevs[idx] = (pNb[idx] == 1) ? 0 : pView->MaxDeviation(pParam.c_str(), false,
&leaves);
pMinDevs[idx] = (pNb[idx] == 1) ? 0 : pView->MinDeviation(pParam.c_str(), false,
&leaves);
pStdDevs[idx] = (pNb[idx] == 1) ? 0 : pView->SigmaDouble(pParam.c_str(), false,
&leaves);
pMaxAbsDevs[idx] = (pNb[idx] == 1) ? 0 : std::max(abs(pMaxDevs[idx]),
abs(pMinDevs[idx]));
} else {
pSums[idx] = 0;
pMeans[idx] = 0;
pMaxDevs[idx] = 0;
pMinDevs[idx] = 0;
pStdDevs[idx] = 0;
pMaxAbsDevs[idx] = 0;
}
return true;
}
//------------------------------------------------------------------------------
// Aggregate nodes
//------------------------------------------------------------------------------
bool
DoubleAggregator::aggregateNodes(
const std::map& nodes,
const size_t& idx, bool includeSelf)
{
double pS, pM, pMAD, pSD, pMiD, pMaD;
pS = pM = pMAD = pSD = 0;
pMiD = DBL_MAX;
pMaD = -DBL_MAX;
long long pN = 0;
for (auto it = nodes.begin(); it != nodes.end(); it++) {
size_t i = it->second->mId;
pS += pSums[i];
pN += pNb[i];
}
if (pN) {
pM = pS / pN;
}
for (auto it = nodes.begin(); it != nodes.end(); it++) {
size_t i = it->second->mId;
if (pNb[i]) { // consider this only if there is something there
pMiD = std::min(pMiD, std::min((pMinDevs[i] + pMeans[i]) - pM,
(pMaxDevs[i] + pMeans[i]) - pM));
pMaD = std::max(pMaD, std::max((pMinDevs[i] + pMeans[i]) - pM,
(pMaxDevs[i] + pMeans[i]) - pM));
pSD += pNb[i] * (pStdDevs[i] * pStdDevs[i] + pMeans[i] * pMeans[i]);
}
}
if (pN) {
pSD = sqrt(pSD / pN - pM * pM);
pMAD = std::max(fabs(pMaD), fabs(pMiD));
}
if (includeSelf) {
pS += pSums[idx];
pN += pNb[idx];
if (pN) {
pM = pS / pN;
}
pMiD = std::min(pMiD,
std::min((pMinDevs[idx] + pMeans[idx]) - pM,
(pMaxDevs[idx] + pMeans[idx]) - pM));
pMaD = std::max(pMaD,
std::max((pMinDevs[idx] + pMeans[idx]) - pM,
(pMaxDevs[idx] + pMeans[idx]) - pM));
pSD += pNb[idx] * (pStdDevs[idx] * pStdDevs[idx] + pMeans[idx] * pMeans[idx]);
if (pN) {
pSD = sqrt(pSD / pN - pM * pM);
pMAD = std::max(fabs(pMaD), fabs(pMiD));
}
}
pSums[idx] = pS;
pMeans[idx] = pM;
pMaxAbsDevs[idx] = pMAD;
pStdDevs[idx] = pSD;
pMinDevs[idx] = pMiD;
pMaxDevs[idx] = pMaD;
pNb[idx] = pN;
return true;
}
//------------------------------------------------------------------------------
// @brief Constructor
// @param param Name of the parameter statistics have to be computed for
//------------------------------------------------------------------------------
LongLongAggregator::LongLongAggregator(const char* param):
pParam(param), pView(NULL)
{}
//------------------------------------------------------------------------------
// @brief Destructor
//------------------------------------------------------------------------------
LongLongAggregator::~LongLongAggregator()
{}
//------------------------------------------------------------------------------
// @brief Set the view ordering the statistics. Needs to be set before running
// the aggregator
// @param view Pointer to the view ordering the statistics
//------------------------------------------------------------------------------
void LongLongAggregator::setView(BaseView* view)
{
pView = view;
}
//------------------------------------------------------------------------------
// Initialize
//------------------------------------------------------------------------------
bool
LongLongAggregator::init(const std::vector& geotags,
const std::vector& depthLevelsIndexes)
{
assert(pView);
pGeoTags = geotags;
pDepthLevelsIndexes = depthLevelsIndexes;
return true;
}
//------------------------------------------------------------------------------
//@brief Get the sums at each tree element
//------------------------------------------------------------------------------
const std::vector* LongLongAggregator::getSums() const
{
return &pSums;
}
//------------------------------------------------------------------------------
// @brief Get the geotags at each tree element
//------------------------------------------------------------------------------
const std::vector* LongLongAggregator::getGeoTags() const
{
return &pGeoTags;
}
//------------------------------------------------------------------------------
// @brief Get the end index (excluded) for a given depth level
// @param depth the maximum depth to be reached (-1 for unlimited)
// @return the index of the first element in the vectors being deeper that depth
//------------------------------------------------------------------------------
size_t LongLongAggregator::getEndIndex(int depth) const
{
if (depth < 0 || depth > (int)pDepthLevelsIndexes.size() - 1) {
depth = pDepthLevelsIndexes.size() - 1;
}
return pDepthLevelsIndexes[depth];
};
//------------------------------------------------------------------------------
// Aggregate leaves
//------------------------------------------------------------------------------
bool
LongLongAggregator::aggregateLeaves(
const std::set& leaves,
const size_t& idx)
{
// The following should happen only at the first call
if ((int)idx > (int)pSums.size() - 1) {
pSums.resize(idx + 1);
}
pSums[idx] = 0;
pSums[idx] = pView->SumLongLong(pParam.c_str(), false, &leaves);
return true;
}
//------------------------------------------------------------------------------
// Aggregate nodes
//------------------------------------------------------------------------------
bool
LongLongAggregator::aggregateNodes(
const std::map& nodes, const size_t& idx,
bool includeSelf)
{
long long pS = 0;
for (auto it = nodes.begin(); it != nodes.end(); it++) {
size_t i = it->second->mId;
pS += pSums[i];
}
if (includeSelf) {
pS += pSums[idx];
}
pSums[idx] = pS;
return true;
};
//----------------------------------------------------------------------------
// Constructor
//----------------------------------------------------------------------------
FsSpace::FsSpace(const char* name)
: BaseView(common::SharedHashLocator::makeForSpace(name)),
mFsBalancer(nullptr), mConverter(nullptr), mGroupBalancer(nullptr),
mGeoBalancer(nullptr), mGroupDrainer(nullptr)
{
mName = name;
mType = "spaceview";
if (mName != eos::common::EOS_SPARE_GROUP) {
mFsBalancer.reset(new FsBalancer(name));
mGroupBalancer = new GroupBalancer(name);
mGeoBalancer = new GeoBalancer(name);
mGroupDrainer.reset(new GroupDrainer(name));
mFileInspector.reset(new FileInspector(name));
}
if (!gDisableDefaults) {
// Disable balancing by default
if (GetConfigMember("balancer").empty()) {
SetConfigMember("balancer", "off");
}
// Set deviation treshold
if (GetConfigMember("balancer.threshold").empty()) {
SetConfigMember("balancer.threshold", "20");
}
// Set balancing rate per balancing stream
if (GetConfigMember("balancer.node.rate").empty()) {
SetConfigMember("balancer.node.rate", "25");
}
// Set parallel balancing streams per node
if (GetConfigMember("balancer.node.ntx").empty()) {
SetConfigMember("balancer.node.ntx", "2");
}
// Set drain rate per drain stream
if (GetConfigMember("drain.node.rate").empty()) {
SetConfigMember("drainer.node.rate", "25");
}
// Set parallel draining streams per node
if (GetConfigMember("drainer.node.ntx").empty()) {
SetConfigMember("drainer.node.ntx", "2");
}
// Set the grace period before drain start on opserror to 1 day
if (GetConfigMember("graceperiod").empty()) {
SetConfigMember("graceperiod", "86400");
}
// Set the time for a drain by default to 1 day
if (GetConfigMember("drainperiod").empty()) {
SetConfigMember("drainperiod", "86400");
}
// Set the scan IO rate by default to 100 MB/s
if (GetConfigMember(eos::common::SCAN_IO_RATE_NAME).empty()) {
SetConfigMember(eos::common::SCAN_IO_RATE_NAME, "100");
}
// Set the scan entry interval by default to 1 week
if (GetConfigMember(eos::common::SCAN_ENTRY_INTERVAL_NAME).empty()) {
SetConfigMember(eos::common::SCAN_ENTRY_INTERVAL_NAME, "604800");
}
// Set the rain scan entry interval by default to 4 weeks
if (GetConfigMember(eos::common::SCAN_RAIN_ENTRY_INTERVAL_NAME).empty()) {
SetConfigMember(eos::common::SCAN_RAIN_ENTRY_INTERVAL_NAME, "2419200");
}
// Set the scan disk rerun interval by default to 4 hours
if (GetConfigMember(eos::common::SCAN_DISK_INTERVAL_NAME).empty()) {
SetConfigMember(eos::common::SCAN_DISK_INTERVAL_NAME, "14400");
}
// Set the scan ns rate by default to 50 entries per second
if (GetConfigMember(eos::common::SCAN_NS_RATE_NAME).empty()) {
SetConfigMember(eos::common::SCAN_NS_RATE_NAME, "50");
}
// Set the scan ns rerun interval by default to 3 days
if (GetConfigMember(eos::common::SCAN_NS_INTERVAL_NAME).empty()) {
SetConfigMember(eos::common::SCAN_NS_INTERVAL_NAME, "259200");
}
// Set the fsck refresh interval by default to 2 hours
if (GetConfigMember(eos::common::FSCK_REFRESH_INTERVAL_NAME).empty()) {
SetConfigMember(eos::common::FSCK_REFRESH_INTERVAL_NAME, "7200");
}
// Disable quota by default
if (GetConfigMember("quota").empty()) {
SetConfigMember("quota", "off");
}
// Set the group modulo to 0
if (GetConfigMember("groupmod").empty()) {
SetConfigMember("groupmod", "0");
}
// Set the group size to 0
if (GetConfigMember("groupsize").empty()) {
SetConfigMember("groupsize", "0");
}
// Disable converter by default
if (GetConfigMember("converter").empty()) {
SetConfigMember("converter", "off");
}
// Set two converter streams by default
if (GetConfigMember("converter.ntx").empty()) {
SetConfigMember("converter.ntx", "2");
}
if (GetConfigMember("groupbalancer").empty()) {
SetConfigMember("groupbalancer", "off");
}
// Set the groupbalancer max number of scheduled files by default
if (GetConfigMember("groupbalancer.ntx").empty()) {
SetConfigMember("groupbalancer.ntx", "10");
}
// Set the groupbalancer threshold by default
if (GetConfigMember("groupbalancer.threshold").empty()) {
SetConfigMember("groupbalancer.threshold", "5");
}
// Set the groupbalancer min file size by default
if (GetConfigMember("groupbalancer.min_file_size").empty()) {
SetConfigMember("groupbalancer.min_file_size", "1G");
}
// Set the groupbalancer max file size by default
if (GetConfigMember("groupbalancer.max_file_size").empty()) {
SetConfigMember("groupbalancer.max_file_size", "16G");
}
if (GetConfigMember("groupbalancer.file_attempts").empty()) {
SetConfigMember("groupbalancer.file_attempts", "50");
}
// Set the default group balancer engine
if (GetConfigMember("groupbalancer.engine").empty()) {
SetConfigMember("groupbalancer.engine", "std");
}
if (GetConfigMember("groupbalancer.min_threshold").empty()) {
SetConfigMember("groupbalancer.min_threshold", "0");
}
if (GetConfigMember("groupbalancer.max_threshold").empty()) {
SetConfigMember("groupbalancer.max_threshold", "0");
}
if (GetConfigMember("geobalancer").empty()) {
SetConfigMember("geobalancer", "off");
}
// Set the geobalancer max number of scheduled files by default
if (GetConfigMember("geobalancer.ntx").empty()) {
SetConfigMember("geobalancer.ntx", "10");
}
// Set the geobalancer threshold by default
if (GetConfigMember("geobalancer.threshold").empty()) {
SetConfigMember("geobalancer.threshold", "5");
}
// Disable lru by default
if (GetConfigMember("lru").empty()) {
SetConfigMember("converter", "off");
}
// Set one week lru interval by default
if (GetConfigMember("lru.interval") == "604800") {
SetConfigMember("converter.ntx", "2");
}
// Set the wfe off by default
if (GetConfigMember("wfe").empty()) {
SetConfigMember("wfe", "off");
}
// Set the wfe interval by default
if (GetConfigMember("wfe.interval").empty()) {
SetConfigMember("wfe.interval", "10");
}
// Set the wfe ntx by default
if (GetConfigMember("wfe.ntx").empty()) {
SetConfigMember("wfe.ntx", "1");
}
// Disable the 'file archived' garbage collector by default
if (GetConfigMember("filearchivedgc").empty()) {
SetConfigMember("filearchivedgc", "off");
}
// Set the default delay in seconds between queries from the tape-aware GC
if (GetConfigMember(tgc::TGC_NAME_QRY_PERIOD_SECS).empty()) {
SetConfigMember(tgc::TGC_NAME_QRY_PERIOD_SECS,
std::to_string(tgc::TGC_DEFAULT_QRY_PERIOD_SECS));
}
// Set the default number of available bytes the garbage collector is targetting
if (GetConfigMember(tgc::TGC_NAME_AVAIL_BYTES).empty()) {
SetConfigMember(tgc::TGC_NAME_AVAIL_BYTES,
std::to_string(tgc::TGC_DEFAULT_AVAIL_BYTES));
}
// Set the default of the script used to determine the number of free bytes in a given EOS space
if (GetConfigMember(tgc::TGC_NAME_FREE_BYTES_SCRIPT).empty()) {
SetConfigMember(tgc::TGC_NAME_FREE_BYTES_SCRIPT,
tgc::TGC_DEFAULT_FREE_BYTES_SCRIPT);
}
// Set the default total number of bytes that must be available before
// garbage collection can start
if (GetConfigMember(tgc::TGC_NAME_TOTAL_BYTES).empty()) {
SetConfigMember(tgc::TGC_NAME_TOTAL_BYTES,
std::to_string(tgc::TGC_DEFAULT_TOTAL_BYTES));
}
//Switch off the tape REST API by default
if (GetConfigMember(rest::TAPE_REST_API_SWITCH_ON_OFF).empty()) {
SetConfigMember(rest::TAPE_REST_API_SWITCH_ON_OFF,
"off");
}
//Switch off the tape REST API STAGE resource by default
if (GetConfigMember(rest::TAPE_REST_API_STAGE_SWITCH_ON_OFF).empty()) {
SetConfigMember(rest::TAPE_REST_API_STAGE_SWITCH_ON_OFF,
"off");
}
}
if (mName == std::string("default")) {
// Disable tracker by default
if (GetConfigMember("tracker").empty()) {
SetConfigMember("tracker", "off");
}
}
}
//----------------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------------
FsSpace::~FsSpace()
{
if (mGroupBalancer) {
delete mGroupBalancer;
}
if (mGeoBalancer) {
delete mGeoBalancer;
}
mGroupBalancer = nullptr;
mGeoBalancer = nullptr;
}
//----------------------------------------------------------------------------
// Stop function stopping threads before destruction
//----------------------------------------------------------------------------
void FsSpace::Stop()
{
if (mGroupBalancer) {
mGroupBalancer->Stop();
}
if (mGeoBalancer) {
mGeoBalancer->Stop();
}
}
//------------------------------------------------------------------------------
// Check if quota is enabled for space
//-----------------------------------------------------------------------------
bool FsView::IsQuotaEnabled(const std::string& space)
{
bool is_enabled = false;
std::string key = "quota";
if (mSpaceView.count(space)) {
std::string is_on = mSpaceView[space]->GetConfigMember(key);
is_enabled = (is_on == "on");
}
return is_enabled;
}
std::string FsView::Df(bool monitoring, bool si, bool readable,
std::string dfpath, bool json)
{
std::string nominal;
std::string network;
double networkmib = 0;
size_t i_nominal = 0;
size_t i_used = 0;
double sizefactor = 1.0;
long long int files = 0;
long long int directories = 0;
int use = 0;
std::string instance = gOFS->MgmOfsInstanceName.c_str();
std::string instancepath = gOFS->MgmProcPath.c_str();
instancepath.erase(instancepath.length() - 5); // remove /proc
std::string path = dfpath.empty() ? instancepath : dfpath;
std::string out;
{
eos::common::RWMutexReadLock viewlock(ViewMutex);
for (auto it = mSpaceView.begin(); it != mSpaceView.end(); ++it) {
if (it->first == "spare") {
// don't account spare
continue;
}
nominal = it->second->GetMember("cfg.nominalsize");
if (nominal.length()) {
i_nominal += std::strtoll(nominal.c_str(), 0, 10);
}
}
for (auto it = mNodeView.begin(); it != mNodeView.end(); ++it) {
network = it->second->GetMember("cfg.stat.net.ethratemib");
if (network.length()) {
networkmib += std::strtoll(network.c_str(), 0, 10);
}
}
{
std::shared_ptr cmd;
// Prefetch path
eos::Prefetcher::prefetchItemAndWait(gOFS->eosView, path, false);
eos::common::RWMutexReadLock viewlock(ViewMutex);
eos::common::RWMutexReadLock lock(gOFS->eosViewRWMutex);
try {
cmd = gOFS->eosView->getContainer(path, false);
} catch (eos::MDException& e) {
errno = e.getErrno();
eos_err("msg=\"exception\" ec=%d emsg=\"%s\"", e.getErrno(),
e.getMessage().str().c_str());
}
if (!cmd) {
// fall back to instance path
try {
path = instancepath;
cmd = gOFS->eosView->getContainer(path, false);
} catch (eos::MDException& e) {
errno = e.getErrno();
eos_err("msg=\"exception\" ec=%d emsg=\"%s\"", e.getErrno(),
e.getMessage().str().c_str());
return "";
}
}
i_used = cmd->getTreeSize();
sizefactor = Policy::GetDefaultSizeFactor(cmd);
files = gOFS->eosFileService->getNumFiles();
directories = gOFS->eosDirectoryService->getNumContainers();
}
if (sizefactor) {
i_nominal /= sizefactor;
}
std::string size = readable ?
eos::common::StringConversion::GetReadableSizeString(i_nominal, si ? "iB" : "B",
si ? 1024 : 1000) :
std::to_string(i_nominal);
std::string used = readable ?
eos::common::StringConversion::GetReadableSizeString(i_used, si ? "iB" : "B",
si ? 1024 : 1000) :
std::to_string(i_used);
use = (int)(100.0 * i_used / i_nominal);
if (use > 100) {
use = 100;
}
std::string suse = std::to_string(use) + (monitoring ? std::string("") :
std::string("%"));
std::string sfiles = readable ?
eos::common::StringConversion::GetReadableSizeString(files, "", 1000) :
std::to_string(files);
std::string sdirectories = readable ?
eos::common::StringConversion::GetReadableSizeString(directories, "", 1000) :
std::to_string(directories);
char _perfratio[1024];
double pcr = networkmib / (si ? 1024.0 : 1000.0) / (i_nominal / (si ?
(1024 * 1024 * 1024 * 1024.0) : (1000 * 1000 * 1000 * 1000.0))); // GB/s per TB
if (i_nominal) {
snprintf(_perfratio, sizeof(_perfratio), "%.02f", pcr); // GB/s per TB
} else {
snprintf(_perfratio, sizeof(_perfratio), "0.00");
}
std::string sperf = _perfratio;
char _sizefactor[1024];
snprintf(_sizefactor, sizeof(_sizefactor), "%.02f", sizefactor);
std::string ssizefactor = _sizefactor;
TableFormatterBase table;
TableData table_data;
std::string format_s = (!monitoring ? "s" : "os");
std::string format_ss = (!monitoring ? "-s" : "os");
if (json) {
Json::Value gjson;
gjson["df"]["instance"] = instance;
gjson["df"]["size"] = (Json::Value::UInt64)i_nominal;
gjson["df"]["used"] = (Json::Value::UInt64)i_used;
gjson["df"]["files"] = (Json::Value::UInt64) files;
gjson["df"]["directories"] = (Json::Value::UInt64) directories;
gjson["df"]["performancecapacityratio-gb-tbs"] = pcr;
gjson["df"]["sizefactor"] = sizefactor;
gjson["df"]["path"] = path;
out += SSTR(gjson).c_str();
return out;
} else {
if (!monitoring) {
table.SetHeader({
std::make_tuple("Instance", 14, format_ss),
std::make_tuple("Size", 8, format_s),
std::make_tuple("Used", 8, format_s),
std::make_tuple("Files", 8, format_s),
std::make_tuple("Directories", 15, format_s),
std::make_tuple("PCR GB/TB*s", 12, format_s),
std::make_tuple("Use%", 6, format_s),
std::make_tuple("Vol-x", 7, format_s),
std::make_tuple("Path", 0, format_s)
});
} else {
table.SetHeader({
std::make_tuple("instance", 14, format_ss),
std::make_tuple("size", 8, format_s),
std::make_tuple("used", 8, format_s),
std::make_tuple("files", 8, format_s),
std::make_tuple("directories", 15, format_s),
std::make_tuple("performancecapacityratio", 12, format_s),
std::make_tuple("usage", 6, format_s),
std::make_tuple("spacefactor", 6, format_s),
std::make_tuple("path", 0, format_s)
});
}
table_data.emplace_back();
TableRow& row = table_data.back();
row.emplace_back(instance, format_ss);
row.emplace_back(size, format_s);
row.emplace_back(used, format_s);
row.emplace_back(sfiles, format_s);
row.emplace_back(sdirectories, format_s);
row.emplace_back(sperf, format_s);
row.emplace_back(suse , format_s);
row.emplace_back(ssizefactor , format_s);
row.emplace_back(path, format_s);
table.AddRows(table_data);
out += table.GenerateTable(HEADER).c_str();
return out;
}
}
}
//----------------------------------------------------------------------------
//! Physical bytes available
//----------------------------------------------------------------------------
bool FsView::UnderNominalQuota(const std::string& space, bool isroot)
{
if (isroot) {
return true;
}
time_t now = time(NULL);
{
XrdSysMutexHelper scope_lock(mUsageMutex);
{
// return cached value
auto it = mUsageOk.find(space);
if (it != mUsageOk.end()) {
if (it->second.second > now) {
return it->second.first;
}
}
}
}
{
auto spaceobj = mSpaceView.find(space);
if (spaceobj == mSpaceView.end()) {
// no space, we don't block by nominal quota
return true;
}
// refresh the nominal value
std::string nominal = spaceobj->second->GetMember("cfg.nominalsize");
if (nominal == "???") {
// no setting, quota is fine
return true;
}
uint64_t nominalbytes = strtoul(nominal.c_str(), 0, 10);
uint64_t usedbytes = 0 ;
for (auto fs = mIdView.begin(); fs != mIdView.end(); ++fs) {
if (fs->second->GetSpace() != space) {
// only account the requested space
continue;
}
usedbytes += fs->second->GetUsedbytes();
}
bool usage_ok = false;
if (usedbytes < nominalbytes) {
usage_ok = true;
}
// store the current values
XrdSysMutexHelper scope_lock(mUsageMutex);
mUsageOk[space].first = usage_ok;
mUsageOk[space].second = now + 30; // cache for 30 seconds
return usage_ok;
}
}
//------------------------------------------------------------------------------
// @brief return's the printout format for a given option
// @param option see the implementation for valid options
// @return std::string with format line passed to the printout routine
//------------------------------------------------------------------------------
std::string
FsView::GetNodeFormat(std::string option)
{
std::string format;
if (option == "m") {
// monitoring format
format = "member=type:format=os|";
format += "member=hostport:format=os|";
format += "member=status:format=os|";
format += "member=cfg.status:format=os|";
format += "member=heartbeatdelta:format=os|";
format += "member=nofs:format=ol|";
format += "avg=stat.disk.load:format=of|";
format += "sig=stat.disk.load:format=of|";
format += "sum=stat.disk.readratemb:format=ol|";
format += "sum=stat.disk.writeratemb:format=ol|";
format += "member=cfg.stat.net.ethratemib:format=ol|";
format += "member=cfg.stat.net.inratemib:format=ol|";
format += "member=cfg.stat.net.outratemib:format=ol|";
format += "sum=stat.ropen:format=ol|";
format += "sum=stat.wopen:format=ol|";
format += "sum=stat.statfs.freebytes:format=ol|";
format += "sum=stat.statfs.usedbytes:format=ol|";
format += "sum=stat.statfs.capacity:format=ol|";
format += "sum=stat.usedfiles:format=ol|";
format += "sum=stat.statfs.ffree:format=ol|";
format += "sum=stat.statfs.fused:format=ol|";
format += "sum=stat.statfs.files:format=ol|";
format += "sum=local.balancer.running:format=ol:tag=local.balancer.running|";
format += "member=cfg.stat.sys.vsize:format=ol|";
format += "member=cfg.stat.sys.rss:format=ol|";
format += "member=cfg.stat.sys.threads:format=ol|";
format += "member=cfg.stat.sys.sockets:format=os|";
format += "member=cfg.stat.sys.eos.version:format=os|";
format += "member=cfg.stat.sys.xrootd.version:format=os|";
format += "member=cfg.stat.sys.kernel:format=os|";
format += "member=cfg.stat.sys.eos.start:format=os|";
format += "member=cfg.stat.sys.uptime:format=os|";
format += "sum=stat.disk.iops?configstatus@rw:format=ol|";
format += "sum=stat.disk.bw?configstatus@rw:format=ol|";
format += "member=cfg.stat.geotag:format=os|";
} else if (option == "io") {
// io format
format = "header=1:member=hostport:width=32:format=-sS|";
format += "member=cfg.stat.geotag:width=16:format=s|";
format += "avg=stat.disk.load:width=10:format=f:tag=diskload|";
format += "sum=stat.disk.readratemb:width=12:format=+l:tag=diskr-MB/s|";
format += "sum=stat.disk.writeratemb:width=12:format=+l:tag=diskw-MB/s|";
format += "member=cfg.stat.net.ethratemib:width=10:format=l:tag=eth-MiB/s|";
format += "member=cfg.stat.net.inratemib:width=10:format=l:tag=ethi-MiB|";
format += "member=cfg.stat.net.outratemib:width=10:format=l:tag=etho-MiB|";
format += "sum=stat.ropen:width=6:format=l:tag=ropen|";
format += "sum=stat.wopen:width=6:format=l:tag=wopen|";
format += "sum=stat.statfs.usedbytes:width=12:format=+l:unit=B:tag=used-bytes|";
format += "sum=stat.statfs.capacity:width=12:format=+l:unit=B:tag=max-bytes|";
format += "sum=stat.usedfiles:width=12:format=+l:tag=used-files|";
format += "sum=stat.statfs.files:width=11:format=+l:tag=max-files|";
format += "sum=local.balancer.running:width=10:format=l:tag=bal-shd|";
format += "sum=stat.disk.iops?configstatus@rw:width=6:format=l:tag=iops|";
format += "sum=stat.disk.bw?configstatus@rw:width=9:format=l:unit=MB:tag=bw";
} else if (option == "sys") {
// system format
format = "header=1:member=hostport:width=32:format=-sS|";
format += "member=cfg.stat.geotag:width=16:format=s|";
format += "member=cfg.stat.sys.vsize:width=12:format=+l:tag=vsize|";
format += "member=cfg.stat.sys.rss:width=12:format=+l:tag=rss|";
format += "member=cfg.stat.sys.threads:width=12:format=+l:tag=threads|";
format += "member=cfg.stat.sys.sockets:width=10:format=s:tag=sockets|";
format += "member=cfg.stat.sys.eos.version:width=12:format=s:tag=eos|";
format += "member=cfg.stat.sys.xrootd.version:width=12:format=s:tag=xrootd|";
format += "member=cfg.stat.sys.kernel:width=30:format=s:tag=kernel version|";
format += "member=cfg.stat.sys.eos.start:width=32:format=s:tag=start|";
format += "member=cfg.stat.sys.uptime:width=80:format=s:tag=uptime";
} else if (option == "fsck") {
// filesystem check statistics format
format = "header=1:member=hostport:width=32:format=-sS|";
format += "sum=stat.fsck.mem_n:width=8:format=l:tag=n(mem)|";
format += "sum=stat.fsck.d_sync_n:width=8:format=l:tag=n(disk)|";
format += "sum=stat.fsck.m_sync_n:width=8:format=l:tag=n(mgm)|";
format += "sum=stat.fsck.orphans_n:width=12:format=l:tag=e(orph)|";
format += "sum=stat.fsck.unreg_n:width=12:format=l:tag=e(unreg)|";
format += "sum=stat.fsck.rep_diff_n:width=12:format=l:tag=e(layout)|";
format += "sum=stat.fsck.rep_missing_n:width=12:format=l:tag=e(miss)|";
format += "sum=stat.fsck.d_mem_sz_diff:width=12:format=l:tag=e(disksize)|";
format += "sum=stat.fsck.m_mem_sz_diff:width=12:format=l:tag=e(mgmsize)|";
format += "sum=stat.fsck.d_cx_diff:width=12:format=l:tag=e(disk-cx)|";
format += "sum=stat.fsck.m_cx_diff:width=12:format=l:tag=e(mgm-cx)";
} else if (option == "l") {
// long format
format = "header=1:member=type:width=10:format=-s|";
format += "member=hostport:width=32:format=sS|";
format += "member=cfg.stat.geotag:width=16:format=s|";
format += "member=status:width=10:format=s|";
format += "member=cfg.status:width=12:format=s:tag=activated|";
format += "member=heartbeatdelta:width=16:format=s|";
format += "member=nofs:width=5:format=s|";
format += "sum=local.balancer.running:width=10:format=l:tag=bal-shd|";
} else {
// default format
format = "header=1:member=type:width=10:format=-s|";
format += "member=hostport:width=32:format=sS|";
format += "member=cfg.stat.geotag:width=16:format=s|";
format += "member=status:width=10:format=s|";
format += "member=cfg.status:width=12:format=s:tag=activated|";
format += "member=heartbeatdelta:width=16:format=s|";
format += "member=nofs:width=5:format=s";
}
return format;
}
//------------------------------------------------------------------------------
// @brief return's the printout format for a given option
// @param option see the implementation for valid options
// @return std;:string with format line passed to the printout routine
//------------------------------------------------------------------------------
std::string
FsView::GetFileSystemFormat(std::string option)
{
std::string format;
if (option == "m") {
// monitoring format
format = "key=host:format=os|";
format += "key=port:format=os|";
format += "key=id:format=os|";
format += "key=uuid:format=os|";
format += "key=path:format=os|";
format += "key=schedgroup:format=os|";
format += "key=stat.alias.host:format=oqs|";
format += "key=stat.alias.port:format=oqs|";
format += "key=stat.boot:format=os|";
format += "key=configstatus:format=os|";
format += "key=headroom:format=os|";
format += "key=stat.errc:format=os|";
format += "key=stat.errmsg:format=oqs|";
format += "key=stat.disk.load:format=of|";
format += "key=stat.disk.readratemb:format=ol|";
format += "key=stat.disk.writeratemb:format=ol|";
format += "key=stat.net.ethratemib:format=ol|";
format += "key=stat.net.inratemib:format=ol|";
format += "key=stat.net.outratemib:format=ol|";
format += "key=stat.ropen:format=ol|";
format += "key=stat.wopen:format=ol|";
format += "key=stat.statfs.freebytes:format=ol|";
format += "key=stat.statfs.usedbytes:format=ol|";
format += "key=stat.statfs.capacity:format=ol|";
format += "key=stat.usedfiles:format=ol|";
format += "key=stat.statfs.ffree:format=ol|";
format += "key=stat.statfs.fused:format=ol|";
format += "key=stat.statfs.files:format=ol|";
format += "key=local.drain:format=os|";
format += "key=local.drain.progress:format=ol:tag=progress|";
format += "key=local.drain.files:format=ol|";
format += "key=local.drain.bytesleft:format=ol|";
format += "key=local.drain.failed:format=ol|";
format += "key=local.drain.timeleft:format=ol|";
format += "key=graceperiod:format=ol|";
format += "key=drainperiod:format=ol|";
format += "key=stat.active:format=os|";
format += "key=scaninterval:format=os|";
format += "key=scan_rain_interval:format=os|";
format += "key=scanreruninterval:format=os|";
format += "key=local.balancer.running:format=ol:tag=local.balancer.running|";
format += "key=stat.disk.iops:format=ol|";
format += "key=stat.disk.bw:format=of|";
format += "key=stat.geotag:format=os|";
format += "key=stat.health:format=os|";
format += "key=stat.health.redundancy_factor:format=os|";
format += "key=stat.health.drives_failed:format=os|";
format += "key=stat.health.drives_total:format=os|";
format += "key=stat.health.indicator:format=os";
} else if (option == "io") {
// io format
format = "header=1:key=hostport:width=32:format=-s|";
format += "key=id:width=6:format=s|";
format += "key=schedgroup:width=16:format=s|";
format += "key=stat.geotag:width=16:format=s|";
format += "key=stat.disk.load:width=10:format=f:tag=diskload|";
format += "key=stat.disk.readratemb:width=12:format=f:tag=diskr-MB/s|";
format += "key=stat.disk.writeratemb:width=12:format=f:tag=diskw-MB/s|";
format += "key=stat.net.ethratemib:width=10:format=l:tag=eth-MiB/s|";
format += "key=stat.net.inratemib:width=10:format=l:tag=ethi-MiB|";
format += "key=stat.net.outratemib:width=10:format=l:tag=etho-MiB|";
format += "key=stat.ropen:width=6:format=l:tag=ropen|";
format += "key=stat.wopen:width=6:format=l:tag=wopen|";
format += "compute=usage:width=6:format=f|";
format += "key=stat.statfs.usedbytes:width=12:format=+l:unit=B:tag=used-bytes|";
format += "key=stat.statfs.capacity:width=12:format=+l:unit=B:tag=max-bytes|";
format += "key=stat.usedfiles:width=12:format=+l:tag=used-files|";
format += "key=stat.statfs.files:width=11:format=+l:tag=max-files|";
format += "key=local.balancer.running:width=10:format=l:tag=bal-shd|";
format += "key=stat.disk.iops:width=6:format=l:tag=iops|";
format += "key=stat.disk.bw:width=9:format=l:unit=MB:tag=bw";
} else if (option == "fsck") {
// filesystem check statistics format
format = "header=1:key=hostport:width=32:format=-s|";
format += "key=id:width=6:format=s|";
format += "key=stat.fsck.mem_n:width=8:format=l:tag=n(mem)|";
format += "key=stat.fsck.d_sync_n:width=8:format=l:tag=n(disk)|";
format += "key=stat.fsck.m_sync_n:width=8:format=l:tag=n(mgm)|";
format += "key=stat.fsck.orphans_n:width=12:format=l:tag=e(orph)|";
format += "key=stat.fsck.unreg_n:width=12:format=l:tag=e(unreg)|";
format += "key=stat.fsck.rep_diff_n:width=12:format=l:tag=e(layout)|";
format += "key=stat.fsck.rep_missing_n:width=12:format=l:tag=e(miss)|";
format += "key=stat.fsck.d_mem_sz_diff:width=12:format=l:tag=e(disksize)|";
format += "key=stat.fsck.m_mem_sz_diff:width=12:format=l:tag=e(mgmsize)|";
format += "key=stat.fsck.d_cx_diff:width=12:format=l:tag=e(disk-cx)|";
format += "key=stat.fsck.m_cx_diff:width=12:format=l:tag=e(mgm-cx)";
} else if (option == "d") {
// drain format
format = "header=1:key=host:width=24:format=-S:condition=local.drain=!nodrain|";
format += "key=port:width=4:format=s|";
format += "key=id:width=6:format=s|";
format += "key=path:width=32:format=s|";
format += "key=local.drain:width=12:format=s|";
format += "key=local.drain.progress:width=12:format=l:tag=progress|";
format += "key=local.drain.files:width=12:format=+l:tag=files|";
format += "key=local.drain.bytesleft:width=12:format=+l:tag=bytes-left:unit=B|";
format += "key=local.drain.timeleft:width=11:format=l:tag=timeleft|";
format += "key=local.drain.failed:width=12:format=+l:tag=failed";
} else if (option == "l") {
// long format
format = "header=1:key=host:width=24:format=-S|";
format += "key=port:width=4:format=s|";
format += "key=stat.alias.host:width=24:format=-s|";
format += "key=stat.alias.port:width=4:format=s|";
format += "key=id:width=6:format=s|";
format += "key=uuid:width=36:format=s|";
format += "key=path:width=32:format=s|";
format += "key=schedgroup:width=16:format=s|";
format += "key=headroom:width=10:format=+f|";
format += "key=stat.boot:width=12:format=s|";
format += "key=configstatus:width=14:format=s|";
format += "key=local.drain:width=12:format=s|";
format += "compute=usage:width=6:format=f|";
format += "key=stat.active:width=8:format=s|";
format += "key=scaninterval:width=14:format=s|";
format += "key=scan_rain_interval:width=20:format=s|";
format += "key=stat.health:width=16:format=s|";
format += "key=statuscomment:width=24:format=s";
} else if (option == "e") {
// error format
format = "header=1:key=host:width=24:format=-S:condition=stat.errc=!0|";
format += "key=id:width=6:format=s|";
format += "key=path:width=32:format=s|";
format += "key=stat.boot:width=12:format=s|";
format += "key=configstatus:width=14:format=s|";
format += "key=local.drain:width=12:format=s|";
format += "key=stat.errc:width=3:format=s|";
format += "key=stat.errmsg:width=0:format=s";
} else {
// default format
format = "header=1:key=host:width=24:format=-S|";
format += "key=port:width=4:format=s|";
format += "key=id:width=6:format=s|";
format += "key=path:width=32:format=s|";
format += "key=schedgroup:width=16:format=s|";
format += "key=stat.geotag:width=16:format=s|";
format += "key=stat.boot:width=12:format=s|";
format += "key=configstatus:width=14:format=s|";
format += "key=local.drain:width=12:format=s|";
format += "compute=usage:width=6:format=f|";
format += "key=stat.active:width=8:format=s|";
format += "key=stat.health:width=16:format=s";
}
return format;
}
//------------------------------------------------------------------------------
// @brief return's the printout format for a given option
// @param option see the implementation for valid options
// @return std;:string with format line passed to the printout routine
//------------------------------------------------------------------------------
std::string
FsView::GetSpaceFormat(std::string option)
{
std::string format;
if (option == "m") {
// monitoring format
format = "member=type:format=os|";
format += "member=name:format=os|";
format += "member=cfg.groupsize:format=ol|";
format += "member=cfg.groupmod:format=ol|";
format += "member=nofs:format=ol|";
format += "avg=stat.disk.load:format=of|";
format += "sig=stat.disk.load:format=of|";
format += "sum=stat.disk.readratemb:format=ol|";
format += "sum=stat.disk.writeratemb:format=ol|";
format += "sum=stat.net.ethratemib:format=ol|";
format += "sum=stat.net.inratemib:format=ol|";
format += "sum=stat.net.outratemib:format=ol|";
format += "sum=stat.ropen:format=ol|";
format += "sum=stat.wopen:format=ol|";
format += "sum=stat.statfs.usedbytes:format=ol|";
format += "sum=stat.statfs.freebytes:format=ol|";
format += "sum=stat.statfs.freebytes?configstatus@rw:format=ol|";
format += "sum=stat.statfs.capacity:format=ol|";
format += "sum=stat.usedfiles:format=ol|";
format += "sum=stat.statfs.ffiles:format=ol|";
format += "sum=stat.statfs.files:format=ol|";
format += "geosched=totalspace:format=ol:tag=sched.capacity|";
format += "sum=stat.statfs.capacity?configstatus@rw:format=ol|";
format += "sum=?configstatus@rw:format=ol|";
format += "member=cfg.quota:format=os|";
format += "member=cfg.nominalsize:format=ol|";
format += "member=cfg.balancer:format=os|";
format += "member=cfg.balancer.threshold:format=ol|";
format += "sum=local.balancer.running:format=ol:tag=local.balancer.running|";
format += "sum=stat.disk.iops?configstatus@rw:format=ol|";
format += "sum=stat.disk.bw?configstatus@rw:format=ol";
} else if (option == "io") {
// io format
format = "header=1:member=name:width=10:format=-s|";
format += "avg=stat.geotag:width=32:format=-s|";
format += "avg=stat.disk.load:width=10:format=f:tag=diskload|";
format += "sum=stat.disk.readratemb:width=12:format=+l:tag=diskr-MB/s|";
format += "sum=stat.disk.writeratemb:width=12:format=+l:tag=diskw-MB/s|";
format += "sum=stat.net.ethratemib:width=10:format=l:tag=eth-MiB/s|";
format += "sum=stat.net.inratemib:width=10:format=l:tag=ethi-MiB|";
format += "sum=stat.net.outratemib:width=10:format=l:tag=etho-MiB|";
format += "sum=stat.ropen:width=6:format=l:tag=ropen|";
format += "sum=stat.wopen:width=6:format=l:tag=wopen|";
format += "sum=stat.statfs.usedbytes:width=12:format=+l:unit=B:tag=used-bytes|";
format += "sum=stat.statfs.capacity:width=12:format=+l:unit=B:tag=max-bytes|";
format += "sum=stat.usedfiles:width=12:format=+l:tag=used-files|";
format += "sum=stat.statfs.files:width=11:format=+l:tag=max-files|";
format += "sum=local.balancer.running:width=10:format=l:tag=bal-shd";
} else if (option == "fsck") {
// filesystem check statistics format
format = "header=1:member=name:width=10:format=-s|";
format += "avg=stat.geotag:width=32:format=-s|";
format += "sum=stat.fsck.mem_n:width=8:format=l:tag=n(mem)|";
format += "sum=stat.fsck.d_sync_n:width=8:format=l:tag=n(disk)|";
format += "sum=stat.fsck.m_sync_n:width=8:format=l:tag=n(mgm)|";
format += "sum=stat.fsck.orphans_n:width=12:format=l:tag=e(orph)|";
format += "sum=stat.fsck.unreg_n:width=12:format=l:tag=e(unreg)|";
format += "sum=stat.fsck.rep_diff_n:width=12:format=l:tag=e(layout)|";
format += "sum=stat.fsck.rep_missing_n:width=12:format=l:tag=e(miss)|";
format += "sum=stat.fsck.d_mem_sz_diff:width=12:format=l:tag=e(disksize)|";
format += "sum=stat.fsck.m_mem_sz_diff:width=12:format=l:tag=e(mgmsize)|";
format += "sum=stat.fsck.d_cx_diff:width=12:format=l:tag=e(disk-cx)|";
format += "sum=stat.fsck.m_cx_diff:width=12:format=l:tag=e(mgm-cx)";
} else if (option == "l") {
// long output format
format = "header=1:member=type:width=10:format=-s|";
format += "member=name:width=16:format=s|";
format += "avg=stat.geotag:width=32:format=-s|";
format += "member=cfg.groupsize:width=12:format=s|";
format += "member=cfg.groupmod:width=12:format=s|";
format += "sum=?*@*:width=6:format=l:tag=N(fs)|";
format += "sum=?configstatus@rw:width=9:format=l:tag=N(fs-rw)|";
format += "sum=stat.statfs.usedbytes:width=15:format=+l:unit=B|";
format += "sum=stat.statfs.capacity:width=14:format=+l:unit=B|";
format += "sum=stat.statfs.capacity?configstatus@rw:width=13:format=+l:tag=capacity(rw):unit=B|";
format += "member=cfg.nominalsize:width=13:format=+l:tag=nom.capacity:unit=B|";
format += "member=cfg.quota:width=6:format=s";
} else {
// default format
format = "header=1:member=type:width=10:format=-s|";
format += "member=name:width=16:format=s|";
format += "avg=stat.geotag:width=32:format=-s|";
format += "member=cfg.groupsize:width=12:format=s|";
format += "member=cfg.groupmod:width=12:format=s|";
format += "member=nofs:width=6:format=s:tag=N(fs)|";
format += "sum=?configstatus@rw:width=9:format=l:tag=N(fs-rw)|";
format += "sum=stat.statfs.usedbytes:width=15:format=+l:unit=B|";
format += "sum=stat.statfs.capacity:width=14:format=+l:unit=B|";
format += "sum=stat.statfs.capacity?configstatus@rw:width=13:format=+l:tag=capacity(rw):unit=B|";
format += "member=cfg.nominalsize:width=13:format=+l:tag=nom.capacity:unit=B|";
format += "geosched=totalspace:width=14:format=+l:tag=sched.capacity:unit=B|";
format += "compute=usage:width=6:format=f:tag=usage|";
format += "member=cfg.quota:width=6:format=s|";
format += "member=cfg.balancer:width=10:format=s:tag=balancing|";
format += "member=cfg.balancer.threshold:width=11:format=+l:tag=threshold|";
format += "member=cfg.converter:width=11:format=s:tag=converter|";
format += "member=cfg.converter.ntx:width=6:format=+l:tag=ntx|";
format += "member=cfg.stat.converter.active:width=8:format=+l:tag=active|";
format += "member=cfg.wfe:width=11:format=s:tag=wfe|";
format += "member=cfg.wfe.ntx:width=6:format=+l:tag=ntx|";
format += "member=cfg.stat.wfe.active:width=8:format=+l:tag=active|";
format += "member=cfg.groupbalancer:width=11:format=s:tag=intergroup";
}
return format;
}
//------------------------------------------------------------------------------
// @brief return's the printout format for a given option
// @param option see the implementation for valid options
// @return std;:string with format line passed to the printout routine
//------------------------------------------------------------------------------
std::string
FsView::GetGroupFormat(std::string option)
{
std::string format;
if (option == "m") {
// monitoring format
format = "member=type:format=os|";
format += "member=cfg.status:format=os|";
format += "member=name:format=os|";
format += "member=nofs:format=os|";
format += "avg=stat.disk.load:format=of|";
format += "sig=stat.disk.load:format=of|";
format += "sum=stat.disk.readratemb:format=ol|";
format += "sum=stat.disk.writeratemb:format=ol|";
format += "sum=stat.net.ethratemib:format=ol|";
format += "sum=stat.net.inratemib:format=ol|";
format += "sum=stat.net.outratemib:format=ol|";
format += "sum=stat.ropen:format=ol|";
format += "sum=stat.wopen:format=ol|";
format += "sum=stat.statfs.usedbytes:format=ol|";
format += "sum=stat.statfs.freebytes:format=ol|";
format += "sum=stat.statfs.capacity:format=ol|";
format += "sum=stat.usedfiles:format=ol|";
format += "sum=stat.statfs.ffree:format=ol|";
format += "sum=stat.statfs.files:format=ol|";
format += "maxdev=stat.statfs.filled:format=of|";
format += "avg=stat.statfs.filled:format=of|";
format += "sig=stat.statfs.filled:format=of|";
format += "member=cfg.stat.balancing:format=os:tag=stat.balancing|";
format += "sum=local.balancer.running:format=ol:tag=local.balancer.running|";
} else if (option == "io") {
// io format
format = "header=1:member=name:width=16:format=-s|";
format += "avg=stat.geotag:width=32:format=s|";
format += "avg=stat.disk.load:width=10:format=f:tag=diskload|";
format += "sum=stat.disk.readratemb:width=12:format=+l:tag=diskr-MB/s|";
format += "sum=stat.disk.writeratemb:width=12:format=+l:tag=diskw-MB/s|";
format += "sum=stat.net.ethratemib:width=10:format=l:tag=eth-MiB/s|";
format += "sum=stat.net.inratemib:width=10:format=l:tag=ethi-MiB|";
format += "sum=stat.net.outratemib:width=10:format=l:tag=etho-MiB|";
format += "sum=stat.ropen:width=6:format=l:tag=ropen|";
format += "sum=stat.wopen:width=6:format=l:tag=wopen|";
format += "sum=stat.statfs.usedbytes:width=12:format=+l:unit=B:tag=used-bytes|";
format += "sum=stat.statfs.capacity:width=12:format=+l:unit=B:tag=max-bytes|";
format += "sum=stat.usedfiles:width=12:format=+l:tag=used-files|";
format += "sum=stat.statfs.files:width=11:format=+l:tag=max-files|";
format += "sum=local.balancer.running:width=10:format=l:tag=bal-shd|";
} else if (option == "l") {
// long format
format = "header=1:member=type:width=10:format=-s|";
format += "member=name:width=16:format=s|";
format += "member=cfg.status:width=12:format=s|";
format += "avg=stat.geotag:width=32:format=s|";
format += "key=stat.geotag:width=16:format=s|";
format += "sum=?*@*:width=6:format=l:tag=N(fs)";
} else {
// default format
format = "header=1:member=type:width=10:format=-s|";
format += "member=name:width=16:format=s|";
format += "member=cfg.status:width=12:format=s|";
format += "avg=stat.geotag:width=32:format=s|";
format += "sum=?*@*:width=6:format=l:tag=N(fs)|";
format += "maxdev=stat.statfs.filled:width=12:format=f|";
format += "avg=stat.statfs.filled:width=12:format=f|";
format += "sig=stat.statfs.filled:width=12:format=f|";
format += "member=cfg.stat.balancing:width=10:format=s|";
format += "sum=local.balancer.running:width=10:format=l:tag=bal-shd|";
}
return format;
}
//------------------------------------------------------------------------------
// Register a filesystem object in the filesystem view
//------------------------------------------------------------------------------
bool
FsView::Register(FileSystem* fs, const common::FileSystemCoreParams& coreParams,
bool registerInGeoTreeEngine)
{
if (!fs) {
return false;
}
// Check for queuepath collision
if (mIdView.lookupByQueuePath(coreParams.getQueuePath())) {
eos_err("msg=\"queuepath already registered\" qpath=\"%s\"",
coreParams.getQueuePath().c_str());
return false;
}
// Check if this is already in the view
if (mIdView.lookupByPtr(fs) != 0) {
// This filesystem is already there, this might be an update
eos::common::FileSystem::fsid_t fsid = mIdView.lookupByPtr(fs);
if (fsid != coreParams.getId()) {
// Remove previous mapping
mIdView.eraseById(fsid);
// Setup new two way mapping
mIdView.registerFileSystem(coreParams.getLocator(), coreParams.getId(), fs);
eos_debug("msg=\"updating mapping\" fsid=%lu fs_ptr=%x",
coreParams.getId(), fs);
}
} else {
mIdView.registerFileSystem(coreParams.getLocator(), coreParams.getId(), fs);
eos_debug("msg=\"registering mapping\" fsid=%lu fs_ptr=%x",
coreParams.getId(), fs);
}
// Align view by nodename (= MQ queue) e.g. /eos/:/fst
// Check if we have already a node view
if (mNodeView.count(coreParams.getFSTQueue())) {
mNodeView[coreParams.getFSTQueue()]->insert(coreParams.getId());
eos_debug("msg=\"inserting into node view\" fst_queue=\"%s\" fsid=%lu",
coreParams.getFSTQueue().c_str(), coreParams.getId());
} else {
FsNode* node = new FsNode(coreParams.getFSTQueue().c_str());
mNodeView[coreParams.getFSTQueue()] = node;
node->insert(coreParams.getId());
node->SetNodeConfigDefault();
eos_debug("msg=\"creating new node and inserting\" fst_queue=\"%s\" "
"fsid=%lu", coreParams.getFSTQueue().c_str(),
coreParams.getId());
}
// Align view by groupname
if (mGroupView.count(coreParams.getGroup())) {
mGroupView[coreParams.getGroup()]->insert(coreParams.getId());
eos_debug("msg=\"inserting into group view\" group=%s fsid=%lu",
coreParams.getGroup().c_str(), coreParams.getId());
} else {
FsGroup* group = new FsGroup(coreParams.getGroup().c_str());
mGroupView[coreParams.getGroup()] = group;
group->insert(coreParams.getId());
group->mIndex = coreParams.getGroupLocator().getIndex();
eos_debug("msg=\"creating and inserting into group view\" group=%s "
"fsid=%lu", coreParams.getGroup().c_str(), coreParams.getId());
}
if (registerInGeoTreeEngine &&
!gOFS->mGeoTreeEngine->insertFsIntoGroup(fs, mGroupView[coreParams.getGroup()],
coreParams)) {
// Roll back the changes
if (UnRegister(fs, false)) {
eos_err("msg=\"could not insert insert fs %u into GeoTreeEngine : "
"fs was unregistered and consistency is KEPT between FsView "
"and GeoTreeEngine", coreParams.getId());
} else {
eos_crit("msg=\"could not insert insert fs %u into GeoTreeEngine: "
"fs could not be unregistered and consistency is BROKEN "
"between FsView and GeoTreeEngine\"", coreParams.getId());
}
return false;
}
mSpaceGroupView[coreParams.getSpace()].insert
(mGroupView[coreParams.getGroup()]);
// Align view by spacename
if (mSpaceView.count(coreParams.getSpace())) {
mSpaceView[coreParams.getSpace()]->insert(coreParams.getId());
eos_debug("msg=\"inserting into space view\" space=%s "
"fsid=%lu fs_ptr=%x", coreParams.getSpace().c_str(),
coreParams.getId(), fs);
} else {
FsSpace* space = new FsSpace(coreParams.getSpace().c_str());
std::string grp_sz = "0";
std::string grp_mod = "24";
// Special case of spare space with has size 0 and mod 0
if (coreParams.getSpace() == eos::common::EOS_SPARE_GROUP) {
grp_mod = "0";
}
// Set new space default parameters
if ((!space->SetConfigMember(std::string("groupsize"), grp_sz, true)) ||
(!space->SetConfigMember(std::string("groupmod"), grp_mod, true))) {
eos_err("msg=\"failed setting space default config\" space=%s",
coreParams.getSpace().c_str());
return false;
}
mSpaceView[coreParams.getSpace()] = space;
space->insert(coreParams.getId());
eos_debug("msg=\"creating and inserting into space view\" space=%s "
"fsid=%lu fs_ptr=%x", coreParams.getSpace().c_str(),
coreParams.getId(), fs);
}
fs->applyCoreParams(coreParams);
StoreFsConfig(fs);
// Trigger a refresh for the FST node which for sure exists
mNodeView[coreParams.getFSTQueue()]->SignalRefresh();
return true;
}
//------------------------------------------------------------------------------
// Store the filesystem configuration in the configuration engine
//------------------------------------------------------------------------------
void
FsView::StoreFsConfig(FileSystem* fs, bool save_config)
{
if (fs) {
std::string key, val;
fs->CreateConfig(key, val);
if (gOFS->mMaster->IsMaster() && FsView::gFsView.mConfigEngine &&
!key.empty() && !val.empty()) {
FsView::gFsView.mConfigEngine->SetConfigValue("fs", key.c_str(), val.c_str(),
true, save_config);
}
}
}
//------------------------------------------------------------------------------
// Move a filesystem in to a target group
//------------------------------------------------------------------------------
bool
FsView::MoveGroup(FileSystem* fs, std::string group_name)
{
if (!fs) {
return false;
}
//@todo(esindril) wrap in a nice method as it can be reused
uint32_t grp_index = 0;
const std::string tgt_group = group_name;
std::string tgt_space = group_name;
if (tgt_space != "spare") {
int pos = tgt_space.find('.');
if (pos == std::string::npos) {
eos_static_err("msg=\"unexpected group format\" grp=\"%s",
group_name.c_str());
return false;
}
tgt_space = tgt_space.substr(0, pos);
try {
grp_index = std::stoul(tgt_group.substr(pos + 1));
} catch (...) {}
}
eos_debug("msg=\"move fs to group\" grp=\"%s\" space=\"%s\" fs_ptr=%x",
tgt_group.c_str(), tgt_space.c_str(), fs);
eos::common::FileSystem::fs_snapshot_t snapshot1;
eos::common::FileSystem::fs_snapshot_t snapshot;
if (fs->SnapShotFileSystem(snapshot1)) {
fs->SetString("schedgroup", group_name.c_str());
FsGroup* oldgroup = mGroupView.count(snapshot1.mGroup) ?
mGroupView[snapshot1.mGroup] : NULL;
if (fs->SnapShotFileSystem(snapshot)) {
// Remove from the original space
if (mSpaceView.count(snapshot1.mSpace)) {
FsSpace* space = mSpaceView[snapshot1.mSpace];
space->erase(snapshot1.mId);
if (!space->size() && (space->mName != "spare")) {
eos_debug("msg=\"unregister from space view\" space=\"%s\"",
space->GetMember("name").c_str());
mSpaceView.erase(snapshot1.mSpace);
delete space;
}
}
// Remove from the original group
if (mGroupView.count(snapshot1.mGroup)) {
FsGroup* group = mGroupView[snapshot1.mGroup];
if (!gOFS->mGeoTreeEngine->removeFsFromGroup(fs, group, false)) {
// roll-back
if (mSpaceView.count(snapshot1.mSpace)) {
mSpaceView[snapshot1.mSpace]->insert(snapshot1.mId);
eos_debug("inserting into space view %s<=>%u fs_ptr=%x",
snapshot1.mSpace.c_str(), snapshot1.mId, fs);
} else {
FsSpace* space = new FsSpace(snapshot1.mSpace.c_str());
mSpaceView[snapshot1.mSpace] = space;
space->insert(snapshot1.mId);
eos_debug("creating/inserting into space view %s<=>%u fs_ptr=%x",
snapshot1.mSpace.c_str(), snapshot1.mId, fs);
}
eos_err("could not remove fs %u from GeoTreeEngine : fs was "
"registered back and consistency is KEPT between FsView"
" and GeoTreeEngine", snapshot.mId);
return false;
}
group->erase(snapshot1.mId);
if (!group->size() && (group->mName != "spare")) {
if (mSpaceGroupView.count(snapshot1.mSpace)) {
mSpaceGroupView[snapshot1.mSpace].erase(mGroupView[snapshot1.mGroup]);
}
eos_debug("msg=\"unregister from group view\" group=\"%s\"",
group->GetMember("name").c_str());
mGroupView.erase(snapshot1.mGroup);
delete group;
}
}
// Check if we have already a group view
if (mGroupView.count(tgt_group)) {
mGroupView[tgt_group]->insert(snapshot.mId);
eos_debug("inserting into group view %s<=>%u fs_ptr=%x",
tgt_group.c_str(), snapshot.mId, fs);
} else {
FsGroup* group = new FsGroup(tgt_group.c_str());
mGroupView[tgt_group] = group;
group->insert(snapshot.mId);
group->mIndex = grp_index;
group->SetConfigMember("status", "on");
eos_debug("creating/inserting into group view %s<=>%u",
tgt_group.c_str(), snapshot.mId, fs);
}
if (!gOFS->mGeoTreeEngine->insertFsIntoGroup(fs, mGroupView[group_name],
fs->getCoreParams())) {
if (fs->SetString("schedgroup", group_name.c_str()) &&
UnRegister(fs, false)) {
if (oldgroup && fs->SetString("schedgroup", oldgroup->mName.c_str()) &&
Register(fs, fs->getCoreParams())) {
eos_err("while moving fs, could not insert fs %u in group %s. fs "
"was registered back to group %s and consistency is KEPT "
"between FsView and GeoTreeEngine",
snapshot.mId, mGroupView[group_name]->mName.c_str(),
oldgroup->mName.c_str());
} else {
eos_err("while moving fs, could not insert fs %u in group %s. fs "
"was unregistered and consistency is KEPT between FsView "
"and GeoTreeEngine", snapshot.mId, mGroupView[group_name]->mName.c_str());
}
} else {
eos_crit("while moving fs, could not insert fs %u in group %s. fs "
"could not be unregistered and consistency is BROKEN between "
"FsView and GeoTreeEngine", snapshot.mId,
mGroupView[group_name]->mName.c_str());
}
return false;
}
mSpaceGroupView[tgt_space].insert(mGroupView[tgt_group]);
// Check if we have already a space view
if (mSpaceView.count(tgt_space)) {
mSpaceView[tgt_space]->insert(snapshot.mId);
eos_debug("inserting into space view %s<=>%u %x",
tgt_space.c_str(), snapshot.mId, fs);
} else {
FsSpace* space = new FsSpace(tgt_space.c_str());
mSpaceView[tgt_space] = space;
space->insert(snapshot.mId);
eos_debug("creating/inserting into space view %s<=>%u %x",
tgt_space.c_str(), snapshot.mId, fs);
}
return true;
}
}
return false;
}
//------------------------------------------------------------------------------
// Remove a file system
//------------------------------------------------------------------------------
bool
FsView::UnRegister(FileSystem* fs, bool unreg_from_geo_tree,
bool notify_fst)
{
if (!fs) {
return false;
}
// Delete in the configuration engine
const std::string key = fs->GetQueuePath();
if (gOFS->mMaster->IsMaster() && FsView::gFsView.mConfigEngine) {
FsView::gFsView.mConfigEngine->DeleteConfigValue("fs", key.c_str());
}
eos::common::FileSystem::fs_snapshot_t snapshot;
if (!fs->SnapShotFileSystem(snapshot)) {
eos_err("msg=\"failed to snapshot file system, abort deregistration\" "
" qpath=\"%s\"", key.c_str());
return false;
}
// Remove fs from node view & evt. remove node view
if (mNodeView.count(snapshot.mQueue)) {
FsNode* node = mNodeView[snapshot.mQueue];
node->erase(snapshot.mId);
}
// Remove fs from group view & evt. remove group view
if (mGroupView.count(snapshot.mGroup)) {
FsGroup* group = mGroupView[snapshot.mGroup];
if (unreg_from_geo_tree
&& !gOFS->mGeoTreeEngine->removeFsFromGroup(fs, group, false)) {
if (Register(fs, fs->getCoreParams(), false)) {
eos_err("could not remove fs %u from GeoTreeEngine : fs was "
"registered back and consistency is KEPT between FsView "
"and GeoTreeEngine", snapshot.mId);
} else {
eos_crit("could not remove fs %u from GeoTreeEngine : fs could not "
"be registered back and consistency is BROKEN between "
"FsView and GeoTreeEngine", snapshot.mId);
}
return false;
}
group->erase(snapshot.mId);
eos_debug("msg=\"unregister group %s from group view\"",
group->GetMember("name").c_str());
if (!group->size()) {
mSpaceGroupView[snapshot.mSpace].erase(mGroupView[snapshot.mGroup]);
mGroupView.erase(snapshot.mGroup);
delete group;
}
}
// Remove fs from space view & evt. remove space view
if (mSpaceView.count(snapshot.mSpace)) {
FsSpace* space = mSpaceView[snapshot.mSpace];
space->erase(snapshot.mId);
eos_debug("msg=\"unregister space %s from space view\"",
space->GetMember("name").c_str());
if (!space->size()) {
mSpaceView.erase(snapshot.mSpace);
delete space;
}
}
// Remove view by filesystem object and filesystem id
if (!mIdView.eraseByPtr(fs)) {
eos_static_crit("msg=\"no such fs to unregister\" fsid=%lu fs_ptr=%x",
snapshot.mId, fs);
}
// Remove mapping
RemoveMapping(snapshot.mId, snapshot.mUuid);
// Notify the FST to delete the fs object from local maps
if (notify_fst) {
fs->DeleteSharedHash();
// Eventually delete the node
if (mNodeView.count(snapshot.mQueue)) {
FsNode* node = mNodeView[snapshot.mQueue];
node->SignalRefresh();
if (node->size() == 0) {
eos_static_debug("msg=\"unregister node %s from node view\"",
node->GetMember("name").c_str());
mNodeView.erase(snapshot.mQueue);
common::SharedHashLocator nodeLocator =
common::SharedHashLocator::makeForNode(snapshot.mQueue);
if (!mq::SharedHashWrapper::deleteHash(gOFS->mMessagingRealm.get(),
nodeLocator)) {
eos_static_err("msg=\"failed to delete shared hash\" queue=\"%s\"",
snapshot.mQueue.c_str());
}
delete node;
}
}
}
delete fs;
return true;
}
//------------------------------------------------------------------------------
// Checks if a node has already a filesystem registered
//------------------------------------------------------------------------------
bool
FsView::ExistsQueue(std::string queue, std::string queuepath)
{
if (mNodeView.count(queue)) {
// Loop over all attached filesystems and compare the queue path
for (auto it = mNodeView[queue]->begin(); it != mNodeView[queue]->end(); ++it) {
FileSystem* fs = FsView::gFsView.mIdView.lookupByID(*it);
if (fs && (fs->GetQueuePath() == queuepath)) {
// This queuepath exists already, we cannot register
return true;
}
}
}
return false;
}
//------------------------------------------------------------------------------
// Add view by nodename (= MQ queue) e.g. /eos/:/fst
//------------------------------------------------------------------------------
bool
FsView::RegisterNode(const char* nodename)
{
std::string nodequeue = nodename;
if (mNodeView.count(nodequeue)) {
eos_debug("msg=\"node already exists\" info=\"%s\"", nodequeue.c_str());
return false;
} else {
FsNode* node = new FsNode(nodequeue.c_str());
mNodeView[nodequeue] = node;
node->SetNodeConfigDefault();
eos_debug("msg=\"creating node\" info=\"%s\"", nodequeue.c_str());
return true;
}
}
//------------------------------------------------------------------------------
// Remove view by nodename (= MQ queue) e.g. /eos/:/fst - we have
// to remove all the connected filesystems via UnRegister(fs) to keep the
// space, group and node views in sync.
//------------------------------------------------------------------------------
bool
FsView::UnRegisterNode(const char* nodename)
{
bool retc = true;
bool has_fs = false;
if (mNodeView.count(nodename)) {
while (mNodeView.count(nodename) &&
(mNodeView[nodename]->begin() != mNodeView[nodename]->end())) {
eos::common::FileSystem::fsid_t fsid = *(mNodeView[nodename]->begin());
FileSystem* fs = mIdView.lookupByID(fsid);
if (fs) {
has_fs = true;
eos_static_debug("msg=\"unregister filesystem\" fsid=%llu node=%s "
"queue=%s", (unsigned long long) fsid, nodename,
fs->GetQueue().c_str());
retc |= UnRegister(fs);
}
}
// Explicitly remove the node from the view here because no fs was removed
if (!has_fs) {
delete mNodeView[nodename];
retc = (mNodeView.erase(nodename) ? true : false);
}
}
return retc;
}
//------------------------------------------------------------------------------
// Add view by spacename (= MQ queue) e.g. /eos/:/fst
//------------------------------------------------------------------------------
bool
FsView::RegisterSpace(const char* spacename)
{
std::string spacequeue = spacename;
if (mSpaceView.count(spacequeue)) {
eos_debug("space is existing");
return false;
} else {
FsSpace* space = new FsSpace(spacequeue.c_str());
mSpaceView[spacequeue] = space;
eos_debug("creating space view %s", spacequeue.c_str());
return true;
}
}
//------------------------------------------------------------------------------
// Remove view by spacename (= MQ queue) e.g. /eos/:/fst
//------------------------------------------------------------------------------
bool
FsView::UnRegisterSpace(const char* spacename)
{
// We have to remove all the connected filesystems via UnRegister(fs) to keep
// space, group and fs views in sync
bool retc = true;
bool has_fs = false;
if (mSpaceView.count(spacename)) {
while (mSpaceView.count(spacename) && mSpaceView[spacename]->size()) {
eos::common::FileSystem::fsid_t fsid = *(mSpaceView[spacename]->begin());
FileSystem* fs = mIdView.lookupByID(fsid);
if (fs) {
has_fs = true;
eos_static_debug("msg=\"unregister filesystem \"fsid=%llu space=%s "
"queue=%s", (unsigned long long) fsid, spacename,
fs->GetQueue().c_str());
retc |= UnRegister(fs);
}
if (mSpaceView.count(spacename) == 0) {
return true;
}
}
if (!has_fs) {
// We have to explicitly remove the space from the view here because no
// fs was removed
if (mSpaceView.count(spacename)) {
delete mSpaceView[spacename];
retc = (mSpaceView.erase(spacename) ? true : false);
}
}
}
return retc;
}
//------------------------------------------------------------------------------
// Add view by groupname e.g. default or default.0
//------------------------------------------------------------------------------
bool
FsView::RegisterGroup(const char* groupname)
{
std::string groupqueue = groupname;
if (mGroupView.count(groupqueue)) {
eos_debug("group is existing");
return false;
} else {
FsGroup* group = new FsGroup(groupqueue.c_str());
mGroupView[groupqueue] = group;
eos_debug("creating group view %s", groupqueue.c_str());
return true;
}
}
//------------------------------------------------------------------------------
// Remove view by groupname e.g. default or default.0
//------------------------------------------------------------------------------
bool
FsView::UnRegisterGroup(const char* groupname)
{
// We have to remove all the connected filesystems via UnRegister(fs) to keep
// the group view in sync.
bool retc = true;
bool has_fs = false;
if (mGroupView.count(groupname)) {
while (mGroupView.count(groupname) &&
(mGroupView[groupname]->begin() != mGroupView[groupname]->end())) {
eos::common::FileSystem::fsid_t fsid = *(mGroupView[groupname]->begin());
FileSystem* fs = mIdView.lookupByID(fsid);
if (fs) {
has_fs = true;
eos_static_debug("msg=\"unregister filesystem fsid=%llu group=%s "
"queue=%s", (unsigned long long) fsid, groupname,
fs->GetQueue().c_str());
retc |= UnRegister(fs);
}
}
if (!has_fs) {
std::string sgroupname = groupname;
std::string spacename = "";
std::string index = "";
// remove the direct group reference here
if (mSpaceGroupView.count(spacename)) {
mSpaceGroupView[spacename].erase(mGroupView[groupname]);
}
// We have to explicitly remove the group from the view here because no
// fs was removed
delete mGroupView[groupname];
retc = (mGroupView.erase(groupname) ? true : false);
eos::common::StringConversion::SplitByPoint(groupname, spacename, index);
}
}
return retc;
}
//------------------------------------------------------------------------------
// Remove all filesystems by erasing all spaces
//------------------------------------------------------------------------------
void
FsView::Reset()
{
{
eos::common::RWMutexReadLock viewlock(ViewMutex);
// Stop all the threads while taking only the read lock
for (auto it = mSpaceView.begin(); it != mSpaceView.end(); ++it) {
it->second->Stop();
}
}
eos::common::RWMutexWriteLock viewlock(ViewMutex);
while (mSpaceView.size()) {
std::string name = mSpaceView.begin()->first;
UnRegisterSpace(name.c_str());
}
// Remove all mappings
mFilesystemMapper.clear();
// Although this shouldn't be necessary, better run an additional cleanup
mSpaceView.clear();
mGroupView.clear();
mNodeView.clear();
mIdView.clear();
}
//------------------------------------------------------------------------------
// Clear all maps and delete all filesystem/group/space objects
//------------------------------------------------------------------------------
void
FsView::Clear()
{
{
eos::common::RWMutexReadLock rd_view_lock(ViewMutex);
// Stop all the threads while taking only thre read lock
for (auto it = mSpaceView.begin(); it != mSpaceView.end(); it++) {
it->second->Stop();
}
}
eos::common::RWMutexWriteLock wr_view_lock(ViewMutex);
while (mSpaceView.size()) {
UnRegisterSpace(mSpaceView.begin()->first.c_str());
}
mFilesystemMapper.clear();
mSpaceView.clear();
mGroupView.clear();
mNodeView.clear();
mIdView.clear();
}
//------------------------------------------------------------------------------
// Find a filesystem specifying a queuepath
//------------------------------------------------------------------------------
FileSystem*
FsView::FindByQueuePath(std::string& queuepath)
{
// Needs an external ViewMutex lock !!!!
for (auto it = mIdView.begin(); it != mIdView.end(); ++it) {
if (it->second && it->second->GetQueuePath() == queuepath) {
return it->second;
}
}
return 0;
}
//------------------------------------------------------------------------------
// Set global config
//------------------------------------------------------------------------------
bool
FsView::SetGlobalConfig(const std::string& key, const std::string& value)
{
if (gOFS != NULL) {
std::string ckey = SSTR(common::InstanceName::getGlobalMgmConfigQueue()
<< "#" << key);
if (value.empty()) {
mq::SharedHashWrapper::makeGlobalMgmHash(gOFS->mMessagingRealm.get()).del(key);
} else {
mq::SharedHashWrapper::makeGlobalMgmHash(gOFS->mMessagingRealm.get()).set(key,
value);
}
if (FsView::gFsView.mConfigEngine) {
if (value.empty()) {
FsView::gFsView.mConfigEngine->DeleteConfigValue("global", ckey.c_str());
} else {
FsView::gFsView.mConfigEngine->SetConfigValue("global", ckey.c_str(),
value.c_str());
}
}
}
return true;
}
//------------------------------------------------------------------------------
// Get global config
//------------------------------------------------------------------------------
std::string
FsView::GetGlobalConfig(const std::string& key)
{
if (gOFS != NULL) {
return mq::SharedHashWrapper::makeGlobalMgmHash(
gOFS->mMessagingRealm.get()).get(key);
}
return "";
}
//------------------------------------------------------------------------------
// Heart beat checker set's filesystem to down if the heart beat is missing
//------------------------------------------------------------------------------
void
FsView::HeartBeatCheck(ThreadAssistant& assistant) noexcept
{
while (!assistant.terminationRequested()) {
assistant.wait_for(std::chrono::seconds(10));
eos::common::RWMutexReadLock fs_rd_lock(ViewMutex);
// Loop over all the nodes and update their status
for (auto it_node = mNodeView.begin();
it_node != mNodeView.end(); ++it_node) {
if (it_node->second == nullptr) {
continue;
}
auto* node = it_node->second;
if (node->HasHeartbeat()) {
if (node->GetActiveStatus() != eos::common::ActiveStatus::kOnline) {
node->SetActiveStatus(eos::common::ActiveStatus::kOnline);
}
// Loop over all files sysytems in the current node and update status
for (auto it_fsid = node->begin(); it_fsid != node->end(); ++it_fsid) {
FileSystem* fs = FsView::gFsView.mIdView.lookupByID(*it_fsid);
if (fs == nullptr) {
continue;
}
std::string group = fs->GetString("schedgroup");
bool is_group_active = false;
if (auto group_key = FsView::gFsView.mGroupView.find(group);
group_key != FsView::gFsView.mGroupView.end()) {
auto group_status = group_key->second->GetConfigMember("status");
is_group_active = group_status == "on" || group_status == "drain";
}
if ((node->GetConfigMember("status") == "on") &&
is_group_active) {
ssize_t max_ropen = fs->GetLongLong("max.ropen");
ssize_t max_wopen = fs->GetLongLong("max.wopen");
bool overloaded = ((max_ropen &&
(max_ropen <= fs->GetLongLong("stat.ropen"))) ||
(max_wopen && (max_wopen <= fs->GetLongLong("stat.wopen"))));
if (!overloaded) {
if (fs->GetActiveStatus() != eos::common::ActiveStatus::kOnline) {
fs->SetActiveStatus(eos::common::ActiveStatus::kOnline);
}
} else {
if (fs->GetActiveStatus() != eos::common::ActiveStatus::kOverload) {
fs->SetActiveStatus(eos::common::ActiveStatus::kOverload);
}
}
} else {
if (fs->GetActiveStatus() != eos::common::ActiveStatus::kOffline) {
fs->SetActiveStatus(eos::common::ActiveStatus::kOffline);
}
}
}
} else {
if (node->GetActiveStatus() != eos::common::ActiveStatus::kOffline) {
node->SetActiveStatus(eos::common::ActiveStatus::kOffline);
}
// Loop over all files sysytems in the current node and update status
for (auto it_fsid = node->begin(); it_fsid != node->end(); ++it_fsid) {
FileSystem* fs = FsView::gFsView.mIdView.lookupByID(*it_fsid);
if (fs == nullptr) {
continue;
}
if (fs->GetActiveStatus() != eos::common::ActiveStatus::kOffline) {
fs->SetActiveStatus(eos::common::ActiveStatus::kOffline);
}
}
}
}
}
}
//------------------------------------------------------------------------------
// Re-apply drain status for file systems to re-trigger draining
//------------------------------------------------------------------------------
void
FsView::ReapplyDrainStatus()
{
eos::common::RWMutexReadLock fs_rd_lock(ViewMutex);
for (auto it = mIdView.begin(); it != mIdView.end(); ++it) {
eos::common::ConfigStatus cs = it->second->GetConfigStatus();
if ((cs == eos::common::ConfigStatus::kDrain) ||
(cs == eos::common::ConfigStatus::kDrainDead) ||
(cs == eos::common::ConfigStatus::kGroupDrain)) {
it->second->SetConfigStatus(cs);
gOFS->mFsScheduler->setDiskStatus(mIdView.lookupSpaceByID(it->first),
it->first, cs);
}
}
}
//------------------------------------------------------------------------------
// Return a view member variable
//------------------------------------------------------------------------------
std::string
BaseView::GetMember(const std::string& member) const
{
if (member == "name") {
return mName;
}
if (member == "type") {
return mType;
}
if (member == "nofs") {
char line[1024];
snprintf(line, sizeof(line) - 1, "%llu", (unsigned long long) size());
return std::string(line);
}
if (member == "heartbeat") {
char line[1024];
snprintf(line, sizeof(line) - 1, "%llu", (unsigned long long) mHeartBeat);
return std::string(line);
}
if (member == "heartbeatdelta") {
char line[1024];
if (labs(time(NULL) - mHeartBeat) > 86400) {
snprintf(line, sizeof(line) - 1, "~");
} else {
snprintf(line, sizeof(line) - 1, "%llu",
(unsigned long long)(time(NULL) - mHeartBeat));
}
return std::string(line);
}
if (member == "status") {
return mStatus;
}
// Check for global config value
const std::string tag = "cfg.";
if (member.find(tag) == 0) {
std::string cfg_member = member;
std::string val = "???";
cfg_member.erase(0, tag.length());
std::string value = GetConfigMember(cfg_member);
if (!value.empty()) {
val = value;
}
// It's otherwise hard to get the default into place
if ((member == "cfg.stat.balancing") && ((val == "") || (val == "???"))) {
val = "idle";
}
if ((member == "cfg.status") && val.empty()) {
val = "off";
}
return val;
}
return "";
}
//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
FsNode::FsNode(const char* name) : BaseView(
common::SharedHashLocator::makeForNode(name))
{
mName = name;
mType = "nodesview";
SetConfigMember("stat.hostport", GetMember("hostport"), false);
eos_static_info("msg=\"FsNode constructor\" name=\"%s\" ptr=%p",
mName.c_str(), this);
mSubscription = mq::SharedHashWrapper(gOFS->mMessagingRealm.get(),
mLocator).subscribe();
if (mSubscription) {
using namespace std::placeholders;
mSubscription->attachCallback(std::bind(&FsNode::ProcessUpdateCb, this, _1));
}
}
//------------------------------------------------------------------------------
// Destructor
//------------------------------------------------------------------------------
FsNode::~FsNode()
{
if (mSubscription) {
mSubscription->detachCallback();
}
eos_static_info("msg=\"FsNode destructor\" name=\"%s\" ptr=%p",
mName.c_str(), this);
}
//------------------------------------------------------------------------------
// Callback to process update for the shared hash
//------------------------------------------------------------------------------
void
FsNode::ProcessUpdateCb(qclient::SharedHashUpdate&& upd)
{
if (eos::common::FST_HEARTBEAT_KEY == upd.key) {
try {
SetHeartBeat(std::stoull(upd.value));
} catch (...) {
eos_static_err("msg=\"skip heartbeat update due to conversion failure\" "
"value=\"%s\"", upd.value.c_str());
}
} else {
eos_static_debug("msg=\"ignore node shared hash update\" key=\"%s\" "
"value=\"%s\"", upd.key.c_str(), upd.value.c_str());
}
}
//------------------------------------------------------------------------------
// Set refresh marker for the FSTs
//------------------------------------------------------------------------------
void
FsNode::SignalRefresh()
{
const auto ts_ms = std::chrono::duration_cast
(std::chrono::steady_clock::now().time_since_epoch()).count();
SetConfigMember(msRefreshTag, std::to_string(ts_ms), true);
}
//------------------------------------------------------------------------------
// Set the configuration default values for a node
//------------------------------------------------------------------------------
void
FsNode::SetNodeConfigDefault()
{
eos_static_info("msg=\"set defaults\" node=%s", mName.c_str());
// Define the manager ID
if (!(GetConfigMember("manager").length())) {
SetConfigMember("manager", gOFS->mMaster->GetMasterId(), true);
}
// @todo(esindril) to be remove in 5.2.1
// By default set 2 balancing streams per node
if (!(GetConfigMember("stat.balance.ntx").length())) {
SetConfigMember("stat.balance.ntx", "2", true);
}
// @todo(esindril) to be remove in 5.2.1
// By default set 25 MB/s stream balancing rate
if (!(GetConfigMember("stat.balance.rate").length())) {
SetConfigMember("stat.balance.rate", "25", true);
}
// Set the default sym key from the sym key store
eos::common::SymKey* symkey = eos::common::gSymKeyStore.GetCurrentKey();
// Store the sym key as configuration member
if (!(GetConfigMember("symkey").length())) {
SetConfigMember("symkey", symkey->GetKey64(), true);
}
// Set the default debug level to notice
if (!(GetConfigMember("debug.level").length())) {
SetConfigMember("debug.level", "info", true);
}
// Set by default the MGM domain e.g. same geographical position as the MGM
if (!(GetConfigMember("domain").length())) {
SetConfigMember("domain", "MGM", true);
}
}
//------------------------------------------------------------------------------
// Get member
//------------------------------------------------------------------------------
std::string
FsNode::GetMember(const std::string& member) const
{
if (member == "hostport") {
std::string hostport =
eos::common::StringConversion::GetStringHostPortFromQueue(mName.c_str());
return hostport;
} else {
return BaseView::GetMember(member);
}
}
//------------------------------------------------------------------------------
// Get node active status
//------------------------------------------------------------------------------
eos::common::ActiveStatus
FsNode::GetActiveStatus()
{
if (GetStatus() == "online") {
return eos::common::ActiveStatus::kOnline;
} else {
return eos::common::ActiveStatus::kOffline;
}
}
//------------------------------------------------------------------------------
// Set node active status
//------------------------------------------------------------------------------
bool
FsNode::SetActiveStatus(eos::common::ActiveStatus active)
{
if (active == eos::common::ActiveStatus::kOnline) {
SetStatus("online");
return SetConfigMember("stat.active", "online", true);
} else {
SetStatus("offline");
return SetConfigMember("stat.active", "offline", true);
}
}
//------------------------------------------------------------------------------
// Check if node has a recent enough heartbeat ie. less then 60 seconds
//------------------------------------------------------------------------------
bool
FsNode::HasHeartbeat() const
{
if (mHeartBeat == 0) {
return false;
}
return isHeartbeatRecent(mHeartBeat.load());
}
//------------------------------------------------------------------------------
// Set a configuration member variable (stored in the config engine)
// If 'isstatus'=true we just store the value in the shared hash but don't flush
// it into the configuration engine.
// => is used to set status variables on config queues (baseview queues)
//------------------------------------------------------------------------------
bool
BaseView::SetConfigMember(std::string key, std::string value,
bool isstatus)
{
bool success = mq::SharedHashWrapper(gOFS->mMessagingRealm.get(),
mLocator).set(key, value);
// Register in the configuration engine
if (gOFS->mMaster->IsMaster() && (!isstatus) && FsView::gFsView.mConfigEngine) {
std::string node_cfg_name = mLocator.getConfigQueue();
node_cfg_name += "#";
node_cfg_name += key;
std::string confval = value;
FsView::gFsView.mConfigEngine->SetConfigValue("global", node_cfg_name.c_str(),
confval.c_str());
}
return success;
}
//------------------------------------------------------------------------------
// Get a configuration member variable (stored in the config engine)
//------------------------------------------------------------------------------
std::string
BaseView::GetConfigMember(std::string key) const
{
return mq::SharedHashWrapper(gOFS->mMessagingRealm.get(), mLocator).get(key);
}
//------------------------------------------------------------------------------
// Get a list of configuration member variables from config engine
//------------------------------------------------------------------------------
bool
BaseView::GetConfigMembers(const std::vector& keys,
std::map& out) const
{
return mq::SharedHashWrapper(gOFS->mMessagingRealm.get(), mLocator).get(keys,
out);
}
//------------------------------------------------------------------------------
// Delete a configuration member variable (stored in the config engine)
//------------------------------------------------------------------------------
bool
BaseView::DeleteConfigMember(std::string key) const
{
bool deleted = mq::SharedHashWrapper(gOFS->mMessagingRealm.get(),
mLocator).del(key);
// Delete in the configuration engine
if (gOFS->mMaster->IsMaster() && FsView::gFsView.mConfigEngine) {
std::string node_cfg_name = mLocator.getConfigQueue();
node_cfg_name += "#";
node_cfg_name += key;
FsView::gFsView.mConfigEngine->DeleteConfigValue("global",
node_cfg_name.c_str());
}
return deleted;
}
//------------------------------------------------------------------------------
// GetConfigKeys
//------------------------------------------------------------------------------
bool
BaseView::GetConfigKeys(std::vector& keys)
{
return mq::SharedHashWrapper(gOFS->mMessagingRealm.get(),
mLocator).getKeys(keys);
}
//------------------------------------------------------------------------------
// Class ConfigResetMonitor
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
ConfigResetMonitor::ConfigResetMonitor():
mOrigConfEngine(nullptr)
{
std::swap(mOrigConfEngine, FsView::gFsView.mConfigEngine);
}
//------------------------------------------------------------------------------
// Destructor
//------------------------------------------------------------------------------
ConfigResetMonitor::~ConfigResetMonitor()
{
if (mOrigConfEngine == nullptr) {
FsView::gFsView.mConfigEngine = gOFS->ConfEngine;
} else {
std::swap(FsView::gFsView.mConfigEngine, mOrigConfEngine);
}
}
//------------------------------------------------------------------------------
// Class FsView
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Add FsChangeListener to all the existing file systems
//------------------------------------------------------------------------------
void
FsView::AddFsChangeListener(std::shared_ptr fs_lst,
const std::set& interests)
{
eos::common::RWMutexReadLock rd_lock(FsView::gFsView.ViewMutex);
for (const auto& elem : FsView::gFsView.mIdView) {
FileSystem* fs = elem.second;
fs->AttachFsListener(fs_lst, interests);
}
}
//------------------------------------------------------------------------------
// Creates a new filesystem id based on a uuid
//------------------------------------------------------------------------------
eos::common::FileSystem::fsid_t
FsView::CreateMapping(std::string fsuuid)
{
return mFilesystemMapper.allocate(fsuuid);
}
//------------------------------------------------------------------------------
// Adds a fsid=uuid pair to the mapping
//------------------------------------------------------------------------------
bool
FsView::ProvideMapping(std::string fsuuid, eos::common::FileSystem::fsid_t fsid)
{
return mFilesystemMapper.injectMapping(fsid, fsuuid);
}
//------------------------------------------------------------------------------
// Return a fsid for a uuid
//------------------------------------------------------------------------------
eos::common::FileSystem::fsid_t
FsView::GetMapping(std::string fsuuid)
{
return mFilesystemMapper.lookup(fsuuid);
}
//------------------------------------------------------------------------------
// Removes a mapping entry by fsid
//------------------------------------------------------------------------------
bool
FsView::RemoveMapping(eos::common::FileSystem::fsid_t fsid)
{
return mFilesystemMapper.remove(fsid);
}
//------------------------------------------------------------------------------
// Removes a mapping entry by providing fsid + uuid
//------------------------------------------------------------------------------
bool
FsView::RemoveMapping(eos::common::FileSystem::fsid_t fsid, std::string fsuuid)
{
return mFilesystemMapper.remove(fsid) | mFilesystemMapper.remove(fsuuid);
}
//------------------------------------------------------------------------------
// Print space information
//------------------------------------------------------------------------------
void
FsView::PrintSpaces(std::string& out, const std::string& table_format,
const std::string& table_mq_format, unsigned int outdepth,
const char* selection, const std::string& filter, const bool dont_color)
{
std::vector selections;
std::string selected = selection ? selection : "";
if (selection) {
eos::common::StringConversion::Tokenize(selected, selections, ",");
}
TableFormatterBase table(dont_color);
for (auto it = mSpaceView.begin(); it != mSpaceView.end(); ++it) {
it->second->Print(table, table_format, table_mq_format, outdepth, filter,
dont_color);
}
out = table.GenerateTable(HEADER, selections);
}
//----------------------------------------------------------------------------
// Print group information
//----------------------------------------------------------------------------
void
FsView::PrintGroups(std::string& out, const std::string& table_format,
const std::string& table_mq_format, unsigned int outdepth,
const char* selection, const bool dont_color)
{
std::vector selections;
std::string selected = selection ? selection : "";
if (selection) {
eos::common::StringConversion::Tokenize(selected, selections, ",");
}
TableFormatterBase table(dont_color);
for (auto it = mGroupView.begin(); it != mGroupView.end(); ++it) {
it->second->Print(table, table_format, table_mq_format, outdepth,
std::string(""), dont_color);
}
out = table.GenerateTable(HEADER, selections);
}
//------------------------------------------------------------------------------
// Print node information
//------------------------------------------------------------------------------
void
FsView::PrintNodes(std::string& out, const std::string& table_format,
const std::string& table_mq_format, unsigned int outdepth,
const char* selection, const bool dont_color)
{
std::vector selections;
std::string selected = selection ? selection : "";
if (selection) {
eos::common::StringConversion::Tokenize(selected, selections, ",");
}
TableFormatterBase table(dont_color);
for (auto it = mNodeView.begin(); it != mNodeView.end(); ++it) {
it->second->Print(table, table_format, table_mq_format, outdepth,
std::string(""), dont_color);
}
out = table.GenerateTable(HEADER, selections);
}
//------------------------------------------------------------------------------
// Converts a config engine definition for a filesystem into the FsView
// representation.
// @note This method needs to be called with the ViewMutex locked for write
//------------------------------------------------------------------------------
bool
FsView::ApplyFsConfig(const char* inkey, const std::string& val,
bool first_unregister)
{
std::map configmap;
if (!common::ConfigParsing::parseFilesystemConfig(val, configmap)) {
eos_err("msg=\"failed parsing fs config entry\" data=\"%s\"",
val.c_str());
return false;
}
common::FileSystemLocator locator;
if (!common::FileSystemLocator::fromQueuePath(configmap["queuepath"],
locator)) {
eos_crit("msg=\"failed parsing queuepath: %s", configmap["queuepath"].c_str());
return false;
}
auto it = configmap.find("id");
if (it == configmap.end()) {
eos_static_err("msg=\"missing id from fs config entry\" value=\"%s\"",
val.c_str());
return false;
}
FileSystem::fsid_t fsid = common::FileSystem::ConvertToFsid(it->second);
if (fsid == 0ul) {
eos_static_err("msg=\"no such fsid 0\" value=\"%s\"", it->second.c_str());
return false;
}
it = configmap.find("uuid");
if (it == configmap.end()) {
eos_static_err("msg=\"missing uuid from fs config entry\" value=\"%s\"",
val.c_str());
return false;
}
const std::string uuid = it->second;
FileSystem* fs = FsView::gFsView.mIdView.lookupByID(fsid);
if (first_unregister && fs) {
if (!UnRegister(fs)) {
eos_static_warning("msg=\"failed to unregister file system\" fsid=%lu",
fsid);
}
fs = nullptr;
}
// Apply only the registration for a new filesystem if it does not exist
if (fs == nullptr) {
if (!ProvideMapping(uuid, fsid)) {
eos_err("msg=\"conflict registering file system id/uuid\""
"fsid=%lu uuid=%s", fsid, uuid.c_str());
return false;
}
fs = new FileSystem(locator, gOFS->mMessagingRealm.get());
}
common::FileSystemUpdateBatch batch;
batch.setId(fsid);
batch.setStringDurable("uuid", uuid);
for (auto it = configmap.begin(); it != configmap.end(); it++) {
// Set config parameters except for the "configstatus" which can trigger a
// drain job. This in turn could try to update the status of the file
// system and will deadlock trying to get the transaction mutex. Therefore,
// we update the configstatus outside this transaction.
if (it->first != "configstatus") {
batch.setStringDurable(it->first, it->second);
}
}
fs->applyBatch(batch);
auto it_cfg = configmap.find("configstatus");
if (it_cfg != configmap.end()) {
fs->SetString(it_cfg->first.c_str(), it_cfg->second.c_str());
}
if (!Register(fs, fs->getCoreParams())) {
eos_err("msg=\"cannot register filesystem from config\" queuepath=\"%s\"",
configmap["queuepath"].c_str());
if (RemoveMapping(fsid, uuid)) {
eos_info("msg=\"remove mapping\" fsid=%lu uuid=%s", fsid, uuid.c_str());
} else {
eos_err("msg=\"failed to remove mapping\" fsid=%lu uuid=%s",
fsid, uuid.c_str());
}
return false;
}
return true;
}
//------------------------------------------------------------------------------
// Converts a config engine definition of a global variable into the FsView
// representation.
//------------------------------------------------------------------------------
bool
FsView::ApplyGlobalConfig(const char* key, std::string& val)
{
// global variables are stored like key=':' val=''
std::string configqueue = key;
std::vector tokens;
std::vector paths;
std::string delimiter = "#";
std::string pathdelimiter = "/";
eos::common::StringConversion::Tokenize(configqueue, tokens, delimiter);
eos::common::StringConversion::Tokenize(configqueue, paths, pathdelimiter);
if (tokens.size() != 2) {
eos_static_err("the key definition of config <%s> is invalid", key);
return false;
}
if (paths.size() < 1) {
eos_static_err("the queue name does not contain any /");
return false;
}
// apply a new token generation value
if (tokens[1] == "token.generation") {
eos_static_info("token-generation := %s", val.c_str());
eos::common::EosTok::sTokenGeneration = strtoull(val.c_str(), 0, 10);
} else if (tokens[1] == "policy.recycle") {
eos_static_info("policy-recycle := %s", val.c_str());
if (val == "on") {
gOFS->enforceRecycleBin = true;
} else {
gOFS->enforceRecycleBin = false;
}
} else if (tokens[1] == "fusex.hbi") {
gOFS->zMQ->gFuseServer.Client().SetHeartbeatInterval(atoi(val.c_str()));
} else if (tokens[1] == "fusex.qti") {
gOFS->zMQ->gFuseServer.Client().SetQuotaCheckInterval(atoi(val.c_str()));
} else if (tokens[1] == "fusex.bca") {
gOFS->zMQ->gFuseServer.Client().SetBroadCastMaxAudience(atoi(val.c_str()));
} else if (tokens[1] == "fusex.bca_match") {
gOFS->zMQ->gFuseServer.Client().SetBroadCastAudienceSuppressMatch(val.c_str());
}
common::SharedHashLocator locator;
if (!common::SharedHashLocator::fromConfigQueue(tokens[0], locator)) {
eos_static_err("could not understand global configuration: %s",
tokens[0].c_str());
return false;
}
mq::SharedHashWrapper hash(gOFS->mMessagingRealm.get(), locator);
bool success = hash.set(tokens[1].c_str(), val.c_str());
hash.releaseLocks();
return success;
}
//------------------------------------------------------------------------------
// Broadcast new manager id to all the FST nodes
//------------------------------------------------------------------------------
void
FsView::BroadcastMasterId(const std::string master_id)
{
eos::common::RWMutexReadLock lock(FsView::gFsView.ViewMutex);
for (auto it = FsView::gFsView.mNodeView.begin();
it != FsView::gFsView.mNodeView.end(); ++it) {
it->second->SetConfigMember("manager", master_id, true);
}
}
//------------------------------------------------------------------------------
// Collect all endpoints (:) matching the given queue or pattern
//------------------------------------------------------------------------------
std::set
FsView::CollectEndpoints(const std::string& queue) const
{
int fst_port;
std::string fst_host;
std::set endpoints;
eos::common::RWMutexReadLock fs_rd_lock(FsView::gFsView.ViewMutex);
for (const auto& elem : FsView::gFsView.mIdView) {
FileSystem* fs = elem.second;
if (fs == nullptr) {
eos_static_err("msg=\"file system null\" fsid=%u", elem.first);
continue;
}
if (queue == "*") {
if (fs->GetActiveStatus() != eos::common::ActiveStatus::kOnline) {
eos_static_err("msg=\"file system not online\" fsid=%u", elem.first);
continue;
}
} else {
if (queue != fs->GetQueue()) {
continue;
} else {
if (fs->GetActiveStatus() != eos::common::ActiveStatus::kOnline) {
eos_static_err("msg=\"file system not online\" fsid=%u", elem.first);
break;
}
}
}
fst_host = fs->GetHost();
fst_port = fs->getCoreParams().getLocator().getPort();
endpoints.insert(SSTR(fst_host << ":" << fst_port));
}
return endpoints;
}
//------------------------------------------------------------------------------
// Get set of unbalanced groups given the threshold
//------------------------------------------------------------------------------
std::map
FsView::GetUnbalancedGroups(const std::string& space_name,
double threshold) const
{
static const std::string metric = "stat.statfs.filled";
std::map unbalanced;
eos::common::RWMutexReadLock fs_rd_lock(ViewMutex);
auto it = mSpaceGroupView.find(space_name);
if (it == mSpaceGroupView.end()) {
return unbalanced;
}
const auto& set_groups = it->second;
for (auto* group : set_groups) {
double dev = group->MaxAbsDeviation(metric.c_str(), false);
eos_static_info("msg=\"collect group info\" group=%s max_dev=%.02f "
"threshold=%.02f", group->mName.c_str(), dev, threshold);
if (dev > threshold) {
unbalanced.emplace(group->mName, dev);
}
}
return unbalanced;
}
//------------------------------------------------------------------------------
// Get sets of file systems from given group which are above/below the average
// given the threshold
//------------------------------------------------------------------------------
FsPrioritySets
FsView::GetFsToBalance(const std::string& group_name, double threshold) const
{
static const std::string metric = "stat.statfs.filled";
FsPrioritySets fs_prio;
eos::common::RWMutexReadLock fs_rd_lock(ViewMutex);
const auto it = mGroupView.find(group_name);
if (it == mGroupView.end()) {
return fs_prio;
}
auto* group = it->second;
double average = group->AverageDouble(metric.c_str(), false);
for (auto it_fs = group->begin(); it_fs != group->end(); ++it_fs) {
auto* fs = mIdView.lookupByID(*it_fs);
if (fs && BaseView::ConsiderForStatistics(fs)) {
const std::string node_port = fs->getCoreParams().getHostPort();
double fs_filled = fs->GetDouble(metric.c_str());
if (fs_filled < average) {
if (average - fs_filled > threshold) {
fs_prio.mPrioLow.emplace(*it_fs, node_port);
} else {
fs_prio.mLow.emplace(*it_fs, node_port);
}
} else {
if (fs_filled - average > threshold) {
fs_prio.mPrioHigh.emplace(*it_fs, node_port);
} else {
fs_prio.mHigh.emplace(*it_fs, node_port);
}
}
}
}
return fs_prio;
}
//----------------------------------------------------------------------------
// Dump balancer thread pool info for each of the existing spaces
//----------------------------------------------------------------------------
void
FsView::DumpBalancerPoolInfo(std::ostringstream& oss,
std::string_view prefix) const
{
eos::common::RWMutexReadLock space_rd_lock(ViewMutex);
for (const auto& elem : mSpaceView) {
if (elem.first != eos::common::EOS_SPARE_GROUP) {
oss << prefix << elem.second->GetBalancerPoolInfo()
<< "space=" << elem.first << std::endl;
}
}
}
//------------------------------------------------------------------------------
// Should the provided fsid participate in statistics calculations?
// Yes, if:
// - The filesystem exists (duh)
// - The filesystem is at-least-RO, booted and online
//
// Call with fsview lock at-least-read locked.
//------------------------------------------------------------------------------
bool BaseView::ConsiderForStatistics(FileSystem* fs)
{
if (!fs) {
return false;
}
if (fs->GetConfigStatus() < eos::common::ConfigStatus::kRO) {
return false;
}
if (fs->GetStatus() != eos::common::BootStatus::kBooted) {
return false;
}
if (fs->GetActiveStatus() == eos::common::ActiveStatus::kOffline) {
return false;
}
return true;
}
//------------------------------------------------------------------------------
// Computes the sum for as long
// param="[?=* subset)
{
eos::common::RWMutexReadLock fs_rd_lock;
if (lock) {
fs_rd_lock.Grab(FsView::gFsView.ViewMutex);
}
long long sum = 0;
std::string sparam = param;
size_t qpos = 0;
std::string key = "";
std::string value = "";
bool isquery = false;
if ((qpos = sparam.find("?")) != std::string::npos) {
std::string query = sparam;
query.erase(0, qpos + 1);
sparam.erase(qpos);
std::vector token;
std::string delimiter = "@";
eos::common::StringConversion::Tokenize(query, token, delimiter);
key = token[0];
value = token[1];
isquery = true;
}
if (isquery && key == "*" && value == "*") {
// we just count the number of entries
if (subset) {
return subset->size();
} else {
return size();
}
}
std::set used_nodes;
fsid_iterator it(subset, this);
std::set unique_fs;
for (; it.valid(); it.next()) {
FileSystem* fs = FsView::gFsView.mIdView.lookupByID(*it);
if (!fs) {
continue;
}
if (fs->getSharedFs() != "" &&
fs->getSharedFs() != "none" &&
(
(sparam == "stat.statfs.usedbytes") ||
(sparam == "stat.statfs.capacity") ||
(sparam == "stat.usedfiles") ||
(sparam == "stat.statfs.files") ||
(sparam == "stat.statfs.ffiles") ||
(sparam == "stat.statfs.freebytes") ||
(sparam == "stat.totalspace")) &&
unique_fs.count(fs->getSharedFs())) {
// don't account shared fs more than once for these keys
continue;
}
// for query sum's we always fold in that a group and host has to be enabled
if (!key.length() || fs->GetString(key.c_str()) == value) {
if (isquery &&
((fs->GetActiveStatus() == eos::common::ActiveStatus::kOffline) ||
(eos::common::FileSystem::GetStatusFromString(
fs->GetString("stat.boot").c_str()) !=
eos::common::BootStatus::kBooted))) {
continue;
}
if (sparam.compare(0, 8, "stat.net") == 0) {
const std::string hostname = fs->getCoreParams().getHost();
if (used_nodes.find(hostname) == used_nodes.end()) {
used_nodes.insert(hostname);
const std::string fst_queue = fs->GetQueue();
auto it = FsView::gFsView.mNodeView.find(fst_queue);
if (it != FsView::gFsView.mNodeView.end()) {
try {
sum += std::stoll(it->second->GetConfigMember(sparam.c_str()));
} catch (...) {}
}
}
} else {
long long v = fs->GetLongLong(sparam.c_str());
if (isquery && v && (sparam == "stat.statfs.capacity")) {
// Correct the capacity(rw) value for headroom
v -= fs->GetLongLong("headroom");
}
sum += v;
}
}
unique_fs.insert(fs->getSharedFs());
}
return sum;
}
//------------------------------------------------------------------------------
// Computes the sum for as double
//------------------------------------------------------------------------------
double
BaseView::SumDouble(const char* param, bool lock,
const std::set* subset)
{
eos::common::RWMutexReadLock fs_rd_lock;
if (lock) {
fs_rd_lock.Grab(FsView::gFsView.ViewMutex);
}
double sum = 0;
fsid_iterator it(subset, this);
for (; it.valid(); it.next()) {
FileSystem* fs = FsView::gFsView.mIdView.lookupByID(*it);
if (fs) {
sum += fs->GetDouble(param);
}
}
return sum;
}
//------------------------------------------------------------------------------
// Computes the average for
//------------------------------------------------------------------------------
// @todo (esindril) The lock parameter should be removed as this function is
// never called without the lock taken
double
BaseView::AverageDouble(const char* param, bool lock,
const std::set* subset)
{
eos::common::RWMutexReadLock fs_rd_lock;
if (lock) {
fs_rd_lock.Grab(FsView::gFsView.ViewMutex);
}
double sum = 0;
int cnt = 0;
fsid_iterator it(subset, this);
for (; it.valid(); it.next()) {
bool consider = true;
FileSystem* fs = FsView::gFsView.mIdView.lookupByID(*it);
if (fs == nullptr) {
continue;
}
if (mType == "groupview") {
consider = ConsiderForStatistics(fs);
}
if (consider) {
cnt++;
sum += fs->GetDouble(param);
}
}
return (cnt) ? (double)(1.0 * sum / cnt) : 0;
}
//------------------------------------------------------------------------------
// Computes the maximum absolute deviation of from the avg of
//------------------------------------------------------------------------------
double
BaseView::MaxAbsDeviation(const char* param, bool lock,
const std::set* subset)
{
eos::common::RWMutexReadLock fs_rd_lock;
if (lock) {
fs_rd_lock.Grab(FsView::gFsView.ViewMutex);
}
double avg = AverageDouble(param, false);
double maxabsdev = 0;
double dev = 0;
fsid_iterator it(subset, this);
for (; it.valid(); it.next()) {
bool consider = true;
FileSystem* fs = FsView::gFsView.mIdView.lookupByID(*it);
if (fs == nullptr) {
continue;
}
if (mType == "groupview") {
consider = ConsiderForStatistics(fs);
}
if (consider) {
dev = fabs(avg - fs->GetDouble(param));
if (dev > maxabsdev) {
maxabsdev = dev;
}
}
}
return maxabsdev;
}
//------------------------------------------------------------------------------
// Computes the maximum deviation of from the avg of
//------------------------------------------------------------------------------
double
BaseView::MaxDeviation(const char* param, bool lock,
const std::set* subset)
{
eos::common::RWMutexReadLock fs_rd_lock;
if (lock) {
fs_rd_lock.Grab(FsView::gFsView.ViewMutex);
}
double avg = AverageDouble(param, false);
double maxdev = -DBL_MAX;
double dev = 0;
fsid_iterator it(subset, this);
for (; it.valid(); it.next()) {
bool consider = true;
FileSystem* fs = FsView::gFsView.mIdView.lookupByID(*it);
if (fs == nullptr) {
continue;
}
if (mType == "groupview") {
consider = ConsiderForStatistics(fs);
}
if (consider) {
dev = -(avg - fs->GetDouble(param));
if (dev > maxdev) {
maxdev = dev;
}
}
}
return maxdev;
}
//------------------------------------------------------------------------------
// Computes the minimum deviation of from the avg of
//------------------------------------------------------------------------------
double
BaseView::MinDeviation(const char* param, bool lock,
const std::set* subset)
{
eos::common::RWMutexReadLock fs_rd_lock;
if (lock) {
fs_rd_lock.Grab(FsView::gFsView.ViewMutex);
}
double avg = AverageDouble(param, false);
double mindev = DBL_MAX;
double dev = 0;
fsid_iterator it(subset, this);
for (; it.valid(); it.next()) {
bool consider = true;
FileSystem* fs = FsView::gFsView.mIdView.lookupByID(*it);
if (fs == nullptr) {
continue;
}
if (mType == "groupview") {
consider = ConsiderForStatistics(fs);
}
if (consider) {
dev = -(avg - fs->GetDouble(param));
if (dev < mindev) {
mindev = dev;
}
}
}
return mindev;
}
//------------------------------------------------------------------------------
// Computes the sigma for
//------------------------------------------------------------------------------
double
BaseView::SigmaDouble(const char* param, bool lock,
const std::set* subset)
{
eos::common::RWMutexReadLock fs_rd_lock;
if (lock) {
fs_rd_lock.Grab(FsView::gFsView.ViewMutex);
}
double avg = AverageDouble(param, false);
double sumsquare = 0;
int cnt = 0;
fsid_iterator it(subset, this);
for (; it.valid(); it.next()) {
bool consider = true;
FileSystem* fs = FsView::gFsView.mIdView.lookupByID(*it);
if (fs == nullptr) {
continue;
}
if (mType == "groupview") {
consider = ConsiderForStatistics(fs);
}
if (consider) {
cnt++;
sumsquare += pow((avg - fs->GetDouble(param)), 2);
}
}
sumsquare = (cnt) ? sqrt(sumsquare / cnt) : 0;
return sumsquare;
}
//------------------------------------------------------------------------------
// Computes the considered count
//------------------------------------------------------------------------------
long long
BaseView::ConsiderCount(bool lock,
const std::set* subset)
{
eos::common::RWMutexReadLock fs_rd_lock;
if (lock) {
fs_rd_lock.Grab(FsView::gFsView.ViewMutex);
}
long long cnt = 0;
fsid_iterator it(subset, this);
for (; it.valid(); it.next()) {
bool consider = true;
FileSystem* fs = FsView::gFsView.mIdView.lookupByID(*it);
if (fs == nullptr) {
continue;
}
if (mType == "groupview") {
consider = ConsiderForStatistics(fs);
}
if (consider) {
cnt++;
}
}
return cnt;
}
//------------------------------------------------------------------------------
// Print user defined format to out
//
// table_format
//-------------
// format has to be provided as a chain (separated by "|" ) of the following tags
// "member=:width=:format=[+][-][so]:unit=:tag="
// -> to print a member variable of the view
// "avg=:width=:format=[fo]" -> to print the average
// "sum=:width=:format=[lo] -> to print a sum
// "sig=:width=:format=[lo] -> to print the standard deviation
// "maxdev=:width=;format=[lo] -> to print the maxdeviation
// "tag=" -> use tag as header not the variable name
// "header=1" -> put a header with description on top!
// This must be the first format tag!!!
//
// table_mq_format
//-----------
// format has to be provided as a chain (separated by "|" ) of the following tags
// "key=:width=:format=[+][-][slfo]:unit=:tag="
// -> to print a key of the attached children
// "header=1" -> put a header with description on top
// This must be the first format tag!!!
// the formats are:
// 's' : print as string
// 'S' : print as short string
// 'l' : print as long long
// 'f' : print as double
// 'o' : print as =
// '-' : left align the printout
// '+' : convert numbers into k,M,G,T,P ranges
// the unit is appended to every number:
// e.g. 1500 with unit=B would end up as '1.5 kB'
// the command only appends to and DOES NOT initialize it
// "tag=" -> use tag as header not the variable name
//------------------------------------------------------------------------------
void
BaseView::Print(TableFormatterBase& table, std::string table_format,
const std::string& table_mq_format, unsigned outdepth,
const std::string& filter, const bool dont_color)
{
// Since we don't display the members with geodepth option, we proceed with
// the non geodepth display first.
if (outdepth > 0) {
Print(table, table_format, table_mq_format, 0, filter, dont_color);
// We force-print the header
if (table_format.find("header=1") == std::string::npos) {
if (table_format.find("header=0") != std::string::npos) {
table_format.replace(table_format.find("header=0"), 8, "header=1");
}
table_format = "header=1:" + table_format;
}
}
std::vector formattoken;
class DoubleAggregatedStats : public std::map
{
BaseView* pThis;
public:
DoubleAggregatedStats(BaseView* This) : pThis(This) {}
DoubleAggregator* operator[](const char* param)
{
if (!count(param)) {
DoubleAggregator* aggreg = new DoubleAggregator(param);
aggreg->setView(pThis);
pThis->runAggregator(aggreg);
insert(std::make_pair(param, aggreg));
}
return find(param)->second;
}
~DoubleAggregatedStats()
{
for (auto it = begin(); it != end(); it++) {
delete it->second;
}
}
};
class LongLongAggregatedStats : public
std::map
{
BaseView* pThis;
public:
LongLongAggregatedStats(BaseView* This) : pThis(This) {}
LongLongAggregator* operator[](const char* param)
{
if (!count(param)) {
LongLongAggregator* aggreg = new LongLongAggregator(param);
aggreg->setView(pThis);
pThis->runAggregator(aggreg);
insert(std::make_pair(param, aggreg));
}
return find(param)->second;
}
~LongLongAggregatedStats()
{
for (auto it = begin(); it != end(); it++) {
delete it->second;
}
}
};
LongLongAggregatedStats longStats(this);
DoubleAggregatedStats doubleStats(this);
unsigned int nLines = 0;
if (outdepth > 0) {
nLines = longStats["lastHeartBeat"]->getGeoTags()->size();
nLines = longStats["lastHeartBeat"]->getEndIndex(outdepth);
} else {
nLines = 1;
}
eos::common::StringConversion::Tokenize(table_format, formattoken, "|");
TableHeader table_header;
TableData table_data;
TableHeader table_mq_header;
TableData table_mq_data;
for (unsigned int l = 0; l < nLines; l++) {
table_data.emplace_back();
table_header.clear();
for (unsigned int i = 0; i < formattoken.size(); i++) {
std::vector tagtoken;
std::map formattags;
eos::common::StringConversion::Tokenize(formattoken[i], tagtoken, ":");
for (unsigned int j = 0; j < tagtoken.size(); j++) {
std::vector keyval;
eos::common::StringConversion::Tokenize(tagtoken[j], keyval, "=");
if (keyval.size() != 2) {
eos_static_err("failed parsing \"%s\", expected 2 tokens");
continue;
}
formattags[keyval[0]] = keyval[1];
}
// To save display space, we don't print out members with geodepth option
if (outdepth > 0 && formattags.count("member")) {
continue;
}
if (formattags.count("format")) {
std::string header = "";
std::string format = formattags["format"];
unsigned int width = (formattags.count("width") ?
atoi(formattags["width"].c_str()) : 0);
std::string unit = (formattags.count("unit") ? formattags["unit"] : "");
if (formattags.count("geosched")) {
if (formattags["geosched"] == "totalspace") {
std::string nogroup;
table_data.back().push_back(
TableCell((long long)gOFS->mGeoTreeEngine->placementSpace(mName, nogroup),
format, unit));
table_header.push_back(std::make_tuple("sched.capacity", width, format));
}
}
// Normal member printout
if (formattags.count("member")) {
if ((format.find("+") != std::string::npos) &&
(format.find("s") == std::string::npos)) {
table_data.back().push_back(
TableCell(strtoll(GetMember(formattags["member"]).c_str(), 0, 10),
format, unit));
} else {
std::string member = GetMember(formattags["member"]).c_str();
if ((format.find("S") != std::string::npos)) {
size_t colon = member.find(":");
size_t dot = member.find(".");
if (dot != std::string::npos) {
member.erase(dot, (colon != std::string::npos) ? colon - dot : colon);
}
}
table_data.back().push_back(TableCell(member, format));
}
// Header
XrdOucString pkey = formattags["member"].c_str();
if ((format.find("o") == std::string::npos)) { //for table output
pkey.replace("stat.statfs.", "");
pkey.replace("stat.", "");
pkey.replace("cfg.", "");
if (formattags.count("tag")) {
pkey = formattags["tag"].c_str();
}
}
header = pkey.c_str();
}
// Compution
if (formattags.count("compute")) {
if (formattags["compute"] == "usage") {
// compute the percentage usage
long long used_bytes = SumLongLong("stat.statfs.usedbytes", false);
long long headroom = SumLongLong("headroom", false);
long long capacity = strtoull(GetMember("cfg.nominalsize").c_str(), 0, 10);
std::string header = "";
std::string format = formattags["format"];
unsigned int width = (formattags.count("width") ?
atoi(formattags["width"].c_str()) : 0);
std::string unit = (formattags.count("unit") ? formattags["unit"] : "");
table_header.push_back(std::make_tuple("usage", width, format));
if (!capacity) {
capacity = SumLongLong("stat.statfs.capacity?configstatus@rw", false);
}
double usage = 0;
if (capacity) {
usage = 100.0 * (used_bytes + headroom) / (capacity);
if (usage > 100.0) {
usage = 100.0;
}
}
table_data.back().push_back(TableCell(usage, format));
}
}
// Sum printout
if (formattags.count("sum")) {
if (!outdepth) {
table_data.back().push_back(
TableCell(SumLongLong(formattags["sum"].c_str(), false),
format, unit));
} else {
table_data.back().push_back(
TableCell((*longStats[formattags["sum"].c_str()]->getSums())[l],
format, unit));
}
// Header
XrdOucString pkey = formattags["sum"].c_str();
if ((format.find("o") == std::string::npos)) {
pkey.replace("stat.statfs.", "");
pkey.replace("stat.", "");
pkey.replace("cfg.", "");
if (!formattags.count("tag")) {
header = "sum(";
header += pkey.c_str();
header += ")";
} else {
header = formattags["tag"].c_str();
}
} else { //for monitoring output
header = "sum.";
header += pkey.c_str();
}
}
// Avg printout
if (formattags.count("avg")) {
if (formattags["avg"] == "stat.geotag") {
if (outdepth) {
// This average means anything only when displaying along the
// topology tree
table_data.back().push_back(
TableCell((*longStats["lastHeartBeat"]->getGeoTags())[l].c_str(),
format));
// Header
XrdOucString pkey = formattags["avg"].c_str();
if ((format.find("o") == std::string::npos)) {
pkey.replace("stat.statfs.", "");
pkey.replace("stat.", "");
pkey.replace("cfg.", "");
header = pkey.c_str();
} else { //for monitoring output
header = "avg.";
header += pkey.c_str();
}
}
} else if (formattags["avg"] == "stat.statfs.filled") {
// Handle filled avg seperately!
if (!outdepth) {
auto used_bytes = SumLongLong("stat.statfs.usedbytes", false);
auto capacity = SumLongLong("stat.statfs.capacity", false);
double filled = capacity == 0 ? 0 : (double)used_bytes / capacity * 100;
table_data.back().push_back(TableCell(filled, format, unit));
} else {
auto used_bytes = (*longStats["stat.statfs.usedbytes"]->getSums())[l];
auto capacity = (*longStats["stat.statfs.capacity"]->getSums())[l];
double filled = capacity == 0 ? 0 : (double)used_bytes / capacity * 100;
table_data.back().push_back(TableCell(filled, format, unit));
}
XrdOucString pkey = formattags["avg"].c_str();
if ((format.find("o") == std::string::npos)) {
pkey.replace("stat.statfs.", "");
pkey.replace("stat.", "");
pkey.replace("cfg.", "");
if (!formattags.count("tag")) {
header = "avg(";
header += pkey.c_str();
header += ")";
} else {
header = formattags["tag"].c_str();
}
} else { //for monitoring output
header = "avg.";
header += pkey.c_str();
}
} else { // If not any of the special cases above
if (!outdepth) {
table_data.back().push_back(
TableCell(AverageDouble(formattags["avg"].c_str(), false),
format, unit));
} else {
table_data.back().push_back(
TableCell((*doubleStats[formattags["avg"].c_str()]->getMeans())[l],
format, unit));
}
// Header
XrdOucString pkey = formattags["avg"].c_str();
if ((format.find("o") == std::string::npos)) {
pkey.replace("stat.statfs.", "");
pkey.replace("stat.", "");
pkey.replace("cfg.", "");
if (!formattags.count("tag")) {
header = "avg(";
header += pkey.c_str();
header += ")";
} else {
header = formattags["tag"].c_str();
}
} else { //for monitoring output
header = "avg.";
header += pkey.c_str();
}
} // end not geotag case
}
// Sig printout
if (formattags.count("sig")) {
if (!outdepth) {
table_data.back().push_back(
TableCell(SigmaDouble(formattags["sig"].c_str(), false),
format, unit));
} else {
table_data.back().push_back(
TableCell((*doubleStats[formattags["sig"].c_str()]->getStdDevs())[l],
format, unit));
}
// Header
XrdOucString pkey = formattags["sig"].c_str();
if ((format.find("o") == std::string::npos)) {
pkey.replace("stat.statfs.", "");
pkey.replace("stat.", "");
pkey.replace("cfg.", "");
if (!formattags.count("tag")) {
header = "sig(";
header += pkey.c_str();
header += ")";
} else {
header = formattags["tag"].c_str();
}
} else { //for monitoring output
header = "sig.";
header += pkey.c_str();
}
}
// MaxDev printout
if (formattags.count("maxdev")) {
if (!outdepth) {
table_data.back().push_back(
TableCell(MaxAbsDeviation(formattags["maxdev"].c_str(), false),
format, unit));
} else {
table_data.back().push_back(
TableCell((*doubleStats[formattags["maxdev"].c_str()]->getMaxAbsDevs())[l],
format, unit));
}
// Header
XrdOucString pkey = formattags["maxdev"].c_str();
if ((format.find("o") == std::string::npos)) {
pkey.replace("stat.statfs.", "");
pkey.replace("stat.", "");
pkey.replace("cfg.", "");
if (!formattags.count("tag")) {
header = "dev(";
header += pkey.c_str();
header += ")";
} else {
header = formattags["tag"].c_str();
}
} else { //for monitoring output
header = "dev.";
header += pkey.c_str();
}
}
// Build header
if (!header.empty()) {
table_header.push_back(std::make_tuple(header, width, format));
}
}
}
} // l from 0 to nLines
if (outdepth > 0) {
// Print table for geotag
TableFormatterBase table_geo(dont_color);
table_geo.SetHeader(table_header);
table_geo.AddRows(table_data);
table.AddString(table_geo.GenerateTable(HEADER).c_str());
} else {
//Get table from MQ side (second table)
if (table_mq_format.length()) {
// If a format was given for the filesystem children, forward it
for (auto it = begin(); it != end(); ++it) {
// auto it_fs = FsView::gFsView.mIdView.find(*it);
FileSystem* fs = FsView::gFsView.mIdView.lookupByID(*it);
if (fs) {
table_mq_header.clear();
fs->Print(table_mq_header, table_mq_data, table_mq_format, filter);
}
}
}
// Print table with information from MGM
if (table_format.length() && !table_mq_format.length()) {
table.SetHeader(table_header);
table.AddRows(table_data);
}
// Print table with information from MGM and MQ. (Option "-l")
if (table_format.length() && table_mq_format.length()) {
table.SetHeader(table_header);
table.AddRows(table_data);
TableFormatterBase table_mq(dont_color);
table_mq.SetHeader(table_mq_header);
table_mq.AddRows(table_mq_data);
table.AddString(table_mq.GenerateTable(HEADER).c_str());
}
// Print table with information only from MQ. (e.g. "fs ls")
if (!table_format.length() && table_mq_format.length()) {
table.SetHeader(table_mq_header);
table.AddSeparator();
table.AddRows(table_mq_data);
}
}
}
//------------------------------------------------------------------------------
// If a filesystem has not yet these parameters defined, we inherit them from
// the space configuration. This function has to be called with the a read lock
// on the View Mutex! It return true if the fs was modified and the caller should
// evt. store the modification to the config
//------------------------------------------------------------------------------
bool
FsSpace::ApplySpaceDefaultParameters(eos::mgm::FileSystem* fs, bool force)
{
if (!fs) {
return false;
}
bool modified = false;
eos::common::FileSystem::fs_snapshot_t snapshot;
if (fs->SnapShotFileSystem(snapshot, false)) {
if (force || (!snapshot.mScanIoRate)) {
if (GetConfigMember(eos::common::SCAN_IO_RATE_NAME).length()) {
fs->SetString(eos::common::SCAN_IO_RATE_NAME,
GetConfigMember(eos::common::SCAN_IO_RATE_NAME).c_str());
modified = true;
}
}
if (force || (!snapshot.mScanEntryInterval)) {
// try to apply the default
if (GetConfigMember(eos::common::SCAN_ENTRY_INTERVAL_NAME).length()) {
modified = true;
fs->SetString(eos::common::SCAN_ENTRY_INTERVAL_NAME,
GetConfigMember(eos::common::SCAN_ENTRY_INTERVAL_NAME).c_str());
}
}
if (force || (!snapshot.mScanRainEntryInterval)) {
if (GetConfigMember(eos::common::SCAN_RAIN_ENTRY_INTERVAL_NAME).length()) {
modified = true;
fs->SetString(eos::common::SCAN_RAIN_ENTRY_INTERVAL_NAME,
GetConfigMember(eos::common::SCAN_RAIN_ENTRY_INTERVAL_NAME).c_str());
}
}
if (force || (!snapshot.mScanDiskInterval)) {
if (GetConfigMember(eos::common::SCAN_DISK_INTERVAL_NAME).length()) {
modified = true;
fs->SetString(eos::common::SCAN_DISK_INTERVAL_NAME,
GetConfigMember(eos::common::SCAN_DISK_INTERVAL_NAME).c_str());
}
}
if (force || (!snapshot.mScanNsInterval)) {
if (GetConfigMember(eos::common::SCAN_NS_INTERVAL_NAME).length()) {
modified = true;
fs->SetString(eos::common::SCAN_NS_INTERVAL_NAME,
GetConfigMember(eos::common::SCAN_NS_INTERVAL_NAME).c_str());
}
}
if (force || (!snapshot.mScanNsRate)) {
if (GetConfigMember(eos::common::SCAN_NS_RATE_NAME).length()) {
fs->SetString(eos::common::SCAN_NS_RATE_NAME,
GetConfigMember(eos::common::SCAN_NS_RATE_NAME).c_str());
modified = true;
}
}
if (force || (!snapshot.mFsckRefreshInterval)) {
if (GetConfigMember(eos::common::FSCK_REFRESH_INTERVAL_NAME).length()) {
fs->SetString(eos::common::FSCK_REFRESH_INTERVAL_NAME,
GetConfigMember(eos::common::FSCK_REFRESH_INTERVAL_NAME).c_str());
modified = true;
}
}
if (force || (!snapshot.mGracePeriod)) {
// try to apply the default
if (GetConfigMember("graceperiod").length()) {
fs->SetString("graceperiod", GetConfigMember("graceperiod").c_str());
modified = true;
}
}
if (force || (!snapshot.mDrainPeriod)) {
// try to apply the default
if (GetConfigMember("drainperiod").length()) {
fs->SetString("drainperiod", GetConfigMember("drainperiod").c_str());
modified = true;
}
}
if (force || (!snapshot.mHeadRoom)) {
// try to apply the default
if (GetConfigMember("headroom").length()) {
fs->SetString("headroom", GetConfigMember("headroom").c_str());
modified = true;
}
}
}
return modified;
}
//------------------------------------------------------------------------------
// Re-evaluates the draining state in all groups and resets the state
//------------------------------------------------------------------------------
void
FsSpace::ResetDraining()
{
eos_static_info("msg=\"reset drain state\" space=\"%s\"", mName.c_str());
eos::common::RWMutexReadLock lock(FsView::gFsView.ViewMutex);
// Iterate over all groups in this space
for (auto sgit = FsView::gFsView.mSpaceGroupView[mName].begin();
sgit != FsView::gFsView.mSpaceGroupView[mName].end();
sgit++) {
bool setactive = false;
std::string lGroup = (*sgit)->mName;
FsGroup::const_iterator git;
for (git = (*sgit)->begin();
git != (*sgit)->end(); git++) {
FileSystem* entry = FsView::gFsView.mIdView.lookupByID(*git);
if (entry) {
eos::common::DrainStatus drainstatus =
(eos::common::FileSystem::GetDrainStatusFromString(
entry->GetString("local.drain").c_str()));
if ((drainstatus == eos::common::DrainStatus::kDraining) ||
(drainstatus == eos::common::DrainStatus::kDrainStalling)) {
// if any mGroup filesystem is draining, all the others have
// to enable the pull for draining!
setactive = true;
}
}
}
// if the mGroup get's disabled we stop the draining
if (FsView::gFsView.mGroupView[lGroup]->GetConfigMember("status") != "on") {
setactive = false;
}
for (git = (*sgit)->begin(); git != (*sgit)->end(); git++) {
eos::mgm::FileSystem* fs = FsView::gFsView.mIdView.lookupByID(*git);
if (fs) {
if (setactive) {
if (fs->GetString("stat.drainer") != "on") {
fs->SetString("stat.drainer", "on");
}
} else {
if (fs->GetString("stat.drainer") != "off") {
fs->SetString("stat.drainer", "off");
}
}
eos_static_info("fsid=%05d state=%s", fs->GetId(),
fs->GetString("stat.drainer").c_str());
}
}
}
}
//------------------------------------------------------------------------------
// Get status of the balancer thread pool
//------------------------------------------------------------------------------
std::string
FsSpace::GetBalancerPoolInfo() const
{
if (mFsBalancer) {
return mFsBalancer->GetThreadPoolInfo() + " ";
}
return std::string();
}
EOSMGMNAMESPACE_END