/*
* @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/LogContext.hpp"
#include "common/threading/System.hpp"
#include "castor/tape/tapeserver/daemon/LabelSession.hpp"
#include "castor/tape/tapeserver/daemon/LabelSessionConfig.hpp"
#include "castor/tape/tapeserver/drive/DriveInterface.hpp"
#include "castor/tape/tapeserver/file/File.hpp"
#include "castor/tape/tapeserver/file/Structures.hpp"
#include "castor/tape/tapeserver/SCSI/Device.hpp"
#include "common/exception/Exception.hpp"
#include
//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
castor::tape::tapeserver::daemon::LabelSession::LabelSession(
cta::server::ProcessCap &capUtils,
cta::tape::daemon::TapedProxy &tapeserver,
cta::mediachanger::MediaChangerFacade &mc,
const legacymsg::TapeLabelRqstMsgBody &clientRequest,
cta::log::Logger &log,
System::virtualWrapper &sysWrapper,
const cta::tape::daemon::TpconfigLine &driveConfig,
const bool force,
const bool lbp,
const LabelSessionConfig &labelSessionConfig,
const std::string & externalEncryptionKeyScript):
m_capUtils(capUtils),
m_tapeserver(tapeserver),
m_mc(mc),
m_request(clientRequest),
m_log(log),
m_sysWrapper(sysWrapper),
m_driveConfig(driveConfig),
m_labelSessionConfig (labelSessionConfig),
m_force(force),
m_lbp(lbp),
m_encryptionControl(externalEncryptionKeyScript) {}
//------------------------------------------------------------------------------
// execute
//------------------------------------------------------------------------------
castor::tape::tapeserver::daemon::Session::EndOfSessionAction
castor::tape::tapeserver::daemon::LabelSession::execute() throw() {
std::string errorMessage;
try {
return exceptionThrowingExecute();
} catch(cta::exception::Exception &ex) {
errorMessage = ex.getMessage().str();
} catch(std::exception &se) {
errorMessage = se.what();
} catch(...) {
errorMessage = "Caught an unknown exception";
}
// Reaching this point means the label session failed and an exception was
// thrown
std::list params;
params.push_back(cta::log::Param("uid", m_request.uid));
params.push_back(cta::log::Param("gid", m_request.gid));
params.push_back(cta::log::Param("tapeVid", m_request.vid));
params.push_back(cta::log::Param("tapeDrive", m_request.drive));
params.push_back(cta::log::Param("logicalLibrary", m_request.logicalLibrary));
params.push_back(cta::log::Param("force", boolToStr(m_force)));
params.push_back(cta::log::Param("lbp", boolToStr(m_lbp)));
params.push_back(cta::log::Param("message", errorMessage));
m_log(cta::log::ERR, "Label session failed", params);
// Send details of exception to tapeserverd and then re-throw
m_tapeserver.labelError(m_request.drive, errorMessage);
return MARK_DRIVE_AS_DOWN;
}
//------------------------------------------------------------------------------
// exceptionThrowingExecute
//------------------------------------------------------------------------------
castor::tape::tapeserver::daemon::Session::EndOfSessionAction
castor::tape::tapeserver::daemon::LabelSession::exceptionThrowingExecute() {
if (!m_labelSessionConfig.useLbp && m_lbp) {
const std::string message = "Tapeserver configuration does not allow label "
"a tape with logical block protection.";
notifyTapeserverOfUserError(message);
return MARK_DRIVE_AS_UP;
}
if (!m_lbp && m_labelSessionConfig.useLbp) {
std::list params;
params.push_back(cta::log::Param("uid", m_request.uid));
params.push_back(cta::log::Param("gid", m_request.gid));
params.push_back(cta::log::Param("tapeVid", m_request.vid));
params.push_back(cta::log::Param("tapeDrive", m_request.drive));
params.push_back(cta::log::Param("logicalLibrary", m_request.logicalLibrary));
params.push_back(cta::log::Param("force", boolToStr(m_force)));
params.push_back(cta::log::Param("lbp", boolToStr(m_lbp)));
m_log(cta::log::WARNING, "Label session configured to use LBP but lbp parameter "
"is not set", params);
}
setProcessCapabilities("cap_sys_rawio+ep");
std::unique_ptr drivePtr = createDrive();
drive::DriveInterface &drive = *drivePtr.get();
if(m_lbp) {
// only crc32c lbp mode is supported
drive.enableCRC32CLogicalBlockProtectionReadWrite();
} else {
drive.disableLogicalBlockProtection();
}
// The label to be written without encryption
m_encryptionControl.disable(drive);
mountTape();
waitUntilTapeLoaded(drive, 60); // 60 = 60 seconds
if(drive.isWriteProtected()) {
const std::string message = "Cannot label the tape because it is write-protected";
notifyTapeserverOfUserError(message);
}
else {
rewindDrive(drive);
// If the user is trying to label a non-empty tape without the force option
if(!m_force && !drive.isTapeBlank()) {
const std::string message = "Cannot label a non-empty tape without the"
" force option";
notifyTapeserverOfUserError(message);
// Else the labeling can go ahead
} else {
if (m_lbp) {
writeLabelWithLbpToTape(drive);
} else {
writeLabelToTape(drive);
}
}
}
unloadTape(m_request.vid, drive);
dismountTape(m_request.vid);
drive.disableLogicalBlockProtection();
return MARK_DRIVE_AS_UP;
}
//------------------------------------------------------------------------------
// setProcessCapabilities
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::LabelSession::setProcessCapabilities(
const std::string &capabilities) {
std::list params;
params.push_back(cta::log::Param("uid", m_request.uid));
params.push_back(cta::log::Param("gid", m_request.gid));
params.push_back(cta::log::Param("tapeVid", m_request.vid));
params.push_back(cta::log::Param("tapeDrive", m_request.drive));
params.push_back(cta::log::Param("logicalLibrary", m_request.logicalLibrary));
params.push_back(cta::log::Param("force", boolToStr(m_force)));
params.push_back(cta::log::Param("lbp", boolToStr(m_lbp)));
m_capUtils.setProcText(capabilities);
params.push_back(cta::log::Param("capabilities", m_capUtils.getProcText()));
m_log(cta::log::INFO, "Label session set process capabilities", params);
}
//------------------------------------------------------------------------------
// createDrive
//------------------------------------------------------------------------------
std::unique_ptr
castor::tape::tapeserver::daemon::LabelSession::createDrive() {
SCSI::DeviceVector dv(m_sysWrapper);
SCSI::DeviceInfo driveInfo = dv.findBySymlink(m_driveConfig.devFilename);
// Instantiate the drive object
std::unique_ptr
drive(drive::createDrive(driveInfo, m_sysWrapper));
if(NULL == drive.get()) {
cta::exception::Exception ex;
ex.getMessage() << "Failed to instantiate drive object";
throw ex;
}
return drive;
}
//------------------------------------------------------------------------------
// mountTape
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::LabelSession::mountTape() {
const cta::mediachanger::LibrarySlot &librarySlot = m_driveConfig.librarySlot();
std::list params;
params.push_back(cta::log::Param("uid", m_request.uid));
params.push_back(cta::log::Param("gid", m_request.gid));
params.push_back(cta::log::Param("tapeVid", m_request.vid));
params.push_back(cta::log::Param("tapeDrive", m_request.drive));
params.push_back(cta::log::Param("logicalLibrary", m_request.logicalLibrary));
params.push_back(cta::log::Param("force", boolToStr(m_force)));
params.push_back(cta::log::Param("lbp", boolToStr(m_lbp)));
params.push_back(cta::log::Param("librarySlot", librarySlot.str()));
m_log(cta::log::INFO, "Label session mounting tape", params);
m_mc.mountTapeReadWrite(m_request.vid, librarySlot);
if(cta::mediachanger::TAPE_LIBRARY_TYPE_MANUAL == librarySlot.getLibraryType()) {
m_log(cta::log::INFO, "Label session did not mounted tape because the media"
" changer is manual", params);
} else {
m_log(cta::log::INFO, "Label session mounted tape", params);
}
}
//------------------------------------------------------------------------------
// waitUntilTapeLoaded
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::LabelSession::waitUntilTapeLoaded(
drive::DriveInterface &drive, const int timeoutSecond) {
std::list params;
params.push_back(cta::log::Param("uid", m_request.uid));
params.push_back(cta::log::Param("gid", m_request.gid));
params.push_back(cta::log::Param("tapeVid", m_request.vid));
params.push_back(cta::log::Param("tapeDrive", m_request.drive));
params.push_back(cta::log::Param("logicalLibrary", m_request.logicalLibrary));
params.push_back(cta::log::Param("force", boolToStr(m_force)));
params.push_back(cta::log::Param("lbp", boolToStr(m_lbp)));
try {
drive.waitUntilReady(timeoutSecond);
m_log(cta::log::INFO, "Label session loaded tape", params);
} catch(cta::exception::Exception &ne) {
cta::exception::Exception ex;
ex.getMessage() << "Failed to wait for tape to be loaded: " <<
ne.getMessage().str();
throw ex;
}
}
//------------------------------------------------------------------------------
// rewindDrive
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::LabelSession::rewindDrive(
drive::DriveInterface &drive) {
std::list params;
params.push_back(cta::log::Param("uid", m_request.uid));
params.push_back(cta::log::Param("gid", m_request.gid));
params.push_back(cta::log::Param("tapeVid", m_request.vid));
params.push_back(cta::log::Param("tapeDrive", m_request.drive));
params.push_back(cta::log::Param("logicalLibrary", m_request.logicalLibrary));
params.push_back(cta::log::Param("force", boolToStr(m_force)));
params.push_back(cta::log::Param("lbp", boolToStr(m_lbp)));
m_log(cta::log::INFO, "Label session rewinding tape", params);
drive.rewind();
m_log(cta::log::INFO, "Label session successfully rewound tape", params);
}
//------------------------------------------------------------------------------
// notifyTapeserverOfUserError
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::LabelSession::
notifyTapeserverOfUserError(const std::string message) {
std::list params;
params.push_back(cta::log::Param("uid", m_request.uid));
params.push_back(cta::log::Param("gid", m_request.gid));
params.push_back(cta::log::Param("tapeVid", m_request.vid));
params.push_back(cta::log::Param("tapeDrive", m_request.drive));
params.push_back(cta::log::Param("logicalLibrary", m_request.logicalLibrary));
params.push_back(cta::log::Param("force", boolToStr(m_force)));
params.push_back(cta::log::Param("lbp", boolToStr(m_lbp)));
params.push_back(cta::log::Param("message", message));
m_log(cta::log::ERR, "Label session encountered user error", params);
m_tapeserver.labelError(m_request.drive, message);
}
//------------------------------------------------------------------------------
// writeLabelToTape
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::LabelSession::writeLabelToTape(
drive::DriveInterface &drive) {
std::list params;
params.push_back(cta::log::Param("uid", m_request.uid));
params.push_back(cta::log::Param("gid", m_request.gid));
params.push_back(cta::log::Param("tapeVid", m_request.vid));
params.push_back(cta::log::Param("tapeDrive", m_request.drive));
params.push_back(cta::log::Param("logicalLibrary", m_request.logicalLibrary));
params.push_back(cta::log::Param("force", boolToStr(m_force)));
params.push_back(cta::log::Param("lbp", boolToStr(m_lbp)));
if(m_lbp) {
m_log(cta::log::WARNING, "LBP mode mismatch. Force labeling without lbp.", params);
}
m_log(cta::log::INFO, "Label session is writing label to tape", params);
tapeFile::LabelSession ls(drive, m_request.vid, false);
m_log(cta::log::INFO, "Label session has written label to tape", params);
}
//------------------------------------------------------------------------------
// writeLabelWithLbpToTape
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::LabelSession::writeLabelWithLbpToTape(
drive::DriveInterface &drive) {
std::list params;
params.push_back(cta::log::Param("uid", m_request.uid));
params.push_back(cta::log::Param("gid", m_request.gid));
params.push_back(cta::log::Param("tapeVid", m_request.vid));
params.push_back(cta::log::Param("tapeDrive", m_request.drive));
params.push_back(cta::log::Param("logicalLibrary", m_request.logicalLibrary));
params.push_back(cta::log::Param("force", boolToStr(m_force)));
params.push_back(cta::log::Param("lbp", boolToStr(m_lbp)));
if(!m_lbp) {
m_log(cta::log::WARNING, "LBP mode mismatch. Force labeling with lbp.", params);
}
m_log(cta::log::INFO, "Label session is writing label with LBP to tape", params);
tapeFile::LabelSession ls(drive, m_request.vid, true);
m_log(cta::log::INFO, "Label session has written label with LBP to tape", params);
}
//------------------------------------------------------------------------------
// unloadTape
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::LabelSession::unloadTape(
const std::string &vid, drive::DriveInterface &drive) {
std::list params;
params.push_back(cta::log::Param("uid", m_request.uid));
params.push_back(cta::log::Param("gid", m_request.gid));
params.push_back(cta::log::Param("tapeVid", m_request.vid));
params.push_back(cta::log::Param("tapeDrive", m_request.drive));
params.push_back(cta::log::Param("logicalLibrary", m_request.logicalLibrary));
params.push_back(cta::log::Param("force", boolToStr(m_force)));
params.push_back(cta::log::Param("lbp", boolToStr(m_lbp)));
// We implement the same policy as with the tape sessions:
// if the librarySlot parameter is "manual", do nothing.
if(cta::mediachanger::TAPE_LIBRARY_TYPE_MANUAL ==
m_driveConfig.librarySlot().getLibraryType()) {
m_log(cta::log::INFO, "Label session not unloading tape because media changer is"
" manual", params);
return;
}
try {
m_log(cta::log::INFO, "Label session unloading tape", params);
drive.unloadTape();
m_log(cta::log::INFO, "Label session unloaded tape", params);
} catch (cta::exception::Exception &ne) {
cta::exception::Exception ex;
ex.getMessage() << "Label session failed to unload tape: " <<
ne.getMessage().str();
throw ex;
}
}
//------------------------------------------------------------------------------
// dismountTape
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::LabelSession::dismountTape(
const std::string &vid) {
const cta::mediachanger::LibrarySlot &librarySlot = m_driveConfig.librarySlot();
std::list params;
params.push_back(cta::log::Param("uid", m_request.uid));
params.push_back(cta::log::Param("gid", m_request.gid));
params.push_back(cta::log::Param("tapeVid", m_request.vid));
params.push_back(cta::log::Param("tapeDrive", m_request.drive));
params.push_back(cta::log::Param("logicalLibrary", m_request.logicalLibrary));
params.push_back(cta::log::Param("force", boolToStr(m_force)));
params.push_back(cta::log::Param("lbp", boolToStr(m_lbp)));
params.push_back(cta::log::Param("librarySlot", librarySlot.str()));
try {
m_log(cta::log::INFO, "Label session dismounting tape", params);
m_mc.dismountTape(vid, librarySlot);
const bool dismountWasManual = cta::mediachanger::TAPE_LIBRARY_TYPE_MANUAL ==
librarySlot.getLibraryType();
if(dismountWasManual) {
m_log(cta::log::INFO, "Label session did not dismount tape because media"
" changer is manual", params);
} else {
m_log(cta::log::INFO, "Label session dismounted tape", params);
}
} catch(cta::exception::Exception &ne) {
cta::exception::Exception ex;
ex.getMessage() << "Label session failed to dismount tape: " <<
ne.getMessage().str();
throw ex;
}
}
//------------------------------------------------------------------------------
// boolToStr
//------------------------------------------------------------------------------
const char *castor::tape::tapeserver::daemon::LabelSession::boolToStr(
const bool value) {
return value ? "true" : "false";
}