Commit 5e8b2709 by Steven Murray

CASTOR-5472 RFE: Enhance the tapegateway protocol so it can support RAO

Added "-l, --recallList file" option to readtpblkid.
With this option the list of block-ID based recalls
can be specified in a file.
parent 53c31c91
Pipeline #176733 passed with stages
in 12 minutes 7 seconds
/******************************************************************************
*
* 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/Exception.hpp"
#include "castor/tape/tpcp/BlkIdRecallsFileParser.hpp"
#include "castor/utils/utils.hpp"
#include <iostream>
#include <fstream>
#include <stdint.h>
//------------------------------------------------------------------------------
// parseFileList
//------------------------------------------------------------------------------
std::list<castor::tape::tpcp::BlkIdRecall>
castor::tape::tpcp::BlkIdRecallsFileParser::parseFile(
const std::string &filename) {
std::list<castor::tape::tpcp::BlkIdRecall> recalls;
const std::list<std::string> lines = readFileIntoList(filename);
uint64_t lineNb = 0;
for(std::list<std::string>::const_iterator itor = lines.begin();
itor != lines.end(); itor++) {
lineNb++;
std::string line = *itor;
// Left and right trim the line
line = castor::utils::trimString(line);
// If the line is not empty string and it is not a comment
if(!line.empty() && !(line.size() > 0 && line[0] == '#')) {
BlkIdRecall recall;
try {
recall = parseLineIntoBlkIdRecall(line);
} catch(castor::exception::Exception &ne) {
castor::exception::Exception ex;
ex.getMessage() << "Failed to parse line " << lineNb << " of file " <<
filename << ": " << ne.getMessage().str();
throw ex;
}
recalls.push_back(recall);
}
}
return recalls;
}
//------------------------------------------------------------------------------
// readFileIntoList
//------------------------------------------------------------------------------
std::list<std::string>
castor::tape::tpcp::BlkIdRecallsFileParser::readFileIntoList(
const std::string &filename) {
std::ifstream file(filename.c_str());
if(!file) {
castor::exception::Exception ex(ECANCELED);
ex.getMessage() << "Failed to open file: Filename='" << filename << "'";
throw ex;
}
std::list<std::string> lines;
while(!file.eof()) {
std::string line;
std::getline(file, line, '\n');
if(!line.empty() || !file.eof()) {
lines.push_back(line);
}
}
return lines;
}
//------------------------------------------------------------------------------
// parseLineIntoBlkIdRecall
//------------------------------------------------------------------------------
castor::tape::tpcp::BlkIdRecall
castor::tape::tpcp::BlkIdRecallsFileParser::parseLineIntoBlkIdRecall(
const std::string &line) {
// The format of the line to be parsed should be:
// fSeq,fileStartBlkId,fileSize,destFile
std::vector<std::string> columns;
// Column values are separated by commas
castor::utils::splitString(line, ',', columns);
const int expectedNbColumns = 4;
if(expectedNbColumns != columns.size()) {
exception::Exception ex;
ex.getMessage() << "BlkIdRecall line should contain exactly 4 comma"
" separated columns: expected=" << expectedNbColumns << ",actual=" <<
columns.size();
throw ex;
}
// Left and right trim each column
columns[0] = castor::utils::trimString(columns[0]);
columns[1] = castor::utils::trimString(columns[1]);
columns[2] = castor::utils::trimString(columns[2]);
columns[3] = castor::utils::trimString(columns[3]);
BlkIdRecall recall;
{
std::stringstream fseq;
fseq << columns[0];
if (!utils::isValidUInt(fseq.str())) {
castor::exception::InvalidArgument ex;
ex.getMessage() << "\tFseq " << fseq.str() <<
" is not a valid unsigned integer";
throw ex;
}
fseq >> recall.fseq;
}
{
std::stringstream startBlkId;
startBlkId << columns[1];
if (!utils::isValidUInt(startBlkId.str())) {
castor::exception::InvalidArgument ex;
ex.getMessage() << "\tStart block ID " << startBlkId.str() <<
" is not a valid unsigned integer";
throw ex;
}
startBlkId >> recall.startBlkId;
}
{
std::stringstream fileSize;
fileSize << columns[2];
if (!utils::isValidUInt(fileSize.str())) {
castor::exception::InvalidArgument ex;
ex.getMessage() << "\tFile size " << fileSize.str() <<
" is not a valid unsigned integer";
throw ex;
}
fileSize >> recall.fileSize;
}
recall.destDiskFile = columns[3];
return recall;
}
/******************************************************************************
*
* 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/tape/tpcp/BlkIdRecall.hpp"
#include <list>
#include <string>
namespace castor {
namespace tape {
namespace tpcp {
/**
* Class responsible for parsing a file of block-ID based recall jobs.
*/
class BlkIdRecallsFileParser {
public:
/**
* Parses the specified file containing block-ID based recall jobs.
*
* This method:
* <ul>
* <li>Trims leading and trailing white space from each line
* <li>Ignores blank lines with or without white space.
* <li>Ignores comment lines, i.e. those starting with a '#' after their
* leading and trailing white space has been trimmed.
* </ul>
*
* @param filename The name of the file to be parsed.
* @return The list of parsed block-ID based recall jobs.
*/
static std::list<BlkIdRecall> parseFile(const std::string &filename);
private:
/**
* Appends each line of the specified file to the specified list of lines.
* The new-line characters are extracted from the file, but they are not
* stored in the lines appended to the list.
*
* An empty line, with or without a delimiting '\n' character will be appended
* to the list of lines as an empty string.
*
* @param filename The filename of the file to be read.
* @return The lists file lines.
*/
static std::list<std::string> readFileIntoList(const std::string &filename);
/**
* Parses the specified line into a BlkIdRecall structure.
*
* The format of the line to be parsed should be:
*
* fSeq,fileStartBlkId,fileSize,destFile
*
* @param line The line tobe parsed.
* @return The BlkIdRecall structure.
*/
static BlkIdRecall parseLineIntoBlkIdRecall(const std::string &line);
}; // class TpcpCommand
} // namespace tpcp
} // namespace tape
} // namespace castor
......@@ -61,6 +61,7 @@ CastorInstallExeManPage (writetp)
# Rules to build and install readtpblkid
################################################################################
set (READTPBLKID_SRC_FILES
BlkIdRecallsFileParser.cpp
Helper.cpp
ReadTpBlkIdCommand.cpp
ReadTpBlkIdMain.cpp)
......
......@@ -44,6 +44,7 @@
#include "castor/tape/tpcp/Constants.hpp"
#include "castor/tape/tpcp/Helper.hpp"
#include "castor/tape/tpcp/ReadTpBlkIdCommand.hpp"
#include "castor/tape/tpcp/BlkIdRecallsFileParser.hpp"
#include "castor/utils/utils.hpp"
#include "Cgetopt.h"
#include "common.h"
......@@ -247,11 +248,12 @@ void castor::tape::tpcp::ReadTpBlkIdCommand::parseCommandLine(const int argc,
char **argv) {
static struct option longopts[] = {
{"debug" , no_argument, NULL, 'd'},
{"help" , no_argument, NULL, 'h'},
{"server" , required_argument, NULL, 's'},
{"rao" , no_argument, NULL, 'r'},
{NULL , 0, NULL, 0 }
{"debug" , no_argument, NULL, 'd'},
{"help" , no_argument, NULL, 'h'},
{"server" , required_argument, NULL, 's'},
{"rao" , no_argument, NULL, 'r'},
{"recallList", required_argument, NULL, 'l'},
{NULL , 0, NULL, 0 }
};
optind = 1;
......@@ -259,7 +261,7 @@ void castor::tape::tpcp::ReadTpBlkIdCommand::parseCommandLine(const int argc,
char c;
while((c = getopt_long(argc, argv, ":dhs:r", longopts, NULL)) != -1) {
while((c = getopt_long(argc, argv, ":dhs:rl:", longopts, NULL)) != -1) {
switch (c) {
case 'd':
......@@ -288,6 +290,18 @@ void castor::tape::tpcp::ReadTpBlkIdCommand::parseCommandLine(const int argc,
m_cmdLine.raoSet = true;
break;
case 'l':
m_cmdLine.recallListSet = true;
if(NULL == optarg) {
castor::exception::Exception ex;
ex.getMessage() <<
"Failed to obtain the argument of the recallList command-line option"
": optarg is NULL";
throw ex;
}
m_cmdLine.recallListFileName = optarg;
break;
case ':':
{
castor::exception::InvalidArgument ex;
......@@ -351,76 +365,93 @@ void castor::tape::tpcp::ReadTpBlkIdCommand::parseCommandLine(const int argc,
throw ex;
}
if(nbArgs < 2) {
castor::exception::InvalidArgument ex;
const int nbRawRecallArgs = nbArgs - 1;
ex.getMessage() << "\tThere must be at least one file to be recalled";
if(m_cmdLine.recallListSet && 0 != nbRawRecallArgs) {
castor::exception::InvalidArgument ex;
ex.getMessage() <<
"\t[fSeq fileStartBlkId fileSize destFile].. command-line arguments\n"
"\tand the -l/--recallList option are mutually exclusive";
throw ex;
}
const int nbRawRecallArgs = nbArgs - 1;
if(0 != nbRawRecallArgs % 4) {
castor::exception::InvalidArgument ex;
if(m_cmdLine.recallListSet) {
m_cmdLine.recalls =
BlkIdRecallsFileParser::parseFile(m_cmdLine.recallListFileName);
}
ex.getMessage() <<
"\tThe arguments composing the list of recalls to be executed must be a\n"
"\tmultiple of four because each recall is described by:\n"
"\t\t1. The tape file sequence number.\n"
"\t\t2. The start of the tape file as a block ID\n"
"\t\t3. The size of the file in bytes.\n"
"\t\t4. The path of the destination disk file.";
if(!m_cmdLine.recallListSet) {
if(0 != nbRawRecallArgs % 4) {
castor::exception::InvalidArgument ex;
throw ex;
}
ex.getMessage() <<
"\tThe arguments composing the list of recalls to be executed must be\n"
"\ta multiple of four because each recall is described by:\n"
"\t\t1. The tape file sequence number.\n"
"\t\t2. The start of the tape file as a block ID\n"
"\t\t3. The size of the file in bytes.\n"
"\t\t4. The path of the destination disk file.";
// Move on to the next command-line argument
optind++;
throw ex;
}
const int nbFilesToBeRecalled = nbRawRecallArgs / 4;
for(int recallFileIndex = 0; recallFileIndex < nbFilesToBeRecalled;
recallFileIndex++) {
BlkIdRecall recall;
// Move on to the next command-line argument
optind++;
{
std::stringstream fseq;
fseq << argv[optind++];
if (!utils::isValidUInt(fseq.str())) {
castor::exception::InvalidArgument ex;
ex.getMessage() << "\tFseq " << fseq.str() <<
" is not a valid unsigned integer";
throw ex;
const int nbFilesToBeRecalled = nbRawRecallArgs / 4;
for(int recallFileIndex = 0; recallFileIndex < nbFilesToBeRecalled;
recallFileIndex++) {
BlkIdRecall recall;
{
std::stringstream fseq;
fseq << argv[optind++];
if (!utils::isValidUInt(fseq.str())) {
castor::exception::InvalidArgument ex;
ex.getMessage() << "\tFseq " << fseq.str() <<
" is not a valid unsigned integer";
throw ex;
}
fseq >> recall.fseq;
}
fseq >> recall.fseq;
}
{
std::stringstream startBlkId;
startBlkId << argv[optind++];
if (!utils::isValidUInt(startBlkId.str())) {
castor::exception::InvalidArgument ex;
ex.getMessage() << "\tStart block ID " << startBlkId.str() <<
" is not a valid unsigned integer";
throw ex;
{
std::stringstream startBlkId;
startBlkId << argv[optind++];
if (!utils::isValidUInt(startBlkId.str())) {
castor::exception::InvalidArgument ex;
ex.getMessage() << "\tStart block ID " << startBlkId.str() <<
" is not a valid unsigned integer";
throw ex;
}
startBlkId >> recall.startBlkId;
}
startBlkId >> recall.startBlkId;
}
{
std::stringstream fileSize;
fileSize << argv[optind++];
if (!utils::isValidUInt(fileSize.str())) {
castor::exception::InvalidArgument ex;
ex.getMessage() << "\tFile size " << fileSize.str() <<
" is not a valid unsigned integer";
throw ex;
{
std::stringstream fileSize;
fileSize << argv[optind++];
if (!utils::isValidUInt(fileSize.str())) {
castor::exception::InvalidArgument ex;
ex.getMessage() << "\tFile size " << fileSize.str() <<
" is not a valid unsigned integer";
throw ex;
}
fileSize >> recall.fileSize;
}
fileSize >> recall.fileSize;
recall.destDiskFile = argv[optind++];
m_cmdLine.recalls.push_back(recall);
}
} // if(!m_cmdLine.recallListSet)
recall.destDiskFile = argv[optind++];
if(m_cmdLine.recalls.empty()) {
castor::exception::InvalidArgument ex;
m_cmdLine.recalls.push_back(recall);
ex.getMessage() << "\tThere must be at least one file to be recalled";
throw ex;
}
}
......@@ -452,12 +483,16 @@ void castor::tape::tpcp::ReadTpBlkIdCommand::usage(std::ostream &os) const {
"\n"
"Options:\n"
"\n"
"\t-d, --debug Turn on the printing of debug information.\n"
"\t-h, --help Print this help messgae and exit.\n"
"\t-s, --server server Specifies the tape server to be used, therefore\n"
"\t overriding the drive scheduling of the VDQM.\n"
"\t-r, --rao Use the tape-gateway protocol targeted at\n"
"\t Recommended Access Order (RAO).\n"
"\t-d, --debug Turn on the printing of debug information.\n"
"\t-h, --help Print this help messgae and exit.\n"
"\t-s, --server server Specifies the tape server to be used, therefore\n"
"\t overriding the drive scheduling of the VDQM.\n"
"\t-r, --rao Use the tape-gateway protocol targeted at\n"
"\t Recommended Access Order (RAO).\n"
"\t-l, --recallList file A comma separated value file containing a list\n"
"\t of recalls. Each line of the file represents\n"
"\t one recall and is of the form:\n"
"\t fSeq,fileStartBlkId,fileSize,destFile\n"
"\n"
"Comments to: Castor.Support@cern.ch" << std::endl;
}
......
......@@ -56,16 +56,26 @@ struct ReadTpBlkIdCommandLine {
bool serverSet;
/**
* The tape server to be used therefore overriding the drive scheduling of
* the VDQM.
*/
char server[CA_MAXHOSTNAMELEN+1];
/**
* True if the tape-gateway protocol targeted at Recommended Access Order
* (RAO) should be used.
*/
bool raoSet;
/**
* The tape server to be used therefore overriding the drive scheduling of
* the VDQM.
* True if the "recall list" filename option has been set.
*/
char server[CA_MAXHOSTNAMELEN+1];
bool recallListSet;
/**
* The filename of the "recall list" file.
*/
std::string recallListFileName;
/**
* The VID of the tape to be mounted.
......@@ -84,8 +94,8 @@ struct ReadTpBlkIdCommandLine {
debugSet(false),
helpSet(false),
serverSet(false),
raoSet(false)
{
raoSet(false),
recallListSet(false) {
castor::utils::setBytes(server, '\0');
castor::utils::setBytes(vid , '\0');
}
......
......@@ -61,6 +61,10 @@ the VDQM will allocate the first free compatible drive.
\fB\-r, \-\-rao
Use the tape-gateway protocol targeted at Recommended Access Order (RAO).
.TP
\fB-l, \-\-recallList file
A comma separated value file containing a list of recalls. Each line of the
file represents one recall and is of the form:
\fBfSeq,fileStartBlkId,fileSize,destFile
.SH "RETURN CODES"
.TP
......
Markdown is supported
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