Commit 0cb68b30 authored by Victor Kotlyar's avatar Victor Kotlyar
Browse files

CASTOR-4740: tapeserverd: DriveGeneric::waitUntilReady() should be based

on SCSI call TEST UNIT READY instead of st driver.

Reiplemented drive::DriveGeneric::waitUntilReady to use TEST UNIT READY
SCSI command.
parent 10b8297d
......@@ -25,6 +25,30 @@
void castor::tape::SCSI::ExceptionLauncher(const SCSI::Structures::LinuxSGIO_t & sgio, std::string context) {
if (SCSI::Status::GOOD != sgio.status) {
throw Exception(sgio.status, (SCSI::Structures::senseData_t<255> *)sgio.sbp, context);
if (SCSI::Status::CHECK_CONDITION == sgio.status) {
unsigned char senseKey;
castor::tape::SCSI::Structures::senseData_t<255> * sense =
(SCSI::Structures::senseData_t<255> *)sgio.sbp;
try {
senseKey = sense->getSenseKey();
} catch (...) {
throw Exception(sgio.status,
(SCSI::Structures::senseData_t<255> *)sgio.sbp, context);
}
switch (senseKey) {
case castor::tape::SCSI::senseKeys::notReady :
throw NotReadyException(sgio.status,
(SCSI::Structures::senseData_t<255> *)sgio.sbp, context);
case castor::tape::SCSI::senseKeys::unitAttention :
throw UnitAttentionException(sgio.status,
(SCSI::Structures::senseData_t<255> *)sgio.sbp, context);
default:
throw Exception(sgio.status,
(SCSI::Structures::senseData_t<255> *)sgio.sbp, context);
}
} else {
throw Exception(sgio.status,
(SCSI::Structures::senseData_t<255> *)sgio.sbp, context);
}
}
}
......@@ -60,7 +60,36 @@ namespace SCSI {
setWhat(w.str());
}
virtual ~Exception() throw () {}
};
}; // class Exception
/**
* Failed with NotReady error.
*/
class NotReadyException : public castor::tape::SCSI::Exception {
public:
/**
* Constructor
*/
NotReadyException(unsigned char status,
castor::tape::SCSI::Structures::senseData_t<255> * sense,
const std::string & context = ""):
Exception(status, sense, context) {};
}; // class NotReadyException
/**
* Failed with UnitAttention error.
*/
class UnitAttentionException : public castor::tape::SCSI::Exception {
public:
/**
* Constructor
*/
UnitAttentionException(unsigned char status,
castor::tape::SCSI::Structures::senseData_t<255> * sense,
const std::string & context = ""):
Exception(status, sense, context) {};
}; // class UnitAttentionException
/**
* Automated exception launcher in case of SCSI command error. Does nothing
* in the absence of errors.
......
......@@ -459,7 +459,7 @@ namespace SCSI {
// bytes 4-n
unsigned char parameterValue[1]; // parameters have variable length
/**
* Gets the parameter value
*
......
......@@ -25,6 +25,8 @@
#include "castor/tape/tapeserver/drive/FakeDrive.hpp"
#include "castor/tape/tapeserver/drive/DriveGeneric.hpp"
#include "castor/utils/Timer.hpp"
namespace castor {
namespace tape {
namespace tapeserver {
......@@ -815,5 +817,64 @@ drive::compressionStats drive::DriveIBM3592::getCompression() {
return driveCompressionStats;
}
//------------------------------------------------------------------------------
// testUnitReady
//------------------------------------------------------------------------------
void drive::DriveGeneric::testUnitReady() const {
SCSI::Structures::testUnitReadyCDB_t cdb;
SCSI::Structures::senseData_t<255> senseBuff;
SCSI::Structures::LinuxSGIO_t sgh;
sgh.setCDB(&cdb);
sgh.setSenseBuffer(&senseBuff);
sgh.dxfer_direction = SG_DXFER_NONE;
/* Manage both system error and SCSI errors. */
castor::exception::Errnum::throwOnMinusOne(
m_sysWrapper.ioctl(m_tapeFD, SG_IO, &sgh),
"Failed SG_IO ioctl in DriveGeneric::testUnitReady");
SCSI::ExceptionLauncher(sgh,"SCSI error in testUnitReady:");
}
//------------------------------------------------------------------------------
// waitUntilReady
//------------------------------------------------------------------------------
bool drive::DriveGeneric::waitUntilReady(int timeoutSecond) {
std::string lastTestUnitReadyExceptionMsg("");
for (castor::utils::Timer t; t.secs() < timeoutSecond;) {
try {
testUnitReady();
} catch (castor::tape::SCSI::NotReadyException &ex){
lastTestUnitReadyExceptionMsg = ex.getMessage().str();
sleep(1);
continue;
} catch (castor::tape::SCSI::UnitAttentionException &ex){
lastTestUnitReadyExceptionMsg = ex.getMessage().str();
sleep(1);
continue;
} catch (...) {
throw;
};
// Test Unit Ready finished with a GOOD status.
// we need to reopen the drive to have the GMT_ONLINE check working (see "man st")
castor::exception::Errnum::throwOnMinusOne(m_sysWrapper.close(m_tapeFD),
std::string("Could not close device file: ") + m_SCSIInfo.nst_dev);
castor::exception::Errnum::throwOnMinusOne(
m_tapeFD = m_sysWrapper.open(m_SCSIInfo.nst_dev.c_str(), O_RDWR | O_NONBLOCK),
std::string("Could not open device file: ") + m_SCSIInfo.nst_dev);
UpdateDriveStatus();
if(m_driveStatus.ready) {
return m_driveStatus.ready;
}
}
castor::exception::Exception ex;
ex.getMessage() << "Cant get the drive ready after waiting " <<
timeoutSecond << " seconds: " << lastTestUnitReadyExceptionMsg;
throw ex;
}
}}}
......@@ -118,27 +118,28 @@ namespace drive {
*/
virtual driveStatus getDriveStatus() {
throw castor::exception::Exception("Not implemented");
}
}
virtual bool waitUntilReady(int timeoutSecond) {
for(int i =0;i<timeoutSecond;++i) {
// we need to reopen the drive to have the GMT_ONLINE check working (see "man st")
castor::exception::Errnum::throwOnMinusOne(m_sysWrapper.close(m_tapeFD),
std::string("Could not close device file: ") + m_SCSIInfo.nst_dev);
castor::exception::Errnum::throwOnMinusOne(
m_tapeFD = m_sysWrapper.open(m_SCSIInfo.nst_dev.c_str(), O_RDWR | O_NONBLOCK),
std::string("Could not open device file: ") + m_SCSIInfo.nst_dev);
UpdateDriveStatus();
if(m_driveStatus.ready) {
return m_driveStatus.ready;
}
//sleep for 1 second
sleep(1);
}
castor::exception::Exception ex("Cant get the drive ready after waiting");
ex.getMessage()<<timeoutSecond<<" seconds";
throw ex;
}
/**
* Test the readiness of the tape drive by using TEST UNIT READY described
* in SPC-4. Throws exceptions if there are any problems and SCSI command
* status is not GOOD. The specific exceptions are thrown for supported by
* st driver sense keys: NotReady and UnitAttention.
*/
virtual void testUnitReady() const;
/**
* Detects readiness of the drive by calling ioctl MTIOCGET and checks if
* the status is GMT_ONLINE. Throws exceptions if the drive is not ready for
* at least timeoutSeconds or any errors occurred. We consider any not GOOD
* SCSI replay with sense keys not equals to NotReady or UnitAttention as
* errors.
*
* @param timeoutSecond The time in seconds for which it waits the drive to
* be ready.
* @return true if the drive has the status GMT_ONLINE.
*/
virtual bool waitUntilReady(int timeoutSecond);
virtual bool hasTapeInPlace() {
UpdateDriveStatus();
......
......@@ -70,114 +70,142 @@ int main ()
std::cout << std::endl << "-- SCSI device: "
<< dev.sg_dev << " (" << dev.nst_dev << ")" << std::endl;
if (dev.type == castor::tape::SCSI::Types::tape) {
std::auto_ptr<castor::tape::tapeserver::drive::DriveInterface> drive(
castor::tape::tapeserver::drive::createDrive(dev, sWrapper));
castor::tape::tapeserver::drive::deviceInfo devInfo;
try {
devInfo = drive->getDeviceInfo();
std::cout << "-- INFO --------------------------------------" << std::endl
<< " devInfo.vendor : '" << devInfo.vendor << "'" << std::endl
<< " devInfo.product : '" << devInfo.product << "'" << std::endl
<< " devInfo.productRevisionLevel : '" << devInfo.productRevisionLevel << "'" << std::endl
<< " devInfo.serialNumber : '" << devInfo.serialNumber << "'" << std::endl
<< "----------------------------------------------" << std::endl;
} catch (std::exception & e) {
fail = 1;
std::string temp = e.what();
std::cout << "----------------------------------------------" << std::endl
<< temp
<< "----------------------------------------------" << std::endl;
}
try {
/**
* We will write on the tape, so prepare 2 blocks
*/
std::cout << "-- INFO --------------------------------------" << std::endl
<< " Rewinding, writing 2 blocks and repositioning on block 2" << std::endl
<< "----------------------------------------------" << std::endl;
drive->rewind();
/* For some unexplained (TODO) reason, mhvtl does not accept blocks smaller than 4 bytes */
drive->writeBlock((void *)"X123", 4);
drive->writeBlock((void *)"Y123", 4);
// Create drive object and open tape device
std::auto_ptr<castor::tape::tapeserver::drive::DriveInterface> drive(
castor::tape::tapeserver::drive::createDrive(dev, sWrapper));
/**
* trying to do position to the block 2.
* From now we could use generic SCSI request for the drive object.
* We should be aware that there might be a problem with tape in the
* drive for example incompatible media installed.
*/
drive->positionToLogicalObject(2);
} catch (std::exception & e) {
fail = 1;
std::string temp = e.what();
std::cout << "-- EXCEPTION ---------------------------------" << std::endl
<< temp
<< "----------------------------------------------" << std::endl;
}
try {
/**
* Gets generic device info for the drive object.
*/
castor::tape::tapeserver::drive::deviceInfo devInfo;
devInfo = drive->getDeviceInfo();
std::cout << "-- INFO --------------------------------------" << std::endl
<< " devInfo.vendor : '" << devInfo.vendor << "'" << std::endl
<< " devInfo.product : '" << devInfo.product << "'" << std::endl
<< " devInfo.productRevisionLevel : '" << devInfo.productRevisionLevel << "'" << std::endl
<< " devInfo.serialNumber : '" << devInfo.serialNumber << "'" << std::endl
<< "----------------------------------------------" << std::endl;
} catch (std::exception & e) {
fail = 1;
std::string temp = e.what();
std::cout << "----------------------------------------------" << std::endl
<< temp
<< "-- INFO --------------------------------------" << std::endl;
continue;
}
try {
/**
* Checks if the drive ready to use the tape installed loaded into it.
*/
drive->waitUntilReady(5);
} catch(castor::exception::Exception &ne) {
std::string temp=ne.getMessage().str();
fail = 1;
std::cout << "----------------------------------------------" << std::endl
<< temp << std::endl
<< "----------------------------------------------" << std::endl;
continue;
}
try {
castor::tape::tapeserver::drive::positionInfo posInfo = drive->getPositionInfo();
std::cout << "-- INFO --------------------------------------" << std::endl
<< " posInfo.currentPosition : " << posInfo.currentPosition <<std::endl
<< " posInfo.oldestDirtyObject : "<< posInfo.oldestDirtyObject <<std::endl
<< " posInfo.dirtyObjectsCount : "<< posInfo.dirtyObjectsCount <<std::endl
<< " posInfo.dirtyBytesCount : " << posInfo.dirtyBytesCount <<std::endl
try {
/**
* We will write on the tape, so prepare 2 blocks
*/
std::cout << "-- INFO --------------------------------------" << std::endl
<< " Rewinding, writing 2 blocks and repositioning on block 2" << std::endl
<< "----------------------------------------------" << std::endl;
drive->rewind();
/* For some unexplained (TODO) reason, mhvtl does not accept blocks smaller than 4 bytes */
drive->writeBlock((void *)"X123", 4);
drive->writeBlock((void *)"Y123", 4);
/**
* trying to do position to the block 2.
*/
drive->positionToLogicalObject(2);
} catch (std::exception & e) {
fail = 1;
std::string temp = e.what();
std::cout << "-- EXCEPTION ---------------------------------" << std::endl
<< temp
<< "----------------------------------------------" << std::endl;
}
try {
castor::tape::tapeserver::drive::positionInfo posInfo = drive->getPositionInfo();
std::cout << "-- INFO --------------------------------------" << std::endl
<< " posInfo.currentPosition : " << posInfo.currentPosition <<std::endl
<< " posInfo.oldestDirtyObject : "<< posInfo.oldestDirtyObject <<std::endl
<< " posInfo.dirtyObjectsCount : "<< posInfo.dirtyObjectsCount <<std::endl
<< " posInfo.dirtyBytesCount : " << posInfo.dirtyBytesCount <<std::endl
<< "----------------------------------------------" << std::endl;
} catch (std::exception & e) {
fail = 1;
std::string temp = e.what();
std::cout << "-- EXCEPTION ---------------------------------" << std::endl
<< temp
<< "----------------------------------------------" << std::endl;
}
try { // switch off compression on the drive
std::cout << "** set density and compression" << std::endl;
drive->setDensityAndCompression(false);
} catch (std::exception & e) {
fail = 1;
std::string temp = e.what();
std::cout << "-- EXCEPTION ---------------------------------" << std::endl
<< temp
<< "----------------------------------------------" << std::endl;
}
try {
/**
* Trying to get compression from the drive-> Read or write should be
* done before to have something in the data fields.
*/
castor::tape::tapeserver::drive::compressionStats comp = drive->getCompression();
std::cout << "-- INFO --------------------------------------" << std::endl
<< " fromHost : " << comp.fromHost << std::endl
<< " toHost : " << comp.toHost << std::endl
<< " fromTape : " << comp.fromTape << std::endl
<< " toTape : " << comp.toTape << std::endl
<< "----------------------------------------------" << std::endl;
} catch (std::exception & e) {
fail = 1;
std::string temp = e.what();
std::cout << "-- EXCEPTION ---------------------------------" << std::endl
<< temp
std::cout << "** clear compression stats" << std::endl;
drive->clearCompressionStats();
comp = drive->getCompression();
std::cout << "-- INFO --------------------------------------" << std::endl
<< " fromHost : " << comp.fromHost << std::endl
<< " toHost : " << comp.toHost << std::endl
<< " fromTape : " << comp.fromTape << std::endl
<< " toTape : " << comp.toTape << std::endl
<< "----------------------------------------------" << std::endl;
}
try { // switch off compression on the drive
std::cout << "** set density and compression" << std::endl;
drive->setDensityAndCompression(false);
} catch (std::exception & e) {
fail = 1;
std::string temp = e.what();
std::cout << "-- EXCEPTION ---------------------------------" << std::endl
<< temp
} catch (std::exception & e) {
fail = 1;
std::string temp = e.what();
std::cout << "-- EXCEPTION ---------------------------------" << std::endl
<< temp
<< "----------------------------------------------" << std::endl;
}
}
std::vector<std::string> Alerts(drive->getTapeAlerts());
while (Alerts.size()) {
std::cout << "Tape alert: " << Alerts.back() << std::endl;
Alerts.pop_back();
}
try {
/**
* Trying to get compression from the drive-> Read or write should be
* done before to have something in the data fields.
* Rewind/Read/Write/Skip Test
*/
castor::tape::tapeserver::drive::compressionStats comp = drive->getCompression();
std::cout << "-- INFO --------------------------------------" << std::endl
<< " fromHost : " << comp.fromHost << std::endl
<< " toHost : " << comp.toHost << std::endl
<< " fromTape : " << comp.fromTape << std::endl
<< " toTape : " << comp.toTape << std::endl
<< "----------------------------------------------" << std::endl;
std::cout << "** clear compression stats" << std::endl;
drive->clearCompressionStats();
comp = drive->getCompression();
std::cout << "-- INFO --------------------------------------" << std::endl
<< " fromHost : " << comp.fromHost << std::endl
<< " toHost : " << comp.toHost << std::endl
<< " fromTape : " << comp.fromTape << std::endl
<< " toTape : " << comp.toTape << std::endl
<< "----------------------------------------------" << std::endl;
} catch (std::exception & e) {
fail = 1;
std::string temp = e.what();
std::cout << "-- EXCEPTION ---------------------------------" << std::endl
<< temp
<< "----------------------------------------------" << std::endl;
}
std::vector<std::string> Alerts(drive->getTapeAlerts());
while (Alerts.size()) {
std::cout << "Tape alert: " << Alerts.back() << std::endl;
Alerts.pop_back();
}
/**
* Rewind/Read/Write/Skip Test
*/
try {
const size_t count = 10;
......@@ -185,152 +213,152 @@ int main ()
memset(data, 0, count);
std::cout << "************** BEGIN: Rewind/Read/Write/Skip Test *************" << std::endl;
std::cout << "Rewinding..." << std::endl;
drive->rewind(); // go back to the beginning of tape after Victor's positioning
print_and_assert_position(*drive, 0);
memset(data, 'a', count-1);
std::cout << "Writing 1st block (9 a's)..." << std::endl;
drive->writeBlock((void *)data, count); // write 9 a's + string term
print_and_assert_position(*drive, 1);
std::cout << "Writing 1st Synchronous filemark..." << std::endl;
drive->writeSyncFileMarks(1); // filemark and flush
print_and_assert_position(*drive, 2);
memset(data, 'b', count-1);
std::cout << "Writing 2nd block (9 b's)..." << std::endl;
drive->writeBlock((void *)data, count); // write 9 b's + string term
print_and_assert_position(*drive, 3);
std::cout << "Writing 2nd Synchronous filemark..." << std::endl;
drive->writeSyncFileMarks(1); // filemark and flush
print_and_assert_position(*drive, 4);
memset(data, 'c', count-1);
std::cout << "Writing 3rd block (9 c's)..." << std::endl;
drive->writeBlock((void *)data, count); // write 9 c's + string term
print_and_assert_position(*drive, 5);
std::cout << "Writing EOD (2 filemarks)..." << std::endl;
drive->writeSyncFileMarks(2); // EOD and flush
print_and_assert_position(*drive, 7);
std::cout << "Rewinding..." << std::endl;
drive->rewind(); // go back to the beginning of tape
print_and_assert_position(*drive, 0);
std::cout << "Reading back 1st block 9 a's)..." << std::endl;
memset(data, 0, count);
drive->readBlock((void *)data, count); // read 9 a's + string term
print_and_assert_position(*drive, 1);
print_and_assert_data("aaaaaaaaa", (const char *)data);
std::cout << "Skipping first file mark..." << std::endl;
memset(data, 0, count);
drive->readBlock((void *)data, count);
print_and_assert_position(*drive, 2);
std::cout << "Reading back 2nd block (9 b's)..." << std::endl;
memset(data, 0, count);
drive->readBlock((void *)data, count); // read 9 b's + string term
print_and_assert_position(*drive, 3);
print_and_assert_data("bbbbbbbbb", (const char *)data);
std::cout << "Skipping first file mark..." << std::endl;
memset(data, 0, count);
drive->readBlock((void *)data, count);
print_and_assert_position(*drive, 4);
std::cout << "Reading back 3rd block (9 c's)..." << std::endl;
memset(data, 0, count);
drive->readBlock((void *)data, count); // read 9 c's + string term
print_and_assert_position(*drive, 5);
print_and_assert_data("ccccccccc", (const char *)data);
std::cout << "Skipping the last two file marks..." << std::endl;
memset(data, 0, count);
drive->readBlock((void *)data, count);
memset(data, 0, count);
drive->readBlock((void *)data, count);
print_and_assert_position(*drive, 7);
std::cout << "Rewinding..." << std::endl;
drive->rewind(); // go back to the beginning of tape
print_and_assert_position(*drive, 0);
std::cout << "Spacing to the end of media..." << std::endl;
drive->spaceToEOM();
print_and_assert_position(*drive, 7);
std::cout << "Rewinding..." << std::endl;
drive->rewind(); // go back to the beginning of tape
print_and_assert_position(*drive, 0);
std::cout << "Fast spacing to the end of media..." << std::endl;
drive->fastSpaceToEOM();
print_and_assert_position(*drive, 7);
std::cout << "Rewinding..." << std::endl;
drive->rewind(); // go back to the beginning of tape
print_and_assert_position(*drive, 0);
std::cout << "Spacing 2 file marks forward..." << std::endl;
drive->spaceFileMarksForward(2);
print_and_assert_position(*drive, 4);
std::cout << "Spacing 1 file mark backwards..." << std::endl;
drive->spaceFileMarksBackwards(1);
print_and_assert_position(*drive, 3);
std::cout << "Rewinding..." << std::endl;
drive->rewind(); // go back to the beginning of tape
print_and_assert_position(*drive, 0);
std::cout << "Spacing 3 file marks forward..." << std::endl;
drive->spaceFileMarksForward(3);
print_and_assert_position(*drive, 6);