From 7363d58fa2c7886fb79ac7d7801a80860c6bf3bc Mon Sep 17 00:00:00 2001 From: Cedric CAFFY <cedric.caffy@cern.ch> Date: Mon, 29 Apr 2019 17:33:28 +0200 Subject: [PATCH] Added the creation of the directory corresponding to the vid of the tape to repack (xroot + local file system) --- scheduler/CMakeLists.txt | 4 +- scheduler/RepackRequestManager.cpp | 16 +++- scheduler/Scheduler.cpp | 93 +++++++++++------- scheduler/Scheduler.hpp | 1 - scheduler/SchedulerTest.cpp | 34 +++---- .../tape/tapeserver/file/CMakeLists.txt | 2 +- .../castor/tape/tapeserver/file/DiskFile.cpp | 94 ++++++++++++++++++- .../castor/tape/tapeserver/file/DiskFile.hpp | 52 ++++++++++ .../file/DiskFileImplementations.hpp | 35 ++++++- .../castor/tape/tapeserver/file/FileTest.cpp | 37 ++++++++ tests/CMakeLists.txt | 2 +- tests/TempDirectory.cpp | 38 ++++++++ tests/TempDirectory.hpp | 34 +++++++ 13 files changed, 375 insertions(+), 67 deletions(-) create mode 100644 tests/TempDirectory.cpp create mode 100644 tests/TempDirectory.hpp diff --git a/scheduler/CMakeLists.txt b/scheduler/CMakeLists.txt index 6f63371d26..9f80a883c3 100644 --- a/scheduler/CMakeLists.txt +++ b/scheduler/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required (VERSION 2.6) include_directories (${CMAKE_CURRENT_SOURCE_DIR} - ${PROJECT_SOURCE_DIR} ${CMAKE_BINARY_DIR}) + ${PROJECT_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${XROOTD_INCLUDE_DIR}) set (CTA_SCHEDULER_SRC_FILES ArchiveJob.cpp @@ -34,7 +34,7 @@ set_property(TARGET ctascheduler PROPERTY VERSION "${CTA_LIBVERSION}") install (TARGETS ctascheduler DESTINATION usr/${CMAKE_INSTALL_LIBDIR}) -target_link_libraries (ctascheduler ctacommon ctaobjectstore ${PROTOBUF3_LIBRARIES} ctaeos) +target_link_libraries (ctascheduler ctacommon ctaobjectstore ${PROTOBUF3_LIBRARIES} ctaeos File) #add_library (ctaschedulerutils SHARED # _old_prototype_DummyScheduler.cpp) diff --git a/scheduler/RepackRequestManager.cpp b/scheduler/RepackRequestManager.cpp index bff93518eb..41d511b5c4 100644 --- a/scheduler/RepackRequestManager.cpp +++ b/scheduler/RepackRequestManager.cpp @@ -38,11 +38,17 @@ void RepackRequestManager::runOnePass(log::LogContext& lc) { if(repackRequest != nullptr){ //We have a RepackRequest that has the status ToExpand, expand it timingList.insertAndReset("expandRepackRequestTime", t); - m_scheduler.expandRepackRequest(repackRequest,timingList,t,lc); - log::ScopedParamContainer params(lc); - params.add("tapeVid",repackRequest->getRepackInfo().vid); - timingList.addToLog(params); - lc.log(log::INFO, "In RepackRequestManager::runOnePass(): Repack Request expanded"); + try{ + m_scheduler.expandRepackRequest(repackRequest,timingList,t,lc); + log::ScopedParamContainer params(lc); + params.add("tapeVid",repackRequest->getRepackInfo().vid); + timingList.addToLog(params); + lc.log(log::INFO, "In RepackRequestManager::runOnePass(): Repack Request expanded"); + } catch(const cta::exception::Exception &e){ + lc.log(log::ERR,e.what()); + repackRequest->fail(); + throw(e); + } } } diff --git a/scheduler/Scheduler.cpp b/scheduler/Scheduler.cpp index ce2415d6cc..c915b3c4f0 100644 --- a/scheduler/Scheduler.cpp +++ b/scheduler/Scheduler.cpp @@ -30,6 +30,8 @@ #include "common/make_unique.hpp" #include "objectstore/RepackRequest.hpp" #include "RetrieveRequestDump.hpp" +#include "tapeserver/castor/tape/tapeserver/file/DiskFileImplementations.hpp" +#include "tapeserver/castor/tape/tapeserver/file/RadosStriperPool.hpp" #include <iostream> #include <sstream> @@ -409,15 +411,6 @@ std::unique_ptr<RepackRequest> Scheduler::getNextRepackRequestToExpand() { return nullptr; } -//------------------------------------------------------------------------------ -// generateRetrieveDstURL -//------------------------------------------------------------------------------ -const std::string Scheduler::generateRetrieveDstURL(const cta::common::dataStructures::DiskFileInfo dfi) const{ - std::ostringstream strStream; - strStream<<"repack:/"<<dfi.path; - return strStream.str(); -} - //------------------------------------------------------------------------------ // expandRepackRequest //------------------------------------------------------------------------------ @@ -446,54 +439,82 @@ void Scheduler::expandRepackRequest(std::unique_ptr<RepackRequest>& repackReques } uint64_t fSeq = repackRequest->m_dbReq->getLastExpandedFSeq() + 1; cta::catalogue::ArchiveFileItor archiveFilesForCatalogue = m_catalogue.getArchiveFilesForRepackItor(repackInfo.vid, fSeq); + std::stringstream dirBufferURL; + dirBufferURL << repackInfo.repackBufferBaseURL << "/" << repackInfo.vid << "/"; + castor::tape::diskFile::DirectoryFactory dirFactory; + std::unique_ptr<castor::tape::diskFile::Directory> dir; + dir.reset(dirFactory.createDirectory(dirBufferURL.str())); + std::set<std::string> filesInDirectory; + if(dir->exist()){ + filesInDirectory = dir->getFilesName(); + } else { + dir->mkdir(); + } while(archiveFilesForCatalogue.hasMore()) { size_t filesCount = 0; uint64_t maxAddedFSeq = 0; std::list<SchedulerDatabase::RepackRequest::Subrequest> retrieveSubrequests; while(filesCount < c_defaultMaxNbFilesForRepack && archiveFilesForCatalogue.hasMore()) { - auto archiveFile = archiveFilesForCatalogue.next(); filesCount++; fSeq++; retrieveSubrequests.push_back(cta::SchedulerDatabase::RepackRequest::Subrequest()); - auto & rsr = retrieveSubrequests.back(); - rsr.archiveFile = archiveFile; - rsr.fSeq = std::numeric_limits<decltype(rsr.fSeq)>::max(); - totalStatsFile.totalBytesToRetrieve += rsr.archiveFile.fileSize; - totalStatsFile.totalFilesToRetrieve += 1; + auto archiveFile = archiveFilesForCatalogue.next(); + auto & retrieveSubRequest = retrieveSubrequests.back(); + + retrieveSubRequest.archiveFile = archiveFile; + retrieveSubRequest.fSeq = std::numeric_limits<decltype(retrieveSubRequest.fSeq)>::max(); + // We have to determine which copynbs we want to rearchive, and under which fSeq we record this file. if (repackInfo.type == RepackType::MoveAndAddCopies || repackInfo.type == RepackType::MoveOnly) { // determine which fSeq(s) (normally only one) lives on this tape. for (auto & tc: archiveFile.tapeFiles) if (tc.vid == repackInfo.vid) { - rsr.copyNbsToRearchive.insert(tc.copyNb); + retrieveSubRequest.copyNbsToRearchive.insert(tc.copyNb); // We make the (reasonable) assumption that the archive file only has one copy on this tape. // If not, we will ensure the subrequest is filed under the lowest fSeq existing on this tape. // This will prevent double subrequest creation (we already have such a mechanism in case of crash and // restart of expansion. - rsr.fSeq = std::min(tc.fSeq, rsr.fSeq); - maxAddedFSeq = std::max(maxAddedFSeq, rsr.fSeq); + retrieveSubRequest.fSeq = std::min(tc.fSeq, retrieveSubRequest.fSeq); totalStatsFile.totalFilesToArchive += 1; - totalStatsFile.totalBytesToArchive += rsr.archiveFile.fileSize; + totalStatsFile.totalBytesToArchive += retrieveSubRequest.archiveFile.fileSize; } } - if (repackInfo.type == RepackType::MoveAndAddCopies || repackInfo.type == RepackType::AddCopiesOnly) { - // We should not get here are the type is filtered at the beginning of the function. - // TODO: add support for expand. - throw cta::exception::Exception("In Scheduler::expandRepackRequest(): expand not yet supported."); + std::stringstream fileName; + fileName << std::setw(9) << std::setfill('0') << retrieveSubRequest.fSeq; + bool createArchiveSubrequest = false; + if(filesInDirectory.count(fileName.str())){ + castor::tape::file::RadosStriperPool radosStriperPool; + castor::tape::diskFile::DiskFileFactory fileFactory("",0,radosStriperPool); + castor::tape::diskFile::ReadFile *fileReader = fileFactory.createReadFile(dirBufferURL.str() + fileName.str()); + if(fileReader->size() == archiveFile.fileSize){ + createArchiveSubrequest = true; + retrieveSubrequests.pop_back(); + //TODO : We don't want to retrieve the file again, create archive subrequest + } } - if ((rsr.fSeq == std::numeric_limits<decltype(rsr.fSeq)>::max()) || rsr.copyNbsToRearchive.empty()) { - log::ScopedParamContainer params(lc); - params.add("fileId", rsr.archiveFile.archiveFileID) - .add("repackVid", repackInfo.vid); - lc.log(log::ERR, "In Scheduler::expandRepackRequest(): no fSeq found for this file on this tape."); - retrieveSubrequests.pop_back(); - } else { - // We found some copies to rearchive. We still have to decide which file path we are going to use. - // File path will be base URL + /<VID>/<fSeq> - std::stringstream fileBufferURL; - fileBufferURL << repackInfo.repackBufferBaseURL << "/" << repackInfo.vid << "/" - << std::setw(9) << std::setfill('0') << rsr.fSeq; - rsr.fileBufferURL = fileBufferURL.str(); + if(!createArchiveSubrequest){ + totalStatsFile.totalBytesToRetrieve += retrieveSubRequest.archiveFile.fileSize; + totalStatsFile.totalFilesToRetrieve += 1; + if (repackInfo.type == RepackType::MoveAndAddCopies || repackInfo.type == RepackType::AddCopiesOnly) { + // We should not get here are the type is filtered at the beginning of the function. + // TODO: add support for expand. + throw cta::exception::Exception("In Scheduler::expandRepackRequest(): expand not yet supported."); + } + if ((retrieveSubRequest.fSeq == std::numeric_limits<decltype(retrieveSubRequest.fSeq)>::max()) || retrieveSubRequest.copyNbsToRearchive.empty()) { + log::ScopedParamContainer params(lc); + params.add("fileId", retrieveSubRequest.archiveFile.archiveFileID) + .add("repackVid", repackInfo.vid); + lc.log(log::ERR, "In Scheduler::expandRepackRequest(): no fSeq found for this file on this tape."); + retrieveSubrequests.pop_back(); + } else { + // We found some copies to rearchive. We still have to decide which file path we are going to use. + // File path will be base URL + /<VID>/<fSeq> + /*std::stringstream fileBufferURL; + fileBufferURL << repackInfo.repackBufferBaseURL << "/" << repackInfo.vid << "/" + << std::setw(9) << std::setfill('0') << rsr.fSeq;*/ + maxAddedFSeq = std::max(maxAddedFSeq,retrieveSubRequest.fSeq); + retrieveSubRequest.fileBufferURL = dirBufferURL.str() + fileName.str(); + } } } // Note: the highest fSeq will be recorded internally in the following call. diff --git a/scheduler/Scheduler.hpp b/scheduler/Scheduler.hpp index 9da22e8fd2..cffd8d3ba2 100644 --- a/scheduler/Scheduler.hpp +++ b/scheduler/Scheduler.hpp @@ -302,7 +302,6 @@ private: std::map<tpType, uint32_t> & existingMountsSummary, std::set<std::string> & tapesInUse, std::list<catalogue::TapeForWriting> & tapeList, double & getTapeInfoTime, double & candidateSortingTime, double & getTapeForWriteTime, log::LogContext & lc); - const std::string generateRetrieveDstURL(const cta::common::dataStructures::DiskFileInfo dfi) const; public: /** diff --git a/scheduler/SchedulerTest.cpp b/scheduler/SchedulerTest.cpp index 7ff994ac94..9a6ef22327 100644 --- a/scheduler/SchedulerTest.cpp +++ b/scheduler/SchedulerTest.cpp @@ -38,6 +38,7 @@ #include "objectstore/JobQueueType.hpp" #include "objectstore/RepackIndex.hpp" #include "tests/TestsCompileTimeSwitches.hpp" +#include "tests/TempDirectory.hpp" #include "common/Timer.hpp" #include "tapeserver/castor/tape/tapeserver/daemon/RecallReportPacker.hpp" #include "objectstore/Algorithms.hpp" @@ -1242,7 +1243,7 @@ TEST_P(SchedulerTest, showqueues) { TEST_P(SchedulerTest, repack) { using namespace cta; - + unitTests::TempDirectory tempDirectory; setupDefaultCatalogue(); Scheduler &scheduler = getScheduler(); @@ -1256,7 +1257,7 @@ TEST_P(SchedulerTest, repack) { // Create and then cancel repack common::dataStructures::SecurityIdentity cliId; std::string tape1 = "Tape"; - scheduler.queueRepack(cliId, tape1, "root://server/repackDir", common::dataStructures::RepackInfo::Type::MoveOnly, lc); + scheduler.queueRepack(cliId, tape1, "file://"+tempDirectory.path(), common::dataStructures::RepackInfo::Type::MoveOnly, lc); { auto repacks = scheduler.getRepacks(); ASSERT_EQ(1, repacks.size()); @@ -1266,7 +1267,7 @@ TEST_P(SchedulerTest, repack) { scheduler.cancelRepack(cliId, tape1, lc); ASSERT_EQ(0, scheduler.getRepacks().size()); // Recreate a repack and get it moved to ToExpand - scheduler.queueRepack(cliId, "Tape2", "root://server/repackDir", common::dataStructures::RepackInfo::Type::MoveOnly, lc); + scheduler.queueRepack(cliId, "Tape2", "file://"+tempDirectory.path(), common::dataStructures::RepackInfo::Type::MoveOnly, lc); { auto repacks = scheduler.getRepacks(); ASSERT_EQ(1, repacks.size()); @@ -1283,6 +1284,7 @@ TEST_P(SchedulerTest, repack) { TEST_P(SchedulerTest, getNextRepackRequestToExpand) { using namespace cta; + unitTests::TempDirectory tempDirectory; setupDefaultCatalogue(); @@ -1294,10 +1296,10 @@ TEST_P(SchedulerTest, getNextRepackRequestToExpand) { // Create a repack request common::dataStructures::SecurityIdentity cliId; std::string tape1 = "Tape"; - scheduler.queueRepack(cliId, tape1, "root://server/repackDir", common::dataStructures::RepackInfo::Type::MoveOnly, lc); + scheduler.queueRepack(cliId, tape1, "file://"+tempDirectory.path(), common::dataStructures::RepackInfo::Type::MoveOnly, lc); std::string tape2 = "Tape2"; - scheduler.queueRepack(cliId,tape2,"root://server/repackDir",common::dataStructures::RepackInfo::Type::AddCopiesOnly,lc); + scheduler.queueRepack(cliId,tape2,"file://"+tempDirectory.path(),common::dataStructures::RepackInfo::Type::AddCopiesOnly,lc); //Test the repack request queued has status Pending ASSERT_EQ(scheduler.getRepack(tape1).status,common::dataStructures::RepackInfo::Status::Pending); @@ -1329,6 +1331,7 @@ TEST_P(SchedulerTest, getNextRepackRequestToExpand) { TEST_P(SchedulerTest, expandRepackRequest) { using namespace cta; + unitTests::TempDirectory tempDirectory; auto &catalogue = getCatalogue(); auto &scheduler = getScheduler(); @@ -1431,7 +1434,7 @@ TEST_P(SchedulerTest, expandRepackRequest) { scheduler.waitSchedulerDbSubthreadsComplete(); { for(uint64_t i = 0; i < nbTapesToRepack ; ++i) { - scheduler.queueRepack(admin,allVid.at(i),"root://repackData/buffer",common::dataStructures::RepackInfo::Type::MoveOnly,lc); + scheduler.queueRepack(admin,allVid.at(i),"file://"+tempDirectory.path(),common::dataStructures::RepackInfo::Type::MoveOnly,lc); } scheduler.waitSchedulerDbSubthreadsComplete(); //scheduler.waitSchedulerDbSubthreadsComplete(); @@ -1468,7 +1471,7 @@ TEST_P(SchedulerTest, expandRepackRequest) { ASSERT_EQ(retrieveJob.request.archiveFileID,archiveFileId++); ASSERT_EQ(retrieveJob.fileSize,compressedFileSize); std::stringstream ss; - ss<<"root://repackData/buffer/"<<allVid.at(i-1)<<"/"<<std::setw(9)<<std::setfill('0')<<j; + ss<<"file://"<<tempDirectory.path()<<"/"<<allVid.at(i-1)<<"/"<<std::setw(9)<<std::setfill('0')<<j; ASSERT_EQ(retrieveJob.request.dstURL, ss.str()); ASSERT_EQ(retrieveJob.tapeCopies[vid].second.copyNb,1); ASSERT_EQ(retrieveJob.tapeCopies[vid].second.checksumType,checksumType); @@ -1569,7 +1572,7 @@ TEST_P(SchedulerTest, expandRepackRequest) { //Testing scheduler retrieve request ASSERT_EQ(schedulerRetrieveRequest.archiveFileID,archiveFileId++); std::stringstream ss; - ss<<"root://repackData/buffer/"<<allVid.at(i-1)<<"/"<<std::setw(9)<<std::setfill('0')<<j; + ss<<"file://"<<tempDirectory.path()<<"/"<<allVid.at(i-1)<<"/"<<std::setw(9)<<std::setfill('0')<<j; ASSERT_EQ(schedulerRetrieveRequest.dstURL,ss.str()); // TODO ASSERT_EQ(schedulerRetrieveRequest.isRepack,true); // TODO ASSERT_EQ(schedulerRetrieveRequest.tapePool,s_tapePoolName); @@ -1642,7 +1645,7 @@ TEST_P(SchedulerTest, expandRepackRequest) { ASSERT_EQ(archiveFile.fileSize,archiveFileSize); ASSERT_EQ(archiveFile.storageClass,s_storageClassName); std::stringstream ss; - ss<<"root://repackData/buffer/"<<allVid.at(tapeIndex-1)<<"/"<<std::setw(9)<<std::setfill('0')<<fileIndex; + ss<<"file://"<<tempDirectory.path()<<"/"<<allVid.at(tapeIndex-1)<<"/"<<std::setw(9)<<std::setfill('0')<<fileIndex; ASSERT_EQ(ar.getSrcURL(),ss.str()); for(auto archiveJob : ar.dumpJobs()){ ASSERT_EQ(archiveJob.status,cta::objectstore::serializers::ArchiveJobStatus::AJS_ToTransferForRepack); @@ -1662,7 +1665,7 @@ TEST_P(SchedulerTest, expandRepackRequest) { TEST_P(SchedulerTest, expandRepackRequestRetrieveFailed) { using namespace cta; using namespace cta::objectstore; - + unitTests::TempDirectory tempDirectory; auto &catalogue = getCatalogue(); auto &scheduler = getScheduler(); auto &schedulerDB = getSchedulerDB(); @@ -1755,9 +1758,8 @@ TEST_P(SchedulerTest, expandRepackRequestRetrieveFailed) { scheduler.waitSchedulerDbSubthreadsComplete(); { - scheduler.queueRepack(admin,vid,"root://repackData/buffer",common::dataStructures::RepackInfo::Type::MoveOnly,lc); + scheduler.queueRepack(admin,vid,"file://"+tempDirectory.path(),common::dataStructures::RepackInfo::Type::MoveOnly,lc); scheduler.waitSchedulerDbSubthreadsComplete(); - //scheduler.waitSchedulerDbSubthreadsComplete(); log::TimingList tl; utils::Timer t; @@ -1903,7 +1905,7 @@ TEST_P(SchedulerTest, expandRepackRequestRetrieveFailed) { TEST_P(SchedulerTest, expandRepackRequestArchiveSuccess) { using namespace cta; using namespace cta::objectstore; - + unitTests::TempDirectory tempDirectory; auto &catalogue = getCatalogue(); auto &scheduler = getScheduler(); auto &schedulerDB = getSchedulerDB(); @@ -1995,7 +1997,7 @@ TEST_P(SchedulerTest, expandRepackRequestArchiveSuccess) { scheduler.waitSchedulerDbSubthreadsComplete(); { - scheduler.queueRepack(admin,vid,"root://repackData/buffer",common::dataStructures::RepackInfo::Type::MoveOnly,lc); + scheduler.queueRepack(admin,vid,"file://"+tempDirectory.path(),common::dataStructures::RepackInfo::Type::MoveOnly,lc); scheduler.waitSchedulerDbSubthreadsComplete(); //scheduler.waitSchedulerDbSubthreadsComplete(); @@ -2149,7 +2151,7 @@ TEST_P(SchedulerTest, expandRepackRequestArchiveSuccess) { TEST_P(SchedulerTest, expandRepackRequestArchiveFailed) { using namespace cta; using namespace cta::objectstore; - + unitTests::TempDirectory tempDirectory; auto &catalogue = getCatalogue(); auto &scheduler = getScheduler(); auto &schedulerDB = getSchedulerDB(); @@ -2241,7 +2243,7 @@ TEST_P(SchedulerTest, expandRepackRequestArchiveFailed) { scheduler.waitSchedulerDbSubthreadsComplete(); { - scheduler.queueRepack(admin,vid,"root://repackData/buffer",common::dataStructures::RepackInfo::Type::MoveOnly,lc); + scheduler.queueRepack(admin,vid,"file://"+tempDirectory.path(),common::dataStructures::RepackInfo::Type::MoveOnly,lc); scheduler.waitSchedulerDbSubthreadsComplete(); log::TimingList tl; diff --git a/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt b/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt index e529ffdced..5dda21d07f 100644 --- a/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt +++ b/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt @@ -62,7 +62,7 @@ target_link_libraries(BasicReadWriteTest add_library(File ${TAPESERVER_FILE_LIBRARY_SRCS}) -target_link_libraries (File XrdCl cryptopp radosstriper) +target_link_libraries (File XrdCl XrdClient cryptopp radosstriper) if(CMAKE_COMPILER_IS_GNUCC) if(GCC_VERSION_GE_4_8_0) diff --git a/tapeserver/castor/tape/tapeserver/file/DiskFile.cpp b/tapeserver/castor/tape/tapeserver/file/DiskFile.cpp index 1417dd0780..78ff7b7e30 100644 --- a/tapeserver/castor/tape/tapeserver/file/DiskFile.cpp +++ b/tapeserver/castor/tape/tapeserver/file/DiskFile.cpp @@ -21,8 +21,9 @@ * @author Castor Dev team, castor-dev@cern.ch *****************************************************************************/ #include <sys/types.h> +#include <sys/stat.h> +#include <xrootd/XrdClient/XrdClientUrlInfo.hh> -#include "castor/tape/tapeserver/file/DiskFile.hpp" #include "castor/tape/tapeserver/file/DiskFileImplementations.hpp" #include "castor/tape/tapeserver/file/RadosStriperPool.hpp" #include "common/exception/Errnum.hpp" @@ -593,4 +594,95 @@ void RadosStriperWriteFile::close() { RadosStriperWriteFile::~RadosStriperWriteFile() throw() {} + +//============================================================================== +// DIRECTORY FACTORY +//============================================================================== +DirectoryFactory::DirectoryFactory(): + m_URLLocalDirectory("^file://(.*)$"), + m_URLXrootDirectory("^(root://.*)$"){} + + +Directory * DirectoryFactory::createDirectory(const std::string& path){ + // URL path parsing + std::vector<std::string> regexResult; + // local file URL? + regexResult = m_URLLocalDirectory.exec(path); + if (regexResult.size()) { + return new LocalDirectory(regexResult[1]); + } + // Xroot URL? + regexResult = m_URLXrootDirectory.exec(path); + if (regexResult.size()) { + return new XRootdDirectory(path); + } + throw cta::exception::Exception("In DirectoryFactory::createDirectory: unknown type of URL"); +} + +//============================================================================== +// LOCAL DIRECTORY +//============================================================================== +LocalDirectory::LocalDirectory(const std::string& path){ + m_URL = path; +} + +void LocalDirectory::mkdir(){ + const int retCode = ::mkdir(m_URL.c_str(),S_IRWXU); + cta::exception::Errnum::throwOnMinusOne(retCode,"In LocalDirectory::mkdir(): failed to create directory at "+m_URL); +} + +bool LocalDirectory::exist(){ + struct stat buffer; + return (stat(m_URL.c_str(), &buffer) == 0); +} + +std::set<std::string> LocalDirectory::getFilesName(){ + std::set<std::string> names; + DIR *dir; + struct dirent *file; + dir = opendir(m_URL.c_str()); + cta::exception::Errnum::throwOnNull(dir,"In LocalDirectory::getFilesName, failed to open directory at "+m_URL); + while((file = readdir(dir)) != NULL){ + char *fileName = file->d_name; + if(strcmp(fileName,".") && strcmp(fileName,"..")){ + names.insert(std::string(file->d_name)); + } + } + cta::exception::Errnum::throwOnMinusOne(::closedir(dir),"In LocalDirectory::getFilesName(), fail to close directory at "+m_URL); + return names; +} + +//============================================================================== +// XROOT DIRECTORY +//============================================================================== +XRootdDirectory::XRootdDirectory(const std::string& path):m_xrootFileSystem(path){ + m_URL = path; + m_truncatedDirectoryURL = this->truncatePath(path); +} + +std::string XRootdDirectory::truncatePath(const std::string &path) { + XrdClientUrlInfo urlInfo(path.c_str()); + return std::string(urlInfo.File.c_str()); +} + +void XRootdDirectory::mkdir() { + XrdCl::XRootDStatus mkdirStatus = m_xrootFileSystem.MkDir(m_truncatedDirectoryURL,XrdCl::MkDirFlags::None,XrdCl::Access::Mode::UR | XrdCl::Access::Mode::UW | XrdCl::Access::Mode::UX,c_xrootTimeout); + cta::exception::XrootCl::throwOnError(mkdirStatus,"In XRootdDirectory::mkdir() : failed to create directory at "+m_URL); +} + +bool XRootdDirectory::exist() { + XrdCl::LocationInfo *locationDirectory; + XrdCl::XRootDStatus statStatus = m_xrootFileSystem.Locate(m_truncatedDirectoryURL,XrdCl::OpenFlags::Flags::Write,locationDirectory,c_xrootTimeout); + cta::exception::XrootCl::throwOnError(statStatus,"In XrootdDirectory::exist(): fail to determine if directory exists."); + if(locationDirectory->GetSize() != 0){ + return true; + } + return false; +} + +std::set<std::string> XRootdDirectory::getFilesName(){ + std::set<std::string> ret; + return ret; +} + }}} //end of namespace diskFile diff --git a/tapeserver/castor/tape/tapeserver/file/DiskFile.hpp b/tapeserver/castor/tape/tapeserver/file/DiskFile.hpp index c650472006..09679b22f2 100644 --- a/tapeserver/castor/tape/tapeserver/file/DiskFile.hpp +++ b/tapeserver/castor/tape/tapeserver/file/DiskFile.hpp @@ -28,6 +28,7 @@ #include <cryptopp/rsa.h> #include <memory> #include <stdint.h> +#include <set> /* * This file only contains the interface declaration of the base classes * the real implementation, which depends on many includes is hidden in @@ -47,6 +48,7 @@ namespace castor { class ReadFile; class WriteFile; + class Directory; /** * Factory class deciding on the type of read/write file type @@ -146,6 +148,56 @@ namespace castor { std::string m_URL; }; + /** + * Factory class deciding what type of Directory subclass + * to instanciate based on the URL passed + */ + class DirectoryFactory{ + typedef cta::utils::Regex Regex; + public: + DirectoryFactory(); + + /** + * Returns the correct directory subclass regarding the path passed in parameter + * @param path the path of the directory to manage + * @return the Directory subclass instance regarding the path passed in parameter + * @throws cta::exception if the path provided does not allow to determine which instance of + * Directory will be instanciated. + */ + Directory * createDirectory(const std::string &path); + + private: + Regex m_URLLocalDirectory; + Regex m_URLXrootDirectory; + }; + + + class Directory { + public: + /** + * Creates a directory + * @throws an exception if the directory could not have been created + */ + virtual void mkdir() = 0; + /** + * Check if the directory exist + * @return true if the directory exists, false otherwise + */ + virtual bool exist() = 0; + /** + * Return all the names of the files present in the directory + * @return + */ + virtual std::set<std::string> getFilesName() = 0; + + virtual ~Directory() throw() {} + protected: + /** + * Storage for the URL + */ + std::string m_URL; + }; + } //end of namespace diskFile }} diff --git a/tapeserver/castor/tape/tapeserver/file/DiskFileImplementations.hpp b/tapeserver/castor/tape/tapeserver/file/DiskFileImplementations.hpp index 6646cfbe83..1dca291626 100644 --- a/tapeserver/castor/tape/tapeserver/file/DiskFileImplementations.hpp +++ b/tapeserver/castor/tape/tapeserver/file/DiskFileImplementations.hpp @@ -23,9 +23,9 @@ #pragma once -#include "castor/tape/tapeserver/file/DiskFile.hpp" -#include "castor/tape/tapeserver/file/Structures.hpp" -#include "castor/tape/tapeserver/daemon/VolumeInfo.hpp" +#include "tapeserver/castor/tape/tapeserver/file/DiskFile.hpp" +#include "tapeserver/castor/tape/tapeserver/file/Structures.hpp" +#include "tapeserver/castor/tape/tapeserver/daemon/VolumeInfo.hpp" #include "common/exception/XrootCl.hpp" #include "common/exception/Exception.hpp" #include <xrootd/XrdCl/XrdClFile.hh> @@ -174,6 +174,33 @@ namespace castor { std::string m_osd; size_t m_writePosition; }; - } //end of namespace diskFile + + class LocalDirectory: public Directory { + public: + LocalDirectory(const std::string& path); + virtual void mkdir() override; + virtual bool exist() override; + virtual std::set<std::string> getFilesName() override; + }; + class XRootdDirectory: public Directory{ + public: + XRootdDirectory(const std::string& path); + virtual void mkdir() override; + virtual bool exist() override; + virtual std::set<std::string> getFilesName() override; + private: + /** + * Remove the root://.../ from the path passed in parameter + * @param path the path to truncate + * @return the truncated path + */ + std::string truncatePath(const std::string &path); + + XrdCl::FileSystem m_xrootFileSystem; + std::string m_truncatedDirectoryURL; // root://.../ part of the path is removed + const uint16_t c_xrootTimeout = 15; + }; + + } //end of namespace diskFile }} diff --git a/tapeserver/castor/tape/tapeserver/file/FileTest.cpp b/tapeserver/castor/tape/tapeserver/file/FileTest.cpp index f9d05ae973..d8c6764783 100644 --- a/tapeserver/castor/tape/tapeserver/file/FileTest.cpp +++ b/tapeserver/castor/tape/tapeserver/file/FileTest.cpp @@ -32,6 +32,7 @@ #include "common/exception/Exception.hpp" #include "scheduler/ArchiveJob.hpp" #include "scheduler/RetrieveJob.hpp" +#include "DiskFileImplementations.hpp" #include <gtest/gtest.h> #include <memory> @@ -302,4 +303,40 @@ namespace unitTests { ASSERT_EQ(strncmp(data1, data2, res1), 0); } while(res1 || res2); } + + TEST(ctaDirectoryTests, directoryExist) { + castor::tape::diskFile::LocalDirectory dir("/tmp/"); + ASSERT_TRUE(dir.exist()); + + castor::tape::diskFile::LocalDirectory dirNotExist("/AZERTY/"); + ASSERT_FALSE(dirNotExist.exist()); + } + + TEST(ctaDirectoryTests, directoryCreate){ + const char * dirTestPath = "/tmp/testDir"; + ::rmdir(dirTestPath); + castor::tape::diskFile::LocalDirectory dir(dirTestPath); + ASSERT_NO_THROW(dir.mkdir()); + ::rmdir(dirTestPath); + } + + TEST(ctaDirectoryTests, directoryFailCreate){ + const char * dirTestPath = "//WRONG/PATH"; + castor::tape::diskFile::LocalDirectory dir(dirTestPath); + ASSERT_THROW(dir.mkdir(),cta::exception::Errnum); + } + + TEST(ctaDirectoryTests, directoryGetFilesName){ + std::string dirTestPath = "/tmp/directoryGetFilesNames"; + std::string rmCommand = "rm -rf "+dirTestPath; + ::system(rmCommand.c_str()); + castor::tape::diskFile::LocalDirectory dir(dirTestPath); + ASSERT_NO_THROW(dir.mkdir()); + char filePath[] = "/tmp/directoryGetFilesNames/fileXXXXXX"; + int fd = ::mkstemp(filePath); + cta::exception::Errnum::throwOnMinusOne(fd,"In directoryGetFilesName, fail mkstemp"); + ::close(fd); + ASSERT_EQ(1,dir.getFilesName().size()); + ::unlink(filePath); + } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f0fbe9917c..18c79e2aef 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -82,7 +82,7 @@ if (OCCI_SUPPORT) endif (OCCI_SUPPORT) add_library(unitTestHelper - TempFile.cpp) + TempFile.cpp TempDirectory.cpp) add_library(systemTestHelperTests SHARED SubprocessSystemTests.cpp) diff --git a/tests/TempDirectory.cpp b/tests/TempDirectory.cpp new file mode 100644 index 0000000000..e8c9b029b2 --- /dev/null +++ b/tests/TempDirectory.cpp @@ -0,0 +1,38 @@ +/** + * The CERN Tape Archive (CTA) project + * Copyright © 2018 CERN + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "TempDirectory.hpp" +#include "common/exception/Errnum.hpp" + +namespace unitTests { + +TempDirectory::TempDirectory(){ + char path[] = "/tmp/testCTADir-XXXXXX"; + char *dirPath = ::mkdtemp(path); + cta::exception::Errnum::throwOnNull(dirPath,"In TempDirectory::TempDirectory() failed to mkdtemp: "); + m_path = std::string(dirPath); +} + +TempDirectory::~TempDirectory(){ + if(m_path.size()){ + std::string rmCommand = "rm -rf "+m_path; + ::system(rmCommand.c_str()); + } +} + +} \ No newline at end of file diff --git a/tests/TempDirectory.hpp b/tests/TempDirectory.hpp new file mode 100644 index 0000000000..c9f93f87f0 --- /dev/null +++ b/tests/TempDirectory.hpp @@ -0,0 +1,34 @@ +/** + * The CERN Tape Archive (CTA) project + * Copyright © 2018 CERN + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once +#include <string> +namespace unitTests { + +class TempDirectory{ + public: + TempDirectory(); + TempDirectory(const std::string& path): m_path(path) {} + std::string path() {return m_path; } + ~TempDirectory(); + private: + std::string m_path; +}; + +} + -- GitLab