Commit ac651df6 authored by Steven Murray's avatar Steven Murray
Browse files

CASTOR-4835 tapeserverd should check for blank tapes for cleaner sessions

Fixed.
parent dee7e5aa
...@@ -48,93 +48,242 @@ castor::tape::tapeserver::daemon::CleanerSession::CleanerSession( ...@@ -48,93 +48,242 @@ castor::tape::tapeserver::daemon::CleanerSession::CleanerSession(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
castor::tape::tapeserver::daemon::Session::EndOfSessionAction castor::tape::tapeserver::daemon::Session::EndOfSessionAction
castor::tape::tapeserver::daemon::CleanerSession::execute() { castor::tape::tapeserver::daemon::CleanerSession::execute() {
m_capUtils.setProcText("cap_sys_rawio+ep"); std::string errorMessage;
try {
return exceptionThrowingExecute();
} catch(castor::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 cleaner failed and an exception was thrown
log::Param params[] = {
log::Param("TPVID", m_vid),
log::Param("unitName", m_driveConfig.unitName),
log::Param("message", errorMessage)};
m_log(LOG_ERR, "Cleaner failed", params);
return MARK_DRIVE_AS_DOWN;
}
//------------------------------------------------------------------------------
// exceptionThrowingExecute
//------------------------------------------------------------------------------
castor::tape::tapeserver::daemon::Session::EndOfSessionAction
castor::tape::tapeserver::daemon::CleanerSession::exceptionThrowingExecute() {
std::list<log::Param> params;
params.push_back(log::Param("TPVID", m_vid));
params.push_back(log::Param("unitName", m_driveConfig.unitName));
setProcessCapabilities("cap_sys_rawio+ep");
std::auto_ptr<tapeserver::drive::DriveInterface> drive(createDrive());
waitUntilDriveIsReady(drive.get());
if(!drive->hasTapeInPlace()) {
m_log(LOG_INFO, "Cleaner found tape drive empty", params);
return MARK_DRIVE_AS_UP;
}
rewindDrive(drive.get());
checkTapeContainsData(drive.get());
const std::string volumeLabelVSN = checkVolumeLabel(drive.get());
unloadTape(volumeLabelVSN, drive.get());
dismountTape(volumeLabelVSN);
return MARK_DRIVE_AS_UP;
}
//------------------------------------------------------------------------------
// setProcessCapabilities
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CleanerSession::setProcessCapabilities(
const std::string &capabilities) {
m_capUtils.setProcText(capabilities);
{ {
log::Param params[] = { log::Param params[] = {
log::Param("capabilities", m_capUtils.getProcText())}; log::Param("capabilities", m_capUtils.getProcText())};
m_log(LOG_INFO, "Cleaner session set process capabilities for using tape", m_log(LOG_INFO, "Cleaner set process capabilities for using tape",
params); params);
} }
}
castor::tape::SCSI::DeviceVector dv(m_sysWrapper); //------------------------------------------------------------------------------
castor::tape::SCSI::DeviceInfo driveInfo = dv.findBySymlink(m_driveConfig.devFilename); // createDrive
//------------------------------------------------------------------------------
// Instantiate the drive object castor::tape::tapeserver::drive::DriveInterface *
std::auto_ptr<castor::tape::tapeserver::drive::DriveInterface> drive( castor::tape::tapeserver::daemon::CleanerSession::createDrive() {
castor::tape::tapeserver::drive::createDrive(driveInfo, m_sysWrapper)); SCSI::DeviceVector dv(m_sysWrapper);
SCSI::DeviceInfo driveInfo = dv.findBySymlink(m_driveConfig.devFilename);
drive::DriveInterface *const drive = drive::createDrive(driveInfo,
m_sysWrapper);
if(NULL == drive.get()) { if(NULL == drive) {
castor::exception::Exception ex; castor::exception::Exception ex;
ex.getMessage() << ex.getMessage() <<
"Cleaner session ended with error. Failed to instantiate drive object"; "Failed to instantiate drive object";
throw ex; throw ex;
} }
//temporization to allow for actions to complete return drive;
}
//------------------------------------------------------------------------------
// waitUntilDriveIsReady
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CleanerSession::waitUntilDriveIsReady(
drive::DriveInterface *const drive) {
if(0 != m_driveReadyDelayInSeconds) { if(0 != m_driveReadyDelayInSeconds) {
std::list<log::Param> params;
params.push_back(log::Param("TPVID", m_vid));
params.push_back(log::Param("unitName", m_driveConfig.unitName));
params.push_back(log::Param("driveReadyDelayInSeconds",
m_driveReadyDelayInSeconds));
try { try {
m_log(LOG_INFO, "Cleaner waiting for drive to be ready", params);
drive->waitUntilReady(m_driveReadyDelayInSeconds); drive->waitUntilReady(m_driveReadyDelayInSeconds);
m_log(LOG_INFO, "Cleaner detected drive is ready", params);
} catch (castor::exception::Exception &ex) { } catch (castor::exception::Exception &ex) {
log::Param params[] = {log::Param("message", ex.getMessage().str())}; params.push_back(log::Param("message", ex.getMessage().str()));
m_log(LOG_INFO, "Cleaner session caught a non-fatal exception whilst" m_log(LOG_INFO, "Cleaner caught non-fatal exception whilst waiting for"
" waiting for the drive to become ready. One of the reasons we get here" " drive to become ready", params);
" is if the drive has no tape inside.", params);
} }
} }
}
//------------------------------------------------------------------------------
// rewindDrive
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CleanerSession::rewindDrive(
drive::DriveInterface *const drive) {
std::list<log::Param> params;
params.push_back(log::Param("TPVID", m_vid));
params.push_back(log::Param("unitName", m_driveConfig.unitName));
m_log(LOG_INFO, "Cleaner rewinding drive", params);
drive->rewind();
m_log(LOG_INFO, "Cleaner successfully rewound drive", params);
}
//------------------------------------------------------------------------------
// checkTapeContainsData
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CleanerSession::checkTapeContainsData(
drive::DriveInterface *const drive) {
std::list<log::Param> params;
params.push_back(log::Param("TPVID", m_vid));
params.push_back(log::Param("unitName", m_driveConfig.unitName));
m_log(LOG_INFO, "Cleaner checking tape contains data", params);
if(drive->isTapeBlank()) {
castor::exception::Exception ex;
ex.getMessage() << "Tape is completely blank when it should be labeled";
throw ex;
}
m_log(LOG_INFO, "Cleaner successfully detected tape contains data", params);
}
//------------------------------------------------------------------------------
// checkVolumeLabel
//------------------------------------------------------------------------------
std::string castor::tape::tapeserver::daemon::CleanerSession::checkVolumeLabel(
drive::DriveInterface *const drive) {
tapeFile::VOL1 vol1;
std::list<log::Param> params;
params.push_back(log::Param("TPVID", m_vid));
params.push_back(log::Param("unitName", m_driveConfig.unitName));
//here we check if the drive contains a tape: if not, there's nothing to clean try {
bool driveHasTapeInPlace = drive->hasTapeInPlace(); drive->readExactBlock((void * )&vol1, sizeof(vol1),
if(driveHasTapeInPlace) { //a tape is still mounted in the drive "[CleanerSession::clean()] - Reading header VOL1");
castor::tape::tapeFile::VOL1 vol1; vol1.verify();
try {
drive->rewind(); const std::string &volumeLabelVSN = vol1.getVSN();
drive->readExactBlock((void * )&vol1, sizeof(vol1), "[CleanerSession::clean()] - Reading header VOL1"); params.push_back(log::Param("volumeLabelVSN", volumeLabelVSN));
vol1.verify();
if(m_vid.empty()) { // vid given is empty m_log(LOG_INFO, "Cleaner read VSN from volume label", params);
log::Param params[] = {log::Param("TPVID", m_vid)};
m_log(LOG_INFO, "Cleaner session received an empty vid.", params); // If the cleaner was given a VID
} if(!m_vid.empty()) {
else if(!(m_vid.compare(vol1.getVSN()))) { // vid provided and vid read on VOL1 correspond if(m_vid == volumeLabelVSN) {
log::Param params[] = {log::Param("TPVID", m_vid)}; m_log(LOG_INFO, "Cleaner detected volume label contains expected VSN",
m_log(LOG_INFO, "Cleaner session received the same vid read on tape.", params); params);
}
else { // vid provided and vid read on VOL1 don NOT correspond!
log::Param params[] = {log::Param("TPVID provided", m_vid), log::Param("TPVID read on label", vol1.getVSN())};
m_log(LOG_WARNING, "Cleaner session received a different TPVID from the one read on tape.", params);
}
} catch(castor::exception::Exception &ne) {
castor::exception::Exception ex;
ex.getMessage() << "Cleaner session could not rewind the tape or read its label. Giving up now. Reason: " << ne.getMessage().str();
m_log(LOG_ERR, ex.getMessage().str());
return MARK_DRIVE_AS_DOWN;
}
try {
// We implement the same policy as with the tape sessions:
// if the librarySlot parameter is "manual", do nothing.
if (mediachanger::TAPE_LIBRARY_TYPE_MANUAL != m_driveConfig.librarySlot.getLibraryType()) {
drive->unloadTape();
m_log(LOG_INFO, "Cleaner session: Tape unloaded");
} else { } else {
m_log(LOG_INFO, "Cleaner session: Tape NOT unloaded (manual mode)"); m_log(LOG_WARNING,
"Cleaner detected volume label does not contain expected VSN", params);
} }
} catch (castor::exception::Exception &ex) {
log::Param params[] = {log::Param("message", ex.getMessage().str())};
m_log(LOG_INFO, "Cleaner session could not unload the tape. Will still try to unmount it in case it is already unloaded.", params);
} }
try {
m_mc.dismountTape(vol1.getVSN(), m_driveConfig.librarySlot.str()); return volumeLabelVSN;
m_log(LOG_INFO, mediachanger::TAPE_LIBRARY_TYPE_MANUAL != m_driveConfig.librarySlot.getLibraryType() ? } catch(castor::exception::Exception &ne) {
"Cleaner session: unmounted tape": castor::exception::Exception ex;
"Cleaner session: tape NOT unmounted (manual mode)"); ex.getMessage() << "Failed to check volume label: " << ne.getMessage().str();
} catch(castor::exception::Exception &ne) { throw ex;
castor::exception::Exception ex; }
ex.getMessage() << "Cleaner session could not unmount the tape. Giving up now. Reason: " << ne.getMessage().str(); }
m_log(LOG_ERR, ex.getMessage().str());
return MARK_DRIVE_AS_DOWN; //------------------------------------------------------------------------------
// unloadTape
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CleanerSession::unloadTape(
const std::string &vid, drive::DriveInterface *const drive) {
std::list<log::Param> params;
params.push_back(log::Param("TPVID", vid));
params.push_back(log::Param("unitName", m_driveConfig.unitName));
// We implement the same policy as with the tape sessions:
// if the librarySlot parameter is "manual", do nothing.
if(mediachanger::TAPE_LIBRARY_TYPE_MANUAL ==
m_driveConfig.librarySlot.getLibraryType()) {
m_log(LOG_INFO, "Cleaner not unloading tape because media changer is"
" manual", params);
return;
}
try {
m_log(LOG_INFO, "Cleaner unloading tape", params);
drive->unloadTape();
m_log(LOG_INFO, "Cleaner unloaded tape", params);
} catch (castor::exception::Exception &ne) {
castor::exception::Exception ex;
ex.getMessage() << "Cleaner failed to unload tape: " <<
ne.getMessage().str();
throw ex;
}
}
//------------------------------------------------------------------------------
// dismountTape
//------------------------------------------------------------------------------
void castor::tape::tapeserver::daemon::CleanerSession::dismountTape(
const std::string &vid) {
std::list<log::Param> params;
params.push_back(log::Param("TPVID", vid));
params.push_back(log::Param("unitName", m_driveConfig.unitName));
try {
m_mc.dismountTape(vid, m_driveConfig.librarySlot.str());
const bool dismountWasManual = mediachanger::TAPE_LIBRARY_TYPE_MANUAL ==
m_driveConfig.librarySlot.getLibraryType();
if(dismountWasManual) {
m_log(LOG_INFO, "Cleaner did not dismount tape because media changer is"
" manual", params);
} else {
m_log(LOG_INFO, "Cleaner dismounted tape", params);
} }
return MARK_DRIVE_AS_UP; } catch(castor::exception::Exception &ne) {
} castor::exception::Exception ex;
else { //the drive is empty here we don't care about the drive being ready or not ex.getMessage() << "Cleaner failed to dismount tape: " <<
return MARK_DRIVE_AS_UP; ne.getMessage().str();
throw ex;
} }
} }
...@@ -127,6 +127,71 @@ namespace daemon { ...@@ -127,6 +127,71 @@ namespace daemon {
* tape inside of it. * tape inside of it.
*/ */
const uint32_t m_driveReadyDelayInSeconds; const uint32_t m_driveReadyDelayInSeconds;
EndOfSessionAction exceptionThrowingExecute();
/**
* Sets the capabilities of the process and logs the result.
*
* @param capabilities The string representation of the capabilities.
*/
void setProcessCapabilities(const std::string &capabilities);
/**
* Creates and returns the object that represents the tape drive to be
* cleaned.
*
* @return The tape drive.
*/
drive::DriveInterface *createDrive();
/**
* Waits for the specified drive to be ready.
*
* @param drive The tape drive.
*/
void waitUntilDriveIsReady(drive::DriveInterface *const drive);
/**
* Rewinds the specified tape drive.
*
* @param drive The tape drive.
*/
void rewindDrive(drive::DriveInterface *const drive);
/**
* Checks the tape in the specified tape drive contains some data where no
* data means the tape does not even contain a volume label.
*
* @param drive The tape drive.
*/
void checkTapeContainsData(drive::DriveInterface *const drive);
/**
* Checks that the tape in the specified drive contains a valid volume
* label.
*
* @param drive The tape drive for which it is assumed the tape to be
* tested is present and rewound to the beginning.
* @return The VSN stored within the colue label.
*/
std::string checkVolumeLabel(drive::DriveInterface *const drive);
/**
* Unloads the specified tape from the specified tape drive.
*
* @param vid The volume identifier of the tape to be unloaded. Please note
* that the value of this field is only used for logging purposes.
* @param drive The tape drive.
*/
void unloadTape(const std::string &vid, drive::DriveInterface *const drive);
/**
* Dismounts the specified tape.
*
* @param vid The volume identifier of the tape to be dismounted.
*/
void dismountTape(const std::string &vid);
}; // class CleanerSession }; // class CleanerSession
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment