From 44fdef7cb7c36a2effb4ec94286bf0ba60ac7c8b Mon Sep 17 00:00:00 2001 From: Victor Kotlyar <Victor.Kotlyar@cern.ch> Date: Mon, 25 Sep 2017 10:59:34 +0200 Subject: [PATCH] Implement 'cta drive rm' command. Command will remove drive or drives by regex from the drive register. The drives must be in state Down unless --force option is set. Usage: rm <drive_name> [--force/-f] If there is any active tapeserver with this drive it will create the entry in the drive register again with Down status. Example of the usage: cta drive rm ^VDSTK Drive VDSTK41 in state Free and force is not set (skipped). Drive VDSTK42 removed. --- objectstore/DriveRegister.cpp | 18 ++++++++ scheduler/OStoreDB/OStoreDB.cpp | 14 ++++++ scheduler/OStoreDB/OStoreDB.hpp | 2 + scheduler/OStoreDB/OStoreDBFactory.hpp | 4 ++ scheduler/Scheduler.cpp | 14 ++++++ scheduler/Scheduler.hpp | 9 ++++ scheduler/SchedulerDatabase.hpp | 8 ++++ xroot_plugins/XrdCtaFile.cpp | 61 ++++++++++++++++++++++++-- xroot_plugins/XrdCtaFile.hpp | 14 ++++++ 9 files changed, 141 insertions(+), 3 deletions(-) diff --git a/objectstore/DriveRegister.cpp b/objectstore/DriveRegister.cpp index 9d086f6e77..89526e960a 100644 --- a/objectstore/DriveRegister.cpp +++ b/objectstore/DriveRegister.cpp @@ -265,6 +265,24 @@ update: } } +//------------------------------------------------------------------------------ +// DriveRegister::removeDrive()) +//------------------------------------------------------------------------------ +void DriveRegister::removeDrive(const std::string & driveName) { + checkPayloadWritable(); + auto driveToRemove = m_payload.mutable_drives()->begin(); + while (driveToRemove != m_payload.mutable_drives()->end()) { + if ( driveToRemove->drivename() == driveName) { + m_payload.mutable_drives()->erase(driveToRemove); + return; + } + driveToRemove++; + } + std::stringstream err; + err << "In DriveRegister::removeDrive(): drive not found: " << driveName; + throw cta::exception::Exception(err.str()); +} + //------------------------------------------------------------------------------ // DriveRegister::isEmpty()) //------------------------------------------------------------------------------ diff --git a/scheduler/OStoreDB/OStoreDB.cpp b/scheduler/OStoreDB/OStoreDB.cpp index b8a9a234a6..a7befdd8e3 100644 --- a/scheduler/OStoreDB/OStoreDB.cpp +++ b/scheduler/OStoreDB/OStoreDB.cpp @@ -1056,6 +1056,20 @@ void OStoreDB::setDesiredDriveState(const std::string& drive, const common::data dr.commit(); } +//------------------------------------------------------------------------------ +// OStoreDB::removeDrive() +//------------------------------------------------------------------------------ +void OStoreDB::removeDrive(const std::string& drive) { + RootEntry re(m_objectStore); + re.fetchNoLock(); + auto driveRegisterAddress = re.getDriveRegisterAddress(); + objectstore::DriveRegister dr(driveRegisterAddress, m_objectStore); + objectstore::ScopedExclusiveLock drl(dr); + dr.fetch(); + dr.removeDrive(drive); + dr.commit(); +} + //------------------------------------------------------------------------------ // OStoreDB::reportDriveStatus() //------------------------------------------------------------------------------ diff --git a/scheduler/OStoreDB/OStoreDB.hpp b/scheduler/OStoreDB/OStoreDB.hpp index 46bce86c52..9eb820dc70 100644 --- a/scheduler/OStoreDB/OStoreDB.hpp +++ b/scheduler/OStoreDB/OStoreDB.hpp @@ -270,6 +270,8 @@ public: std::list<cta::common::dataStructures::DriveState> getDriveStates() const override; void setDesiredDriveState(const std::string& drive, const common::dataStructures::DesiredDriveState & desiredState) override; + + void removeDrive(const std::string & drive) override; void reportDriveStatus(const common::dataStructures::DriveInfo& driveInfo, cta::common::dataStructures::MountType mountType, common::dataStructures::DriveStatus status, time_t reportTime, uint64_t mountSessionId, uint64_t byteTransfered, diff --git a/scheduler/OStoreDB/OStoreDBFactory.hpp b/scheduler/OStoreDB/OStoreDBFactory.hpp index e4c5ee9e98..8c10b22943 100644 --- a/scheduler/OStoreDB/OStoreDBFactory.hpp +++ b/scheduler/OStoreDB/OStoreDBFactory.hpp @@ -152,6 +152,10 @@ public: return m_OStoreDB.setDesiredDriveState(drive, desiredState); } + void removeDrive(const std::string & drive) override { + return m_OStoreDB.removeDrive(drive); + } + void reportDriveStatus(const common::dataStructures::DriveInfo& driveInfo, cta::common::dataStructures::MountType mountType, common::dataStructures::DriveStatus status, time_t reportTime, uint64_t mountSessionId, uint64_t byteTransfered, uint64_t filesTransfered, double latestBandwidth, const std::string& vid, const std::string& tapepool) override { diff --git a/scheduler/Scheduler.cpp b/scheduler/Scheduler.cpp index 6623adec25..d8bac3f0ab 100644 --- a/scheduler/Scheduler.cpp +++ b/scheduler/Scheduler.cpp @@ -371,6 +371,20 @@ void Scheduler::setDesiredDriveState(const common::dataStructures::SecurityIdent lc.log(log::INFO, "In Scheduler::setDesiredDriveState(): success."); } +//------------------------------------------------------------------------------ +// removeDrive +//------------------------------------------------------------------------------ +void Scheduler::removeDrive(const common::dataStructures::SecurityIdentity &cliIdentity, + const std::string &driveName, log::LogContext & lc) { + utils::Timer t; + m_db.removeDrive(driveName); + auto schedulerDbTime = t.secs(); + log::ScopedParamContainer spc(lc); + spc.add("drive", driveName) + .add("schedulerDbTime", schedulerDbTime); + lc.log(log::INFO, "In Scheduler::removeDrive(): success."); +} + //------------------------------------------------------------------------------ // setDesiredDriveState //------------------------------------------------------------------------------ diff --git a/scheduler/Scheduler.hpp b/scheduler/Scheduler.hpp index 49a9f93ee2..db4fa6f16d 100644 --- a/scheduler/Scheduler.hpp +++ b/scheduler/Scheduler.hpp @@ -216,6 +216,15 @@ public: void setDesiredDriveState(const cta::common::dataStructures::SecurityIdentity &cliIdentity, const std::string &driveName, const bool up, const bool force, log::LogContext & lc); + /** + * Remove drive from the drive register. + * + * @param cliIdentity The identity of the user requesting the drive removal. + * @param driveName The drive name + */ + void removeDrive(const cta::common::dataStructures::SecurityIdentity &cliIdentity, + const std::string &driveName, log::LogContext & lc); + /** * Reports the state of the drive to the object store. This information is then reported * to the user through the command line interface, via getDriveStates(). This function diff --git a/scheduler/SchedulerDatabase.hpp b/scheduler/SchedulerDatabase.hpp index ed15f63eb2..f6c8914285 100644 --- a/scheduler/SchedulerDatabase.hpp +++ b/scheduler/SchedulerDatabase.hpp @@ -478,6 +478,14 @@ public: */ virtual void setDesiredDriveState(const std::string & drive, const cta::common::dataStructures::DesiredDriveState & state) = 0; + /** + * Remove drive from the drive register. + * Will throw an exception is the drive does not exist. + * + * @param drive The drive name. + */ + virtual void removeDrive(const std::string & drive) = 0; + /** * Sets the drive status in the object store. The drive status will be recorded in all cases, * although some historical information is needed to provide an accurate view of the diff --git a/xroot_plugins/XrdCtaFile.cpp b/xroot_plugins/XrdCtaFile.cpp index 6d225af666..e185bfc457 100644 --- a/xroot_plugins/XrdCtaFile.cpp +++ b/xroot_plugins/XrdCtaFile.cpp @@ -1835,14 +1835,18 @@ std::string XrdCtaFile::xCom_drive() { log::LogContext lc(m_log); std::stringstream cmdlineOutput; std::stringstream help; - help << m_requestTokens.at(0) << " dr/drive up/down/ls (it is a synchronous command):" << std::endl + help << m_requestTokens.at(0) << " dr/drive up/down/ls/rm (it is a synchronous command):" << std::endl << "Set the requested state of the drives. The drives will complete any running mount" << std::endl << "unless it is preempted with the --force option." << std::endl << "\tup <drives_name>" << std::endl << "\tdown <drives_name> [--force/-f]" << std::endl << "" << std::endl - << "List the states for one or all drives" << std::endl - << "\tls [<drive_name>]" << std::endl; + << "List the states for one or all drives." << std::endl + << "\tls [<drive_name>]" << std::endl + << "" << std::endl + << "Remove drive or drives by regex. The drives must be in state Down" + " unless --force option is set." << std::endl + << "\trm <drive_name> [--force/-f]" << std::endl; if (hasOption("-?", "--help")) { return help.str(); @@ -1871,6 +1875,22 @@ std::string XrdCtaFile::xCom_drive() { // Check if the force option was present. bool force=reply.options.size() && (reply.options.at(0).option == "f"); changeStateForDrivesByRegex(m_requestTokens.at(3), lc, cmdlineOutput, false, force); + } else if ("rm" == m_requestTokens.at(2)) { + // Parse the command line for option and drive name. + cta::utils::GetOpThreadSafe::Request req; + for (size_t i=2; i<m_requestTokens.size(); i++) + req.argv.push_back(m_requestTokens.at(i)); + req.optstring = { "f" }; + struct ::option longOptions[] = { { "force", no_argument, 0 , 'f' }, { 0, 0, 0, 0 } }; + req.longopts = longOptions; + auto reply = cta::utils::GetOpThreadSafe::getOpt(req); + // We should have one and only one no-option argument, the drive name. + if (reply.remainder.size() != 1) { + throw cta::exception::UserError(help.str()); + } + // Check if the force option was present. + bool force=reply.options.size() && (reply.options.at(0).option == "f"); + removeDrivesByRegex(m_requestTokens.at(3), lc, cmdlineOutput, force); } else if ("ls" == m_requestTokens.at(2)) { if ((m_requestTokens.size() == 3) || (m_requestTokens.size() == 4)) { // We will dump all the drives, and select the one asked for if needed. @@ -1994,6 +2014,41 @@ void XrdCtaFile::changeStateForDrivesByRegex(const std::string ®ex, } } +//------------------------------------------------------------------------------ +// removeDrivesByRegex +//------------------------------------------------------------------------------ +void XrdCtaFile::removeDrivesByRegex(const std::string ®ex, + log::LogContext &lc, std::stringstream &cmdlineOutput, + const bool isForceSet) { + cta::utils::Regex driveNameRegex(regex.c_str()); + auto driveStates = m_scheduler->getDriveStates(m_cliIdentity, lc); + bool drivesNotFound = true; + for (auto driveState: driveStates) { + const auto regexResult = driveNameRegex.exec(driveState.driveName); + if (regexResult.size()) { + const std::set<int> downDriveStatuses = { + (int)cta::common::dataStructures::DriveStatus::Down, + (int)cta::common::dataStructures::DriveStatus::Shutdown, + (int)cta::common::dataStructures::DriveStatus::Unknown}; + if (downDriveStatuses.count((int)driveState.driveStatus) + || isForceSet ) { + m_scheduler->removeDrive(m_cliIdentity, driveState.driveName, lc); + cmdlineOutput << "Drive " << driveState.driveName << " removed" + << (isForceSet?" (forced)":"") + << "." << std::endl; + } else { + cmdlineOutput << "Drive " << driveState.driveName << " in state " + << cta::common::dataStructures::toString(driveState.driveStatus) + << " and force is not set (skipped)." << std::endl; + } + drivesNotFound = false; + } + } + if (drivesNotFound) { + cmdlineOutput << "Drives not found by regex: " << regex << std::endl; + } +} + //------------------------------------------------------------------------------ // xCom_listpendingarchives //------------------------------------------------------------------------------ diff --git a/xroot_plugins/XrdCtaFile.hpp b/xroot_plugins/XrdCtaFile.hpp index 4b843874c1..c93a1ec6fb 100644 --- a/xroot_plugins/XrdCtaFile.hpp +++ b/xroot_plugins/XrdCtaFile.hpp @@ -375,6 +375,20 @@ protected: void changeStateForDrivesByRegex(const std::string ®ex, log::LogContext &lc, std::stringstream &cmdlineOutput, const bool isMakeUp, const bool isForceSet); + + /** + * Remove drives from the drive register by a given regex. By default drive + * should be in state Down to be removed. If a force flag is set then drive + * will be removed with any state. + * + * @param regex The regex to be used to match drive name. + * @param lc The log context. + * @param cmdlineOutput The string stream to stream output. + * @param isForceSet The boolean value for force parameter. + */ + void removeDrivesByRegex(const std::string ®ex, + log::LogContext &lc, std::stringstream &cmdlineOutput, + const bool isForceSet); /** * Returns the response string properly formatted in a table -- GitLab