//------------------------------------------------------------------------------
//! @file ICmdHelper.hh
//------------------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2017 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 .*
************************************************************************/
#pragma once
#include "proto/ConsoleRequest.pb.h"
#include "console/GlobalOptions.hh"
#include
#include
#include
//------------------------------------------------------------------------------
//! Class ICmdHelper
//! @brief Abstract base class to be inherited in all the command
//! implementations
//------------------------------------------------------------------------------
class ICmdHelper
{
public:
//----------------------------------------------------------------------------
//! ExecutionOutcome struct: Stores output from a single execution
//----------------------------------------------------------------------------
struct ExecutionOutcome {
ExecutionOutcome() : result(""), error(""), errc(0) {}
ExecutionOutcome(const std::string& res, const std::string& err = "", int c = 0)
: result(res), error(err), errc(c) {}
std::string result; ///< String holding the result
std::string error; ///< String holding the error message
int errc; ///< Command return code
};
//----------------------------------------------------------------------------
//! Constructor
//----------------------------------------------------------------------------
ICmdHelper(const GlobalOptions& opts):
mReq(), mIsAdmin(false), mIsSilent(false),
mGlobalOpts(opts)
{
if (opts.mJsonFormat) {
mReq.set_format(eos::console::RequestProto::JSON);
}
if (!opts.mComment.empty()) {
mReq.set_comment(opts.mComment);
}
if (!isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO)) {
mReq.set_dontcolor(true);
}
}
//----------------------------------------------------------------------------
//! Destructor
//----------------------------------------------------------------------------
virtual ~ICmdHelper() = default;
//----------------------------------------------------------------------------
//! Parse command line input
//!
//! @param arg input
//! @param opts global options parse before current command
//!
//! @return true if successful, otherwise false
//----------------------------------------------------------------------------
virtual bool ParseCommand(const char* arg) = 0;
//----------------------------------------------------------------------------
//! Execute command and display any output information
//! @note When this methods is called the generic request object mReq needs
//! to already contain the specific command object.
//!
//! @param print_err flag to enable the display of any potential errors
//! @param add_route flag if eos.route opaque info needs to be added
//!
//! @return command return code
//----------------------------------------------------------------------------
int Execute(bool print_err = true, bool add_route = false);
//----------------------------------------------------------------------------
//! Execute command without displaying the result
//!
//! @param add_route flag if eos.route opaque info needs to be added
//!
//! @return command return code
//----------------------------------------------------------------------------
int ExecuteWithoutPrint(bool add_route = false);
//----------------------------------------------------------------------------
//! Get command output string
//----------------------------------------------------------------------------
std::string GetResult();
//----------------------------------------------------------------------------
//! Get command error string
//----------------------------------------------------------------------------
std::string GetError();
//----------------------------------------------------------------------------
//! Get error code
//----------------------------------------------------------------------------
inline int GetErrc()
{
return mOutcome.errc;
}
//----------------------------------------------------------------------------
//! Check if commands needs confirmation from the client
//----------------------------------------------------------------------------
inline bool NeedsConfirmation() const
{
return mNeedsConfirmation;
}
//------------------------------------------------------------------------------
//! Method used for user confirmation of the specified command
//!
//! @return true if operation confirmed, otherwise false
//------------------------------------------------------------------------------
static bool ConfirmOperation();
//------------------------------------------------------------------------------
//! Add eos.route opaque info depending on the type of request and on the
//! default route configuration
//!
//! @param cmd URL opaque info collected so far to which we can append extra
//! route information
//------------------------------------------------------------------------------
void AddRouteInfo(std::string& cmd);
//----------------------------------------------------------------------------
//! Inject simulated data. After calling this function, ALL responses from
//! this class will be simulated, and there's no turning back.
//----------------------------------------------------------------------------
void InjectSimulated(const std::string& command,
const ExecutionOutcome& outcome)
{
mSimulationMode = true;
mSimulatedData.emplace(FakeEntry{command, outcome});
}
//----------------------------------------------------------------------------
//! Check whether simulation was successful, ie we received the exact
//! commands in the specified order.
//----------------------------------------------------------------------------
bool CheckSimulationSuccessful(std::string& message)
{
message = mSimulationErrors;
return mSimulatedData.empty() && mSimulationErrors.empty();
}
//----------------------------------------------------------------------------
//! Process command response string
//!
//! @param response command response string
//!
//! @return 0 if successful, otherwise error code
//----------------------------------------------------------------------------
int ProcessResponse(const std::string& response);
protected:
#ifdef IN_TEST_HARNESS
public:
#endif
//----------------------------------------------------------------------------
//! Execute command using the xrootd client
//!
//! @param full_url full url containing the MGM endpoint, command encoding
//! and any other global options
//!
//! @return 0 if successful, otherwise error code
//----------------------------------------------------------------------------
int RawExecute(const std::string& full_url);
//----------------------------------------------------------------------------
//! Guess a default 'route' e.g. home directory - this code is duplicated
//! on purpose in ConsoleMain but will be dropped from there in the future.
//!
//! @param verbose flag indicating whether to print selected route
//! @return the computed default route
//----------------------------------------------------------------------------
std::string DefaultRoute(bool verbose = true);
//----------------------------------------------------------------------------
//! Print debug message to console
//----------------------------------------------------------------------------
inline void PrintDebugMsg(const std::string& message) const {
std::cout << "> " << message << std::endl;
}
eos::console::RequestProto mReq; ///< Generic request object send to the MGM
bool mIsAdmin; ///< If true execute as admin, otherwise as user
bool mIsSilent; ///< If true execute command but don't display anything
//! If true it requires a strong user confirmation before executing the command
bool mNeedsConfirmation {false};
bool mIsLocal {false}; ///< Mark if command is executed only client side
GlobalOptions mGlobalOpts; ///< Global options for all commands
ExecutionOutcome mOutcome; ///< Stores outcome of last operation
//----------------------------------------------------------------------------
//! FakeEntry struct: Stores information about a fake request / response pair
//----------------------------------------------------------------------------
struct FakeEntry {
std::string expectedCommand;
ExecutionOutcome outcome;
};
//----------------------------------------------------------------------------
//! Simulation mode: Expect calls in the following order, and provide the
//! given fake responses
//----------------------------------------------------------------------------
bool mSimulationMode = false;
std::queue mSimulatedData;
std::string mSimulationErrors;
};