// ---------------------------------------------------------------------- // File: Quota.hh // 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 .* ************************************************************************/ #ifndef __EOSMGM_QUOTA__HH__ #define __EOSMGM_QUOTA__HH__ // this is needed because of some openssl definition conflict! #undef des_set_key #include #include #include "mgm/Scheduler.hh" #include "common/Logging.hh" #include "common/LayoutId.hh" #include "common/Mapping.hh" #include "common/RWMutex.hh" #include "namespace/interface/IQuota.hh" #include "XrdOuc/XrdOucString.hh" EOSMGMNAMESPACE_BEGIN class Quota; //------------------------------------------------------------------------------ //! Class SpaceQuota //------------------------------------------------------------------------------ class SpaceQuota : public eos::common::LogId { friend class Quota; public: //---------------------------------------------------------------------------- //! Constructor //---------------------------------------------------------------------------- SpaceQuota(const char* path); //---------------------------------------------------------------------------- //! Destructor //---------------------------------------------------------------------------- ~SpaceQuota() = default; //---------------------------------------------------------------------------- //! Get space name //---------------------------------------------------------------------------- inline const char* GetSpaceName() const { return pPath.c_str(); } //---------------------------------------------------------------------------- //! Get namespace quota node //---------------------------------------------------------------------------- inline eos::IQuotaNode* GetQuotaNode() { return mQuotaNode; } //---------------------------------------------------------------------------- //! Get space layout size factor //---------------------------------------------------------------------------- double GetLayoutSizeFactor() { return mLayoutSizeFactor; } //---------------------------------------------------------------------------- //! Check user and/or group quota. If both are present, they both have to be //! fullfilled. //! //! @param uid user id //! @param gid group id //! @param desired_vol desired volume (size) //! @param desired_inodes desired number of inodes //! //! @return true if user has enough quota, otherwise false //---------------------------------------------------------------------------- bool CheckWriteQuota(uid_t uid, gid_t gid, long long desired_vol, unsigned int desired_inodes); //---------------------------------------------------------------------------- //! Print quota information //---------------------------------------------------------------------------- void PrintOut(XrdOucString& output, long long int uid_sel = -1, long long int gid_sel = -1, bool monitoring = false, bool translate_ids = false); //---------------------------------------------------------------------------- //! Quota type tags //---------------------------------------------------------------------------- enum eQuotaTag { kUserBytesIs = 1, kUserLogicalBytesIs = 2, kUserLogicalBytesTarget = 3, kUserBytesTarget = 4, kUserFilesIs = 5, kUserFilesTarget = 6, kGroupBytesIs = 7, kGroupLogicalBytesIs = 8, kGroupLogicalBytesTarget = 9, kGroupBytesTarget = 10, kGroupFilesIs = 11, kGroupFilesTarget = 12, kAllUserBytesIs = 13, kAllUserLogicalBytesIs = 14, kAllUserLogicalBytesTarget = 15, kAllUserBytesTarget = 16, kAllGroupBytesIs = 17, kAllGroupLogicalBytesIs = 18, kAllGroupLogicalBytesTarget = 19, kAllGroupBytesTarget = 20, kAllUserFilesIs = 21, kAllUserFilesTarget = 22, kAllGroupFilesIs = 23, kAllGroupFilesTarget = 24 }; private: //---------------------------------------------------------------------------- //! Get quota //! //! @param tag quota type tag (eQuotaTag) //! @param id uid/gid/project id //! //! @return requested quota value //---------------------------------------------------------------------------- long long GetQuota(unsigned long tag, unsigned long id); //---------------------------------------------------------------------------- //! Set quota //! //! @param tag quota type tag (eQuotaTag) //! @param id user/group id //! @param value quota value to set //---------------------------------------------------------------------------- void SetQuota(unsigned long tag, unsigned long id, unsigned long long value); //---------------------------------------------------------------------------- //! Reset quota //! //! @param tag quota type tag (eQuotaTag) //! @param uid uid/gid/project id //! //! @warning Caller needs to hold a lock on mMutex //---------------------------------------------------------------------------- void ResetQuota(unsigned long tag, unsigned long id); //---------------------------------------------------------------------------- //! Add quota //! //! @param tag quota type tag (eQuotaTag) //! @param id user/group id //! @param value quota value to be added //! //! @warning Caller needs to hold a lock on mMutex. //---------------------------------------------------------------------------- void AddQuota(unsigned long tag, unsigned long id, long long value); //---------------------------------------------------------------------------- //! Account ns quota values into the space quota view. //---------------------------------------------------------------------------- void AccountNsToSpace(); //---------------------------------------------------------------------------- //! Update quota from the ns quota node for the given identity only if the //! requested path is actually a ns quota node. //! //! @param uid user id //! @param gid group id //! @param upd_proj_quota if true then update also the project quota //---------------------------------------------------------------------------- void UpdateFromQuotaNode(uid_t uid, gid_t, bool upd_proj_quota); //---------------------------------------------------------------------------- //! Refresh quota all quota values for current space //! //! @warning Caller needs to hold a read-lock on both eosViewRWMutex and //! pMapMutex //---------------------------------------------------------------------------- void Refresh(time_t age = 0); //---------------------------------------------------------------------------- //! Calculate the size factor used to estimate the logical available bytes //---------------------------------------------------------------------------- void UpdateLogicalSizeFactor(); //---------------------------------------------------------------------------- //! Update target quota values //---------------------------------------------------------------------------- void UpdateTargetSums(); //---------------------------------------------------------------------------- //! Update current quota values //---------------------------------------------------------------------------- void UpdateIsSums(); //---------------------------------------------------------------------------- //! Update ns quota node address referred to by current space quota //! //! @return true if update successful, otherwise false //! @warning Caller needs to hold a read-lock on eosViewRWMutex //---------------------------------------------------------------------------- bool UpdateQuotaNodeAddress(); //---------------------------------------------------------------------------- //! Serialize index //---------------------------------------------------------------------------- inline unsigned long long Index(unsigned long tag, unsigned long id) { return ((tag << 32) | id); } //---------------------------------------------------------------------------- //! Deserialize index //---------------------------------------------------------------------------- inline unsigned long UnIndex(unsigned long long reindex) { return (reindex >> 32) & 0xffffffff; } //---------------------------------------------------------------------------- //! Check if quota is enabled - needs a lock on the FsView //! //! @return true if quota enabled, otherwise false //! @warning Caller needs to hold a read-lock on gFsView::ViewMutex //---------------------------------------------------------------------------- bool IsEnabled(); //---------------------------------------------------------------------------- //! Remove quota //! //! @param tag quota type tag //! @param uid uid/gid/project id //! //! @return true if quota deleted, false if quota not found //---------------------------------------------------------------------------- bool RmQuota(unsigned long tag, unsigned long id); //---------------------------------------------------------------------------- //! Get current quota value as percentage of the available one //! //! @param is current quota value //! @param avail available quota value //! //! @return string representation of the percentage value //---------------------------------------------------------------------------- const float GetQuotaPercentage(unsigned long long is, unsigned long long avail); //---------------------------------------------------------------------------- //! Get quota status //! //! @para is current quota value //! @param avail available quota value //! //! @return string representing the status i.e. ignored/ok/warning/exceeded //---------------------------------------------------------------------------- const char* GetQuotaStatus(unsigned long long is, unsigned long long avail); //---------------------------------------------------------------------------- //! Convert int tag to string representation //! //! @param tag int tag value //! //! @return string representation of the tag //---------------------------------------------------------------------------- static const char* GetTagAsString(int tag); //---------------------------------------------------------------------------- //! Convert int tag to string description //! //! @param tag int tag value //! //! @return string tag description //---------------------------------------------------------------------------- static const char* GetTagName(int tag); //---------------------------------------------------------------------------- //! Convert string tag to int representation //! //! @param tag string tag //! //! @return int representation of the tag //---------------------------------------------------------------------------- static unsigned long GetTagFromString(const std::string& tag); //---------------------------------------------------------------------------- //! Convert int tag to user or group category //! //! @param tag int tag value //! //! @return user/group category //---------------------------------------------------------------------------- static const char* GetTagCategory(int tag); std::string pPath; ///< quota node path eos::IQuotaNode* mQuotaNode; ///< corresponding ns quota node XrdSysMutex mMutex; ///< mutex to protect access to mMapIdQuota time_t mLastEnableCheck; ///< timestamp of the last check time_t mLastRefresh; ///< timestamp of last refresh call double mLayoutSizeFactor; ///< layout dependent size factor bool mDirtyTarget; ///< mark to recompute target values //! Map for user view, depending on eQuota and uid/gid std::map mMapIdQuota; }; //------------------------------------------------------------------------------ //! Class Quota //------------------------------------------------------------------------------ class Quota: eos::common::LogId { public: enum IdT { kUid, kGid }; ///< Id type enum enum Type { kUnknown, kVolume, kInode, kAll }; ///< Quota types //---------------------------------------------------------------------------- //! Create space quota //! //! @param path quota node path which needs to be '/' terminated //! //! @return true if successful, otherwise false //---------------------------------------------------------------------------- static bool Create(const std::string& path); //---------------------------------------------------------------------------- //! Check if quota node for path exists //! //! @param qpath path to search for //! //! @return true if quota node exists, otherwise false //---------------------------------------------------------------------------- static bool Exists(const std::string& qpath); //---------------------------------------------------------------------------- //! Check if there is a quota node responsible for the given path //! //! @param path path for which a quota node is searched, which needs to be //! '/' terminated //! //! @return true if quota node exists, otherwise false //---------------------------------------------------------------------------- static bool ExistsResponsible(const std::string& path); //---------------------------------------------------------------------------- //! Get individual quota values //! //! @param vid client virtual identity //! @param path path //! @param max_bytes max bytes value //! @param free_bytes free bytes value //! @param logical request to return the layout corrected quota values //! //---------------------------------------------------------------------------- static void GetIndividualQuota(eos::common::VirtualIdentity& vid, const std::string& path, long long& max_bytes, long long& free_bytes, long long& max_files, long long& free_files, bool logical = false); //---------------------------------------------------------------------------- //! Set quota type of id (uid/gid) //! //! @param qpath quota path //! @param id uid or gid value depending on the id_type //! @param id_type type of id, can be uid or gid //! @param quota_type type of quota to remove, can be inode or volume //! @param value quota value to be set //! @param msg message returned to the client //! @param retc error number returned to the client //! //! @return true if quota set successful, otherwise false //---------------------------------------------------------------------------- static bool SetQuotaTypeForId(const std::string& qpath, long id, Quota::IdT id_type, Quota::Type quota_type, unsigned long long value, std::string& msg, int& retc); //---------------------------------------------------------------------------- //! Set quota specified by the quota tag. //! //! @param qpath quota path //! @param quota_tag string representation of the SpaceQuota::eQuotaTag. From //! this we can deduce the quota type and the id type. //! @param id uid or gid value depending on the space_tag //! @param value quota value to be set //! //! @return true if quota set successful, otherwise false //---------------------------------------------------------------------------- static bool SetQuotaForTag(const std::string& qpath, const std::string& quota_tag, long id, unsigned long long value); //---------------------------------------------------------------------------- //! Remove all quota types for an id //! //! @param path quota node path //! @param id uid or gid value depending on the id_type //! @param id_type type of id, can be uid or gid //! @param msg message returned to the client //! @param retc error number returned to the client //! //! @return true if operation successful, otherwise false //---------------------------------------------------------------------------- static bool RmQuotaForId(const std::string& path, long id, Quota::IdT id_type, std::string& msg, int& retc); //---------------------------------------------------------------------------- //! Remove quota type for id //! //! @param qpath quota node path //! @param id uid or gid value depending on the id_type //! @param id_type type of id, can be uid or gid //! @param quota_type type of quota to remove, can be inode or volume //! @param msg message returned to the client //! @param retc error number returned to the client //! //! @return true if operation successful, otherwise false //---------------------------------------------------------------------------- static bool RmQuotaTypeForId(const std::string& qpath, long id, Quota::IdT id_type, Quota::Type quota_type, std::string& msg, int& retc); //------------------------------------------------------------------------------ //! Remove quota specified by the quota tag //! //! @param qpath quota node path //! @param quota_tag string representation of the SpaceQuota::eQuotaTag. From //! this we can deduce the quota type and the id type. //! @param id uid or gid value depending on the space_tag //! //! @return true if quota set successful, otherwise false //------------------------------------------------------------------------------ static bool RmQuotaForTag(const std::string& space, const std::string& quota_stag, long id); //---------------------------------------------------------------------------- //! Removes a quota node //! //! @param qpath quota node path to be removed //! @param msg message returned to the client //! @param retc error number returned to the client //! //! @return true if operation successful, otherwise false //---------------------------------------------------------------------------- static bool RmSpaceQuota(const std::string& qpath, std::string& msg, int& retc); //---------------------------------------------------------------------------- //! Get group quota values for a particular path and id //! //! @param qpath quota node path //! @param id uid/gid/projectid //! //! @return map between quota types and values. The map contains 4 entries //! corresponding to the following keys: kGroupBytesIs, //! kGroupBytesTarget, kGroupFilesIs and kGroupFilesTarget //---------------------------------------------------------------------------- static std::map GetGroupStatistics(const std::string& qpath, long id); //---------------------------------------------------------------------------- //! Update SpaceQuota from the namespace quota only if the requested path is //! actually a ns quota node. This also performs an update for the project //! quota. //! //! @param path path //! @param uid user id //! @param gid group id //! //! @return true if update successful, otherwise it means that current path //! doesn't point to a ns quota node and return false. //---------------------------------------------------------------------------- static bool UpdateFromNsQuota(const std::string& path, uid_t uid, gid_t gid); //---------------------------------------------------------------------------- //! Check if the requested volume and inode values respect the quota //! //! @param path path //! @param uid user id //! @param gid group id //! @param desired_vol desired space //! @param desired_inodes desired number of inondes //! //! @return true if quota is respected, otherwise false //---------------------------------------------------------------------------- static bool Check(const std::string& path, uid_t uid, gid_t gid, long long desired_vol, unsigned int desired_inodes); //---------------------------------------------------------------------------- //! Callback function to calculate how much pyhisical space a file occupies //! //! @param file file MD object //! //! @return physical size depending on file layout type //---------------------------------------------------------------------------- static uint64_t MapSizeCB(const eos::IFileMD* file); //---------------------------------------------------------------------------- //! Load function to initialize all SpaceQuota's with the quota node //! definition from the namespace //---------------------------------------------------------------------------- static void LoadNodes(); //---------------------------------------------------------------------------- //! Clean-up all space quotas by deleting them and clearing the map //---------------------------------------------------------------------------- static void CleanUp(); //---------------------------------------------------------------------------- //! Print out quota information //! //! @return true if operation successful, otherwise false and populate the //! output string with the error messsage //---------------------------------------------------------------------------- static bool PrintOut(const std::string& path, XrdOucString& output, long long int uid_sel = -1, long long int gid_sel = -1, bool monitoring = false, bool translate_ids = false); //---------------------------------------------------------------------------- //! Take the decision where to place a new file in the system. The core of the //! implementation is in the Scheduler and GeoTreeEngine. //! //! @param space quota space name //! @param path file path //! @param vid virtual id of client //! @param grouptag group tag for placement //! @param lid layout to be placed //! @param alreadyused_filsystems filesystems to avoid //! @param selected_filesystems filesystems selected by scheduler //! @param dataproxys if non null, schedule dataproxys for each fs //! @param firewallentpts if non null, schedule firewall entry points for each fs //! @param plctpolicy indicates if placement should be local/spread/hybrid //! @param plctTrgGeotag indicates close to which Geotag collocated stripes //! should be placed //! @param truncate indicates placement with truncation //! @param forched_scheduling_group_index forced index for the scheduling //! subgroup to be used //! @param bookingsize size to book for the placement //! //! @return 0 if placement successful, otherwise a non-zero value //! ENOSPC - no space quota defined for current space //! EDQUOT - no quota node found or not enough quota to place //! @warning Must be called with a lock on the FsView::gFsView::ViewMutex //---------------------------------------------------------------------------- static int FilePlacement(Scheduler::PlacementArguments* args); //---------------------------------------------------------------------------- //! @brief Retrieve the kAllGroupLogicalBytesIs and kAllGroupLogicalBytesTarget //! values for the quota nodes. //! //! @return a map with the paths of the quota nodes and the corresponding //! values //---------------------------------------------------------------------------- static std::map> GetAllGroupsLogicalQuotaValues(); //---------------------------------------------------------------------------- //! Get quota for requested user and group by path //! //! @param path path for which to search for a quota node //! @param uid user id //! @param gid group id //! @param avail_files inode quota left //! @param avail_bytes size quota left //! @param quota_inode inode of the quota node //! //! @return 0 if successful //---------------------------------------------------------------------------- static int QuotaByPath(const char* path, uid_t uid, gid_t gid, long long& avail_files, long long& avail_bytes, eos::IContainerMD::id_t& quota_inode); //---------------------------------------------------------------------------- //! Get quota for requested user and group by quota inode //! //! @param qino inode of quota node //! @param uid user id //! @param gid group id //! @param avail_files inode quota left //! @param avail_bytes size quota left //! //! @return 0 if successful //---------------------------------------------------------------------------- static int QuotaBySpace(const eos::IContainerMD::id_t, uid_t uid, gid_t gid, long long& avail_files, long long& avail_bytes); //---------------------------------------------------------------------------- //! Get space quota node path looking for the most specific //! match. //! //! @param path path for which we search for a responsible space quota //! //! @return path name to space quota //---------------------------------------------------------------------------- static std::string GetResponsibleSpaceQuotaPath(const std::string& path); //---------------------------------------------------------------------------- //! Get logical free und max bytes for this space //---------------------------------------------------------------------------- static void GetStatfs(const std::string& path, unsigned long long& maxbytes, unsigned long long& freebytes); //---------------------------------------------------------------------------- //! Remove file from corresponding quota node // //! @param fid file identifier //! //! @return true if file remove successfully and quota node exists //---------------------------------------------------------------------------- static bool RemoveFile(eos::IFileMD::id_t fid); //---------------------------------------------------------------------------- //! Remove file from corresponding quota node // //! @param fid file identifier //! //! @return true if file added successful and quota node exists //---------------------------------------------------------------------------- static bool AddFile(eos::IFileMD::id_t fid); static gid_t gProjectId; ///< gid indicating project quota static eos::common::RWMutex pMapMutex; ///< Protect access to pMapQuota private: //---------------------------------------------------------------------------- //! Private method to collect desired info from a quota node //! //! @param squota quota object //! @param uid user id //! @param gid group id //! @param avail_files inode quota left //! @param avail_bytes size quota left //! //! @return 0 if successful //! @note locks should be taken outside this method //---------------------------------------------------------------------------- static int GetQuotaInfo(SpaceQuota* squota, uid_t uid, gid_t gid, long long& avail_files, long long& avail_bytes); //---------------------------------------------------------------------------- //! Get space quota object for exact path //! //! @param qpath path of the quota node //! //! @return SpaceQuota object //---------------------------------------------------------------------------- static SpaceQuota* GetSpaceQuota(const std::string& qpath); //---------------------------------------------------------------------------- //! Get space quota node responsible for path looking for the most specific //! match. //! //! @param path path for which we search for a responsible space quotap //! //! @return SpaceQuota object //---------------------------------------------------------------------------- static SpaceQuota* GetResponsibleSpaceQuota(const std::string& path); //---------------------------------------------------------------------------- //! Make sure the path ends with a / //! //! @param path input path //! //! @return / terminated path //---------------------------------------------------------------------------- static inline std::string NormalizePath(const std::string& ipath) { std::string path = ipath; if (!path.empty() && (path.back() != '/')) { path += '/'; } return path; } //! Map from path to SpaceQuota object static std::map pMapQuota; //! Map from container id to SpaceQuota object static std::map pMapInodeQuota; }; EOSMGMNAMESPACE_END #endif