/* * @project The CERN Tape Archive (CTA) * @copyright Copyright(C) 2003-2021 CERN * @license 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 . */ #include "common/log/Logger.hpp" #include "common/log/LogContext.hpp" #include "common/threading/System.hpp" #include "castor/tape/tapeserver/daemon/EmptyDriveProbe.hpp" #include "castor/tape/tapeserver/daemon/DataTransferSession.hpp" #include "castor/tape/tapeserver/daemon/DiskReadThreadPool.hpp" #include "castor/tape/tapeserver/daemon/DiskWriteThreadPool.hpp" #include "castor/tape/tapeserver/daemon/MigrationTaskInjector.hpp" #include "castor/tape/tapeserver/daemon/RecallReportPacker.hpp" #include "castor/tape/tapeserver/daemon/RecallTaskInjector.hpp" #include "castor/tape/tapeserver/daemon/TapeWriteSingleThread.hpp" #include "castor/tape/tapeserver/daemon/TapeReadSingleThread.hpp" #include "castor/tape/tapeserver/daemon/TapeServerReporter.hpp" #include "castor/tape/tapeserver/daemon/VolumeInfo.hpp" #include "castor/tape/tapeserver/drive/DriveInterface.hpp" #include "castor/tape/tapeserver/SCSI/Device.hpp" #include "common/exception/Exception.hpp" #include "scheduler/RetrieveMount.hpp" #include "castor/tape/tapeserver/RAO/RAOParams.hpp" #include #include //------------------------------------------------------------------------------ //Constructor //------------------------------------------------------------------------------ castor::tape::tapeserver::daemon::DataTransferSession::DataTransferSession( const std::string & hostname, cta::log::Logger & log, System::virtualWrapper & sysWrapper, const cta::tape::daemon::TpconfigLine & driveConfig, cta::mediachanger::MediaChangerFacade & mc, cta::tape::daemon::TapedProxy & initialProcess, cta::server::ProcessCap & capUtils, const DataTransferConfig & castorConf, cta::Scheduler & scheduler): m_log(log), m_sysWrapper(sysWrapper), m_driveConfig(driveConfig), m_castorConf(castorConf), m_driveInfo({driveConfig.unitName, cta::utils::getShortHostname(), driveConfig.logicalLibrary}), m_mc(mc), m_intialProcess(initialProcess), m_capUtils(capUtils), m_scheduler(scheduler) { } //------------------------------------------------------------------------------ // setProcessCapabilities //------------------------------------------------------------------------------ /** * This function will try to set the cap_sys_rawio capability that is needed * for by tape thread to access /dev/nst */ void castor::tape::tapeserver::daemon::DataTransferSession::setProcessCapabilities( const std::string &capabilities){ cta::log::LogContext lc(m_log); try { m_capUtils.setProcText(capabilities); cta::log::LogContext::ScopedParam sp(lc, cta::log::Param("capabilities", m_capUtils.getProcText())); lc.log(cta::log::INFO, "Set process capabilities for using tape"); } catch(const cta::exception::Exception &ne) { lc.log(cta::log::ERR, "Failed to set process capabilities for using the tape "); } } //------------------------------------------------------------------------------ //DataTransferSession::execute //------------------------------------------------------------------------------ /** * Function's synopsis * 1) Prepare the logging environment * Create a sticky thread name, which will be overridden by the other threads * 2a) Get initial information from the client * 2b) Log The result * Then branch to the right execution */ castor::tape::tapeserver::daemon::Session::EndOfSessionAction castor::tape::tapeserver::daemon::DataTransferSession::execute() { // 1) Prepare the logging environment cta::log::LogContext lc(m_log); // Create a sticky thread name, which will be overridden by the other threads lc.pushOrReplace(cta::log::Param("thread", "MainThread")); lc.pushOrReplace(cta::log::Param("tapeDrive", m_driveConfig.unitName)); setProcessCapabilities("cap_sys_rawio+ep"); // 2a) Determine if we want to mount at all (for now) // This variable will allow us to see if we switched from down to up and start a // empty drive probe session if so. bool downUpTransition = false; schedule: while (true) { try { auto desiredState = m_scheduler.getDesiredDriveState(m_driveConfig.unitName, lc); if (!desiredState.up) { downUpTransition = true; // We wait a bit before polling the scheduler again. // TODO: parametrize the duration? m_scheduler.reportDriveStatus(m_driveInfo, cta::common::dataStructures::MountType::NoMount, cta::common::dataStructures::DriveStatus::Down, lc); sleep (5); } else { break; } } catch (cta::Scheduler::NoSuchDrive & e) { // The object store does not even know about this drive. We will report our state // (default status is down). m_scheduler.reportDriveStatus(m_driveInfo, cta::common::dataStructures::MountType::NoMount, cta::common::dataStructures::DriveStatus::Down, lc); } } // If we get here after seeing a down desired state, we are transitioning from // down to up. In such a case, we will run an empty if (downUpTransition) { downUpTransition = false; m_scheduler.reportDriveStatus(m_driveInfo, cta::common::dataStructures::MountType::NoMount, cta::common::dataStructures::DriveStatus::Probing, lc); castor::tape::tapeserver::daemon::EmptyDriveProbe emptyDriveProbe(m_log, m_driveConfig, m_sysWrapper); lc.log(cta::log::INFO, "Transition from down to up detected. Will check if a tape is in the drive."); if (!emptyDriveProbe.driveIsEmpty()) { m_scheduler.reportDriveStatus(m_driveInfo, cta::common::dataStructures::MountType::NoMount, cta::common::dataStructures::DriveStatus::Down, lc); cta::common::dataStructures::SecurityIdentity securityIdentity; cta::common::dataStructures::DesiredDriveState driveState; driveState.up = false; driveState.forceDown = false; std::string errorMsg = "A tape was detected in the drive. Putting the drive down."; cta::optional probeErrorMsg = emptyDriveProbe.getProbeErrorMsg(); if(probeErrorMsg) errorMsg = probeErrorMsg.value(); int logLevel = cta::log::ERR; driveState.setReasonFromLogMsg(logLevel,errorMsg); m_scheduler.setDesiredDriveState(securityIdentity, m_driveConfig.unitName, driveState, lc); lc.log(logLevel, errorMsg); goto schedule; } else { lc.log(cta::log::INFO, "No tape detected in the drive. Proceeding with scheduling."); } } // 2b) Get initial mount information std::unique_ptr tapeMount; // As getting next mount could be long, we report the drive as up immediately. m_scheduler.reportDriveStatus(m_driveInfo, cta::common::dataStructures::MountType::NoMount, cta::common::dataStructures::DriveStatus::Up, lc); try { if (m_scheduler.getNextMountDryRun(m_driveConfig.logicalLibrary, m_driveConfig.unitName, lc)) tapeMount.reset(m_scheduler.getNextMount(m_driveConfig.logicalLibrary, m_driveConfig.unitName, lc).release()); } catch (cta::exception::Exception & e) { cta::log::ScopedParamContainer localParams(lc); std::string exceptionMsg = e.getMessageValue(); int logLevel = cta::log::ERR; localParams.add("errorMessage", exceptionMsg); lc.log(logLevel, "Error while scheduling new mount. Putting the drive down. Stack trace follows."); lc.logBacktrace(logLevel, e.backtrace()); m_scheduler.reportDriveStatus(m_driveInfo, cta::common::dataStructures::MountType::NoMount, cta::common::dataStructures::DriveStatus::Down, lc); cta::common::dataStructures::SecurityIdentity cliId; cta::common::dataStructures::DesiredDriveState driveState; driveState.up = false; driveState.forceDown = false; driveState.setReasonFromLogMsg(cta::log::ERR,exceptionMsg); m_scheduler.setDesiredDriveState(cliId, m_driveConfig.unitName, driveState, lc); return MARK_DRIVE_AS_DOWN; } // No mount to be done found, that was fast... if (!tapeMount.get()) { lc.log(cta::log::DEBUG, "No new mount found. (sleeping 10 seconds)"); m_scheduler.reportDriveStatus(m_driveInfo, cta::common::dataStructures::MountType::NoMount, cta::common::dataStructures::DriveStatus::Up, lc); sleep (10); goto schedule; // return MARK_DRIVE_AS_UP; } m_volInfo.vid=tapeMount->getVid(); m_volInfo.mountType=tapeMount->getMountType(); m_volInfo.nbFiles=tapeMount->getNbFiles(); m_volInfo.mountId=tapeMount->getMountTransactionId(); // 2c) ... and log. // Make the DGN and TPVID parameter permanent. cta::log::ScopedParamContainer params(lc); params.add("tapeVid", m_volInfo.vid) .add("mountId", tapeMount->getMountTransactionId()); { cta::log::ScopedParamContainer localParams(lc); localParams.add("tapebridgeTransId", tapeMount->getMountTransactionId()) .add("mountType", mountTypeToString(m_volInfo.mountType)); lc.log(cta::log::INFO, "Got volume from client"); } // Depending on the type of session, branch into the right execution switch(m_volInfo.mountType) { case cta::common::dataStructures::MountType::Retrieve: return executeRead(lc, dynamic_cast(tapeMount.get())); case cta::common::dataStructures::MountType::ArchiveForUser: case cta::common::dataStructures::MountType::ArchiveForRepack: return executeWrite(lc, dynamic_cast(tapeMount.get())); case cta::common::dataStructures::MountType::Label: return executeLabel(lc, dynamic_cast(tapeMount.get())); default: return MARK_DRIVE_AS_UP; } } //------------------------------------------------------------------------------ //DataTransferSession::executeRead //------------------------------------------------------------------------------ castor::tape::tapeserver::daemon::Session::EndOfSessionAction castor::tape::tapeserver::daemon::DataTransferSession::executeRead(cta::log::LogContext & lc, cta::RetrieveMount *retrieveMount) { // We are ready to start the session. We need to create the whole machinery // in order to get the task injector ready to check if we actually have a // file to recall. // findDrive does not throw exceptions (it catches them to log errors) // A NULL pointer is returned on failure retrieveMount->setFetchEosFreeSpaceScript(m_castorConf.fetchEosFreeSpaceScript); std::unique_ptr drive(findDrive(m_driveConfig,lc,retrieveMount)); if(!drive.get()) return MARK_DRIVE_AS_DOWN; // We can now start instantiating all the components of the data path { // Allocate all the elements of the memory management (in proper order // to refer them to each other) RecallReportPacker rrp(retrieveMount, lc); rrp.disableBulk(); //no bulk needed anymore RecallWatchDog rwd(15,60*10,m_intialProcess,*retrieveMount,m_driveConfig.unitName,lc); RecallMemoryManager mm(m_castorConf.nbBufs, m_castorConf.bufsz,lc); TapeServerReporter tsr(m_intialProcess, m_driveConfig, m_hostname, m_volInfo, lc); //we retrieved the detail from the client in execute, so at this point //we can already report ! tsr.reportState(cta::tape::session::SessionState::Mounting, cta::tape::session::SessionType::Retrieve); TapeReadSingleThread trst(*drive, m_mc, tsr, m_volInfo, m_castorConf.bulkRequestRecallMaxFiles,m_capUtils,rwd,lc,rrp, m_castorConf.useLbp, m_castorConf.useRAO, m_castorConf.externalEncryptionKeyScript,*retrieveMount, m_castorConf.tapeLoadTimeout); DiskWriteThreadPool dwtp(m_castorConf.nbDiskThreads, rrp, rwd, lc, m_castorConf.xrootPrivateKey, m_castorConf.xrootTimeout); RecallTaskInjector rti(mm, trst, dwtp, *retrieveMount, m_castorConf.bulkRequestRecallMaxFiles, m_castorConf.bulkRequestRecallMaxBytes,lc); // Workaround for bug CASTOR-4829: tapegateway: should request positioning by blockid for recalls instead of fseq // In order to implement the fix, the task injector needs to know the type // of the client trst.setTaskInjector(&rti); rrp.setWatchdog(rwd); rti.setDriveInterface(trst.getDriveReference()); // We are now ready to put everything in motion. First step is to check // we get any concrete job to be done from the client (via the task injector) cta::utils::Timer timer; // The RecallTaskInjector and the TapeReadSingleThread share the promise if (m_castorConf.useRAO) { castor::tape::tapeserver::rao::RAOParams raoDataConfig(m_castorConf.useRAO,m_castorConf.raoLtoAlgorithm, m_castorConf.raoLtoAlgorithmOptions,m_volInfo.vid); rti.initRAO(raoDataConfig, &m_scheduler.getCatalogue()); } bool noFilesToRecall = false; if (rti.synchronousFetch(noFilesToRecall)) { //adapt the recall task injector (starting from synchronousFetch) // We got something to recall. Time to start the machinery trst.setWaitForInstructionsTime(timer.secs()); rwd.startThread(); trst.startThreads(); dwtp.startThreads(); rrp.startThreads(); rti.startThreads(); tsr.startThreads(); // This thread is now going to be idle until the system unwinds at the end // of the session // All client notifications are done by the report packer, including the // end of session rti.waitThreads(); dwtp.waitThreads(); rrp.setDiskDone(); trst.waitThreads(); rrp.setTapeDone(); rrp.waitThread(); tsr.waitThreads(); rwd.stopAndWaitThread(); return trst.getHardwareStatus(); } else { // Just log this was an empty mount and that's it. The memory management // will be deallocated automatically. int priority = cta::log::ERR; std::string status = "success"; if(noFilesToRecall){ //If this is an empty mount because no files have been popped from the queue, it is just a warning priority = cta::log::WARNING; status = "failure"; } lc.log(priority, "Aborting recall mount startup: empty mount"); std::string mountId = retrieveMount->getMountTransactionId(); std::string mountType = cta::common::dataStructures::toString(retrieveMount->getMountType()); cta::log::Param errorMessageParam("errorMessage", "Aborted: empty recall mount"); cta::log::Param mountIdParam("mountId", mountId); cta::log::Param mountTypeParam("mountType", mountType); cta::log::Param statusParam("status",status); cta::log::LogContext::ScopedParam sp1(lc, errorMessageParam); try { retrieveMount->abort(); rwd.updateStats(TapeSessionStats()); rwd.reportStats(); std::list paramList { errorMessageParam, mountIdParam, mountTypeParam, statusParam }; m_intialProcess.addLogParams(m_driveConfig.unitName,paramList); cta::log::LogContext::ScopedParam sp08(lc, cta::log::Param("MountTransactionId", mountId)); cta::log::LogContext::ScopedParam sp11(lc, cta::log::Param("errorMessage", "Aborted: empty recall mount")); lc.log(priority, "Notified client of end session with error"); } catch(cta::exception::Exception & ex) { cta::log::LogContext::ScopedParam sp1(lc, cta::log::Param("notificationError", ex.getMessageValue())); lc.log(cta::log::ERR, "Failed to notified client of end session with error"); } // Empty mount, hardware is OK return MARK_DRIVE_AS_UP; } } } //------------------------------------------------------------------------------ //DataTransferSession::executeWrite //------------------------------------------------------------------------------ castor::tape::tapeserver::daemon::Session::EndOfSessionAction castor::tape::tapeserver::daemon::DataTransferSession::executeWrite( cta::log::LogContext & lc, cta::ArchiveMount *archiveMount) { // We are ready to start the session. We need to create the whole machinery // in order to get the task injector ready to check if we actually have a // file to migrate. // 1) Get hold of the drive error logs are done inside the findDrive function std::unique_ptr drive(findDrive(m_driveConfig,lc,archiveMount)); if (!drive.get()) return MARK_DRIVE_AS_DOWN; // Once we got hold of the drive, we can run the session { //dereferencing configLine is safe, because if configLine were not valid, //then findDrive would have return NULL and we would have not end up there TapeServerReporter tsr(m_intialProcess, m_driveConfig, m_hostname,m_volInfo,lc); MigrationMemoryManager mm(m_castorConf.nbBufs, m_castorConf.bufsz,lc); MigrationReportPacker mrp(archiveMount, lc); MigrationWatchDog mwd(15,60*10,m_intialProcess,*archiveMount,m_driveConfig.unitName,lc); TapeWriteSingleThread twst(*drive, m_mc, tsr, mwd, m_volInfo, lc, mrp, m_capUtils, m_castorConf.maxFilesBeforeFlush, m_castorConf.maxBytesBeforeFlush, m_castorConf.useLbp, m_castorConf.externalEncryptionKeyScript, *archiveMount, m_castorConf.tapeLoadTimeout); DiskReadThreadPool drtp(m_castorConf.nbDiskThreads, m_castorConf.bulkRequestMigrationMaxFiles, m_castorConf.bulkRequestMigrationMaxBytes, mwd, lc, m_castorConf.xrootPrivateKey, m_castorConf.xrootTimeout); MigrationTaskInjector mti(mm, drtp, twst, *archiveMount, m_castorConf.bulkRequestMigrationMaxFiles, m_castorConf.bulkRequestMigrationMaxBytes,lc); drtp.setTaskInjector(&mti); twst.setTaskInjector(&mti); mrp.setWatchdog(mwd); cta::utils::Timer timer; bool noFilesToMigrate = false; if (mti.synchronousInjection(noFilesToMigrate)) { const uint64_t firstFseqFromClient = mti.firstFseqToWrite(); //the last fseq written on the tape is the first file's fseq minus one twst.setlastFseq(firstFseqFromClient-1); //we retrieved the detail from the client in execute, so at this point //we can report we will mount the tape. // TODO: create a "StartingSession" state as the mounting will happen in // the to-be-created tape thread. try { tsr.reportState(cta::tape::session::SessionState::Mounting, cta::tape::session::SessionType::Archive); } catch (cta::exception::Exception & e) { cta::log::LogContext::ScopedParam sp1(lc, cta::log::Param("errorMessage", e.getMessage().str())); lc.log(cta::log::INFO, "Aborting the session after problem with mount details. Notifying the client."); mrp.synchronousReportEndWithErrors(e.getMessageValue(), 666, lc); return MARK_DRIVE_AS_UP; } catch (...) { lc.log(cta::log::INFO, "Aborting the session after problem with mount details (unknown exception). Notifying the client."); mrp.synchronousReportEndWithErrors("Unknown exception while checking session parameters with VMGR", 666, lc); return MARK_DRIVE_AS_UP; } // We have something to do: start the session by starting all the // threads. mm.startThreads(); drtp.startThreads(); mwd.startThread(); twst.setWaitForInstructionsTime(timer.secs()); twst.startThreads(); mrp.startThreads(); mti.startThreads(); tsr.startThreads(); // Synchronise with end of threads mti.waitThreads(); mrp.waitThread(); twst.waitThreads(); drtp.waitThreads(); mm.waitThreads(); tsr.waitThreads(); mwd.stopAndWaitThread(); return twst.getHardwareStatus(); } else { // Just log this was an empty mount and that's it. The memory management // will be deallocated automatically. int priority = cta::log::ERR; std::string status = "failure"; if(noFilesToMigrate){ priority = cta::log::WARNING; status = "success"; } lc.log(priority, "Aborting migration mount startup: empty mount"); std::string mountId = archiveMount->getMountTransactionId(); std::string mountType = cta::common::dataStructures::toString(archiveMount->getMountType()); cta::log::Param errorMessageParam("errorMessage", "Aborted: empty migration mount"); cta::log::Param mountIdParam("mountId", mountId); cta::log::Param mountTypeParam("mountType",mountType); cta::log::Param statusParam("status", status); cta::log::LogContext::ScopedParam sp1(lc, errorMessageParam); try { archiveMount->complete(); mwd.updateStats(TapeSessionStats()); mwd.reportStats(); std::list paramList { errorMessageParam, mountIdParam, mountTypeParam, statusParam }; m_intialProcess.addLogParams(m_driveConfig.unitName,paramList); cta::log::LogContext::ScopedParam sp1(lc, cta::log::Param("MountTransactionId", mountId)); lc.log(priority, "Notified client of end session with error"); } catch(cta::exception::Exception & ex) { cta::log::LogContext::ScopedParam sp1(lc, cta::log::Param("notificationError", ex.getMessageValue())); lc.log(cta::log::ERR, "Failed to notified client of end session with error"); } // Empty mount, hardware safe return MARK_DRIVE_AS_UP; } } } //------------------------------------------------------------------------------ //DataTransferSession::executeLabel //------------------------------------------------------------------------------ castor::tape::tapeserver::daemon::Session::EndOfSessionAction castor::tape::tapeserver::daemon::DataTransferSession::executeLabel(cta::log::LogContext& lc, cta::LabelMount* labelMount) { throw cta::exception::Exception("In DataTransferSession::executeLabel(): not implemented"); // TODO } //------------------------------------------------------------------------------ //DataTransferSession::findDrive //------------------------------------------------------------------------------ /* * Function synopsis : * 1) Get hold of the drive and check it. * --- Check If we did not find the drive in the tpConfig, we have a problem * 2) Try to find the drive * Log if we do not find it * 3) Try to open it, log if we fail */ /** * Try to find the drive that is described by m_request.driveUnit * @param lc For logging purpose * @return the drive if found, NULL otherwise */ castor::tape::tapeserver::drive::DriveInterface * castor::tape::tapeserver::daemon::DataTransferSession::findDrive(const cta::tape::daemon::TpconfigLine &driveConfig, cta::log::LogContext& lc, cta::TapeMount *mount) { // Find the drive in the system's SCSI devices castor::tape::SCSI::DeviceVector dv(m_sysWrapper); castor::tape::SCSI::DeviceInfo driveInfo; try { driveInfo = dv.findBySymlink(driveConfig.devFilename); } catch (castor::tape::SCSI::DeviceVector::NotFound & e) { // We could not find this drive in the system's SCSI devices cta::log::LogContext::ScopedParam sp09(lc, cta::log::Param("devFilename", driveConfig.devFilename)); lc.log(cta::log::ERR, "Drive not found on this path"); std::stringstream errMsg; errMsg << "Drive not found on this path" << lc; mount->abort(); cta::log::LogContext::ScopedParam sp10(lc, cta::log::Param("tapebridgeTransId", mount->getMountTransactionId())); cta::log::LogContext::ScopedParam sp13(lc, cta::log::Param("errorMessage", errMsg.str())); lc.log(cta::log::ERR, "Notified client of end session with error"); return NULL; } catch (cta::exception::Exception & e) { // We could not find this drive in the system's SCSI devices cta::log::LogContext::ScopedParam sp09(lc, cta::log::Param("devFilename", driveConfig.devFilename)); cta::log::LogContext::ScopedParam sp10(lc, cta::log::Param("errorMessage", e.getMessageValue())); lc.log(cta::log::ERR, "Error looking to path to tape drive"); std::stringstream errMsg; errMsg << "Error looking to path to tape drive: " << lc; mount->abort(); cta::log::LogContext::ScopedParam sp11(lc, cta::log::Param("tapebridgeTransId", mount->getMountTransactionId())); cta::log::LogContext::ScopedParam sp14(lc, cta::log::Param("errorMessage", errMsg.str())); lc.log(cta::log::ERR, "Notified client of end session with error"); return NULL; } catch (...) { // We could not find this drive in the system's SCSI devices cta::log::LogContext::ScopedParam sp09(lc, cta::log::Param("devFilename", driveConfig.devFilename)); lc.log(cta::log::ERR, "Unexpected exception while looking for drive"); std::stringstream errMsg; errMsg << "Unexpected exception while looking for drive" << lc; mount->abort(); cta::log::LogContext::ScopedParam sp10(lc, cta::log::Param("tapebridgeTransId", mount->getMountTransactionId())); cta::log::LogContext::ScopedParam sp13(lc, cta::log::Param("errorMessage", errMsg.str())); lc.log(cta::log::ERR, "Notified client of end session with error"); return NULL; } try { std::unique_ptr drive; drive.reset(castor::tape::tapeserver::drive::createDrive(driveInfo, m_sysWrapper)); if (drive.get()) drive->config = driveConfig; return drive.release(); } catch (cta::exception::Exception & e) { // We could not find this drive in the system's SCSI devices cta::log::LogContext::ScopedParam sp09(lc, cta::log::Param("devFilename", driveConfig.devFilename)); cta::log::LogContext::ScopedParam sp10(lc, cta::log::Param("errorMessage", e.getMessageValue())); lc.log(cta::log::ERR, "Error opening tape drive"); std::stringstream errMsg; errMsg << "Error opening tape drive" << lc; mount->abort(); cta::log::LogContext::ScopedParam sp11(lc, cta::log::Param("tapebridgeTransId", mount->getMountTransactionId())); cta::log::LogContext::ScopedParam sp14(lc, cta::log::Param("errorMessage", errMsg.str())); lc.log(cta::log::ERR, "Notified client of end session with error"); return NULL; } catch (...) { // We could not find this drive in the system's SCSI devices cta::log::LogContext::ScopedParam sp09(lc, cta::log::Param("devFilename", driveConfig.devFilename)); lc.log(cta::log::ERR, "Unexpected exception while opening drive"); std::stringstream errMsg; errMsg << "Unexpected exception while opening drive" << lc; mount->abort(); cta::log::LogContext::ScopedParam sp10(lc, cta::log::Param("tapebridgeTransId", mount->getMountTransactionId())); cta::log::LogContext::ScopedParam sp13(lc, cta::log::Param("errorMessage", errMsg.str())); lc.log(cta::log::ERR, "Notified client of end session with error"); return NULL; } } //------------------------------------------------------------------------------ // destructor //------------------------------------------------------------------------------ castor::tape::tapeserver::daemon::DataTransferSession::~DataTransferSession() throw () { try { google::protobuf::ShutdownProtobufLibrary(); } catch(...) { m_log(cta::log::ERR, "google::protobuf::ShutdownProtobufLibrary() threw an" " unexpected exception"); } } //----------------------------------------------------------------------------- // volumeModeToString //----------------------------------------------------------------------------- const char *castor::tape::tapeserver::daemon::DataTransferSession:: mountTypeToString(const cta::common::dataStructures::MountType mountType) const throw() { switch(mountType) { case cta::common::dataStructures::MountType::Retrieve: return "Retrieve"; case cta::common::dataStructures::MountType::ArchiveForUser : return "ArchiveForUser"; case cta::common::dataStructures::MountType::ArchiveForRepack : return "ArchiveForRepack"; case cta::common::dataStructures::MountType::Label: return "Label"; default : return "UNKNOWN"; } }