//------------------------------------------------------------------------------
// File: Configuration.cc
// Author: Elvin-Alin Sindrilaru - CERN
//------------------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2011 CERN/Switzerland *
* *
* 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
#include
#include
#include
#include
#include
/*----------------------------------------------------------------------------*/
#include
/*----------------------------------------------------------------------------*/
#include "Configuration.hh"
#include "DirEos.hh"
#include "ProtoIo.hh"
#include "common/StringConversion.hh"
/*----------------------------------------------------------------------------*/
using namespace std;
EOSBMKNAMESPACE_BEGIN
//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
Configuration::Configuration():
eos::common::LogId()
{
mPbConfig = new ConfigProto();
}
//------------------------------------------------------------------------------
// Destructor
//------------------------------------------------------------------------------
Configuration::~Configuration()
{
mFileNames.clear();
if (mPbConfig)
{
delete mPbConfig;
}
}
//------------------------------------------------------------------------------
// Set the low level configuration object taking the ownership
//------------------------------------------------------------------------------
void
Configuration::SetPbConfig(ConfigProto* pbConfig)
{
if (mPbConfig) delete mPbConfig;
mPbConfig = pbConfig;
}
//------------------------------------------------------------------------------
// Get the low level configuration object (ProtBuf object)
//------------------------------------------------------------------------------
ConfigProto&
Configuration::GetPbConfig() const
{
return *mPbConfig;
}
//------------------------------------------------------------------------------
// Generate file names used for the write operations
//------------------------------------------------------------------------------
void
Configuration::GenerateFileNames()
{
string gen_filename;
uuid_t gen_uuid;
char char_uuid[40];
uint32_t num_files = mPbConfig->numfiles();
uint32_t num_jobs = mPbConfig->numjobs();
if (mPbConfig->access() == ConfigProto_AccessMode_CONCURRENT)
{
// All jobs access the same files
mFileNames.reserve(num_files);
for (uint32_t i = 0; i < num_files; i++)
{
uuid_generate_time(gen_uuid);
uuid_unparse(gen_uuid, char_uuid);
gen_filename = mPbConfig->benchmarkdir();
gen_filename += char_uuid;
mFileNames.push_back(gen_filename);
}
}
else if (mPbConfig->access() == ConfigProto_AccessMode_PARALLEL)
{
// Each job gets a separate set of files on which it works
mFileNames.reserve(num_jobs * num_files);
for (uint32_t i = 0; i < num_jobs; i++)
{
for (uint32_t j = 0; j < num_files; j++)
{
uuid_generate_time(gen_uuid);
uuid_unparse(gen_uuid, char_uuid);
gen_filename = mPbConfig->benchmarkdir();
gen_filename += char_uuid;
mFileNames.push_back(gen_filename);
}
}
}
}
//------------------------------------------------------------------------------
// Print configuration
//------------------------------------------------------------------------------
void
Configuration::Print()
{
std::stringstream sstr;
sstr << left << setw(190) << setfill('*') << "" << endl;
std::string star_line = sstr.str();
sstr.str("");
sstr << left << setw(190) << setfill('-') << "" << endl;
std::string minus_line = sstr.str();
cout << endl << star_line
<< setw(100) << right << "C o n f i g u r a t i o n" << endl
<< star_line
<< setw(30) << left << setfill('.') << "EOS instance" << setfill(' ')
<< setw(40) << left << mPbConfig->benchmarkinstance()
<< setw(30) << left << setfill('.') << "Test path" << setfill(' ')
<< setw(40) << left << mPbConfig->benchmarkdir() << endl
<< setw(30) << left << setfill('.') << "File size" << setfill(' ')
<< setw(40) << left
<< eos::common::StringConversion::GetPrettySize(mPbConfig->filesize())
<< setw(30) << left << setfill('.') << "Block size" << setfill(' ')
<< setw(40) << left
<< eos::common::StringConversion::GetPrettySize(mPbConfig->blocksize())
<< endl
<< setw(30) << left << setfill('.') << "File layout" << setfill(' ')
<< setw(40) << left << GetFileLayout(mPbConfig->filelayout())
<< setw(30) << left << setfill('.') << "Number of files" << setfill(' ')
<< setw(40) << left << mPbConfig->numfiles() << endl
<< setw(30) << left << setfill('.') << "Job type" << setfill(' ')
<< setw(40) << left << (mPbConfig->jobtype() ? "process" : "thread")
<< setw(30) << left << setfill('.') << "Number of jobs" << setfill(' ')
<< setw(40) << left << mPbConfig->numjobs() << endl
<< setw(30) << left << setfill('.') << "Operation" << setfill(' ')
<< setw(40) << left << GetOperation(mPbConfig->operation())
<< setw(30) << left << setfill('.') << "Access mode" << setfill(' ')
<< setw(40) << left << (mPbConfig->access() ? "parallel" : "concurrent")
<< endl
<< setw(30) << left << setfill('.') << "Read pattern" << setfill(' ')
<< setw(40) << left << GetPattern(mPbConfig->pattern()) << endl;
if (mPbConfig->pattern() == ConfigProto_PatternType_RANDOM)
{
cout << "Number of requests: " << mPbConfig->offset_size() << endl;
cout << "Requests (offset, length): " << endl;
for (int i = 0; i < mPbConfig->offset_size(); i++)
{
cout << "( " << left << setw(10) << mPbConfig->offset(i)
<< "," << left << setw(10) << mPbConfig->length(i) << " ) ";
if ((i + 1) % 4 == 0) cout << endl;
}
cout << endl;
}
cout << minus_line << endl << endl;
}
//------------------------------------------------------------------------------
// Check whether directory path exists and has correct attributes, if it does
// not exist then it is created and set the correct attributes.
//------------------------------------------------------------------------------
bool
Configuration::CheckDirAndFiles()
{
bool ret = true;
DirEos* dir = new DirEos(mPbConfig->benchmarkdir(),
mPbConfig->benchmarkinstance());
// Check if directory exists and if not create it
if (!dir->Exist())
{
if (!dir->Create())
{
eos_err("Could not create working directory");
ret = false;
}
// Set directory attributes to match the required configuration
if (!dir->SetConfig(*mPbConfig))
{
eos_err("Error while trying to set attributes to the dir");
ret = false;
}
}
else if (!dir->MatchConfig(*mPbConfig))
{
ret = false;
}
if (!ret)
{
delete dir;
return ret;
}
// If operation is read only then we have to check that we have enough files
// in the benchmark directoy, if not we abort
if ((mPbConfig->operation() == ConfigProto_OperationType_READ_GW) ||
(mPbConfig->operation() == ConfigProto_OperationType_READ_PIO))
{
uint32_t required_files = 0;
mFileNames = dir->GetMatchingFiles(mPbConfig->filesize());
if (mPbConfig->access() == ConfigProto_AccessMode_CONCURRENT)
{
required_files = mPbConfig->numfiles();
}
else if (mPbConfig->access() == ConfigProto_AccessMode_PARALLEL)
{
required_files = mPbConfig->numjobs() * mPbConfig->numfiles();
}
if (mFileNames.size() <= required_files)
{
eos_err("Not enough files in dir for the read operation");
ret = false;
}
}
else
{
// Generate the file names used for the benchmark run
GenerateFileNames();
}
delete dir;
return ret;
}
//------------------------------------------------------------------------------
// Create configuration file - accept input from the console and build up the
// configuration object which is then written to the file supplied as an arg.
//------------------------------------------------------------------------------
bool
Configuration::CreateConfigFile(const string& outputFile)
{
string input_value = "";
uint64_t block_size;
ConfigProto_OperationType op_type;
// Get benchmark instance
cout << "Benchmarked instance: ";
while (input_value.empty())
{
if (!getline(cin, input_value))
{
return false;
}
}
mPbConfig->set_benchmarkinstance(input_value);
// Get benchmark directory where operations are done
input_value = "";
cout << "Benchmark directory: ";
while (input_value.empty())
{
if (!getline(cin, input_value))
{
return false;
}
}
// Make sure that the directory ends with one "/"
if (*input_value.rbegin() != '/')
{
input_value += "/";
}
mPbConfig->set_benchmarkdir(input_value);
// Get the file size
while (1)
{
size_t pos;
uint64_t file_size = 0;
input_value = "";
cout << "File size (KB|MB|GB): ";
while (input_value.empty())
{
if (!getline(cin, input_value))
{
return false;
}
}
if ((pos = input_value.rfind("KB")) != string::npos)
{
// File size given in KB
input_value = input_value.erase(pos);
file_size = eos::common::KB;
}
else if ((pos = input_value.rfind("MB")) != string::npos)
{
// File size given in MB
input_value = input_value.erase(pos);
file_size = eos::common::MB;
}
else if ((pos = input_value.rfind("GB")) != string::npos)
{
// File size given in GB
input_value = input_value.erase(pos);
file_size = eos::common::GB;
}
else
{
cout << "Input value is invalid! " << endl;
continue;
}
char* pEnd;
float size;
if ((size = strtol(input_value.c_str(), &pEnd, 10)) == 0L)
{
cout << "Input value is invalid! " << endl;
continue;
}
file_size *= size;
mPbConfig->set_filesize(file_size);
break;
}
// Get the number of files
while (1)
{
input_value = "";
cout << "Number of files: ";
while (input_value.empty())
{
if (!getline(cin, input_value))
{
return false;
}
}
char* pEnd;
uint32_t no_files;
if ((no_files = strtol(input_value.c_str(), &pEnd, 10)) == 0L)
{
cout << "Input value is invalid! " << endl;
continue;
}
mPbConfig->set_numfiles(no_files);
break;
}
// Get block size for rd/wr operations
while (1)
{
size_t pos;
input_value = "";
cout << "Block size (KB|MB|GB): ";
while (input_value.empty())
{
if (!getline(cin, input_value))
{
return false;
}
}
if ((pos = input_value.rfind("KB")) != string::npos)
{
// File size given in KB
input_value = input_value.erase(pos);
block_size = eos::common::KB;
}
else if ((pos = input_value.rfind("MB")) != string::npos)
{
// File size given in MB
input_value = input_value.erase(pos);
block_size = eos::common::MB;
}
else if ((pos = input_value.rfind("GB")) != string::npos)
{
// File size given in GB
input_value = input_value.erase(pos);
block_size = eos::common::GB;
}
else
{
cout << "Input value is invalid! " << endl;
continue;
}
char* pEnd;
uint64_t size;
if ((size = strtol(input_value.c_str(), &pEnd, 10)) == 0L)
{
cout << "Input value is invalid! " << endl;
continue;
}
block_size *= size;
mPbConfig->set_blocksize(block_size);
break;
}
// Get file layout for benchmark
while (1)
{
ConfigProto_FileLayoutType file_type;
input_value = "";
cout << "File layout (plain|replica|raiddp|raid6|archive): ";
while (input_value.empty())
{
if (!getline(cin, input_value))
{
return false;
}
}
file_type = Configuration::GetFileLayout(input_value);
if (file_type == ConfigProto_FileLayoutType_NOLAYOUT)
{
cout << "Input value is invalid!" << endl;
continue;
}
mPbConfig->set_filelayout(file_type);
break;
}
// For the replica type of layaout get the number of replicas
if (mPbConfig->filelayout() == ConfigProto_FileLayoutType_REPLICA)
{
while (1)
{
input_value = "";
cout << "Number of replicas: ";
while (input_value.empty())
{
if (!getline(cin, input_value))
{
return false;
}
}
char* pEnd;
uint32_t no_replicas;
if ((no_replicas = strtol(input_value.c_str(), &pEnd, 10)) == 0L)
{
cout << "Input value is invalid! " << endl;
continue;
}
mPbConfig->set_noreplicas(no_replicas);
break;
}
}
// Get type of execution task
while (1)
{
ConfigProto_JobType job_type;
input_value = "";
cout << "Execution type (thread|process): ";
while (input_value.empty())
{
if (!getline(cin, input_value))
{
return false;
}
}
if (input_value.compare("thread") == 0)
{
job_type = ConfigProto_JobType_THREAD;
}
else if (input_value.compare("process") == 0)
{
job_type = ConfigProto_JobType_PROCESS;
}
else
{
cout << "Input value is invalid!" << endl;
continue;
}
mPbConfig->set_jobtype(job_type);
break;
}
// Get the number of jobs to be launched (threads/processes)
while (1)
{
input_value = "";
cout << "Number of jobs: ";
while (input_value.empty())
{
if (!getline(cin, input_value))
{
return false;
}
}
char* pEnd;
uint32_t no_jobs;
if ((no_jobs = strtol(input_value.c_str(), &pEnd, 10)) == 0L)
{
cout << "Input value is invalid! " << endl;
continue;
}
mPbConfig->set_numjobs(no_jobs);
break;
}
// Get operation type
while (1)
{
input_value = "";
cout << "Operation (write|read_gw|read_pio|rdwr_gw|rdwr_pio): ";
while (input_value.empty())
{
if (!getline(cin, input_value))
{
return false;
}
}
op_type = Configuration::GetOperation(input_value);
if (op_type == ConfigProto_OperationType_NOTYPE)
{
cout << "Input value is invalid!" << endl;
continue;
}
mPbConfig->set_operation(op_type);
break;
}
// Get pattern type and generate set of random requests if needed
if (op_type != ConfigProto_OperationType_WRITE)
{
while (1)
{
ConfigProto_PatternType pattern_type;
input_value = "";
cout << "Read pattern (full|random): ";
while (input_value.empty())
{
if (!getline(cin, input_value))
{
return false;
}
}
pattern_type = Configuration::GetPattern(input_value);
if (pattern_type == ConfigProto_PatternType_NOPATTERN)
{
cout << "Input value is invalid!" << endl;
continue;
}
mPbConfig->set_pattern(pattern_type);
// Generate a set of random (offset, length) pairs
uint64_t offset;
uint64_t length;
srand(time(NULL));
if (pattern_type == ConfigProto_PatternType_RANDOM)
{
uint32_t no_requests;
while (1)
{
input_value = "";
cout << "Number of requests: ";
while (input_value.empty())
{
if (!getline(cin, input_value))
{
return false;
}
}
char* pEnd;
if ((no_requests = strtol(input_value.c_str(), &pEnd, 10)) == 0L)
{
cout << "Input value is invalid! " << endl;
continue;
}
break;
}
// Generate randomly the read requests
uint64_t file_size = mPbConfig->filesize();
for (uint32_t i = 0; i < no_requests; i++)
{
offset = (rand() % file_size) + 1;
length = (rand() % (file_size - offset)) + 1;
mPbConfig->add_offset(offset);
mPbConfig->add_length(length);
}
}
break;
}
}
// If multiple jobs then decide on the type of access
if (mPbConfig->numjobs() > 1)
{
// Get the type of access (parallel/concurrent)
// parallel - no two jobs access the same file
// concurrent - all jobs access the same files
while (1)
{
ConfigProto_AccessMode access_type;
input_value = "";
cout << "Access type (parallel/concurrent): ";
while (input_value.empty())
{
if (!getline(cin, input_value))
{
return false;
}
}
if (input_value.compare("parallel") == 0)
{
access_type = ConfigProto_AccessMode_PARALLEL;
}
else if (input_value.compare("concurrent") == 0)
{
access_type = ConfigProto_AccessMode_CONCURRENT;
}
else
{
cout << "Input value is invalid!" << endl;
continue;
}
mPbConfig->set_access(access_type);
break;
}
}
// Write the configuration to the supplied output file
ProtoWriter writer(outputFile);
if (!writer(*mPbConfig))
{
cout << "Error while writing configuration to file" << endl;
return false;
}
return true;
}
//------------------------------------------------------------------------------
// Read in configuration from file
//------------------------------------------------------------------------------
bool
Configuration::ReadFromFile(const string& fileName)
{
ProtoReader reader(fileName);
ConfigProto* tmp_conf = reader.ReadNext();
if (tmp_conf)
{
SetPbConfig(tmp_conf);
return true;
}
return false;
}
//------------------------------------------------------------------------------
// Get string representation for the file layout
//------------------------------------------------------------------------------
string
Configuration::GetFileLayout(ConfigProto_FileLayoutType fileType)
{
if (fileType == ConfigProto_FileLayoutType_PLAIN) return "plain";
if (fileType == ConfigProto_FileLayoutType_REPLICA) return "replica";
if (fileType == ConfigProto_FileLayoutType_RAIDDP) return "raiddp";
if (fileType == ConfigProto_FileLayoutType_RAID6) return "raid6";
if (fileType == ConfigProto_FileLayoutType_ARCHIVE) return "archive";
return "";
}
//------------------------------------------------------------------------------
// Get int representation for the file layout
//------------------------------------------------------------------------------
ConfigProto_FileLayoutType
Configuration::GetFileLayout(const string& fileType)
{
if (fileType.compare("plain") == 0) return ConfigProto_FileLayoutType_PLAIN;
if (fileType.compare("replica") == 0) return ConfigProto_FileLayoutType_REPLICA;
if (fileType.compare("raiddp") == 0) return ConfigProto_FileLayoutType_RAIDDP;
if (fileType.compare("raid6") == 0) return ConfigProto_FileLayoutType_RAID6;
if (fileType.compare("archive") == 0) return ConfigProto_FileLayoutType_ARCHIVE;
return ConfigProto_FileLayoutType_NOLAYOUT;
}
//------------------------------------------------------------------------------
// Get string representation for the operation layout
//------------------------------------------------------------------------------
string
Configuration::GetOperation(ConfigProto_OperationType opType)
{
if (opType == ConfigProto_OperationType_WRITE) return "write";
if (opType == ConfigProto_OperationType_READ_GW) return "read_gw";
if (opType == ConfigProto_OperationType_READ_PIO) return "read_pio";
if (opType == ConfigProto_OperationType_RDWR_GW) return "rdwr_gw";
if (opType == ConfigProto_OperationType_RDWR_PIO) return "rdwr_pio";
return "";
}
//------------------------------------------------------------------------------
// Get int representation for the operation type
//------------------------------------------------------------------------------
ConfigProto_OperationType
Configuration::GetOperation(const string& opType)
{
if (opType.compare("write") == 0) return ConfigProto_OperationType_WRITE;
if (opType.compare("read_gw") == 0) return ConfigProto_OperationType_READ_GW;
if (opType.compare("read_pio") == 0) return ConfigProto_OperationType_READ_PIO;
if (opType.compare("rdwr_gw") == 0) return ConfigProto_OperationType_RDWR_GW;
if (opType.compare("rdwr_pio") == 0) return ConfigProto_OperationType_RDWR_PIO;
return ConfigProto_OperationType_NOTYPE;
}
//------------------------------------------------------------------------------
// Get string representation for the pattern type
//------------------------------------------------------------------------------
string
Configuration::GetPattern(ConfigProto_PatternType patternType)
{
if (patternType == ConfigProto_PatternType_FULL) return "full";
if (patternType == ConfigProto_PatternType_RANDOM) return "random";
return "";
}
//------------------------------------------------------------------------------
// Get int representation for the pattern type
//------------------------------------------------------------------------------
ConfigProto_PatternType
Configuration::GetPattern(const string& patternType)
{
if (patternType.compare("full") == 0) return ConfigProto_PatternType_FULL;
if (patternType.compare("random") == 0) return ConfigProto_PatternType_RANDOM;
return ConfigProto_PatternType_NOPATTERN;
}
//------------------------------------------------------------------------------
// Compute hash value for the current object as the hash value of a string
// made up by concatenating some of the fields of the current object
//------------------------------------------------------------------------------
size_t
Configuration::GetHash()
{
size_t hash_value;
std::stringstream sstr;
sstr << mPbConfig->filesize() << mPbConfig->numfiles() << mPbConfig->blocksize()
<< mPbConfig->operation() << mPbConfig->filelayout() << mPbConfig->noreplicas()
<< mPbConfig->jobtype() << mPbConfig->numjobs() << mPbConfig->access()
<< mPbConfig->pattern();
if (mPbConfig->pattern() == ConfigProto_PatternType_RANDOM)
{
for (int32_t i = 0; i < mPbConfig->offset_size(); i++)
{
sstr << mPbConfig->offset(i) << mPbConfig->length(i);
}
}
hash_value = std::hash()(sstr.str());
return hash_value;
}
EOSBMKNAMESPACE_END