//------------------------------------------------------------------------------ // File com_proto_convert.cc // Author: Mihai Patrascoiu - CERN //------------------------------------------------------------------------------ /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2019 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 "common/LayoutId.hh" #include "common/StringTokenizer.hh" #include "console/ConsoleMain.hh" #include "console/commands/ICmdHelper.hh" void com_convert_help(); //------------------------------------------------------------------------------ //! Class ConvertHelper //------------------------------------------------------------------------------ class ConvertHelper: public ICmdHelper { public: //---------------------------------------------------------------------------- //! Constructor //! //! @param opts global options //---------------------------------------------------------------------------- ConvertHelper(const GlobalOptions& opts) : ICmdHelper(opts) {} //---------------------------------------------------------------------------- //! Destructor //---------------------------------------------------------------------------- ~ConvertHelper() override = default; //---------------------------------------------------------------------------- //! Parse command line input //! //! @param arg input //! //! @return true if successful, otherwise false //---------------------------------------------------------------------------- bool ParseCommand(const char* arg) override; private: //---------------------------------------------------------------------------- //! Parse identifier string and construct identifier proto object. //---------------------------------------------------------------------------- eos::console::ConvertProto_IdentifierProto* ParseIdentifier(XrdOucString path); //---------------------------------------------------------------------------- //! Parse conversion string and construct conversion proto object. //! Returns null if conversion string is invalid. //---------------------------------------------------------------------------- eos::console::ConvertProto_ConversionProto* ParseConversion(eos::common::StringTokenizer& tokenizer); }; //------------------------------------------------------------------------------ // Parse command line input //------------------------------------------------------------------------------ bool ConvertHelper::ParseCommand(const char* arg) { eos::console::ConvertProto* convert = mReq.mutable_convert(); eos::common::StringTokenizer tokenizer(arg); tokenizer.GetLine(); XrdOucString token; if (!tokenizer.NextToken(token)) { return false; } if (token == "enable" || token == "disable") { auto action = (token == "enable") ? eos::console::ConvertProto_ActionProto::ENABLE : eos::console::ConvertProto_ActionProto::DISABLE; convert->mutable_action()->set_action(action); } else if (token == "status") { convert->mutable_status(); } else if (token == "config") { eos::console::ConvertProto_ConfigProto* config = convert->mutable_config(); bool option = false; while (tokenizer.NextToken(token)) { if (token.beginswith("--maxthreads")) { uint32_t maxthreads = 0ul; if (token.beginswith("--maxthreads=")) { token.replace("--maxthreads=", ""); } else { tokenizer.NextToken(token); } try { maxthreads = std::stoul(token.c_str()); if (maxthreads == 0) { throw std::invalid_argument("value zero not allowed"); } } catch (...) { std::cerr << "error: invalid value for ='" << token << "'" << std::endl; return false; } config->set_maxthreads(maxthreads); option = true; } else if (token.beginswith("--maxqueuesize")) { uint32_t max_queue_size = 0ul; if (token.beginswith("--maxqueuesize=")) { token.replace("--maxqueuesize=", ""); } else { tokenizer.NextToken(token); } try { max_queue_size = std::stoul(token.c_str()); if (max_queue_size == 0) { throw std::invalid_argument("value zero not allowed"); } } catch (...) { std::cerr << "error: invalid value for ='" << token << "'" << std::endl; return false; } config->set_maxqueuesize(max_queue_size); option = true; } else { std::cerr << "warning: unknown config option '" << token << "'" << std::endl; } } if (!option) { std::cerr << "error: no valid config option given" << std::endl; return false; } } else if (token == "file") { eos::console::ConvertProto_FileProto* file = convert->mutable_file(); if (!tokenizer.NextToken(token)) { return false; } file->set_allocated_identifier(ParseIdentifier(token)); eos::console::ConvertProto_ConversionProto* conversion = ParseConversion(tokenizer); if (conversion == nullptr) { return false; } file ->set_allocated_conversion(conversion); // Placeholder for options } else if (token == "rule") { if (!tokenizer.NextToken(token)) { return false; } eos::console::ConvertProto_RuleProto* rule = convert->mutable_rule(); rule->set_allocated_identifier(ParseIdentifier(token)); eos::console::ConvertProto_ConversionProto* conversion = ParseConversion(tokenizer); if (conversion == nullptr) { return false; } rule->set_allocated_conversion(conversion); } else if (token == "list") { eos::console::ConvertProto_ListProto* list = convert->mutable_list(); if (!tokenizer.NextToken(token)) { list->set_type("failed"); } else { if ((token != "--failed") && (token != "--pending")) { std::cerr << "error: unknown listing option \'" << token << "\'" << std::endl; return false; } else { token.erase(0, 2); list->set_type(token.c_str()); } } } else if (token == "clear") { eos::console::ConvertProto_ClearProto* clear = convert->mutable_clear(); if (!tokenizer.NextToken(token)) { std::cerr << "error: clear subcommand requires an option" << std::endl; return false; } else { if ((token != "--failed") && (token != "--pending")) { std::cerr << "error: unknown clear option \'" << token << "\'" << std::endl; return false; } else { token.erase(0, 2); clear->set_type(token.c_str()); } } } else { return false; } return true; } //---------------------------------------------------------------------------- // Parse string identifier and construct identifier proto object //---------------------------------------------------------------------------- eos::console::ConvertProto_IdentifierProto* ConvertHelper::ParseIdentifier(XrdOucString path) { auto identifier = new eos::console::ConvertProto_IdentifierProto{}; auto id = 0ull; if (Path2FileDenominator(path, id)) { identifier->set_fileid(id); } else if (Path2ContainerDenominator(path, id)) { identifier->set_containerid(id); } else { path = abspath(path.c_str()); identifier->set_path(path.c_str()); } return identifier; } //---------------------------------------------------------------------------- // Parse conversion string and construct conversion proto object. // Returns null if conversion string is invalid //---------------------------------------------------------------------------- eos::console::ConvertProto_ConversionProto* ConvertHelper::ParseConversion(eos::common::StringTokenizer& tokenizer) { std::string token; std::string layout; std::string space; std::string placement; std::string checksum; int replica = 0; size_t pos; bool ok; // Lambda function to validate layout string auto validLayout = [](const std::string & layout) { return eos::common::LayoutId::GetLayoutFromString(layout) != -1; }; // Lambda function to validate placement policy string auto validPlacement = [](const std::string & placement) { if (placement == "scattered" || placement == "hybrid" || placement == "gathered") { return true; } return false; }; // Lambda function to validate checksum string auto validChecksum = [](const std::string & checksum) { using eos::common::LayoutId; auto xs_id = LayoutId::GetChecksumFromString(checksum); return ((xs_id > -1) && (xs_id != LayoutId::eChecksum::kNone)); }; if (!tokenizer.NextToken(token)) { std::cerr << "error: missing argument" << std::endl; return nullptr; } if ((pos = token.find(":")) == std::string::npos) { std::cerr << "error: invalid format" << std::endl; return nullptr; } layout = token.substr(0, pos); try { replica = std::stol(token.substr(pos + 1)); } catch (...) { std::cerr << "error: failed to interpret replica number '" << token.substr(pos + 1) << "'" << endl; return nullptr; } if (!validLayout(layout)) { std::cerr << "error: invalid layout '" << layout << "'" << std::endl; return nullptr; } if (replica < 1 || replica > 32) { std::cerr << "error: invalid replica number=" << replica << " (must be between 1 and 32)" << std::endl; return nullptr; } while (tokenizer.NextToken(token)) { if ((ok = validChecksum(token))) { checksum = std::move(token); } else if ((ok = validPlacement(token))) { placement = std::move(token); } else if ((ok = space.empty())) { space = std::move(token); } if (!ok) { std::cerr << "error: could not interpret '" << token << "' argument" << std::endl; return nullptr; } } auto conversion = new eos::console::ConvertProto_ConversionProto{}; conversion->set_layout(layout); conversion->set_replica(replica); conversion->set_space(space); conversion->set_placement(placement); conversion->set_checksum(checksum); return conversion; } //------------------------------------------------------------------------------ // Convert command entry point //------------------------------------------------------------------------------ int com_convert(char* arg) { if (wants_help(arg)) { com_convert_help(); global_retc = EINVAL; return EINVAL; } ConvertHelper convert(gGlobalOpts); if (!convert.ParseCommand(arg)) { com_convert_help(); global_retc = EINVAL; return EINVAL; } global_retc = convert.Execute(); return global_retc; } //------------------------------------------------------------------------------ // Print help message //------------------------------------------------------------------------------ void com_convert_help() { std::ostringstream oss; oss << "Usage: convert " << std::endl << " convert enable/disable " << std::endl << " enable or disable the converter engine " << std::endl << std::endl << " convert status " << std::endl << " print converter engine statistics " << std::endl << std::endl << " convert config