Commit 3b6f19be authored by Steven Murray's avatar Steven Murray
Browse files

Added the ability to force dismount a tape using the generic interface

To forcefully dismount a tape means to rewind and eject the tape
where necessary.  This modification adds the ability to force dismount
to the MediaChangerFacade and the CASTOR acsd daemon.
parent ccf12c55
......@@ -271,8 +271,7 @@ void castor::acs::AcsDaemon::mainEventLoop() {
//------------------------------------------------------------------------------
// handleEvents
//------------------------------------------------------------------------------
bool castor::acs::AcsDaemon::handleEvents()
{
bool castor::acs::AcsDaemon::handleEvents() {
try {
const int timeout = 100; // 100 milliseconds
m_reactor.handleEvents(timeout);
......@@ -332,8 +331,7 @@ void castor::acs::AcsDaemon::handlePendingRequests() {
//------------------------------------------------------------------------------
// handlePendingSignals
//------------------------------------------------------------------------------
bool castor::acs::AcsDaemon::handlePendingSignals()
throw() {
bool castor::acs::AcsDaemon::handlePendingSignals() throw() {
bool continueMainEventLoop = true;
int sig = 0;
sigset_t allSignals;
......
......@@ -58,8 +58,6 @@ int main(const int argc, char *const *const argv) {
// and errorMessage has been set accordingly
std::cerr << "Aborting: " << errorMessage << std::endl;
std::cerr << std::endl;
std::cerr << acs::AcsDismountCmdLine::getUsage();
return 1;
}
......
/******************************************************************************
*
* This file is part of the Castor project.
* See http://castor.web.cern.ch/castor
*
* Copyright (C) 2003 CERN
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
*
* @author Castor Dev team, castor-dev@cern.ch
*****************************************************************************/
#include "castor/acs/AcsForceDismountTape.hpp"
#include "castor/exception/ForceDismountFailed.hpp"
//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
castor::acs::AcsForceDismountTape::AcsForceDismountTape(
const std::string &vid,
const uint32_t acs,
const uint32_t lsm,
const uint32_t panel,
const uint32_t drive,
Acs &acsWrapper,
log::Logger &log,
const AcsDaemonConfig &castorConf):
AcsLibraryInteraction(acsWrapper,log),
m_volId(acsWrapper.str2Volid(vid)),
m_driveId(acsWrapper.alpd2DriveId(acs,lsm,panel,drive)),
m_acsWrapper(acsWrapper),
m_log(log),
m_castorConf(castorConf) {
}
//------------------------------------------------------------------------------
// execute
//------------------------------------------------------------------------------
void castor::acs::AcsForceDismountTape::execute() const {
syncForceDismount();
}
//------------------------------------------------------------------------------
// asyncExecute
//------------------------------------------------------------------------------
void castor::acs::AcsForceDismountTape::asyncExecute(const SEQ_NO seqNo) const {
asyncForceDismount(seqNo);
}
//------------------------------------------------------------------------------
// syncForceDismount
//------------------------------------------------------------------------------
void castor::acs::AcsForceDismountTape::syncForceDismount() const {
const SEQ_NO requestSeqNumber = 1;
ALIGNED_BYTES buf[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)];
try {
sendForceDismountRequest(requestSeqNumber);
requestResponsesUntilFinal(requestSeqNumber, buf,
m_castorConf.queryInterval,
m_castorConf.cmdTimeout);
processForceDismountResponse(buf);
} catch(castor::exception::Exception &ex) {
castor::exception::ForceDismountFailed df;
df.getMessage() << "Failed to force dismount volume " <<
m_volId.external_label << ": " << ex.getMessage().str();
throw df;
}
}
//------------------------------------------------------------------------------
// asyncDismount
//------------------------------------------------------------------------------
void castor::acs::AcsForceDismountTape::asyncForceDismount(const SEQ_NO seqNo)
const {
try {
sendForceDismountRequest(seqNo);
} catch(castor::exception::Exception &ex) {
castor::exception::ForceDismountFailed df;
df.getMessage() << "Failed to send dismount request to ACS " <<
m_volId.external_label << ": " << ex.getMessage().str();
throw df;
}
}
//------------------------------------------------------------------------------
// sendDismountRequest
//------------------------------------------------------------------------------
void castor::acs::AcsForceDismountTape::sendForceDismountRequest(
const SEQ_NO seqNumber) const {
const LOCKID lockId = 0; // No lock
const BOOLEAN force = TRUE;
std::stringstream dbgMsg;
dbgMsg << "Calling Acs::dismount() with seqNumber=" << seqNumber;
m_log(LOG_DEBUG, dbgMsg.str());
const STATUS s = m_acsWrapper.dismount(seqNumber, lockId, m_volId,
m_driveId, force);
dbgMsg.str("");
dbgMsg << "Acs::dismount() for seqNumber=" << seqNumber << " returned " <<
acs_status(s);
m_log(LOG_DEBUG,dbgMsg.str());
if(STATUS_SUCCESS != s) {
castor::exception::ForceDismountFailed ex;
ex.getMessage() << "Failed to send request to force dismount volume " <<
m_volId.external_label << " from drive " <<
m_acsWrapper.driveId2Str(m_driveId) << ": force=" <<
(force ? "TRUE" : "FALSE") << ": " << acs_status(s);
throw ex;
}
}
//------------------------------------------------------------------------------
// processForceDismountResponse
//------------------------------------------------------------------------------
void castor::acs::AcsForceDismountTape::processForceDismountResponse(
ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) const {
const ACS_DISMOUNT_RESPONSE *const msg = (ACS_DISMOUNT_RESPONSE *)buf;
if(STATUS_SUCCESS != msg->dismount_status) {
castor::exception::ForceDismountFailed ex;
ex.getMessage() << "Status of force dismount response is not success: " <<
acs_status(msg->dismount_status);
throw ex;
}
}
//------------------------------------------------------------------------------
// destructor
//------------------------------------------------------------------------------
castor::acs::AcsForceDismountTape::~AcsForceDismountTape() throw() {
}
/******************************************************************************
*
* This file is part of the Castor project.
* See http://castor.web.cern.ch/castor
*
* Copyright (C) 2003 CERN
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
*
* @author Castor Dev team, castor-dev@cern.ch
*****************************************************************************/
#pragma once
#include "castor/acs/Acs.hpp"
#include "castor/acs/AcsDaemonConfig.hpp"
#include "castor/acs/AcsLibraryInteraction.hpp"
#include "castor/log/Logger.hpp"
namespace castor {
namespace acs {
/**
* Class responsible for dismounting tapes through ACS API .
*/
class AcsForceDismountTape: public AcsLibraryInteraction {
public:
/**
* Constructor.
*/
AcsForceDismountTape(
const std::string &vid, const uint32_t acs,
const uint32_t lsm, const uint32_t panel, const uint32_t drive,
Acs &acsWrapper, log::Logger &log,
const AcsDaemonConfig &castorConf);
/**
* Destructor.
*/
~AcsForceDismountTape() throw();
/**
* Execute force dismount request through ACS API.
*/
void execute() const;
/**
* Execute asynchronous force dismount request through ACS API.
*
* @param The value of sequence number for ACS API.
*/
void asyncExecute(const SEQ_NO seqNo) const;
protected:
/**
* Force dismounts the tape with the specified m_volId from the drive with the
* specified m_driveId.
*
* This method does not return until the dismount has either succeeded, failed
* or the specified timeout has been reached.
*/
void syncForceDismount() const;
/**
* Force dismounts the tape with the specified m_volId from the drive with the
* specified m_driveId.
* This method sends a dismount request to ACSLS and returns.
*
* @param The value of sequence number for ACS API.
*/
void asyncForceDismount(const SEQ_NO seqNo) const;
/**
* Sends the force dismount request to ACSLS.
*
* @param seqNumber The sequence number to be used in the request.
*/
void sendForceDismountRequest(const SEQ_NO seqNumber) const;
/**
* Throws castor::exception::DismountFailed if the mount was not
* successful.
*
* @param buf The mount-response message.
*/
void processForceDismountResponse(
ALIGNED_BYTES (&buf)[MAX_MESSAGE_SIZE / sizeof(ALIGNED_BYTES)]) const;
/**
* VOLID
*/
VOLID m_volId;
/**
* DRIVEID
*/
DRIVEID m_driveId;
/**
* Object providing c wrapper for ACS commands.
*/
Acs &m_acsWrapper;
/**
* Logger.
*/
log::Logger &m_log;
/**
* The configuration parameters for the CASTOR ACS daemon.
*/
const AcsDaemonConfig m_castorConf;
}; // class AcsForceDismountTape
} // namespace acs
} // namespace castor
......@@ -22,6 +22,7 @@
#include "castor/acs/Constants.hpp"
#include "castor/acs/AcsMessageHandler.hpp"
#include "castor/acs/AcsDismountTape.hpp"
#include "castor/acs/AcsForceDismountTape.hpp"
#include "castor/acs/AcsMountTapeReadOnly.hpp"
#include "castor/acs/AcsMountTapeReadWrite.hpp"
#include "castor/acs/Acs.hpp"
......@@ -31,6 +32,7 @@
#include "castor/messages/AcsMountTapeReadOnly.pb.h"
#include "castor/messages/AcsMountTapeReadWrite.pb.h"
#include "castor/messages/AcsDismountTape.pb.h"
#include "castor/messages/AcsForceDismountTape.pb.h"
#include <sstream>
......@@ -204,21 +206,27 @@ castor::messages::Frame castor::acs::AcsMessageHandler::
dispatchMsgHandler(const messages::Frame &rqst) {
m_log(LOG_DEBUG, "AcsMessageHandler dispatching message handler");
switch(rqst.header.msgtype()) {
case messages::MSG_TYPE_ACSMOUNTTAPEREADONLY:
return handleAcsMountTapeReadOnly(rqst);
const messages::MsgType msgType = (messages::MsgType)rqst.header.msgtype();
switch(msgType) {
case messages::MSG_TYPE_ACSMOUNTTAPEREADONLY:
return handleAcsMountTapeReadOnly(rqst);
case messages::MSG_TYPE_ACSMOUNTTAPEREADWRITE:
return handleAcsMountTapeReadWrite(rqst);
case messages::MSG_TYPE_ACSMOUNTTAPEREADWRITE:
return handleAcsMountTapeReadWrite(rqst);
case messages::MSG_TYPE_ACSDISMOUNTTAPE:
return handleAcsDismountTape(rqst);
case messages::MSG_TYPE_ACSDISMOUNTTAPE:
return handleAcsDismountTape(rqst);
default:
case messages::MSG_TYPE_ACSFORCEDISMOUNTTAPE:
return handleAcsForceDismountTape(rqst);
default:
{
const std::string msgTypeStr = messages::msgTypeToString(msgType);
castor::exception::Exception ex;
ex.getMessage() << "Failed to dispatch message handler"
": Unknown request type: msgtype=" << rqst.header.msgtype();
": Unexpected request type: msgType=" << msgType << " msgTypeStr=" <<
msgTypeStr;
throw ex;
}
}
......@@ -363,6 +371,56 @@ castor::messages::Frame castor::acs::AcsMessageHandler::
}
}
//------------------------------------------------------------------------------
// handleAcsForceDismountTape
//------------------------------------------------------------------------------
castor::messages::Frame castor::acs::AcsMessageHandler::
handleAcsForceDismountTape(const messages::Frame& rqst) {
m_log(LOG_DEBUG, "Handling AcsDismountTape message");
try {
messages::AcsForceDismountTape rqstBody;
rqst.parseBodyIntoProtocolBuffer(rqstBody);
const std::string vid = rqstBody.vid();
const uint32_t acs = rqstBody.acs();
const uint32_t lsm = rqstBody.lsm();
const uint32_t panel = rqstBody.panel();
const uint32_t drive = rqstBody.drive();
log::Param params[] = {log::Param("TPVID", vid),
log::Param("acs", acs),
log::Param("lsm", lsm),
log::Param("panel", panel),
log::Param("drive", drive)};
m_log(LOG_INFO, "Force dismount tape", params);
castor::acs::AcsImpl acsWrapper;
castor::acs::AcsForceDismountTape acsForceDismountTape(vid, acs, lsm,
panel, drive, acsWrapper, m_log, m_castorConf);
try {
acsForceDismountTape.execute();
m_log(LOG_INFO,"Tape successfully force dismounted", params);
} catch (castor::exception::Exception &ne) {
m_log(LOG_ERR,"Tape force dismount failed: "+ne.getMessage().str(),
params);
throw;
}
const messages::Frame reply = createReturnValueFrame(0);
return reply;
} catch(castor::exception::Exception &ne) {
castor::exception::Exception ex;
ex.getMessage() << "Failed to handle AcsForceDismountTape message: " <<
ne.getMessage().str();
throw ex;
} catch(...) {
castor::exception::Exception ex;
ex.getMessage() << "Failed to handle AcsForceDismountTape message: "
<< "Caught an unknown exception";
throw ex;
}
}
//------------------------------------------------------------------------------
// createReturnValueFrame
//------------------------------------------------------------------------------
......
......@@ -137,13 +137,21 @@ private:
*/
messages::Frame handleAcsMountTapeReadWrite(const messages::Frame &rqst);
/**
/**
* Handles the dismount tape request.
*
* @param rqst The request.
* @return The reply.
*/
messages::Frame handleAcsDismountTape(const messages::Frame &rqst);
/**
* Handles the force dismount tape request.
*
* @param rqst The request.
* @return The reply.
*/
messages::Frame handleAcsForceDismountTape(const messages::Frame &rqst);
/**
* The reactor to which new CASTOR ACS daemon connection handlers are to
......
......@@ -58,8 +58,6 @@ int main(const int argc, char *const *const argv) {
// and errorMessage has been set accordingly
std::cerr << "Aborting: " << errorMessage << std::endl;
std::cerr << std::endl;
std::cerr << acs::AcsMountCmdLine::getUsage();
return 1;
}
......
......@@ -181,9 +181,11 @@ void castor::acs::AcsPendingRequests::checkAndAddRequest(
utils::hexDump(address.getData(), address.size()))
};
m_log(LOG_DEBUG, "AcsPendingRequests::checkAndAddRequest", params);
switch(rqst.header.msgtype()) {
case messages::MSG_TYPE_ACSMOUNTTAPEREADONLY:
case messages::MSG_TYPE_ACSMOUNTTAPEREADWRITE:
const messages::MsgType msgType = (messages::MsgType)rqst.header.msgtype();
switch(msgType) {
case messages::MSG_TYPE_ACSMOUNTTAPEREADONLY:
case messages::MSG_TYPE_ACSMOUNTTAPEREADWRITE:
{
castor::exception::Exception ex;
ex.getMessage() << "Failed to check request"
......@@ -191,14 +193,16 @@ void castor::acs::AcsPendingRequests::checkAndAddRequest(
rqst.header.msgtype();
throw ex;
}
case messages::MSG_TYPE_ACSDISMOUNTTAPE:
checkAndAddRequestDismountTape(address, empty, rqst, socket);
break;
default:
case messages::MSG_TYPE_ACSDISMOUNTTAPE:
checkAndAddRequestDismountTape(address, empty, rqst, socket);
break;
default:
{
const std::string msgTypeStr = messages::msgTypeToString(msgType);
castor::exception::Exception ex;
ex.getMessage() << "Failed to check request"
": Unknown request type: msgtype=" << rqst.header.msgtype();
": Unexpected request type: msgType=" << msgType << " msgTypeStr=" <<
msgTypeStr;
throw ex;
}
}
......
......@@ -57,8 +57,6 @@ int main(const int argc, char *const *const argv) {
// and errorMessage has been set accordingly
std::cerr << "Aborting: " << errorMessage << std::endl;
std::cerr << std::endl;
std::cerr << acs::AcsQueryVolumeCmdLine::getUsage();
return 1;
}
......
......@@ -60,6 +60,7 @@ if (STK_API_LIB AND STK_UTL_LIB AND STK_IPC_LIB AND STK_CL_LIB)
AcsDaemonConfig.cpp
AcsDaemonMain.cpp
AcsDismountTape.cpp
AcsForceDismountTape.cpp
AcsImpl.cpp
AcsLibraryInteraction.cpp
AcsMessageHandler.cpp
......
/******************************************************************************
*
* This file is part of the Castor project.
* See http://castor.web.cern.ch/castor
*
* Copyright (C) 2003 CERN
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
*
* @author Castor Dev team, castor-dev@cern.ch
*****************************************************************************/
#include "castor/exception/ForceDismountFailed.hpp"
#include "h/serrno.h"
// -----------------------------------------------------------------------------
// Constructor
// -----------------------------------------------------------------------------
castor::exception::ForceDismountFailed::ForceDismountFailed():
castor::exception::Exception(ETFDISMOUNTFAILED) {
}
/******************************************************************************
*
* This file is part of the Castor project.
* See http://castor.web.cern.ch/castor
*
* Copyright (C) 2003 CERN
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
*
* @author Castor Dev team, castor-dev@cern.ch
*****************************************************************************/
#pragma once
#include "castor/exception/Exception.hpp"
namespace castor {
namespace exception {
/**
* Failed to dismount volume.
*/
class ForceDismountFailed : public castor::exception::Exception {
public:
/**
* Constructor
*/
ForceDismountFailed();
}; // class ForceDismountFailed
} // namespace exception
} // namespace castor
......@@ -41,6 +41,19 @@ public:
*/
virtual ~RmcProxy() throw() = 0;
/**
* Requests the media changer to mount the specified tape for read-only
* access into the drive in the specified library slot.
*
* Please note that this method provides a best-effort service because not all
* media changers support read-only mounts.
*
* @param vid The volume identifier of the tape.
* @param librarySlot The library slot containing the tape drive.
*/
virtual void mountTapeReadOnly(const std::string &vid,
const mediachanger::ScsiLibrarySlot &librarySlot) = 0;
/**
* Requests the media changer to mount of the specified tape for read/write
* access into the drive in the specified library slot.
......@@ -61,6 +74,20 @@ public:
virtual void dismountTape(const std::string &vid,
const mediachanger::ScsiLibrarySlot &librarySlot) = 0;