From 032e3261de3b941350c610cd3177a407ada2d53d Mon Sep 17 00:00:00 2001 From: Cedric CAFFY <cedric.caffy@hotmail.fr> Date: Thu, 4 Apr 2019 04:29:50 -0400 Subject: [PATCH] Added a check while mounting a tape for writing : if some tape files located in the writing tape are not superseded by others, the write session will fail. Note: The test CatalogueTest.exist_non_superseded_files_after_fseq is supposed to fail. --- catalogue/Catalogue.hpp | 17 ++ catalogue/CatalogueRetryWrapper.hpp | 9 + catalogue/CatalogueTest.cpp | 112 ++++++++++++ catalogue/DummyCatalogue.hpp | 4 +- catalogue/RdbmsCatalogue.cpp | 82 +++++++++ catalogue/RdbmsCatalogue.hpp | 12 +- scheduler/ArchiveMount.cpp | 10 ++ scheduler/ArchiveMount.hpp | 8 + .../daemon/DataTransferSessionTest.cpp | 159 ++++++++++++++++++ .../daemon/TapeWriteSingleThread.cpp | 3 +- 10 files changed, 413 insertions(+), 3 deletions(-) diff --git a/catalogue/Catalogue.hpp b/catalogue/Catalogue.hpp index 318cdbbd28..f415ab968d 100644 --- a/catalogue/Catalogue.hpp +++ b/catalogue/Catalogue.hpp @@ -336,6 +336,14 @@ public: * @param vid The volume identifier of the tape to be reclaimed. */ virtual void reclaimTape(const common::dataStructures::SecurityIdentity &admin, const std::string &vid) = 0; + + /** + * This method should ONLY be used for TESTS. It does exactly the same as the real reclaimTape but it does not verify the + * SUPERSEDED_BY_VID and SUPERSEDED_BY_FSEQ attributes + * @param admin The administrator. + * @param vid The volume identifier of the tape to be reclaimed. + */ + virtual void fakeReclaimTapeForTests(const common::dataStructures::SecurityIdentity& admin, const std::string &vid) = 0; virtual void modifyTapeMediaType(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &mediaType) = 0; virtual void modifyTapeVendor(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &vendor) = 0; @@ -589,6 +597,15 @@ public: * @return True if the tape exists. */ virtual bool tapeExists(const std::string &vid) const = 0; + + /** + * Returns true if non superseded files exist after fSeq in the tape where vid is passed in parameter + * + * @param vid the vid of the tape to check if non superseded files exist after fSeq + * @param fSeq the fSeq after which we want to check if non superseded files exist + * @return true if non superseded files exist, false otherwise + */ + virtual bool existNonSupersededFilesAfterFSeq(const std::string &vid, const uint64_t fSeq) const = 0; }; // class Catalogue diff --git a/catalogue/CatalogueRetryWrapper.hpp b/catalogue/CatalogueRetryWrapper.hpp index e192baafca..4800ff60bb 100644 --- a/catalogue/CatalogueRetryWrapper.hpp +++ b/catalogue/CatalogueRetryWrapper.hpp @@ -224,6 +224,10 @@ public: void reclaimTape(const common::dataStructures::SecurityIdentity &admin, const std::string &vid) override { return retryOnLostConnection(m_log, [&]{return m_catalogue->reclaimTape(admin, vid);}, m_maxTriesToConnect); } + + void fakeReclaimTapeForTests(const common::dataStructures::SecurityIdentity &admin, const std::string &vid) override { + return retryOnLostConnection(m_log, [&]{return m_catalogue->fakeReclaimTapeForTests(admin, vid);}, m_maxTriesToConnect); + } void modifyTapeMediaType(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &mediaType) override { return retryOnLostConnection(m_log, [&]{return m_catalogue->modifyTapeMediaType(admin, vid, mediaType);}, m_maxTriesToConnect); @@ -376,6 +380,11 @@ public: bool tapeExists(const std::string &vid) const override { return retryOnLostConnection(m_log, [&]{return m_catalogue->tapeExists(vid);}, m_maxTriesToConnect); } + + bool existNonSupersededFilesAfterFSeq(const std::string& vid, const uint64_t fSeq) const override { + return retryOnLostConnection(m_log,[&]{return m_catalogue->existNonSupersededFilesAfterFSeq(vid,fSeq);},m_maxTriesToConnect); + } + protected: diff --git a/catalogue/CatalogueTest.cpp b/catalogue/CatalogueTest.cpp index ffd0827a4b..b090af865f 100644 --- a/catalogue/CatalogueTest.cpp +++ b/catalogue/CatalogueTest.cpp @@ -11512,6 +11512,118 @@ TEST_P(cta_catalogue_CatalogueTest, reclaimTape_full_lastFSeq_1_one_tape_file_su } } +TEST_P(cta_catalogue_CatalogueTest, exist_non_superseded_files_after_fseq) { + using namespace cta; + + const std::string diskInstanceName1 = "disk_instance_1"; + + ASSERT_TRUE(m_catalogue->getTapes().empty()); + + const std::string vid1 = "VID123"; + const std::string vid2 = "VID234"; + const std::string mediaType = "media_type"; + const std::string vendor = "vendor"; + const std::string logicalLibraryName = "logical_library_name"; + const std::string tapePoolName = "tape_pool_name"; + const std::string vo = "vo"; + const uint64_t capacityInBytes = (uint64_t)10 * 1000 * 1000 * 1000 * 1000; + const bool disabledValue = true; + const bool fullValue = false; + const std::string createTapeComment = "Create tape"; + + m_catalogue->createLogicalLibrary(m_admin, logicalLibraryName, "Create logical library"); + m_catalogue->createTapePool(m_admin, tapePoolName, vo, 2, true, "Create tape pool"); + m_catalogue->createTape(m_admin, vid1, mediaType, vendor, logicalLibraryName, tapePoolName, capacityInBytes, + disabledValue, fullValue, createTapeComment); + + //A tape with no tape file have no files after FSeq 0 + ASSERT_FALSE(m_catalogue->existNonSupersededFilesAfterFSeq(vid1,0)); + + const uint64_t archiveFileId = 1234; + + ASSERT_FALSE(m_catalogue->getArchiveFilesItor().hasMore()); + ASSERT_THROW(m_catalogue->getArchiveFileById(archiveFileId), exception::Exception); + + common::dataStructures::StorageClass storageClass; + storageClass.diskInstance = diskInstanceName1; + storageClass.name = "storage_class"; + storageClass.nbCopies = 1; + storageClass.comment = "Create storage class"; + m_catalogue->createStorageClass(m_admin, storageClass); + + /* + * Insert a file in the tape vid1 + */ + { + const uint64_t archiveFileSize = 1; + const std::string tapeDrive = "tape_drive"; + const std::string checksumType = "checksum_type"; + const std::string checksumValue = "checksum_value"; + + auto file1WrittenUP=cta::make_unique<cta::catalogue::TapeFileWritten>(); + auto & file1Written = *file1WrittenUP; + std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet; + file1WrittenSet.insert(file1WrittenUP.release()); + file1Written.archiveFileId = archiveFileId; + file1Written.diskInstance = storageClass.diskInstance; + file1Written.diskFileId = "5678"; + file1Written.diskFilePath = "/public_dir/public_file"; + file1Written.diskFileUser = "public_disk_user"; + file1Written.diskFileGroup = "public_disk_group"; + file1Written.size = archiveFileSize; + file1Written.checksumType = checksumType; + file1Written.checksumValue = checksumValue; + file1Written.storageClassName = storageClass.name; + file1Written.vid = vid1; + file1Written.fSeq = 1; + file1Written.blockId = 4321; + file1Written.compressedSize = 1; + file1Written.copyNb = 1; + file1Written.tapeDrive = tapeDrive; + m_catalogue->filesWrittenToTape(file1WrittenSet); + } + //One file written : this file is not superseded by another one, existNonSupersededFilesAfterFSeq = true + ASSERT_TRUE(m_catalogue->existNonSupersededFilesAfterFSeq(vid1,0)); + //No file after the only file inserted, existNonSupersededFilesAfterFseq = false + ASSERT_FALSE(m_catalogue->existNonSupersededFilesAfterFSeq(vid1,1)); + + //Insert another file in another tape that will supersed the first one in vid1 + { + m_catalogue->createTape(m_admin, vid2, mediaType, vendor, logicalLibraryName, tapePoolName, capacityInBytes, + disabledValue, fullValue, createTapeComment); + const uint64_t archiveFileSize = 1; + const std::string tapeDrive = "tape_drive"; + const std::string checksumType = "checksum_type"; + const std::string checksumValue = "checksum_value"; + + auto file1WrittenUP=cta::make_unique<cta::catalogue::TapeFileWritten>(); + auto & file1Written = *file1WrittenUP; + std::set<cta::catalogue::TapeItemWrittenPointer> file1WrittenSet; + file1WrittenSet.insert(file1WrittenUP.release()); + file1Written.archiveFileId = archiveFileId; + file1Written.diskInstance = storageClass.diskInstance; + file1Written.diskFileId = "5678"; + file1Written.diskFilePath = "/public_dir/public_file"; + file1Written.diskFileUser = "public_disk_user"; + file1Written.diskFileGroup = "public_disk_group"; + file1Written.size = archiveFileSize; + file1Written.checksumType = checksumType; + file1Written.checksumValue = checksumValue; + file1Written.storageClassName = storageClass.name; + file1Written.vid = vid2; + file1Written.fSeq = 1; + file1Written.blockId = 4321; + file1Written.compressedSize = 1; + file1Written.copyNb = 1; + file1Written.tapeDrive = tapeDrive; + m_catalogue->filesWrittenToTape(file1WrittenSet); + } + //The tape files written to tape vid2 are not superseded by any file, but the tape files in vid1 + //are superseded by the tape files in vid2 + ASSERT_FALSE(m_catalogue->existNonSupersededFilesAfterFSeq(vid1,0)); + ASSERT_TRUE(m_catalogue->existNonSupersededFilesAfterFSeq(vid2,0)); +} + TEST_P(cta_catalogue_CatalogueTest, ping) { using namespace cta; diff --git a/catalogue/DummyCatalogue.hpp b/catalogue/DummyCatalogue.hpp index 60c7bd35a0..d6a8cd5756 100644 --- a/catalogue/DummyCatalogue.hpp +++ b/catalogue/DummyCatalogue.hpp @@ -105,6 +105,7 @@ public: const std::string &storageClassName, const common::dataStructures::UserIdentity &user) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } common::dataStructures::RetrieveFileQueueCriteria prepareToRetrieveFile(const std::string& instanceName, const uint64_t archiveFileId, const common::dataStructures::UserIdentity& user, log::LogContext &lc) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } void reclaimTape(const common::dataStructures::SecurityIdentity& admin, const std::string& vid) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } + void fakeReclaimTapeForTests(const common::dataStructures::SecurityIdentity& admin, const std::string& vid) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } void setTapeDisabled(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const bool disabledValue) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } void setTapeFull(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const bool fullValue) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } void setTapePoolEncryption(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const bool encryptionValue) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } @@ -113,7 +114,8 @@ public: void tapeMountedForArchive(const std::string& vid, const std::string& drive) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } void tapeMountedForRetrieve(const std::string& vid, const std::string& drive) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } bool tapePoolExists(const std::string& tapePoolName) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } - + bool existNonSupersededFilesAfterFSeq(const std::string& vid, const uint64_t fSeq) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } + // Special functions for unit tests. void addEnabledTape(const std::string & vid) { threading::MutexLocker lm(m_tapeEnablingMutex); diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp index e7bead2479..ccd3b89e92 100644 --- a/catalogue/RdbmsCatalogue.cpp +++ b/catalogue/RdbmsCatalogue.cpp @@ -1791,6 +1791,31 @@ bool RdbmsCatalogue::tapeExists(rdbms::Conn &conn, const std::string &vid) const } } +bool RdbmsCatalogue::existNonSupersededFilesAfterFSeq(const std::string& vid, const uint64_t fSeq) const { + try{ + auto conn = m_connPool.getConn(); + const char *const sql = + "SELECT VID " + "FROM TAPE_FILE " + "WHERE " + "VID = :VID AND " + "FSEQ > :FSEQ AND " + "SUPERSEDED_BY_VID IS NULL AND " + "SUPERSEDED_BY_FSEQ IS NULL"; + auto stmt = conn.createStmt(sql); + stmt.bindString(":VID",vid); + stmt.bindUint64(":FSEQ",fSeq); + auto rset = stmt.executeQuery(); + return rset.next(); + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str()); + throw; + } +} + + //------------------------------------------------------------------------------ // deleteTape //------------------------------------------------------------------------------ @@ -2263,6 +2288,63 @@ void RdbmsCatalogue::reclaimTape(const common::dataStructures::SecurityIdentity } } +//------------------------------------------------------------------------------ +//fakeReclaimTapeForTests +//------------------------------------------------------------------------------ + +void RdbmsCatalogue::fakeReclaimTapeForTests(const common::dataStructures::SecurityIdentity& admin, const std::string& vid) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE SET " + "DATA_IN_BYTES = 0," + "LAST_FSEQ = 0," + "IS_FULL = '0'," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "VID = :UPDATE_VID AND " + "IS_FULL != '0'"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql); + stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt.bindUint64(":LAST_UPDATE_TIME", now); + stmt.bindString(":UPDATE_VID", vid); + stmt.executeNonQuery(); + + // If the update failed due to a user error + if(0 == stmt.getNbAffectedRows()) { + // Try to determine the user error + // + // Please note that this is a best effort diagnosis because there is no + // lock on the database to prevent other concurrent updates from taking + // place on the TAPE and TAPE_FILE tables + TapeSearchCriteria searchCriteria; + searchCriteria.vid = vid; + const auto tapes = getTapes(conn, searchCriteria); + + if(tapes.empty()) { + throw exception::UserError(std::string("Cannot reclaim tape ") + vid + " because it does not exist"); + } else { + if(!tapes.front().full) { + throw exception::UserError(std::string("Cannot reclaim tape ") + vid + " because it is not FULL"); + } else { + throw exception::UserError(std::string("Cannot reclaim tape ") + vid + " because there is at least one tape" + " file in the catalogue that is on the tape"); + } + } + } + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str()); + throw; + } +} + + //------------------------------------------------------------------------------ // getTapeLogFromRset //------------------------------------------------------------------------------ diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp index 779e7ca789..96cd3363da 100644 --- a/catalogue/RdbmsCatalogue.hpp +++ b/catalogue/RdbmsCatalogue.hpp @@ -330,7 +330,7 @@ public: * @param vid The volume identifier of the tape to be reclaimed. */ void reclaimTape(const common::dataStructures::SecurityIdentity &admin, const std::string &vid) override; - + void fakeReclaimTapeForTests(const common::dataStructures::SecurityIdentity& admin, const std::string& vid) override; void modifyTapeMediaType(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &mediaType) override; void modifyTapeVendor(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &vendor) override; void modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &logicalLibraryName) override; @@ -713,6 +713,16 @@ protected: * @return True if the tape exists. */ bool tapeExists(rdbms::Conn &conn, const std::string &vid) const; + + /** + * Returns true if non superseded files exist after fSeq in the tape where vid is passed in parameter + * + * @param vid the vid of the tape to check if non superseded files exist after fSeq + * @param fSeq the fSeq after which we want to check if non superseded files exist + * @return true if non superseded files exist, false otherwise + */ + bool existNonSupersededFilesAfterFSeq(const std::string& vid, const uint64_t fSeq) const override; + /** * Returns the list of tapes that meet the specified search criteria. diff --git a/scheduler/ArchiveMount.cpp b/scheduler/ArchiveMount.cpp index 7e61432be7..0e3cd30889 100644 --- a/scheduler/ArchiveMount.cpp +++ b/scheduler/ArchiveMount.cpp @@ -104,6 +104,16 @@ uint32_t cta::ArchiveMount::getNbFiles() const { return m_dbMount->nbFilesCurrentlyOnTape; } +//------------------------------------------------------------------------------ +// checkTapeFSeqForWriting +//------------------------------------------------------------------------------ +void cta::ArchiveMount::checkTapeFSeqForWriting(uint64_t fSeq) const { + if(m_catalogue.existNonSupersededFilesAfterFSeq(getVid(),fSeq)){ + throw cta::exception::Exception("Non superseded files have been detected in the tape "+getVid() +" after "+std::to_string(fSeq)); + } +} + + //------------------------------------------------------------------------------ // createDiskReporter //------------------------------------------------------------------------------ diff --git a/scheduler/ArchiveMount.hpp b/scheduler/ArchiveMount.hpp index 62d2e7f83e..5f93bacb4d 100644 --- a/scheduler/ArchiveMount.hpp +++ b/scheduler/ArchiveMount.hpp @@ -172,6 +172,14 @@ namespace cta { */ uint32_t getNbFiles() const override; + /** + * Checks wether the writing is possible after the FSeq passed in parameter + * If TapeFiles are located after FSeq and are not superseded by other TapeFiles + * we throw an exception : We don't want these files to be lost. + * @param fSeq : The fSeq after which we want to check if the writing is possible + */ + void checkTapeFSeqForWriting(uint64_t fSeq) const; + /** * Creates a disk reporter for the ArchiveJob (this is a wrapper). * @param URL: report address diff --git a/tapeserver/castor/tape/tapeserver/daemon/DataTransferSessionTest.cpp b/tapeserver/castor/tape/tapeserver/daemon/DataTransferSessionTest.cpp index 1f75a42c24..ac371d79d0 100644 --- a/tapeserver/castor/tape/tapeserver/daemon/DataTransferSessionTest.cpp +++ b/tapeserver/castor/tape/tapeserver/daemon/DataTransferSessionTest.cpp @@ -1848,6 +1848,165 @@ TEST_P(DataTransferSessionTest, DataTransferSessionTapeFullOnFlushMigration) { "mountTotalReadRetries=\"25\" mountTotalWriteRetries=\"25\" mountWriteTransients=\"10\"")); } +TEST_P(DataTransferSessionTest, WriteDataInTapeWithNonSupersededFilesOnIt) { + // 0) Prepare the logger for everyone + cta::log::StringLogger logger("dummy","tapeServerUnitTest",cta::log::DEBUG); + cta::log::LogContext logContext(logger); + + setupDefaultCatalogue(); + // 1) prepare the fake scheduler + std::string vid = s_vid; + std::string vid2 = s_vid+"2"; + // cta::MountType::Enum mountType = cta::MountType::RETRIEVE; + + // 3) Prepare the necessary environment (logger, plus system wrapper), + castor::tape::System::mockWrapper mockSys; + mockSys.delegateToFake(); + mockSys.disableGMockCallsCounting(); + mockSys.fake.setupForVirtualDriveSLC6(); + + // 4) Create the scheduler + auto & catalogue = getCatalogue(); + auto & scheduler = getScheduler(); + + // Always use the same requester + const cta::common::dataStructures::SecurityIdentity requester("user", "group"); + + // List to remember the path of each remote file so that the existance of the + // files can be tested for at the end of the test + std::list<std::string> remoteFilePaths; + + // 5) Create the environment for the migration to happen (library + tape) + const std::string libraryComment = "Library comment"; + catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName, + libraryComment); + { + auto libraries = catalogue.getLogicalLibraries(); + ASSERT_EQ(1, libraries.size()); + ASSERT_EQ(s_libraryName, libraries.front().name); + ASSERT_EQ(libraryComment, libraries.front().comment); + } + const uint64_t capacityInBytes = 12345678; + const std::string tapeComment = "Tape comment"; + bool disabled = false; + bool full = false; + catalogue.createTape(s_adminOnAdminHost, s_vid, s_mediaType, s_vendor, s_libraryName, s_tapePoolName, capacityInBytes, + disabled, full, tapeComment); + catalogue.createTape(s_adminOnAdminHost, vid2, s_mediaType, s_vendor, s_libraryName, s_tapePoolName, capacityInBytes, + disabled, full, tapeComment); + + // Create the mount criteria + catalogue.createMountPolicy(requester, "immediateMount", 1000, 0, 1000, 0, 1, "Policy comment"); + catalogue.createRequesterMountRule(requester, "immediateMount", s_diskInstance, requester.username, "Rule comment"); + + //delete is unnecessary + //pointer with ownership will be passed to the application, + //which will do the delete + mockSys.fake.m_pathToDrive["/dev/nst0"] = new castor::tape::tapeserver::drive::FakeDrive(); + + // We can prepare files for writing on the drive. + // Tempfiles are in this scope so they are kept alive + std::vector<std::unique_ptr<unitTests::TempFile>> sourceFiles; + std::list<uint64_t> archiveFileIds; + { + // Label the tape + castor::tape::tapeFile::LabelSession ls(*mockSys.fake.m_pathToDrive["/dev/nst0"], s_vid, false); + catalogue.tapeLabelled(s_vid, "T10D6116"); + mockSys.fake.m_pathToDrive["/dev/nst0"]->rewind(); + + // Create the files and schedule the archivals + for(int fseq=1; fseq <= 10 ; fseq ++) { + // Create a source file. + sourceFiles.emplace_back(cta::make_unique<unitTests::TempFile>()); + sourceFiles.back()->randomFill(1000); + remoteFilePaths.push_back(sourceFiles.back()->path()); + // Schedule the archival of the file + cta::common::dataStructures::ArchiveRequest ar; + ar.checksumType="ADLER32"; + ar.checksumValue=sourceFiles.back()->adler32(); + ar.storageClass=s_storageClassName; + ar.srcURL=std::string("file://") + sourceFiles.back()->path(); + ar.requester.name = requester.username; + ar.requester.group = "group"; + ar.fileSize = 1000; + ar.diskFileID = std::to_string(fseq); + ar.diskFileInfo.path = "y"; + ar.diskFileInfo.owner = "z"; + ar.diskFileInfo.group = "g"; + const auto archiveFileId = scheduler.checkAndGetNextArchiveFileId(s_diskInstance, ar.storageClass, ar.requester, logContext); + archiveFileIds.push_back(archiveFileId); + scheduler.queueArchiveWithGivenId(archiveFileId,s_diskInstance,ar,logContext); + } + } + scheduler.waitSchedulerDbSubthreadsComplete(); + // Report the drive's existence and put it up in the drive register. + cta::tape::daemon::TpconfigLine driveConfig("T10D6116", "TestLogicalLibrary", "/dev/tape_T10D6116", "manual"); + cta::common::dataStructures::DriveInfo driveInfo; + driveInfo.driveName=driveConfig.unitName; + driveInfo.logicalLibrary=driveConfig.logicalLibrary; + driveInfo.host="host"; + // We need to create the drive in the registry before being able to put it up. + scheduler.reportDriveStatus(driveInfo, cta::common::dataStructures::MountType::NoMount, cta::common::dataStructures::DriveStatus::Down, logContext); + scheduler.setDesiredDriveState(s_adminOnAdminHost, driveConfig.unitName, true, false, logContext); + + // Create the data transfer session + DataTransferConfig castorConf; + castorConf.bufsz = 1024*1024; // 1 MB memory buffers + castorConf.nbBufs = 10; + castorConf.bulkRequestRecallMaxBytes = UINT64_C(100)*1000*1000*1000; + castorConf.bulkRequestRecallMaxFiles = 1000; + castorConf.bulkRequestMigrationMaxBytes = UINT64_C(100)*1000*1000*1000; + castorConf.bulkRequestMigrationMaxFiles = 1000; + castorConf.nbDiskThreads = 1; + cta::log::DummyLogger dummyLog("dummy", "dummy"); + cta::mediachanger::MediaChangerFacade mc(dummyLog); + cta::server::ProcessCap capUtils; + castor::messages::TapeserverProxyDummy initialProcess; + DataTransferSession sess("tapeHost", logger, mockSys, driveConfig, mc, initialProcess, capUtils, castorConf, scheduler); + sess.execute(); + + catalogue.setTapeFull(requester,vid,true); + //Fake reclaim the tape to reset tape's counters + catalogue.fakeReclaimTapeForTests(requester,vid); + + //Try to write files on tape vid = TstVid + // Create the files and schedule the archivals + { + for(int fseq=1; fseq <= 10 ; fseq ++) { + // Create a source file. + sourceFiles.emplace_back(cta::make_unique<unitTests::TempFile>()); + sourceFiles.back()->randomFill(1000); + remoteFilePaths.push_back(sourceFiles.back()->path()); + // Schedule the archival of the file + cta::common::dataStructures::ArchiveRequest ar; + ar.checksumType="ADLER32"; + ar.checksumValue=sourceFiles.back()->adler32(); + ar.storageClass=s_storageClassName; + ar.srcURL=std::string("file://") + sourceFiles.back()->path(); + ar.requester.name = requester.username; + ar.requester.group = "group"; + ar.fileSize = 1000; + ar.diskFileID = std::to_string(fseq); + ar.diskFileInfo.path = "y"; + ar.diskFileInfo.owner = "z"; + ar.diskFileInfo.group = "g"; + const auto archiveFileId = scheduler.checkAndGetNextArchiveFileId(s_diskInstance, ar.storageClass, ar.requester, logContext); + archiveFileIds.push_back(archiveFileId); + scheduler.queueArchiveWithGivenId(archiveFileId,s_diskInstance,ar,logContext); + } + } + scheduler.waitSchedulerDbSubthreadsComplete(); + { + DataTransferSession sess("tapeHost2", logger, mockSys, driveConfig, mc, initialProcess, capUtils, castorConf, scheduler); + sess.execute(); + } + //The session should fail because of the checking of writing in a tape with files that are not superseded + std::string logToCheck = logger.getLog(); + ASSERT_NE(std::string::npos, logToCheck.find("MSG=\"In MigrationReportPacker::reportEndOfSessionWithErrors(), " + "pushing a report.\" thread=\"TapeWrite\" tapeDrive=\"T10D6116\" tapeVid=\"TstVid\" mountId=\"2\" " + "ErrorMesage=\"Non superseded files have been detected in the tape TstVid after 0\" type=\"ReportEndofSessionWithErrors\"")); +} + #undef TEST_MOCK_DB #ifdef TEST_MOCK_DB static cta::MockSchedulerDatabaseFactory mockDbFactory; diff --git a/tapeserver/castor/tape/tapeserver/daemon/TapeWriteSingleThread.cpp b/tapeserver/castor/tape/tapeserver/daemon/TapeWriteSingleThread.cpp index 11b2b998c2..713a0002f6 100644 --- a/tapeserver/castor/tape/tapeserver/daemon/TapeWriteSingleThread.cpp +++ b/tapeserver/castor/tape/tapeserver/daemon/TapeWriteSingleThread.cpp @@ -319,6 +319,8 @@ void castor::tape::tapeserver::daemon::TapeWriteSingleThread::run() { params.add("capacityInBytes",m_archiveMount.getCapacityInBytes()); m_logContext.log(cta::log::INFO, "Tape session started"); mountTapeReadWrite(); + currentErrorToCount = "Error_tapeFSeqCheckAndTapeFileCleanup"; + m_archiveMount.checkTapeFSeqForWriting(m_lastFseq); currentErrorToCount = "Error_tapeLoad"; waitForDrive(); currentErrorToCount = "Error_checkingTapeAlert"; @@ -603,4 +605,3 @@ void castor::tape::tapeserver::daemon::TapeWriteSingleThread::logSCSIMetrics() { m_logContext.log(cta::log::ERR, "Exception in logging volume statistics"); } } - -- GitLab