diff --git a/objectstore/DriveRegister.cpp b/objectstore/DriveRegister.cpp index 9d086f6e778fb358fa97d7412e63916485a5179d..89526e960a5b84d0fdc5057d6558030690e7243d 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 b8a9a234a6e469fbf03aaa7ab980fff8a4b61863..a7befdd8e3a87e06ad328c54d9b246e3c90d5100 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 46bce86c527ad588c0f5f78be65f4d965da14c1e..9eb820dc707f850aa0c22aa5f65a380312981d6a 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 e4c5ee9e98f27b78362f1e0ec4cb397595c7fbe8..8c10b2294340059ef24ca345363b9dd74595adb7 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 6623adec25e57f356170957ca0351df79d7019d4..d8bac3f0abe16a8b5eea1a9c8b3212692f4fd0be 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 49a9f93ee29cce89fd462bc83feb474400cca052..db4fa6f16dddd8c08dfbc0dcacd1b5301b4a05e9 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 ed15f63eb2d551e8d6abc22d44605f7da1b2e3d7..f6c8914285ec1761894eac0785611dc3b71482b7 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 6d225af666835bcacc894ef760895ad315b72de0..e185bfc457d3ce68fd5536f78c891e17d9d81782 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 4b843874c1970127cd90002cf3afc8e616712632..c93a1ec6fb960a7e86f4094631f4adfaa960822c 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