diff --git a/Base/CMakeLists.txt b/Base/CMakeLists.txt index abef3d5d8..e4d3771d6 100644 --- a/Base/CMakeLists.txt +++ b/Base/CMakeLists.txt @@ -27,10 +27,47 @@ set(srcs $<$:src/ecflow/base/ssl_connection.hpp> $<$:src/ecflow/base/SslClient.hpp> src/ecflow/base/cts/ClientToServerCmd.hpp - src/ecflow/base/cts/CtsApi.hpp src/ecflow/base/cts/CtsCmdRegistry.hpp src/ecflow/base/cts/EditHistoryMgr.hpp - src/ecflow/base/cts/TaskApi.hpp + src/ecflow/base/cts/task/AbortCmd.hpp + src/ecflow/base/cts/task/CompleteCmd.hpp + src/ecflow/base/cts/task/CtsWaitCmd.hpp + src/ecflow/base/cts/task/EventCmd.hpp + src/ecflow/base/cts/task/InitCmd.hpp + src/ecflow/base/cts/task/LabelCmd.hpp + src/ecflow/base/cts/task/MeterCmd.hpp + src/ecflow/base/cts/task/QueueCmd.hpp + src/ecflow/base/cts/task/TaskApi.hpp + src/ecflow/base/cts/task/TaskCmd.hpp + src/ecflow/base/cts/user/AlterCmd.hpp + src/ecflow/base/cts/user/BeginCmd.hpp + src/ecflow/base/cts/user/CFileCmd.hpp + src/ecflow/base/cts/user/CheckPtCmd.hpp + src/ecflow/base/cts/user/ClientHandleCmd.hpp + src/ecflow/base/cts/user/CSyncCmd.hpp + src/ecflow/base/cts/user/CtsApi.hpp + src/ecflow/base/cts/user/CtsCmd.hpp + src/ecflow/base/cts/user/CtsNodeCmd.hpp + src/ecflow/base/cts/user/DeleteCmd.hpp + src/ecflow/base/cts/user/EditScriptCmd.hpp + src/ecflow/base/cts/user/ForceCmd.hpp + src/ecflow/base/cts/user/FreeDepCmd.hpp + src/ecflow/base/cts/user/GroupCTSCmd.hpp + src/ecflow/base/cts/user/LoadDefsCmd.hpp + src/ecflow/base/cts/user/LogCmd.hpp + src/ecflow/base/cts/user/LogMessageCmd.hpp + src/ecflow/base/cts/user/MoveCmd.hpp + src/ecflow/base/cts/user/OrderNodeCmd.hpp + src/ecflow/base/cts/user/PathsCmd.hpp + src/ecflow/base/cts/user/PlugCmd.hpp + src/ecflow/base/cts/user/QueryCmd.hpp + src/ecflow/base/cts/user/ReplaceNodeCmd.hpp + src/ecflow/base/cts/user/RequeueNodeCmd.hpp + src/ecflow/base/cts/user/RunNodeCmd.hpp + src/ecflow/base/cts/user/ServerVersionCmd.hpp + src/ecflow/base/cts/user/ShowCmd.hpp + src/ecflow/base/cts/user/UserCmd.hpp + src/ecflow/base/cts/user/ZombieCmd.hpp src/ecflow/base/stc/BlockClientZombieCmd.hpp src/ecflow/base/stc/DefsCache.hpp src/ecflow/base/stc/DefsCmd.hpp @@ -64,39 +101,48 @@ set(srcs $<$:src/ecflow/base/Openssl.cpp> $<$:src/ecflow/base/ssl_connection.cpp> $<$:src/ecflow/base/SslClient.cpp> - src/ecflow/base/cts/AlterCmd.cpp - src/ecflow/base/cts/BeginCmd.cpp - src/ecflow/base/cts/CFileCmd.cpp - src/ecflow/base/cts/CSyncCmd.cpp - src/ecflow/base/cts/CheckPtCmd.cpp - src/ecflow/base/cts/ClientHandleCmd.cpp src/ecflow/base/cts/ClientToServerCmd.cpp - src/ecflow/base/cts/CtsApi.cpp - src/ecflow/base/cts/CtsCmd.cpp src/ecflow/base/cts/CtsCmdRegistry.cpp - src/ecflow/base/cts/CtsNodeCmd.cpp - src/ecflow/base/cts/DeleteCmd.cpp src/ecflow/base/cts/EditHistoryMgr.cpp - src/ecflow/base/cts/EditScriptCmd.cpp - src/ecflow/base/cts/ForceCmd.cpp - src/ecflow/base/cts/FreeDepCmd.cpp - src/ecflow/base/cts/GroupCTSCmd.cpp - src/ecflow/base/cts/LoadDefsCmd.cpp - src/ecflow/base/cts/LogCmd.cpp - src/ecflow/base/cts/LogMessageCmd.cpp - src/ecflow/base/cts/OrderNodeCmd.cpp - src/ecflow/base/cts/PathsCmd.cpp - src/ecflow/base/cts/PlugCmd.cpp - src/ecflow/base/cts/QueryCmd.cpp - src/ecflow/base/cts/ReplaceNodeCmd.cpp - src/ecflow/base/cts/RequeueNodeCmd.cpp - src/ecflow/base/cts/RunNodeCmd.cpp - src/ecflow/base/cts/ServerVersionCmd.cpp - src/ecflow/base/cts/ShowCmd.cpp - src/ecflow/base/cts/TaskApi.cpp - src/ecflow/base/cts/TaskCmds.cpp - src/ecflow/base/cts/UserCmd.cpp - src/ecflow/base/cts/ZombieCmd.cpp + src/ecflow/base/cts/task/AbortCmd.cpp + src/ecflow/base/cts/task/CompleteCmd.cpp + src/ecflow/base/cts/task/CtsWaitCmd.cpp + src/ecflow/base/cts/task/EventCmd.cpp + src/ecflow/base/cts/task/InitCmd.cpp + src/ecflow/base/cts/task/LabelCmd.cpp + src/ecflow/base/cts/task/MeterCmd.cpp + src/ecflow/base/cts/task/QueueCmd.cpp + src/ecflow/base/cts/task/TaskApi.cpp + src/ecflow/base/cts/task/TaskCmd.cpp + src/ecflow/base/cts/user/AlterCmd.cpp + src/ecflow/base/cts/user/BeginCmd.cpp + src/ecflow/base/cts/user/CFileCmd.cpp + src/ecflow/base/cts/user/CSyncCmd.cpp + src/ecflow/base/cts/user/CheckPtCmd.cpp + src/ecflow/base/cts/user/ClientHandleCmd.cpp + src/ecflow/base/cts/user/CtsApi.cpp + src/ecflow/base/cts/user/CtsCmd.cpp + src/ecflow/base/cts/user/CtsNodeCmd.cpp + src/ecflow/base/cts/user/DeleteCmd.cpp + src/ecflow/base/cts/user/EditScriptCmd.cpp + src/ecflow/base/cts/user/ForceCmd.cpp + src/ecflow/base/cts/user/FreeDepCmd.cpp + src/ecflow/base/cts/user/GroupCTSCmd.cpp + src/ecflow/base/cts/user/LoadDefsCmd.cpp + src/ecflow/base/cts/user/LogCmd.cpp + src/ecflow/base/cts/user/LogMessageCmd.cpp + src/ecflow/base/cts/user/MoveCmd.cpp + src/ecflow/base/cts/user/OrderNodeCmd.cpp + src/ecflow/base/cts/user/PathsCmd.cpp + src/ecflow/base/cts/user/PlugCmd.cpp + src/ecflow/base/cts/user/QueryCmd.cpp + src/ecflow/base/cts/user/ReplaceNodeCmd.cpp + src/ecflow/base/cts/user/RequeueNodeCmd.cpp + src/ecflow/base/cts/user/RunNodeCmd.cpp + src/ecflow/base/cts/user/ServerVersionCmd.cpp + src/ecflow/base/cts/user/ShowCmd.cpp + src/ecflow/base/cts/user/UserCmd.cpp + src/ecflow/base/cts/user/ZombieCmd.cpp src/ecflow/base/stc/BlockClientZombieCmd.cpp src/ecflow/base/stc/DefsCache.cpp src/ecflow/base/stc/DefsCmd.cpp diff --git a/Base/src/ecflow/base/ZombieCtrl.cpp b/Base/src/ecflow/base/ZombieCtrl.cpp index 55b9db987..5dee7a857 100644 --- a/Base/src/ecflow/base/ZombieCtrl.cpp +++ b/Base/src/ecflow/base/ZombieCtrl.cpp @@ -13,7 +13,8 @@ #include #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/task/TaskCmd.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Submittable.hpp" #include "ecflow/node/Suite.hpp" diff --git a/Base/src/ecflow/base/cts/ClientToServerCmd.cpp b/Base/src/ecflow/base/cts/ClientToServerCmd.cpp index 379b318d3..865bc8731 100644 --- a/Base/src/ecflow/base/cts/ClientToServerCmd.cpp +++ b/Base/src/ecflow/base/cts/ClientToServerCmd.cpp @@ -15,6 +15,7 @@ #include "ecflow/base/AbstractServer.hpp" #include "ecflow/base/cts/EditHistoryMgr.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/base/stc/ServerToClientCmd.hpp" #include "ecflow/core/Calendar.hpp" #include "ecflow/core/Host.hpp" @@ -225,39 +226,3 @@ void ClientToServerCmd::add_delete_edit_history(Defs* defs, const std::string& p print(ss, path); // custom print defs->add_edit_history(Str::ROOT_PATH(), ss); } - -CEREAL_REGISTER_TYPE(ServerVersionCmd) -CEREAL_REGISTER_TYPE(CtsCmd) -CEREAL_REGISTER_TYPE(CSyncCmd) -CEREAL_REGISTER_TYPE(ClientHandleCmd) -CEREAL_REGISTER_TYPE(CtsNodeCmd) -CEREAL_REGISTER_TYPE(PathsCmd) -CEREAL_REGISTER_TYPE(DeleteCmd) -CEREAL_REGISTER_TYPE(CheckPtCmd) -CEREAL_REGISTER_TYPE(LoadDefsCmd) -CEREAL_REGISTER_TYPE(LogCmd) -CEREAL_REGISTER_TYPE(LogMessageCmd) -CEREAL_REGISTER_TYPE(BeginCmd) -CEREAL_REGISTER_TYPE(ZombieCmd) -CEREAL_REGISTER_TYPE(InitCmd) -CEREAL_REGISTER_TYPE(EventCmd) -CEREAL_REGISTER_TYPE(MeterCmd) -CEREAL_REGISTER_TYPE(LabelCmd) -CEREAL_REGISTER_TYPE(QueueCmd) -CEREAL_REGISTER_TYPE(AbortCmd) -CEREAL_REGISTER_TYPE(CtsWaitCmd) -CEREAL_REGISTER_TYPE(CompleteCmd) -CEREAL_REGISTER_TYPE(RequeueNodeCmd) -CEREAL_REGISTER_TYPE(OrderNodeCmd) -CEREAL_REGISTER_TYPE(RunNodeCmd) -CEREAL_REGISTER_TYPE(ReplaceNodeCmd) -CEREAL_REGISTER_TYPE(ForceCmd) -CEREAL_REGISTER_TYPE(FreeDepCmd) -CEREAL_REGISTER_TYPE(CFileCmd) -CEREAL_REGISTER_TYPE(EditScriptCmd) -CEREAL_REGISTER_TYPE(PlugCmd) -CEREAL_REGISTER_TYPE(AlterCmd) -CEREAL_REGISTER_TYPE(MoveCmd) -CEREAL_REGISTER_TYPE(GroupCTSCmd) -CEREAL_REGISTER_TYPE(ShowCmd) -CEREAL_REGISTER_TYPE(QueryCmd) diff --git a/Base/src/ecflow/base/cts/ClientToServerCmd.hpp b/Base/src/ecflow/base/cts/ClientToServerCmd.hpp index 51a0c9193..b78c149bd 100644 --- a/Base/src/ecflow/base/cts/ClientToServerCmd.hpp +++ b/Base/src/ecflow/base/cts/ClientToServerCmd.hpp @@ -13,17 +13,10 @@ #include -#include "ecflow/attribute/Variable.hpp" -#include "ecflow/attribute/Zombie.hpp" #include "ecflow/base/Cmd.hpp" -#include "ecflow/base/stc/PreAllocatedReply.hpp" -#include "ecflow/core/CheckPt.hpp" -#include "ecflow/core/Child.hpp" -#include "ecflow/core/NOrder.hpp" #include "ecflow/core/PrintStyle.hpp" -#include "ecflow/core/Serialization.hpp" -#include "ecflow/node/Flag.hpp" #include "ecflow/node/NodeFwd.hpp" +#include "ecflow/core/Serialization.hpp" class AbstractServer; class AbstractClientEnv; @@ -200,2006 +193,4 @@ class ClientToServerCmd { } }; -//================================================================================= -// Task Commands -// ================================================================================ -class TaskCmd : public ClientToServerCmd { -protected: - TaskCmd(const std::string& pathToSubmittable, - const std::string& jobsPassword, - const std::string& process_or_remote_id, - int try_no) - : submittable_(nullptr), - path_to_submittable_(pathToSubmittable), - jobs_password_(jobsPassword), - process_or_remote_id_(process_or_remote_id), - try_no_(try_no) { - assert(!hostname().empty()); - } - - TaskCmd() = default; - -public: - bool isWrite() const override { return true; } - int timeout() const override { return 190; } // ECFLOW-157 80 -> 190 - - const std::string& path_to_node() const { return path_to_submittable_; } - const std::string& jobs_password() const { return jobs_password_; } - const std::string& process_or_remote_id() const { return process_or_remote_id_; } - int try_no() const { return try_no_; } - virtual ecf::Child::CmdType child_type() const = 0; - - bool equals(ClientToServerCmd*) const override; - bool task_cmd() const override { return true; } - bool connect_to_different_servers() const override { return true; } - - bool password_missmatch() const { return password_missmatch_; } - bool pid_missmatch() const { return pid_missmatch_; } - -protected: - /// Overridden to do nothing since Task based commands don't need _user_ based authentication - void setup_user_authentification(const std::string& /*user*/, const std::string& /*passwd*/) override {} - bool setup_user_authentification(AbstractClientEnv&) override { return true; } - void setup_user_authentification() override {} - - bool authenticate(AbstractServer*, - STC_Cmd_ptr&) const override; /// Task have their own mechanism,can throw std::runtime_error - Submittable* get_submittable(AbstractServer* as) const; // can throw std::runtime_error - -protected: - mutable Submittable* submittable_{ - nullptr}; // stored during authentication and re-used handle request, not persisted, server side only - -private: - std::string path_to_submittable_; - std::string jobs_password_; - std::string process_or_remote_id_; - int try_no_{0}; - -private: - mutable bool password_missmatch_{ - false}; // stored during authentication and re-used handle request, not persisted, server side only - mutable bool pid_missmatch_{ - false}; // stored during authentication and re-used handle request, not persisted, server side only - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), - CEREAL_NVP(path_to_submittable_), - CEREAL_NVP(jobs_password_), - CEREAL_NVP(process_or_remote_id_), - CEREAL_NVP(try_no_)); - } -}; - -class InitCmd final : public TaskCmd { -public: - InitCmd(const std::string& pathToTask, - const std::string& jobsPassword, - const std::string& process_or_remote_id, - int try_no, - const std::vector& vec = {}) - : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), - var_to_add_(vec) {} - - InitCmd() : TaskCmd() {} - - const std::vector& variables_to_add() const { return var_to_add_; } - - void print(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - ecf::Child::CmdType child_type() const override { return ecf::Child::INIT; } - -private: - std::vector var_to_add_; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this)); - CEREAL_OPTIONAL_NVP(ar, var_to_add_, [this]() { return !var_to_add_.empty(); }); // conditionally save - } -}; - -class CompleteCmd final : public TaskCmd { -public: - CompleteCmd(const std::string& pathToTask, - const std::string& jobsPassword, - const std::string& process_or_remote_id = "", - int try_no = 1, - const std::vector& vec = std::vector()) - : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), - var_to_del_(vec) {} - CompleteCmd() : TaskCmd() {} - - const std::vector& variables_to_delete() const { return var_to_del_; } - - void print(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - ecf::Child::CmdType child_type() const override { return ecf::Child::COMPLETE; } - -private: - std::vector var_to_del_; // variables to delete on task - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this)); - CEREAL_OPTIONAL_NVP(ar, var_to_del_, [this]() { return !var_to_del_.empty(); }); // conditionally save - } -}; - -/// A child command that evaluates a expression. If the expression is false. -/// Then client invoker will block. -class CtsWaitCmd final : public TaskCmd { -public: - CtsWaitCmd(const std::string& pathToTask, - const std::string& jobsPassword, - const std::string& process_or_remote_id, - int try_no, - const std::string& expression); - CtsWaitCmd() : TaskCmd() {} - - const std::string& expression() const { return expression_; } - - void print(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - ecf::Child::CmdType child_type() const override { return ecf::Child::WAIT; } - - std::string expression_; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(expression_)); - } -}; - -class AbortCmd final : public TaskCmd { -public: - AbortCmd(const std::string& pathToTask, - const std::string& jobsPassword, - const std::string& process_or_remote_id, - int try_no = 1, - const std::string& reason = ""); - AbortCmd() : TaskCmd() {} - - const std::string& reason() const { return reason_; } - - void print(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - ecf::Child::CmdType child_type() const override { return ecf::Child::ABORT; } - - std::string reason_; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(reason_)); - } -}; - -class EventCmd final : public TaskCmd { -public: - EventCmd(const std::string& pathToTask, - const std::string& jobsPassword, - const std::string& process_or_remote_id, - int try_no, - const std::string& eventName, - bool value = true) // true = set(default), false = clear - : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), - name_(eventName), - value_(value) {} - EventCmd() : TaskCmd() {} - - const std::string& name() const { return name_; } - bool value() const { return value_; } - - void print(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - ecf::Child::CmdType child_type() const override { return ecf::Child::EVENT; } - -private: - std::string name_; // the events name - bool value_{true}; // true = set(default), false = clear - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(name_)); - CEREAL_OPTIONAL_NVP(ar, value_, [this]() { return !value_; }); // conditionally save if value is false - } -}; - -class MeterCmd final : public TaskCmd { -public: - MeterCmd(const std::string& pathToTask, - const std::string& jobsPassword, - const std::string& process_or_remote_id, - int try_no, - const std::string& meterName, - int meterValue) - : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), - name_(meterName), - value_(meterValue) {} - MeterCmd() : TaskCmd() {} - - const std::string& name() const { return name_; } - int value() const { return value_; } - - void print(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - ecf::Child::CmdType child_type() const override { return ecf::Child::METER; } - -private: - std::string name_; // the meters name - int value_{0}; // the meters value - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(name_), CEREAL_NVP(value_)); - } -}; - -class LabelCmd final : public TaskCmd { -public: - LabelCmd(const std::string& pathToTask, - const std::string& jobsPassword, - const std::string& process_or_remote_id, - int try_no, - const std::string& name, - const std::string& label) - : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), - name_(name), - label_(label) {} - LabelCmd() : TaskCmd() {} - - const std::string& name() const { return name_; } - const std::string& label() const { return label_; } - - void print(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - ecf::Child::CmdType child_type() const override { return ecf::Child::LABEL; } - -private: - std::string name_; // the label name - std::string label_; // a single label, or multi-line label - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(name_), CEREAL_NVP(label_)); - } -}; - -class QueueAttr; -class QueueCmd final : public TaskCmd { -public: - QueueCmd(const std::string& pathToTask, - const std::string& jobsPassword, - const std::string& process_or_remote_id, - int try_no, - const std::string& queueName, - const std::string& action, - const std::string& step = "", - const std::string& path_to_node_with_queue = "") // if empty search for queue up node tree - : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), - name_(queueName), - action_(action), - step_(step), - path_to_node_with_queue_(path_to_node_with_queue) {} - QueueCmd() : TaskCmd() {} - - const std::string& name() const { return name_; } - const std::string& action() const { return action_; } - const std::string& step() const { return step_; } - const std::string& path_to_node_with_queue() const { return path_to_node_with_queue_; } - - void print(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - ecf::Child::CmdType child_type() const override { return ecf::Child::QUEUE; } - - std::string handle_queue(QueueAttr& queue_attr) const; - -private: - std::string name_; // the queue name - std::string action_; // [ active | aborted | complete | no_of_aborted ] - std::string step_; // will be empty when action is [ active | no_of_aborted] - std::string path_to_node_with_queue_; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), - CEREAL_NVP(name_), - CEREAL_NVP(action_), - CEREAL_NVP(step_), - CEREAL_NVP(path_to_node_with_queue_)); - } -}; - -//================================================================================= -// User Commands -// ================================================================================ -class UserCmd : public ClientToServerCmd { -public: - UserCmd() = default; - - const std::string& user() const { return user_; } - const std::string& passwd() const { return pswd_; } - - void setup_user_authentification(const std::string& user, const std::string& passwd) override; - bool setup_user_authentification(AbstractClientEnv&) override; - void setup_user_authentification() override; - -protected: - bool equals(ClientToServerCmd*) const override; - bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; - bool do_authenticate(AbstractServer* as, STC_Cmd_ptr&, const std::string& path) const; - bool do_authenticate(AbstractServer* as, STC_Cmd_ptr&, const std::vector& paths) const; - - /// Prompt the user for confirmation: If user responds with no, will exit client - static void prompt_for_confirmation(const std::string& prompt); - - /// All user commands will be pre_fixed with "--" and post_fixed with :user@host - void user_cmd(std::string& os, const std::string& the_cmd) const; - - static int time_out_for_load_sync_and_get(); - - // The order is preserved during the split. Paths assumed to start with '/' char - static void split_args_to_options_and_paths(const std::vector& args, - std::vector& options, - std::vector& paths, - bool treat_colon_in_path_as_path = false); - -private: - std::string user_; - std::string pswd_; - bool cu_ = false; // custom user, i.e used set_user_name() || ECF_USER || --user -> only check this password - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(user_)); - CEREAL_OPTIONAL_NVP(ar, pswd_, [this]() { return !pswd_.empty(); }); // conditionally save - CEREAL_OPTIONAL_NVP(ar, cu_, [this]() { return cu_; }); // conditionally save - } -}; - -// ======================================================================== -// This Command should NEVER be changed -// This will allow new client to ask OLD server about its version -// ======================================================================== -class ServerVersionCmd final : public UserCmd { -public: - ServerVersionCmd() = default; - - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - const char* theArg() const override; - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this)); - } -}; - -// This command is used to encapsulate all commands that are -// simple signals to the server. This helps to cut down on the -// number of global symbols used by boost serialisation. -// ========================================================================= -// *** IMPORTANT ***: If any of these commands in the future need arguments, -// *** then ensure to place a DUMMY enum in its place. -// *** This will allow a *newer* development client to still send message to a older server. -// *** i.e like terminating the server -// *** IMPORTANT: For any new commands, must be added to the end, for each major release -// *** - STATS_RESET was introduced in release 4.0.5 -// ========================================================================= -class CtsCmd final : public UserCmd { -public: - enum Api { - NO_CMD, - RESTORE_DEFS_FROM_CHECKPT, - RESTART_SERVER, - SHUTDOWN_SERVER, - HALT_SERVER, - TERMINATE_SERVER, - RELOAD_WHITE_LIST_FILE, - FORCE_DEP_EVAL, - PING, - GET_ZOMBIES, - STATS, - SUITES, - DEBUG_SERVER_ON, - DEBUG_SERVER_OFF, - SERVER_LOAD, - STATS_RESET, - RELOAD_PASSWD_FILE, - STATS_SERVER, - RELOAD_CUSTOM_PASSWD_FILE - }; - - explicit CtsCmd(Api a) : api_(a) {} - CtsCmd() = default; - - Api api() const { return api_; } - - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - bool isWrite() const override; - bool cmd_updates_defs() const override; - bool terminate_cmd() const override { return api_ == TERMINATE_SERVER; } - bool ping_cmd() const override { return api_ == PING; } - int timeout() const override; - - bool handleRequestIsTestable() const override; - - const char* theArg() const override; - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - - Api api_{NO_CMD}; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(api_)); - } -}; - -class CheckPtCmd final : public UserCmd { -public: - CheckPtCmd(ecf::CheckPt::Mode m, int interval, int checkpt_save_time_alarm) - : mode_(m), - check_pt_interval_(interval), - check_pt_save_time_alarm_(checkpt_save_time_alarm) {} - CheckPtCmd() = default; - - ecf::CheckPt::Mode mode() const { return mode_; } - int check_pt_interval() const { return check_pt_interval_; } - int check_pt_save_time_alarm() const { return check_pt_save_time_alarm_; } - - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - bool isWrite() const override; - bool is_mutable() const override; - const char* theArg() const override; - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - -private: - ecf::CheckPt::Mode mode_{ecf::CheckPt::UNDEFINED}; - int check_pt_interval_{0}; - int check_pt_save_time_alarm_{0}; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), - CEREAL_NVP(mode_), - CEREAL_NVP(check_pt_interval_), - CEREAL_NVP(check_pt_save_time_alarm_)); - } -}; - -// Client---(CSyncCmd::SYNC_FULL)---->Server-----(SSyncCmd)--->client: -// Client---(CSyncCmd::SYNC)--------->Server-----(SSyncCmd)--->client: -// Client---(CSyncCmd::SYNC_CLOCK)--->Server-----(SSyncCmd)--->client: -// Client---(CSyncCmd::NEWS)--------->Server-----(SNewsCmd)--->client: -class CSyncCmd final : public UserCmd { -public: - enum Api { NEWS, SYNC, SYNC_FULL, SYNC_CLOCK }; - - CSyncCmd(Api a, - unsigned int client_handle, - unsigned int client_state_change_no, - unsigned int client_modify_change_no) - : api_(a), - client_handle_(client_handle), - client_state_change_no_(client_state_change_no), - client_modify_change_no_(client_modify_change_no) {} - explicit CSyncCmd(unsigned int client_handle) : api_(SYNC_FULL), client_handle_(client_handle) {} - CSyncCmd() = default; - - Api api() const { return api_; } - int client_state_change_no() const { return client_state_change_no_; } - int client_modify_change_no() const { return client_modify_change_no_; } - int client_handle() const { return client_handle_; } - - void set_client_handle(int client_handle) override { client_handle_ = client_handle; } // used by group_cmd - void print(std::string&) const override; - std::string print_short() const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - int timeout() const override; - - const char* theArg() const override; - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - /// Custom handling of command logging to add additional debug on same line - /// makes it easier to debug errors in syncing. - void do_log(AbstractServer*) const override; - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - - Api api_{SYNC}; - int client_handle_{0}; - int client_state_change_no_{0}; - int client_modify_change_no_{0}; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), - CEREAL_NVP(api_), - CEREAL_NVP(client_handle_), - CEREAL_NVP(client_state_change_no_), - CEREAL_NVP(client_modify_change_no_)); - } -}; - -class ClientHandleCmd final : public UserCmd { -public: - enum Api { REGISTER, DROP, DROP_USER, ADD, REMOVE, AUTO_ADD, SUITES }; - - explicit ClientHandleCmd(Api api = AUTO_ADD) : api_(api) {} - - ClientHandleCmd(int client_handle, const std::vector& suites, bool add_add_new_suites) - : api_(REGISTER), - client_handle_(client_handle), - suites_(suites), - auto_add_new_suites_(add_add_new_suites) {} - - explicit ClientHandleCmd(int client_handle) : api_(DROP), client_handle_(client_handle) {} - - explicit ClientHandleCmd(const std::string& drop_user) : api_(DROP_USER), drop_user_(drop_user) {} - - ClientHandleCmd(int client_handle, const std::vector& suites, Api api) - : api_(api), // Must be ADD or REMOVE - client_handle_(client_handle), - suites_(suites) {} - - ClientHandleCmd(int client_handle, bool add_add_new_suites) - : api_(AUTO_ADD), - client_handle_(client_handle), - auto_add_new_suites_(add_add_new_suites) {} - - Api api() const { return api_; } - const std::string& drop_user() const { return drop_user_; } - - bool cmd_updates_defs() const override; - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override; - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - - // called in the server - void set_group_cmd(const GroupCTSCmd* cmd) override { group_cmd_ = cmd; } - -private: - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - - Api api_; - int client_handle_{0}; - std::string drop_user_; - std::vector suites_; - bool auto_add_new_suites_{false}; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), - CEREAL_NVP(api_), - CEREAL_NVP(client_handle_), - CEREAL_NVP(drop_user_), - CEREAL_NVP(suites_), - CEREAL_NVP(auto_add_new_suites_)); - } - -private: - const GroupCTSCmd* group_cmd_ = nullptr; // not persisted only used in server -}; - -// Collection of commands, that all take a abs node path as their only arg -// Reduce number of global symbols caused by boost serialisation -// Previously they were all separate commands -// -// Client---(CtsNodeCmd(GET))---->Server-----(DefsCmd | SNodeCmd )--->client: -// When doHandleRequest is called in the server it will return DefsCmd -// The DefsCmd is used to transport the node tree hierarchy to/from the server -// -// CHECK_JOB_GEN_ONLY command will traverse hierarchically from the given node path -// and force generation of jobs. (i.e independently of dependencies). -// This is used in *testing* only, so that we can compare/test/verify -// job generation with the old version. -// if absNodepath is empty we will generate jobs for all tasks -class CtsNodeCmd final : public UserCmd { -public: - enum Api { NO_CMD, JOB_GEN, CHECK_JOB_GEN_ONLY, GET, WHY, GET_STATE, MIGRATE }; - CtsNodeCmd(Api a, const std::string& absNodePath) : api_(a), absNodePath_(absNodePath) {} - explicit CtsNodeCmd(Api a) : api_(a) { assert(a != NO_CMD); } - CtsNodeCmd() = default; - - Api api() const { return api_; } - const std::string& absNodePath() const { return absNodePath_; } - - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - PrintStyle::Type_t show_style() const override; - - int timeout() const override; - bool isWrite() const override; - bool handleRequestIsTestable() const override { return !terminate_cmd(); } - bool why_cmd(std::string& nodePath) const override; - bool get_cmd() const override { return api_ == GET; } - - const char* theArg() const override; - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; - -private: - Api api_{NO_CMD}; - std::string absNodePath_; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(api_), CEREAL_NVP(absNodePath_)); - } -}; - -// DELETE If paths_ empty will delete all suites (beware) else will delete the chosen nodes. -class DeleteCmd final : public UserCmd { -public: - explicit DeleteCmd(const std::vector& paths, bool force = false) - : group_cmd_(nullptr), - paths_(paths), - force_(force) {} - explicit DeleteCmd(const std::string& absNodePath, bool force = false); - DeleteCmd() = default; - - const std::vector& paths() const { return paths_; } - bool force() const { return force_; } - - void print(std::string&) const override; - void print_only(std::string&) const override; - void print(std::string& os, const std::string& path) const override; - - bool equals(ClientToServerCmd*) const override; - bool isWrite() const override { return true; } - - const char* theArg() const override; - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - - // called in the server - void set_group_cmd(const GroupCTSCmd* cmd) override { group_cmd_ = cmd; } - - static void check_for_active_or_submitted_tasks(AbstractServer* as, Node* theNodeToDelete); - -private: - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; - void cleanup() override { std::vector().swap(paths_); } /// run in the server, after handlerequest - -private: - const GroupCTSCmd* group_cmd_{nullptr}; // not persisted only used in server - std::vector paths_; - bool force_{false}; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(paths_), CEREAL_NVP(force_)); - } -}; - -// DELETE If paths_ empty will delete all suites (beware) else will delete the chosen nodes. -class PathsCmd final : public UserCmd { -public: - enum Api { NO_CMD, SUSPEND, RESUME, KILL, STATUS, CHECK, EDIT_HISTORY, ARCHIVE, RESTORE }; - - PathsCmd(Api api, const std::vector& paths, bool force = false) - : api_(api), - paths_(paths), - force_(force) {} - PathsCmd(Api api, const std::string& absNodePath, bool force = false); - explicit PathsCmd(Api api) : api_(api) { assert(api != NO_CMD); } - PathsCmd() = default; - - Api api() const { return api_; } - const std::vector& paths() const { return paths_; } - bool force() const { return force_; } - - void print(std::string&) const override; - std::string print_short() const override; - void print_only(std::string&) const override; - void print(std::string& os, const std::string& path) const override; - - bool equals(ClientToServerCmd*) const override; - bool isWrite() const override; - - const char* theArg() const override; - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; - void cleanup() override { std::vector().swap(paths_); } /// run in the server, after handlerequest - - void my_print(std::string& os, const std::vector& paths) const; - void my_print_only(std::string& os, const std::vector& paths) const; - -private: - Api api_{NO_CMD}; - std::vector paths_; - bool force_{false}; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(api_), CEREAL_NVP(paths_), CEREAL_NVP(force_)); - } -}; - -/// The LogCmd is paired with SStringCmd -/// Client---(LogCmd)---->Server-----(SStringCmd)--->client: -/// When doHandleRequest is called in the server it will return SStringCmd -/// The SStringCmd is used to transport the log file contents to the client -class LogCmd final : public UserCmd { -public: - enum LogApi { GET, CLEAR, FLUSH, NEW, PATH }; - explicit LogCmd(LogApi a, - int get_last_n_lines = 0); // for zero we take default from log. Avoid adding dependency on log.hpp - explicit LogCmd(const std::string& path); // NEW - LogCmd(); - - LogApi api() const { return api_; } - int get_last_n_lines() const { return get_last_n_lines_; } - const std::string& new_path() const { return new_path_; } - - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - bool isWrite() const override; - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - - LogApi api_{LogCmd::GET}; - int get_last_n_lines_; // default to 100 -> ECFLOW-174 - std::string new_path_; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(api_), CEREAL_NVP(get_last_n_lines_), CEREAL_NVP(new_path_)); - } -}; - -/// Simply writes the message to the log file -class LogMessageCmd final : public UserCmd { -public: - explicit LogMessageCmd(const std::string& msg) : msg_(msg) {} - LogMessageCmd() = default; - - const std::string& msg() const { return msg_; } - - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - std::string msg_; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(msg_)); - } -}; - -// class Begin: if suiteName is empty we will begin all suites -class BeginCmd final : public UserCmd { -public: - explicit BeginCmd(const std::string& suiteName, bool force = false); - BeginCmd() = default; - - const std::string& suiteName() const { return suiteName_; } - bool force() const { return force_; } - - int timeout() const override { return 80; } - - bool isWrite() const override { return true; } - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - -private: - std::string suiteName_; - bool force_{false}; // reset begin status on suites & bypass checks, can create zombies, used in test only - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(suiteName_), CEREAL_NVP(force_)); - } -}; - -class ZombieCmd final : public UserCmd { -public: - ZombieCmd(ecf::User::Action uc, - const std::vector& paths, - const std::string& process_id, - const std::string& password) - : user_action_(uc), - process_id_(process_id), - password_(password), - paths_(paths) {} - explicit ZombieCmd(ecf::User::Action uc = ecf::User::BLOCK) : user_action_(uc) {} - - const std::vector& paths() const { return paths_; } - const std::string& process_or_remote_id() const { return process_id_; } - const std::string& password() const { return password_; } - - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override; - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - void cleanup() override { std::vector().swap(paths_); } /// run in the server, after handlerequest - - ecf::User::Action user_action_; - std::string process_id_; // should be empty for multiple paths and when using CLI - std::string password_; // should be empty for multiple paths and when using CLI - std::vector paths_; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), - CEREAL_NVP(user_action_), - CEREAL_NVP(process_id_), - CEREAL_NVP(password_), - CEREAL_NVP(paths_)); - } -}; - -class RequeueNodeCmd final : public UserCmd { -public: - enum Option { NO_OPTION, ABORT, FORCE }; - - explicit RequeueNodeCmd(const std::vector& paths, Option op = NO_OPTION) - : paths_(paths), - option_(op) {} - - explicit RequeueNodeCmd(const std::string& absNodepath, Option op = NO_OPTION) - : paths_(std::vector(1, absNodepath)), - option_(op) {} - - RequeueNodeCmd() = default; - - const std::vector& paths() const { return paths_; } - Option option() const { return option_; } - - bool isWrite() const override { return true; } - void print(std::string&) const override; - void print_only(std::string&) const override; - void print(std::string& os, const std::string& path) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; - void cleanup() override { std::vector().swap(paths_); } /// run in the server, after doHandleRequest - -private: - mutable std::vector paths_; // mutable to allow swap to clear & reclaim memory, as soon as possible - Option option_{NO_OPTION}; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(paths_), CEREAL_NVP(option_)); - } -}; - -class OrderNodeCmd final : public UserCmd { -public: - OrderNodeCmd(const std::string& absNodepath, NOrder::Order op) : absNodepath_(absNodepath), option_(op) {} - OrderNodeCmd() = default; - - const std::string& absNodepath() const { return absNodepath_; } - NOrder::Order option() const { return option_; } - - bool isWrite() const override { return true; } - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; - -private: - std::string absNodepath_; - NOrder::Order option_{NOrder::TOP}; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(absNodepath_), CEREAL_NVP(option_)); - } -}; - -// The absNodepath must be provided -class RunNodeCmd final : public UserCmd { -public: - RunNodeCmd(const std::string& absNodepath, bool force, bool test = false) - : paths_(std::vector(1, absNodepath)), - force_(force), - test_(test) {} - - RunNodeCmd(const std::vector& paths, bool force, bool test = false) - : paths_(paths), - force_(force), - test_(test) {} - - RunNodeCmd() = default; - - const std::vector& paths() const { return paths_; } - bool force() const { return force_; } - - bool isWrite() const override { return true; } - void print(std::string&) const override; - void print_only(std::string&) const override; - void print(std::string& os, const std::string& path) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; - void cleanup() override { std::vector().swap(paths_); } /// run in the server, after doHandleRequest - -private: - std::vector paths_; - bool force_{false}; - bool test_{false}; // only for test, hence we don't serialise this - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(paths_), CEREAL_NVP(force_)); - } -}; - -// Does Nothing in the server, however allows client code to display the -// returned Defs in different showStyles -// This class has no need for persistence, i.e client side only -class ShowCmd final : public UserCmd { -public: - explicit ShowCmd(PrintStyle::Type_t s = PrintStyle::DEFS) : style_(s) {} - - // returns the showStyle - bool show_cmd() const override { return true; } - PrintStyle::Type_t show_style() const override { return style_; } - - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - // The Show Cmd is processed on the client side, - // Likewise the doHandleRequest does nothing, - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - - PrintStyle::Type_t style_; - - // Persistence is still required since show command can be *USED* in a *GROUP* command - // However its ONLY used on the client side, hence no need to serialise data members - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this)); - } -}; - -// Will *load* the suites, into the server. -// Additionally the server will try to resolve extern's. The extern are references -// to Node, events, meters, limits, variables defined on another suite. -class LoadDefsCmd final : public UserCmd { -public: - explicit LoadDefsCmd(const defs_ptr& defs, bool force = false); - explicit LoadDefsCmd(const std::string& defs_filename, - bool force = false, - bool check_only = false /* not persisted */, - bool print = false /* not persisted */, - bool stats = false /* not persisted */, - const std::vector>& client_env = - std::vector>()); - LoadDefsCmd() = default; - - // Uses by equals only - const std::string& defs_as_string() const { return defs_; } - - bool isWrite() const override { return true; } - int timeout() const override { return time_out_for_load_sync_and_get(); } - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - static Cmd_ptr create(const std::string& defs_filename, - bool force, - bool check_only, - bool print, - bool stats, - AbstractClientEnv* clientEnv); - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the command as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - - bool force_{false}; - std::string defs_; - std::string defs_filename_; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(force_), CEREAL_NVP(defs_), CEREAL_NVP(defs_filename_)); - } -}; - -class ReplaceNodeCmd final : public UserCmd { -public: - ReplaceNodeCmd(const std::string& node_path, bool createNodesAsNeeded, defs_ptr client_defs, bool force); - ReplaceNodeCmd(const std::string& node_path, bool createNodesAsNeeded, const std::string& path_to_defs, bool force); - ReplaceNodeCmd() = default; - - const std::string& the_client_defs() const { return clientDefs_; } - const std::string& pathToNode() const { return pathToNode_; } - const std::string& path_to_defs() const { return path_to_defs_; } - bool createNodesAsNeeded() const { return createNodesAsNeeded_; } - bool force() const { return force_; } - - bool isWrite() const override { return true; } - int timeout() const override { return 300; } - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - - // void set_client_env(const std::vector >& env ) { client_env_ = env;} // only - // used in test - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; - void cleanup() override { std::string().swap(clientDefs_); } /// run in the server, after command send to client - - bool createNodesAsNeeded_{false}; - bool force_{false}; - std::string pathToNode_; - std::string path_to_defs_; // Can be empty if defs loaded in memory via python api - std::string clientDefs_; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), - CEREAL_NVP(createNodesAsNeeded_), - CEREAL_NVP(force_), - CEREAL_NVP(pathToNode_), - CEREAL_NVP(path_to_defs_), - CEREAL_NVP(clientDefs_)); - } -}; - -// Set the state on the affected node ONLY. -// If recursive is used set the state, on node and _ALL_ nodes _beneath -// setRepeatToLastValue set, only make sense when used with recursive. -// stateOrEvent, string is one of: -// < unknown | suspended | complete | queued | submitted | active | aborted | clear | set > -class ForceCmd final : public UserCmd { -public: - ForceCmd(const std::vector& paths, - const std::string& stateOrEvent, - bool recursive, - bool setRepeatToLastValue) - : paths_(paths), - stateOrEvent_(stateOrEvent), - recursive_(recursive), - setRepeatToLastValue_(setRepeatToLastValue) {} - ForceCmd(const std::string& path, const std::string& stateOrEvent, bool recursive, bool setRepeatToLastValue) - : paths_(std::vector(1, path)), - stateOrEvent_(stateOrEvent), - recursive_(recursive), - setRepeatToLastValue_(setRepeatToLastValue) {} - ForceCmd() = default; - - // Uses by equals only - const std::vector paths() const { return paths_; } - const std::string& stateOrEvent() const { return stateOrEvent_; } - bool recursive() const { return recursive_; } - bool setRepeatToLastValue() const { return setRepeatToLastValue_; } - - bool isWrite() const override { return true; } - void print(std::string&) const override; - std::string print_short() const override; - void print_only(std::string&) const override; - void print(std::string& os, const std::string& path) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; - void cleanup() override { std::vector().swap(paths_); } /// run in the server, after doHandleRequest - - void my_print(std::string& os, const std::vector& paths) const; - void my_print_only(std::string& os, const std::vector& paths) const; - -private: - std::vector paths_; - std::string stateOrEvent_; - bool recursive_{false}; - bool setRepeatToLastValue_{false}; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), - CEREAL_NVP(paths_), - CEREAL_NVP(stateOrEvent_), - CEREAL_NVP(recursive_), - CEREAL_NVP(setRepeatToLastValue_)); - } -}; - -// Free Dependencies -class FreeDepCmd final : public UserCmd { -public: - explicit FreeDepCmd(const std::vector& paths, - bool trigger = true, - bool all = false, // day, date, time, today, trigger, cron - bool date = false, - bool time = false // includes time, day, date, today, cron - ) - : paths_(paths), - trigger_(trigger), - all_(all), - date_(date), - time_(time) {} - - explicit FreeDepCmd(const std::string& path, - bool trigger = true, - bool all = false, // day, date, time, today, trigger, cron - bool date = false, - bool time = false // includes time, day, date, today, cron - ) - : paths_(std::vector(1, path)), - trigger_(trigger), - all_(all), - date_(date), - time_(time) {} - - FreeDepCmd() = default; - - // Uses by equals only - const std::vector& paths() const { return paths_; } - bool trigger() const { return trigger_; } - bool all() const { return all_; } - bool date() const { return date_; } - bool time() const { return time_; } - - bool isWrite() const override { return true; } - void print(std::string&) const override; - void print_only(std::string&) const override; - void print(std::string& os, const std::string& path) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; - void cleanup() override { std::vector().swap(paths_); } /// run in the server, after doHandleRequest - -private: - std::vector paths_; - bool trigger_{true}; - bool all_{false}; - bool date_{false}; - bool time_{false}; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), - CEREAL_NVP(paths_), - CEREAL_NVP(trigger_), - CEREAL_NVP(all_), - CEREAL_NVP(date_), - CEREAL_NVP(time_)); - } -}; - -class AlterCmd final : public UserCmd { -public: - enum Delete_attr_type { - DEL_VARIABLE, - DEL_TIME, - DEL_TODAY, - DEL_DATE, - DEL_DAY, - DEL_CRON, - DEL_EVENT, - DEL_METER, - DEL_LABEL, - DEL_TRIGGER, - DEL_COMPLETE, - DEL_REPEAT, - DEL_LIMIT, - DEL_LIMIT_PATH, - DEL_INLIMIT, - DEL_ZOMBIE, - DELETE_ATTR_ND, - DEL_LATE, - DEL_QUEUE, - DEL_GENERIC - }; - - enum Change_attr_type { - VARIABLE, - CLOCK_TYPE, - CLOCK_DATE, - CLOCK_GAIN, - EVENT, - METER, - LABEL, - TRIGGER, - COMPLETE, - REPEAT, - LIMIT_MAX, - LIMIT_VAL, - DEFSTATUS, - CHANGE_ATTR_ND, - CLOCK_SYNC, - LATE, - TIME, - TODAY - }; - - enum Add_attr_type { - ADD_TIME, - ADD_TODAY, - ADD_DATE, - ADD_DAY, - ADD_ZOMBIE, - ADD_VARIABLE, - ADD_ATTR_ND, - ADD_LATE, - ADD_LIMIT, - ADD_INLIMIT, - ADD_LABEL - }; - - // Python - AlterCmd(const std::vector& paths, - const std::string& alterType, /* one of [ add | change | delete | set_flag | clear_flag ] */ - const std::string& attrType, - const std::string& name, - const std::string& value); - // add - AlterCmd(const std::string& path, Add_attr_type attr, const std::string& name, const std::string& value = "") - : paths_(std::vector(1, path)), - name_(name), - value_(value), - add_attr_type_(attr) {} - AlterCmd(const std::vector& paths, - Add_attr_type attr, - const std::string& name, - const std::string& value = "") - : paths_(paths), - name_(name), - value_(value), - add_attr_type_(attr) {} - // delete - AlterCmd(const std::string& path, Delete_attr_type del, const std::string& name = "", const std::string& value = "") - : paths_(std::vector(1, path)), - name_(name), - value_(value), - del_attr_type_(del) {} - AlterCmd(const std::vector& paths, - Delete_attr_type del, - const std::string& name = "", - const std::string& value = "") - : paths_(paths), - name_(name), - value_(value), - del_attr_type_(del) {} - // change - AlterCmd(const std::string& path, Change_attr_type attr, const std::string& name, const std::string& value = "") - : paths_(std::vector(1, path)), - name_(name), - value_(value), - change_attr_type_(attr) {} - AlterCmd(const std::vector& paths, - Change_attr_type attr, - const std::string& name, - const std::string& value = "") - : paths_(paths), - name_(name), - value_(value), - change_attr_type_(attr) {} - // flag - AlterCmd(const std::string& path, ecf::Flag::Type ft, bool flag) - : paths_(std::vector(1, path)), - flag_type_(ft), - flag_(flag) {} - AlterCmd(const std::vector& paths, ecf::Flag::Type ft, bool flag) - : paths_(paths), - flag_type_(ft), - flag_(flag) {} - // sort - AlterCmd(const std::string& path, const std::string& name, const std::string& value) - : paths_(std::vector(1, path)), - name_(name), - value_(value) {} - AlterCmd(const std::vector& paths, const std::string& name, const std::string& value) - : paths_(paths), - name_(name), - value_(value) {} - - AlterCmd() = default; - - // Uses by equals only - const std::vector& paths() const { return paths_; } - const std::string& name() const { return name_; } - const std::string& value() const { return value_; } - Delete_attr_type delete_attr_type() const { return del_attr_type_; } - Change_attr_type change_attr_type() const { return change_attr_type_; } - Add_attr_type add_attr_type() const { return add_attr_type_; } - ecf::Flag::Type flag_type() const { return flag_type_; } - bool flag() const { return flag_; } - - bool isWrite() const override { return true; } - void print(std::string&) const override; - void print(std::string& os, const std::string& path) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - STC_Cmd_ptr alter_server_state(AbstractServer*) const; - bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; - void cleanup() override { std::vector().swap(paths_); } /// run in the server, after doHandleRequest - - void my_print(std::string& os, const std::vector& paths) const; - - Add_attr_type get_add_attr_type(const std::string&) const; - void createAdd(Cmd_ptr& cmd, std::vector& options, std::vector& paths) const; - void extract_name_and_value_for_add(Add_attr_type, - std::string& name, - std::string& value, - std::vector& options, - std::vector& paths) const; - void check_for_add(Add_attr_type, const std::string& name, const std::string& value) const; - - Delete_attr_type get_delete_attr_type(const std::string&) const; - void - createDelete(Cmd_ptr& cmd, const std::vector& options, const std::vector& paths) const; - void extract_name_and_value_for_delete(Delete_attr_type, - std::string& name, - std::string& value, - const std::vector& options, - const std::vector& paths) const; - void check_for_delete(Delete_attr_type, const std::string& name, const std::string& value) const; - - Change_attr_type get_change_attr_type(const std::string&) const; - void createChange(Cmd_ptr& cmd, std::vector& options, std::vector& paths) const; - void extract_name_and_value_for_change(Change_attr_type, - std::string& name, - std::string& value, - std::vector& options, - std::vector& paths) const; - void check_for_change(Change_attr_type, const std::string& name, const std::string& value) const; - - ecf::Flag::Type get_flag_type(const std::string&) const; - void create_flag(Cmd_ptr& cmd, - const std::vector& options, - const std::vector& paths, - bool flag) const; - - void check_sort_attr_type(const std::string&) const; - void create_sort_attributes(Cmd_ptr& cmd, - const std::vector& options, - const std::vector& paths) const; - - void alter_and_attr_type(std::string& alter_type, std::string& attr_type) const; - -private: - std::vector paths_; - std::string name_; - std::string value_; - Add_attr_type add_attr_type_{ADD_ATTR_ND}; - Delete_attr_type del_attr_type_{DELETE_ATTR_ND}; - Change_attr_type change_attr_type_{CHANGE_ATTR_ND}; - ecf::Flag::Type flag_type_{ecf::Flag::NOT_SET}; - bool flag_{false}; // true means set false means clear - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), - CEREAL_NVP(paths_), - CEREAL_NVP(name_), - CEREAL_NVP(value_), - CEREAL_NVP(add_attr_type_), - CEREAL_NVP(del_attr_type_), - CEREAL_NVP(change_attr_type_), - CEREAL_NVP(flag_type_), - CEREAL_NVP(flag_)); - } -}; - -//================================================================================ -// Paired with SStringCmd -// Client---(CFileCmd)---->Server-----(SStringCmd)--->client: -//================================================================================ -class CFileCmd final : public UserCmd { -public: - enum File_t { ECF, JOB, JOBOUT, MANUAL, KILL, STAT }; - CFileCmd(const std::string& pathToNode, File_t file, size_t max_lines) - : file_(file), - pathToNode_(pathToNode), - max_lines_(max_lines) {} - CFileCmd(const std::string& pathToNode, const std::string& file_type, const std::string& max_lines); - CFileCmd() = default; - - // Uses by equals only - const std::string& pathToNode() const { return pathToNode_; } - File_t fileType() const { return file_; } - size_t max_lines() const { return max_lines_; } - - static std::vector fileTypesVec(); - static std::string toString(File_t); - - bool handleRequestIsTestable() const override { return false; } - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; - - File_t file_{ECF}; - std::string pathToNode_; - size_t max_lines_{0}; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(file_), CEREAL_NVP(pathToNode_), CEREAL_NVP(max_lines_)); - } -}; - -//================================================================================ -// Paired with SStringCmd -// Client---(EditScriptCmd)---->Server-----(SStringCmd)--->client: -//================================================================================ -class EditScriptCmd final : public UserCmd { -public: - enum EditType { EDIT, PREPROCESS, SUBMIT, PREPROCESS_USER_FILE, SUBMIT_USER_FILE }; - EditScriptCmd(const std::string& path_to_node, EditType et) // EDIT or PREPROCESS - : edit_type_(et), - path_to_node_(path_to_node) {} - - EditScriptCmd(const std::string& path_to_node, const NameValueVec& user_variables) - : edit_type_(SUBMIT), - path_to_node_(path_to_node), - user_variables_(user_variables) {} - - EditScriptCmd(const std::string& path_to_node, const std::vector& user_file_contents) - : edit_type_(PREPROCESS_USER_FILE), - path_to_node_(path_to_node), - user_file_contents_(user_file_contents) {} - - EditScriptCmd(const std::string& path_to_node, - const NameValueVec& user_variables, - const std::vector& user_file_contents, - bool create_alias, - bool run_alias) - : edit_type_(SUBMIT_USER_FILE), - path_to_node_(path_to_node), - user_file_contents_(user_file_contents), - user_variables_(user_variables), - alias_(create_alias), - run_(run_alias) {} - - EditScriptCmd() = default; - - // Uses by equals only - const std::string& path_to_node() const { return path_to_node_; } - EditType edit_type() const { return edit_type_; } - bool alias() const { return alias_; } - bool run() const { return run_; } - - bool handleRequestIsTestable() const override { return false; } - bool isWrite() const override; - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; - void cleanup() override { - std::vector().swap(user_file_contents_); - } /// run in the server, after doHandleRequest - -private: - EditType edit_type_{EDIT}; - std::string path_to_node_; - mutable std::vector user_file_contents_; - NameValueVec user_variables_; - bool alias_{false}; - bool run_{false}; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), - CEREAL_NVP(edit_type_), - CEREAL_NVP(path_to_node_), - CEREAL_NVP(user_file_contents_), - CEREAL_NVP(user_variables_), - CEREAL_NVP(alias_), - CEREAL_NVP(run_)); - } -}; - -class PlugCmd final : public UserCmd { -public: - PlugCmd(const std::string& source, const std::string& dest) : source_(source), dest_(dest) {} - PlugCmd() = default; - - // Uses by equals only - const std::string& source() const { return source_; } - const std::string& dest() const { return dest_; } - - int timeout() const override { return 120; } - bool handleRequestIsTestable() const override { return false; } - bool isWrite() const override { return true; } - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - -private: - std::string source_; - std::string dest_; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(source_), CEREAL_NVP(dest_)); - } -}; - -class MoveCmd final : public UserCmd { -public: - MoveCmd(const std::pair& host_port, Node* src, const std::string& dest); - MoveCmd(); - ~MoveCmd() override; - - Node* source() const; - const std::string& src_node() const { return src_node_; } - const std::string& dest() const { return dest_; } - - bool handleRequestIsTestable() const override { return false; } - bool isWrite() const override { return true; } - - void print(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - - bool check_source() const; - -private: - std::string src_node_; - std::string src_host_; - std::string src_port_; - std::string src_path_; - std::string dest_; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), - CEREAL_NVP(src_node_), - CEREAL_NVP(src_host_), - CEREAL_NVP(src_port_), - CEREAL_NVP(src_path_), - CEREAL_NVP(dest_)); - } -}; - -class QueryCmd final : public UserCmd { -public: - QueryCmd(const std::string& query_type, - const std::string& path_to_attribute, - const std::string& attribute, - const std::string& path_to_task) - : query_type_(query_type), - path_to_attribute_(path_to_attribute), - attribute_(attribute), - path_to_task_(path_to_task) {} - QueryCmd() : UserCmd() {} - ~QueryCmd() override; - - const std::string& query_type() const { return query_type_; } - const std::string& path_to_attribute() const { return path_to_attribute_; } - const std::string& attribute() const { return attribute_; } - const std::string& path_to_task() const { return path_to_task_; } - - void print(std::string&) const override; - void print_only(std::string&) const override; - bool equals(ClientToServerCmd*) const override; - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - bool handleRequestIsTestable() const override { return false; } - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - -private: - std::string query_type_; // [ state | dstate | event | meter | label | trigger ] - std::string path_to_attribute_; - std::string attribute_; // [ event_name | meter_name | label_name | variable_name | trigger expression] empty for - // state and dstate - std::string path_to_task_; // The task the invoked this command, needed for logging - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), - CEREAL_NVP(query_type_), - CEREAL_NVP(path_to_attribute_), - CEREAL_NVP(attribute_), - CEREAL_NVP(path_to_task_)); - } -}; - -// The group command allows a series of commands to be be executed: -// -// Client---(GroupCTSCmd)---->Server-----(GroupSTCCmd | StcCmd(OK) | Error )--->client: -// -// If client->server contains GetDefs cmd and log file commands, then a group -// command will be created for returning to the client -// -// If group command contains multiple [ CtsNodeCmd(GET) | LogCmd --get ] commands then -// all the results are returned back to the client, HOWEVER when client calls -// Cmd::defs() | Cmd::get_string() only the first data is returned. -// -class GroupCTSCmd final : public UserCmd { -public: - GroupCTSCmd(const std::string& list_of_commands, AbstractClientEnv* clientEnv); - explicit GroupCTSCmd(Cmd_ptr cmd) : cli_(false) { addChild(cmd); } - GroupCTSCmd() = default; - - bool isWrite() const override; - bool cmd_updates_defs() const override; - - PrintStyle::Type_t show_style() const override; - bool get_cmd() const override; - bool task_cmd() const override; - bool terminate_cmd() const override; - bool why_cmd(std::string&) const override; - bool group_cmd() const override { return true; } - - void set_client_handle(int client_handle) const; // used in group sync with client register - - void print(std::string&) const override; - std::string print_short() const override; - bool equals(ClientToServerCmd*) const override; - - void addChild(Cmd_ptr childCmd); - const std::vector& cmdVec() const { return cmdVec_; } - - const char* theArg() const override { return arg(); } - void addOption(boost::program_options::options_description& desc) const override; - void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; - - void add_edit_history(Defs*) const override; - -private: - static const char* arg(); // used for argument parsing - static const char* desc(); // The description of the argument as provided to user - - void setup_user_authentification(const std::string& user, const std::string& passwd) override; - bool setup_user_authentification(AbstractClientEnv&) override; - void setup_user_authentification() override; - - bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; - STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; - void cleanup() override; // cleanup all children - -private: - std::vector cmdVec_; - bool cli_{true}; - - friend class cereal::access; - template - void serialize(Archive& ar, std::uint32_t const /*version*/) { - ar(cereal::base_class(this), CEREAL_NVP(cmdVec_), CEREAL_NVP(cli_)); - } -}; - -std::ostream& operator<<(std::ostream& os, const ServerVersionCmd&); -std::ostream& operator<<(std::ostream& os, const CtsCmd&); -std::ostream& operator<<(std::ostream& os, const CtsNodeCmd&); -std::ostream& operator<<(std::ostream& os, const DeleteCmd&); -std::ostream& operator<<(std::ostream& os, const PathsCmd&); -std::ostream& operator<<(std::ostream& os, const CheckPtCmd&); -std::ostream& operator<<(std::ostream& os, const LoadDefsCmd&); -std::ostream& operator<<(std::ostream& os, const LogCmd&); -std::ostream& operator<<(std::ostream& os, const LogMessageCmd&); -std::ostream& operator<<(std::ostream& os, const BeginCmd&); -std::ostream& operator<<(std::ostream& os, const ZombieCmd&); -std::ostream& operator<<(std::ostream& os, const InitCmd&); -std::ostream& operator<<(std::ostream& os, const EventCmd&); -std::ostream& operator<<(std::ostream& os, const MeterCmd&); -std::ostream& operator<<(std::ostream& os, const LabelCmd&); -std::ostream& operator<<(std::ostream& os, const CompleteCmd&); -std::ostream& operator<<(std::ostream& os, const CtsWaitCmd&); -std::ostream& operator<<(std::ostream& os, const AbortCmd&); -std::ostream& operator<<(std::ostream& os, const RequeueNodeCmd&); -std::ostream& operator<<(std::ostream& os, const OrderNodeCmd&); -std::ostream& operator<<(std::ostream& os, const RunNodeCmd&); -std::ostream& operator<<(std::ostream& os, const ReplaceNodeCmd&); -std::ostream& operator<<(std::ostream& os, const ForceCmd&); -std::ostream& operator<<(std::ostream& os, const FreeDepCmd&); -std::ostream& operator<<(std::ostream& os, const CFileCmd&); -std::ostream& operator<<(std::ostream& os, const PlugCmd&); -std::ostream& operator<<(std::ostream& os, const AlterCmd&); -std::ostream& operator<<(std::ostream& os, const MoveCmd&); -std::ostream& operator<<(std::ostream& os, const GroupCTSCmd&); -std::ostream& operator<<(std::ostream& os, const QueryCmd&); - #endif /* ecflow_base_cts_ClientToServerCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/CtsCmdRegistry.cpp b/Base/src/ecflow/base/cts/CtsCmdRegistry.cpp index 1a9d8d3b0..8209b3552 100644 --- a/Base/src/ecflow/base/cts/CtsCmdRegistry.cpp +++ b/Base/src/ecflow/base/cts/CtsCmdRegistry.cpp @@ -13,7 +13,41 @@ #include #include "ecflow/base/AbstractClientEnv.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/task/AbortCmd.hpp" +#include "ecflow/base/cts/task/CompleteCmd.hpp" +#include "ecflow/base/cts/task/CtsWaitCmd.hpp" +#include "ecflow/base/cts/task/EventCmd.hpp" +#include "ecflow/base/cts/task/InitCmd.hpp" +#include "ecflow/base/cts/task/LabelCmd.hpp" +#include "ecflow/base/cts/task/MeterCmd.hpp" +#include "ecflow/base/cts/task/QueueCmd.hpp" +#include "ecflow/base/cts/user/AlterCmd.hpp" +#include "ecflow/base/cts/user/BeginCmd.hpp" +#include "ecflow/base/cts/user/CFileCmd.hpp" +#include "ecflow/base/cts/user/CSyncCmd.hpp" +#include "ecflow/base/cts/user/CheckPtCmd.hpp" +#include "ecflow/base/cts/user/ClientHandleCmd.hpp" +#include "ecflow/base/cts/user/CtsCmd.hpp" +#include "ecflow/base/cts/user/CtsNodeCmd.hpp" +#include "ecflow/base/cts/user/DeleteCmd.hpp" +#include "ecflow/base/cts/user/EditScriptCmd.hpp" +#include "ecflow/base/cts/user/ForceCmd.hpp" +#include "ecflow/base/cts/user/FreeDepCmd.hpp" +#include "ecflow/base/cts/user/GroupCTSCmd.hpp" +#include "ecflow/base/cts/user/LoadDefsCmd.hpp" +#include "ecflow/base/cts/user/LogCmd.hpp" +#include "ecflow/base/cts/user/LogMessageCmd.hpp" +#include "ecflow/base/cts/user/OrderNodeCmd.hpp" +#include "ecflow/base/cts/user/PathsCmd.hpp" +#include "ecflow/base/cts/user/PlugCmd.hpp" +#include "ecflow/base/cts/user/QueryCmd.hpp" +#include "ecflow/base/cts/user/ReplaceNodeCmd.hpp" +#include "ecflow/base/cts/user/RequeueNodeCmd.hpp" +#include "ecflow/base/cts/user/RunNodeCmd.hpp" +#include "ecflow/base/cts/user/ServerVersionCmd.hpp" +#include "ecflow/base/cts/user/ShowCmd.hpp" +#include "ecflow/base/cts/user/ZombieCmd.hpp" + namespace po = boost::program_options; CtsCmdRegistry::CtsCmdRegistry(bool addGroupCmd) { diff --git a/Base/src/ecflow/base/cts/TaskCmds.cpp b/Base/src/ecflow/base/cts/TaskCmds.cpp deleted file mode 100644 index 2387f6f61..000000000 --- a/Base/src/ecflow/base/cts/TaskCmds.cpp +++ /dev/null @@ -1,1364 +0,0 @@ -/* - * Copyright 2009- ECMWF. - * - * This software is licensed under the terms of the Apache Licence version 2.0 - * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. - * In applying this licence, ECMWF does not waive the privileges and immunities - * granted to it by virtue of its status as an intergovernmental organisation - * nor does it submit to any jurisdiction. - */ - -#include - -#include "ecflow/attribute/QueueAttr.hpp" -#include "ecflow/base/AbstractClientEnv.hpp" -#include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/TaskApi.hpp" -#include "ecflow/core/Converter.hpp" -#include "ecflow/core/Log.hpp" -#include "ecflow/core/Str.hpp" -#include "ecflow/node/Defs.hpp" -#include "ecflow/node/Expression.hpp" -#include "ecflow/node/Submittable.hpp" -#include "ecflow/node/SuiteChanged.hpp" - -using namespace ecf; -using namespace std; -using namespace boost; -namespace po = boost::program_options; - -// #define DEBUG_ZOMBIE 1 - -//////////////////////////////////////////////////////////////////////////////////////////////// -bool TaskCmd::equals(ClientToServerCmd* rhs) const { - auto* the_rhs = dynamic_cast(rhs); - if (!the_rhs) - return false; - if (path_to_submittable_ != the_rhs->path_to_node()) - return false; - if (jobs_password_ != the_rhs->jobs_password()) - return false; - if (process_or_remote_id_ != the_rhs->process_or_remote_id()) - return false; - if (try_no_ != the_rhs->try_no()) - return false; - return ClientToServerCmd::equals(rhs); -} - -// ********************************************************************************** -// IMPORTANT: In the current SMS/ECF only the init child command, passes the -// process_or_remote_id_, for *ALL* other child commands this is empty. -// The Automated tests get round this via setting ECF_RID in the header/tail job includes -// However since this may not be in .sms/.ecf files, we cannot rely on it -// Hence we need to be able to handle *EMPTY* process_or_remote_id_ for child commands -// -// Task State | Child | Log | Explanation -// ------------------------------------------------------------------------------------------------------------- -// SUBMITTED | child!=INIT | ERR | Script has child command in back ground, Bug in script, out of order -// ACTIVE | INIT & pid & passwd match | WAR | two init commands, overloaded server, or 2*init in script, *FOB*. -// Forgive ACTIVE | INIT & pid & passwd !match | ERR | two init commands, Task started again by user. COMPLETE | -// COMPLETE | WAR | two complete, zombie, overloaded server. *FOB*, Allow job to complete. Forgive. -// COMPLETE | child != COMPLETE | ERR | zombie -// ABORTED | ABORTED | WAR | two aborted, zombie, overloaded server. *FOB*, allow process to -// exit. Forgive ABORTED | child != ABORTED | WAR | zombie -// ------------------------------------------------------------------------------------------------------------------- -// -// zombie type | PID | password | explanation -// --------------------------------------------------------------------------------------------------------------- -// ECF_PID | X | matches | PID miss-match, but password matches. Job scheduled twice. Check submitter -// ECF_PID_PASSWD | X | X | Both PID and password miss-match. Re-queue & submit of active job -// ECF_PASSWD | matches | X | Password miss-match, PID matches, system has re-cycled PID or hacked job file? -// ECF | matches | matches | overloaded server,Two init commands or task complete or aborted but receives -// another child cmd USER | ? | ? | User initiated zombie whilst task was active or submitted, -// command is recorded in zombie PATH | n/a | n/a | Task not found. Nodes replaced whilst jobs were -// running -// ---------------------------------------------------------------------------------------------------------------- -bool TaskCmd::authenticate(AbstractServer* as, STC_Cmd_ptr& theReply) const { -#ifdef DEBUG_ZOMBIE - std::cout << " TaskCmd::authenticate " << Child::to_string(child_type()); - std::cout << " " << path_to_submittable_ << " " << process_or_remote_id_ << " " << jobs_password_ << " " << try_no_; - const Zombie& zombie = as->zombie_ctrl().find(path_to_submittable_, process_or_remote_id_, jobs_password_); - if (!zombie.empty()) - std::cout << " " << zombie; - else { - const Zombie& zombiep = as->zombie_ctrl().find_by_path_only(path_to_submittable_); - if (!zombiep.empty()) - std::cout << " find_by_path_only: " << zombiep; - } -#endif - /// *************************************************************************** - /// Task based cmd have their own authentication mechanism, hence we - /// Don't need to call the base class authenticate - /// ************************************************************************** - - if (!as->allowTaskCommunication()) { - // This is not an Error, hence we don't throw exception - theReply = PreAllocatedReply::block_client_server_halted_cmd(); - return false; - } - - submittable_ = get_submittable(as); - if (!submittable_) { -#ifdef DEBUG_ZOMBIE - std::cout << ": PATH Zombie\n"; -#endif - // Create path zombie, if not already created: - std::string action_taken; - static_cast(as->zombie_ctrl().handle_path_zombie(as, this, action_taken, theReply)); - - // distinguish output by using *path* - std::stringstream ss; - ss << " zombie(*path*) : chd:" << ecf::Child::to_string(child_type()) << " : " << path_to_submittable_ << " : " - << process_or_remote_id_ << " : " << jobs_password_ << " : action(" << action_taken << ")"; - log(Log::ERR, ss.str()); - return false; - } - - // If the CMD *WAS* created with Submittable::DUMMY_JOBS_PASSWORD then we were testing - // This will be copied from client to server, hence we want to avoid same check in the - // server. when handleRequest is called() - // DO NOT place #ifdef DEBUG otherwise test will start failing for the release build - if (jobs_password_ == Submittable::DUMMY_JOBS_PASSWORD()) { - return true; - } - - SuiteChanged1 changed(submittable_->suite()); - - /// Check if User wants to explicitly bypass password checking - /// This can be done via AlterCmd by adding a variable on the task, ECF_PASS with value - /// Submittable::FREE_JOBS_PASSWORD Note: this *does not* look for the variable up the node tree, only on the task. - std::string ecf_pass_value; - if (submittable_->findVariableValue(Str::ECF_PASS(), ecf_pass_value)) { - - if (ecf_pass_value == Submittable::FREE_JOBS_PASSWORD()) { - submittable_->flag().clear(ecf::Flag::ZOMBIE); - return true; - } - } - - /// Handle corner case ,where we have two jobs with different process id, but same password - /// Can happen if jobs is started externally, - /// or via test(i.e submit 1,submit 2(force)) before job1 active its password is overridden - bool submittable_allready_aborted = false; - bool submittable_allready_active = false; - bool submittable_allready_complete = false; - password_missmatch_ = false; - pid_missmatch_ = false; - - /// *** In complete state, the password is empty. - if (submittable_->jobsPassword() != jobs_password_) { -#ifdef DEBUG_ZOMBIE - std::cout << ": submittable pass(" << submittable_->jobsPassword() << ") != jobs_password_(" << jobs_password_ - << ")"; -#endif - password_missmatch_ = true; - } - - /// When state is in SUBMITTED, its process/remote_id is EMPTY. It will be set by the INIT child command. - /// Hence we can *NOT* mark it as pid_missmatch. - /// - /// *** See Note above: Not all child commands pass a process_id. *** - /// *** Hence this test for zombies is ONLY valid if process sets the process_or_remote_id_ **** - /// *** User should really set ECF_RID in the scripts - /// *** In submitted state, the pid is empty. hence clear pid_missmatch_ below - /// *** In complete state, the pid is empty. hence clear pid_missmatch_ below - if (!submittable_->process_or_remote_id().empty() && !process_or_remote_id_.empty() && - submittable_->process_or_remote_id() != process_or_remote_id_) { -#ifdef DEBUG_ZOMBIE - std::cout << ":task pid(" << submittable_->process_or_remote_id() << ") != process pid(" - << process_or_remote_id_ << ")"; -#endif - pid_missmatch_ = true; - } - - switch (submittable_->state()) { - case NState::SUBMITTED: { - // The pid on the task will be empty - if (child_type() != Child::INIT) { - std::stringstream ss; - ss << path_to_submittable_ - << " When a node is submitted, expected next child command to be INIT but received " - << Child::to_string(child_type()); - log(Log::ERR, ss.str()); - } - break; - } - - case NState::ACTIVE: { - if (child_type() == Child::INIT) { -#ifdef DEBUG_ZOMBIE - std::cout << ":(child_type() == Child::INIT) && submittable_->state() == NState::ACTIVE)"; -#endif - // *IF* password and pid matches be more forgiving. How can this case arise: - // i.e server is heavily overloaded, client calls init, which times out because server is busy - // Client then sends init again. In this case rather than treating it as a zombie, we will let it - // through providing the password and pid matches. - if (!password_missmatch_ && !pid_missmatch_) { - string ret = " [ overloaded || --init*2 ](pid and passwd match) : chd:"; - ret += ecf::Child::to_string(child_type()); - ret += " : "; - ret += path_to_submittable_; - ret += " : already active : action(fob)"; - log(Log::WAR, ret); - theReply = PreAllocatedReply::ok_cmd(); - return false; - } - submittable_allready_active = true; - } - break; - } - - case NState::COMPLETE: { -#ifdef DEBUG_ZOMBIE - std::cout << ": submittable_->state() == NState::COMPLETE)"; -#endif - if (child_type() == Child::COMPLETE) { - // Note: when a node completes, we clear tasks password and pid, to save memory on checkpt & network - // bandwidth (We could choose not to clear, This would allow us to disambiguate between 2/ and 3/ - // below). HOWEVER: - // - // How can this situation arise: - // 1/ Two calls to --complete (rare) - // 2/ Overloaded server. Client send --complete to server, but it is overload and does not respond, - // the client then - // times out. Server handles the request. When client tries again we get here. (possible) - // 3/ Zombie, two separate process. (possible, typically done by user action) - // - // For all three it should be safe to just fob: - // 1/ Two calls to --complete # Be forgiving - // 2/ Overloaded server # The correct course of action - // 3/ zombie # The zombie has completed anyway, don't bother blocking it - - submittable_->flag().clear(ecf::Flag::ZOMBIE); - as->zombie_ctrl().remove_by_path(path_to_submittable_); - - string ret = " [ overloaded || zombie || --complete*2 ] : chd:"; - ret += ecf::Child::to_string(child_type()); - ret += " : "; - ret += path_to_submittable_; - ret += " : already complete : action(fob)"; - log(Log::WAR, ret); - theReply = PreAllocatedReply::ok_cmd(); - return false; - } - - // If Task state is complete, and we receive **any** child command then it is a zombie - submittable_allready_complete = true; - password_missmatch_ = false; - pid_missmatch_ = false; - break; - } - - case NState::ABORTED: { -#ifdef DEBUG_ZOMBIE - std::cout << ": submittable_->state() == NState::ABORTED)"; -#endif - - if (child_type() == Child::ABORT) { - - if (!password_missmatch_ && !pid_missmatch_) { - - as->zombie_ctrl().remove(submittable_); - - string ret = " [ overloaded || --abort*2 ] (pid and passwd match) : chd:"; - ret += ecf::Child::to_string(child_type()); - ret += " : "; - ret += path_to_submittable_; - ret += " : already aborted : action(fob)"; - log(Log::WAR, ret); - theReply = PreAllocatedReply::ok_cmd(); - return false; - } - } - - // If Task state is aborted, and we receive **any** child command then it is a zombie - submittable_allready_aborted = true; - password_missmatch_ = false; - pid_missmatch_ = false; - break; - } - case NState::QUEUED: - break; // WTF - case NState::UNKNOWN: - break; // WTF - } - -#ifdef DEBUG_ZOMBIE - std::cout << "\n"; -#endif - - if (password_missmatch_ || pid_missmatch_ || submittable_allready_active || submittable_allready_complete || - submittable_allready_aborted) { - /// If the task has adopted we return true, and carry on as normal - std::string action_taken; - if (!as->zombie_ctrl().handle_zombie(submittable_, this, action_taken, theReply)) { - - // LOG failure: Include type of zombie. - // ** NOTE **: the zombie may have been removed by user actions. i.e if fob and child cmd is abort | - // complete, etc - std::stringstream ss; - ss << " zombie"; - const Zombie& theZombie = - as->zombie_ctrl().find(path_to_submittable_, process_or_remote_id_, jobs_password_); - if (!theZombie.empty()) - ss << "(" << theZombie.type_str() << ")"; - - ss << " : chd:" << ecf::Child::to_string(child_type()); - ss << " : " << path_to_submittable_ << "(" << NState::toString(submittable_->state()) << ")"; - ss << " : " << process_or_remote_id_ << " : " << jobs_password_; - if (submittable_allready_active) - ss << " : already active"; - if (submittable_allready_complete) - ss << " : already complete"; - if (submittable_allready_aborted) - ss << " : already aborted"; - if (password_missmatch_) - ss << " : passwd != [ task:" << submittable_->jobsPassword() << " child:" << jobs_password_ << " ]"; - if (pid_missmatch_) - ss << " : pid != [ task:" << submittable_->process_or_remote_id() << " child:" << process_or_remote_id_ - << " ]"; - ss << " : action(" << action_taken << ")"; - log(Log::ERR, ss.str()); - return false; - } - } - return true; -} - -Submittable* TaskCmd::get_submittable(AbstractServer* as) const { - node_ptr node = as->defs()->findAbsNode(path_to_submittable_); - if (!node.get()) { - return nullptr; - } - - return node->isSubmittable(); -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - -void InitCmd::print(std::string& os) const { - os += Str::CHILD_CMD(); - os += "init "; - os += path_to_node(); - if (!var_to_add_.empty()) { - os += " --add"; - for (const auto& var_to_add : var_to_add_) { - os += " "; - os += var_to_add.name(); - os += "="; - os += var_to_add.theValue(); - } - } -} - -bool InitCmd::equals(ClientToServerCmd* rhs) const { - auto* the_rhs = dynamic_cast(rhs); - if (!the_rhs) - return false; - if (var_to_add_ != the_rhs->variables_to_add()) - return false; - return TaskCmd::equals(rhs); -} - -STC_Cmd_ptr InitCmd::doHandleRequest(AbstractServer* as) const { - as->update_stats().task_init_++; - - { // update suite change numbers before job submission. submittable_ setup during authentication - SuiteChanged1 changed(submittable_->suite()); - submittable_->init(process_or_remote_id()); // will set task->set_state(NState::ACTIVE); - - for (const auto& var_to_add : var_to_add_) { - submittable_->addVariable(var_to_add); // will update or add variable - } - } - - // Do job submission in case any triggers dependent on NState::ACTIVE - as->increment_job_generation_count(); - return PreAllocatedReply::ok_cmd(); -} - -const char* InitCmd::arg() { - return TaskApi::initArg(); -} -const char* InitCmd::desc() { - return "Mark task as started(active). For use in the '.ecf' script file *only*\n" - "Hence the context is supplied via environment variables.\n" - " arg1(string) = process_or_remote_id The process id of the job or remote_id\n" - " Using remote id allows the jobs to be killed\n" - " arg2(--add)(optional)= add/update variables as name value pairs\n\n" - "If this child command is a zombie, then the default action will be to *block*.\n" - "The default can be overridden by using zombie attributes.\n" - "Otherwise the blocking period is defined by ECF_TIMEOUT.\n\n" - "Usage:\n" - " ecflow_client --init=$$\n" - " ecflow_client --init=$$ --add name=value name2=value2 # add/update variables to task"; -} - -void InitCmd::addOption(boost::program_options::options_description& desc) const { - desc.add_options()(InitCmd::arg(), po::value(), InitCmd::desc())( - "add", - po::value>()->multitoken(), - "Add variables e.g. name1=value1 name2=value2. Can only be used in combination with --init command."); -} - -void InitCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { - std::string process_or_remote_id = vm[arg()].as(); - - if (clientEnv->debug()) - cout << " InitCmd::create " << InitCmd::arg() << " clientEnv->task_path(" << clientEnv->task_path() - << ") clientEnv->jobs_password(" << clientEnv->jobs_password() << ") clientEnv->process_or_remote_id(" - << clientEnv->process_or_remote_id() << ") clientEnv->task_try_no(" << clientEnv->task_try_no() - << ") process_or_remote_id(" << process_or_remote_id << ") clientEnv->under_test(" - << clientEnv->under_test() << ")\n"; - - std::string errorMsg; - if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { - throw std::runtime_error("InitCmd: " + errorMsg); - } - - /// if ECF_RID is specified then it *MUST* be the same as input argument - /// On cca we ECF_RID can be specified under test, and therefore fail this check, hence we use - /// clientEnv->under_test() - if (!clientEnv->under_test() && !clientEnv->process_or_remote_id().empty() && - clientEnv->process_or_remote_id() != process_or_remote_id) { - std::stringstream ss; - ss << "remote id(" << process_or_remote_id - << ") passed as an argument, not the same the client environment ECF_RID(" - << clientEnv->process_or_remote_id() << ")"; - throw std::runtime_error(ss.str()); - } - - std::vector variable_vec; - if (vm.count("add")) { - vector var_args = vm["add"].as>(); - if (!var_args.empty()) { - variable_vec.reserve(var_args.size()); - for (const auto& v : var_args) { - std::vector tokens; - Str::split(v, tokens, "="); - if (tokens.size() != 2) { - throw std::runtime_error( - "Could not parse variable provided to --add; Expected var1=value1 var2=value2 but found " + v); - } - variable_vec.emplace_back(tokens[0], tokens[1]); - } - } - } - - cmd = std::make_shared(clientEnv->task_path(), - clientEnv->jobs_password(), - process_or_remote_id, - clientEnv->task_try_no(), - variable_vec); -} -////////////////////////////////////////////////////////////////////////////////////////////////// - -void CompleteCmd::print(std::string& os) const { - os += Str::CHILD_CMD(); - os += "complete "; - os += path_to_node(); - if (!var_to_del_.empty()) { - os += " --remove"; - for (const auto& var_to_del : var_to_del_) { - os += " "; - os += var_to_del; - } - } -} - -bool CompleteCmd::equals(ClientToServerCmd* rhs) const { - auto* the_rhs = dynamic_cast(rhs); - if (!the_rhs) - return false; - if (var_to_del_ != the_rhs->variables_to_delete()) - return false; - return TaskCmd::equals(rhs); -} - -STC_Cmd_ptr CompleteCmd::doHandleRequest(AbstractServer* as) const { - as->update_stats().task_complete_++; - - { - /// If there is an associated zombie, remove from the list. Must match, - /// Do this before task->complete(), since that clears password & process id - /// remove(..) uses password/ process id to match the right zombie - as->zombie_ctrl().remove(submittable_); - - // update suite change numbers before job submission, submittable_ setup during authentication - SuiteChanged1 changed(submittable_->suite()); - submittable_->complete(); // will set task->set_state(NState::COMPLETE); - - for (const auto& var_to_delete : var_to_del_) { - submittable_->delete_variable_no_error(var_to_delete); - } - } - - // Do job submission in case any triggers dependent on NState::COMPLETE - as->increment_job_generation_count(); - return PreAllocatedReply::ok_cmd(); -} - -const char* CompleteCmd::arg() { - return TaskApi::completeArg(); -} -const char* CompleteCmd::desc() { - return "Mark task as complete. For use in the '.ecf' script file *only*\n" - "Hence the context is supplied via environment variables\n\n" - "If this child command is a zombie, then the default action will be to *block*.\n" - "The default can be overridden by using zombie attributes.\n" - "Otherwise the blocking period is defined by ECF_TIMEOUT.\n" - "The init command allows variables to be added, and complete command\n" - "allows for them to be removed.\n" - " arg1(--remove)(optional) = a list of variables to removed from this task\n\n" - "Usage:\n" - " ecflow_client --complete\n" - " ecflow_client --complete --remove name1 name2 # delete variables name1 and name2 on the task"; -} - -void CompleteCmd::addOption(boost::program_options::options_description& desc) const { - desc.add_options()(CompleteCmd::arg(), CompleteCmd::desc())( - "remove", po::value>()->multitoken(), "remove variables i.e name name2"); -} -void CompleteCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { - if (clientEnv->debug()) - cout << " CompleteCmd::create " << CompleteCmd::arg() << " task_path(" << clientEnv->task_path() - << ") password(" << clientEnv->jobs_password() << ") remote_id(" << clientEnv->process_or_remote_id() - << ") try_no(" << clientEnv->task_try_no() << ")\n"; - - std::string errorMsg; - if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { - throw std::runtime_error("CompleteCmd: " + errorMsg); - } - - std::vector variable_vec; - if (vm.count("remove")) - variable_vec = vm["remove"].as>(); - - cmd = std::make_shared(clientEnv->task_path(), - clientEnv->jobs_password(), - clientEnv->process_or_remote_id(), - clientEnv->task_try_no(), - variable_vec); -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - -CtsWaitCmd::CtsWaitCmd(const std::string& pathToTask, - const std::string& jobsPassword, - const std::string& process_or_remote_id, - int try_no, - const std::string& expression) - : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), - expression_(expression) { - // Parse expression to make sure its valid - static_cast(Expression::parse(expression, "CtsWaitCmd:")); // will throw for errors -} - -void CtsWaitCmd::print(std::string& os) const { - os += Str::CHILD_CMD(); - os += "wait "; - os += expression_; - os += " "; - os += path_to_node(); -} - -bool CtsWaitCmd::equals(ClientToServerCmd* rhs) const { - auto* the_rhs = dynamic_cast(rhs); - if (!the_rhs) - return false; - if (expression_ != the_rhs->expression()) - return false; - return TaskCmd::equals(rhs); -} - -STC_Cmd_ptr CtsWaitCmd::doHandleRequest(AbstractServer* as) const { - as->update_stats().task_wait_++; - - SuiteChanged1 changed(submittable_->suite()); - - // Parse the expression, should not fail since client should have already check expression parses - // The complete expression have been parsed and we have created the abstract syntax tree - // We now need CHECK the AST for path nodes, event and meter. repeats,etc. - // *** This will also set the Node pointers *** - // If the expression references paths that don't exist throw an error - // This can be captured in the ecf script, which should then abort the task - // Otherwise we will end up blocking indefinitely - std::unique_ptr ast = - submittable_->parse_and_check_expressions(expression_, true, "CtsWaitCmd:"); // will throw for errors - - // Evaluate the expression - if (ast->evaluate()) { - - submittable_->flag().clear(ecf::Flag::WAIT); - - // expression evaluates, return OK - return PreAllocatedReply::ok_cmd(); - } - - submittable_->flag().set(ecf::Flag::WAIT); - - // Block/wait while expression is false - return PreAllocatedReply::block_client_on_home_server_cmd(); -} - -const char* CtsWaitCmd::arg() { - return TaskApi::waitArg(); -} -const char* CtsWaitCmd::desc() { - return "Evaluates an expression, and block while the expression is false.\n" - "For use in the '.ecf' file *only*, hence the context is supplied via environment variables\n" - " arg1 = string(expression)\n\n" - "Usage:\n" - " ecflow_client --wait=\"/suite/taskx == complete\""; -} - -void CtsWaitCmd::addOption(boost::program_options::options_description& desc) const { - desc.add_options()(CtsWaitCmd::arg(), po::value(), CtsWaitCmd::desc()); -} -void CtsWaitCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { - std::string expression = vm[arg()].as(); - - if (clientEnv->debug()) - cout << " CtsWaitCmd::create " << CtsWaitCmd::arg() << " task_path(" << clientEnv->task_path() << ") password(" - << clientEnv->jobs_password() << ") remote_id(" << clientEnv->process_or_remote_id() << ") try_no(" - << clientEnv->task_try_no() << ") expression(" << expression << ")\n"; - - std::string errorMsg; - if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { - throw std::runtime_error("CtsWaitCmd: " + errorMsg); - } - - cmd = std::make_shared(clientEnv->task_path(), - clientEnv->jobs_password(), - clientEnv->process_or_remote_id(), - clientEnv->task_try_no(), - expression); -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - -AbortCmd::AbortCmd(const std::string& pathToTask, - const std::string& jobsPassword, - const std::string& process_or_remote_id, - int try_no, - const std::string& reason) - : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), - reason_(reason) { - if (!reason_.empty()) { - // Do not use "\n" | ';' in Submittable::abr_, as this can mess up, --migrate output - // Which would then affect --load. - Str::replace(reason_, "\n", ""); - Str::replace(reason_, ";", " "); - } -} - -void AbortCmd::print(std::string& os) const { - os += Str::CHILD_CMD(); - os += "abort "; - os += path_to_node(); - os += " "; - os += reason_; -} - -bool AbortCmd::equals(ClientToServerCmd* rhs) const { - auto* the_rhs = dynamic_cast(rhs); - if (!the_rhs) - return false; - if (reason_ != the_rhs->reason()) - return false; - return TaskCmd::equals(rhs); -} - -STC_Cmd_ptr AbortCmd::doHandleRequest(AbstractServer* as) const { - as->update_stats().task_abort_++; - - assert(isWrite()); // isWrite used in handleRequest() to control check pointing - - { - /// If there is an associated zombie, remove from the list - as->zombie_ctrl().remove(submittable_); - - // update suite change numbers before job submission, submittable_ setup during authentication - SuiteChanged1 changed(submittable_->suite()); - - string theReason = reason_; - if (theReason.empty()) - theReason = "Trap raised in job file"; - - submittable_->aborted(theReason); // will set task->set_state(NState::ABORTED); - } - - // Do job submission in case any triggers dependent on NState::ABORTED - // If task try number is less than ECF_TRIES we attempt to re-submit the job.(ie if still in limit) - as->increment_job_generation_count(); - return PreAllocatedReply::ok_cmd(); -} - -const char* AbortCmd::arg() { - return TaskApi::abortArg(); -} -const char* AbortCmd::desc() { - return "Mark task as aborted. For use in the '.ecf' script file *only*\n" - "Hence the context is supplied via environment variables\n" - " arg1 = (optional) string(reason)\n" - " Optionally provide a reason why the abort was raised\n\n" - "If this child command is a zombie, then the default action will be to *block*.\n" - "The default can be overridden by using zombie attributes.\n" - "Otherwise the blocking period is defined by ECF_TIMEOUT.\n\n" - "Usage:\n" - " ecflow_client --abort=reasonX"; -} - -void AbortCmd::addOption(boost::program_options::options_description& desc) const { - desc.add_options()(AbortCmd::arg(), po::value()->implicit_value(string()), AbortCmd::desc()); -} -void AbortCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { - std::string reason = vm[arg()].as(); - - if (clientEnv->debug()) - cout << " AbortCmd::create " << AbortCmd::arg() << " task_path(" << clientEnv->task_path() << ") password(" - << clientEnv->jobs_password() << ") remote_id(" << clientEnv->process_or_remote_id() << ") try_no(" - << clientEnv->task_try_no() << ") reason(" << reason << ")\n"; - - std::string errorMsg; - if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { - throw std::runtime_error("AbortCmd: " + errorMsg); - } - - cmd = std::make_shared(clientEnv->task_path(), - clientEnv->jobs_password(), - clientEnv->process_or_remote_id(), - clientEnv->task_try_no(), - reason); -} -////////////////////////////////////////////////////////////////////////////////////////////////// - -bool EventCmd::equals(ClientToServerCmd* rhs) const { - auto* the_rhs = dynamic_cast(rhs); - if (!the_rhs) - return false; - if (name_ != the_rhs->name()) - return false; - if (value_ != the_rhs->value()) - return false; - return TaskCmd::equals(rhs); -} - -void EventCmd::print(std::string& os) const { - os += Str::CHILD_CMD(); - os += "event "; - os += name_; - os += " "; - if (value_) - os += "1 "; - else - os += "0 "; - os += path_to_node(); -} - -STC_Cmd_ptr EventCmd::doHandleRequest(AbstractServer* as) const { - as->update_stats().task_event_++; - - { // update suite change numbers before job submission, task_ setup during authentication - SuiteChanged1 changed(submittable_->suite()); - - // The name could either be "string" or an integer either way it should be unique - if (!submittable_->set_event(name_, value_)) { - std::string ss; - ss = "Event request failed as event '"; - ss += name_; - ss += "' does not exist on task "; - ss += path_to_node(); - ecf::log(Log::ERR, ss); - return PreAllocatedReply::ok_cmd(); - } - } - - // Do job submission in case any triggers dependent on events - as->increment_job_generation_count(); - return PreAllocatedReply::ok_cmd(); -} - -const char* EventCmd::arg() { - return TaskApi::eventArg(); -} -const char* EventCmd::desc() { - return "Change event. For use in the '.ecf' script file *only*\n" - "Hence the context is supplied via environment variables\n" - " arg1(string | int) = event-name\n\n" - " arg2(string)(optional) = [ set | clear] default value is set\n\n" - "If this child command is a zombie, then the default action will be to *fob*,\n" - "i.e allow the ecflow client command to complete without an error\n" - "The default can be overridden by using zombie attributes.\n\n" - "Usage:\n" - " ecflow_client --event=ev # set the event, default since event initial value is clear\n" - " ecflow_client --event=ev set # set the event, explicit\n" - " ecflow_client --event=ev clear # clear the event, use when event initial value is set\n"; -} - -void EventCmd::addOption(boost::program_options::options_description& desc) const { - desc.add_options()(EventCmd::arg(), po::value>()->multitoken(), EventCmd::desc()); -} -void EventCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { - vector args = vm[arg()].as>(); - std::string event; - if (args.size() >= 1) - event = args[0]; - - bool value = true; - if (args.size() == 2) { - if (args[1] == "set") - value = true; - else if (args[1] == "clear") - value = false; - else { - std::stringstream ss; - ss << "EventCmd: The second argument must be [ set | clear ] but found " << args[1]; - throw std::runtime_error(ss.str()); - } - } - - if (clientEnv->debug()) - cout << " EventCmd::create " << EventCmd::arg() << " task_path(" << clientEnv->task_path() << ") password(" - << clientEnv->jobs_password() << ") remote_id(" << clientEnv->process_or_remote_id() << ") try_no(" - << clientEnv->task_try_no() << ") event(" << event << ")" - << ") value(" << value << ")\n"; - - std::string errorMsg; - if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { - throw std::runtime_error("EventCmd: " + errorMsg); - } - - cmd = std::make_shared(clientEnv->task_path(), - clientEnv->jobs_password(), - clientEnv->process_or_remote_id(), - clientEnv->task_try_no(), - event, - value); -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - -bool MeterCmd::equals(ClientToServerCmd* rhs) const { - auto* the_rhs = dynamic_cast(rhs); - if (!the_rhs) - return false; - if (name_ != the_rhs->name()) - return false; - if (value_ != the_rhs->value()) - return false; - return TaskCmd::equals(rhs); -} - -void MeterCmd::print(std::string& os) const { - os += Str::CHILD_CMD(); - os += "meter "; - os += name_; - os += " "; - os += ecf::convert_to(value_); - os += " "; - os += path_to_node(); -} - -STC_Cmd_ptr MeterCmd::doHandleRequest(AbstractServer* as) const { - as->update_stats().task_meter_++; - - { // Added scope for SuiteChanged1 changed: i.e update suite change numbers before job submission - // submittable_ setup during authentication - SuiteChanged1 changed(submittable_->suite()); - - /// Allow meter to set any valid value that is in range because: - /// - When we have a network failure, and restoration. The meter tasks, will come in random, order. - /// - When task is executed without a requee the meter value will less than maximum - /// - /// This has *IMPLICATION*, if the meter is used in a trigger, using a equality - /// operator, then the trigger will always hold. hence suite designers need to - /// aware of this. - try { - - Meter& the_meter = submittable_->find_meter(name_); - if (the_meter.empty()) { - LOG(Log::ERR, - "MeterCmd::doHandleRequest: failed as meter '" << name_ << "' does not exist on task " - << path_to_node()); - return PreAllocatedReply::ok_cmd(); - } - - /// Invalid meter values(out or range) will raise exceptions. - /// Just ignore the request rather than failing client cmd - the_meter.set_value(value_); - } - catch (std::exception& e) { - LOG(Log::ERR, "MeterCmd::doHandleRequest: failed for task " << path_to_node() << ". " << e.what()); - return PreAllocatedReply::ok_cmd(); - } - } - - // Do job submission in case any triggers dependent on meters - as->increment_job_generation_count(); - return PreAllocatedReply::ok_cmd(); -} - -const char* MeterCmd::arg() { - return TaskApi::meterArg(); -} -const char* MeterCmd::desc() { - return "Change meter. For use in the '.ecf' script file *only*\n" - "Hence the context is supplied via environment variables\n" - " arg1(string) = meter-name\n" - " arg2(int) = the new meter value\n\n" - "If this child command is a zombie, then the default action will be to *fob*,\n" - "i.e allow the ecflow client command to complete without an error\n" - "The default can be overridden by using zombie attributes.\n\n" - "Usage:\n" - " ecflow_client --meter=my_meter 20"; -} - -void MeterCmd::addOption(boost::program_options::options_description& desc) const { - desc.add_options()(MeterCmd::arg(), po::value>()->multitoken(), MeterCmd::desc()); -} -void MeterCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { - vector args = vm[arg()].as>(); - - if (clientEnv->debug()) { - dumpVecArgs(MeterCmd::arg(), args); - cout << " MeterCmd::create " << MeterCmd::arg() << " task_path(" << clientEnv->task_path() << ") password(" - << clientEnv->jobs_password() << ") remote_id(" << clientEnv->process_or_remote_id() << ") try_no(" - << clientEnv->task_try_no() << ")\n"; - } - - if (args.size() != 2) { - std::stringstream ss; - ss << "MeterCmd: Two arguments expected, found " << args.size() - << " Please specify , ie --meter=name 100\n"; - throw std::runtime_error(ss.str()); - } - - int value = 0; - try { - std::string strVal = args[1]; - value = ecf::convert_to(strVal); - } - catch (const ecf::bad_conversion&) { - throw std::runtime_error("MeterCmd: Second argument must be a integer, i.e. --meter=name 100\n"); - } - - std::string errorMsg; - if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { - throw std::runtime_error("MeterCmd: " + errorMsg); - } - - cmd = std::make_shared(clientEnv->task_path(), - clientEnv->jobs_password(), - clientEnv->process_or_remote_id(), - clientEnv->task_try_no(), - args[0], - value); -} -////////////////////////////////////////////////////////////////////////////////////////////////// - -bool LabelCmd::equals(ClientToServerCmd* rhs) const { - auto* the_rhs = dynamic_cast(rhs); - if (!the_rhs) - return false; - if (name_ != the_rhs->name()) - return false; - if (label_ != the_rhs->label()) - return false; - return TaskCmd::equals(rhs); -} - -void LabelCmd::print(std::string& os) const { - os += Str::CHILD_CMD(); - os += "label "; - os += name_; - os += " '"; - os += label_; - os += "' "; - os += path_to_node(); -} - -STC_Cmd_ptr LabelCmd::doHandleRequest(AbstractServer* as) const { - as->update_stats().task_label_++; - - assert(isWrite()); // isWrite used in handleRequest() to control check pointing - - // submittable_ setup during authentication - if (submittable_->findLabel(name_)) { - - SuiteChanged1 changed(submittable_->suite()); - submittable_->changeLabel(name_, label_); - } - // else { - // // ECFLOW-175, avoid filling up log file. Can get thousands of these messages, especially form MARS - // std::string ss; - // ss = "Label request failed as label '"; ss += name_; ss += "' does not exist on task "; ss += path_to_node(); - // ecf::log(Log::ERR,ss); - //} - - // Note: reclaiming memory for label_ earlier make *no* difference to performance of server - - return PreAllocatedReply::ok_cmd(); -} - -const char* LabelCmd::arg() { - return TaskApi::labelArg(); -} -const char* LabelCmd::desc() { - return "Change Label. For use in the '.ecf' script file *only*\n" - "Hence the context is supplied via environment variables\n" - " arg1 = label-name\n" - " arg2 = The new label value\n" - " The labels values can be single or multi-line(space separated quoted strings)\n\n" - "If this child command is a zombie, then the default action will be to *fob*,\n" - "i.e allow the ecflow client command to complete without an error\n" - "The default can be overridden by using zombie attributes.\n\n" - "Usage:\n" - " ecflow_client --label=progressed merlin"; -} - -void LabelCmd::addOption(boost::program_options::options_description& desc) const { - desc.add_options()(LabelCmd::arg(), po::value>()->multitoken(), LabelCmd::desc()); -} -void LabelCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { - vector args = vm[arg()].as>(); - - if (clientEnv->debug()) { - dumpVecArgs(LabelCmd::arg(), args); - cout << " LabelCmd::create " << LabelCmd::arg() << " task_path(" << clientEnv->task_path() << ") password(" - << clientEnv->jobs_password() << ") remote_id(" << clientEnv->process_or_remote_id() << ") try_no(" - << clientEnv->task_try_no() << ")\n"; - } - - if (args.size() < 2) { - std::stringstream ss; - ss << "LabelCmd: At least 2 arguments expected. Please specify: \n"; - throw std::runtime_error(ss.str()); - } - - std::string labelName = args[0]; - args.erase(args.begin()); // remove name from vector of strings - std::string labelValue; - for (size_t i = 0; i < args.size(); i++) { - if (i != 0) - labelValue += " "; - labelValue += args[i]; - } - - std::string errorMsg; - if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { - throw std::runtime_error("LabelCmd: " + errorMsg); - } - - cmd = std::make_shared(clientEnv->task_path(), - clientEnv->jobs_password(), - clientEnv->process_or_remote_id(), - clientEnv->task_try_no(), - labelName, - labelValue); -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - -bool QueueCmd::equals(ClientToServerCmd* rhs) const { - auto* the_rhs = dynamic_cast(rhs); - if (!the_rhs) - return false; - if (name_ != the_rhs->name()) - return false; - if (action_ != the_rhs->action()) - return false; - if (step_ != the_rhs->step()) - return false; - if (path_to_node_with_queue_ != the_rhs->path_to_node_with_queue()) - return false; - return TaskCmd::equals(rhs); -} - -void QueueCmd::print(std::string& os) const { - os += Str::CHILD_CMD(); - os += TaskApi::queue_arg(); - os += " "; - os += name_; - os += " "; - os += action_; - os += " "; - os += step_; - os += " "; - if (path_to_node_with_queue_.empty()) { - os += path_to_node(); - return; - } - - os += path_to_node_with_queue_; - os += " "; - os += path_to_node(); -} - -STC_Cmd_ptr QueueCmd::doHandleRequest(AbstractServer* as) const { - as->update_stats().task_queue_++; - std::string result; - - ////////////////////////////////////////////////////////////////////////////// - // Return the current string value, and then increment the index - ////////////////////////////////////////////////////////////////////////////// - { // Added scope for SuiteChanged1 changed: i.e update suite change numbers before job submission - // submittable_ setup during authentication - SuiteChanged1 changed(submittable_->suite()); - - if (!path_to_node_with_queue_.empty()) { - Defs* defs = submittable_->defs(); - if (defs) { - node_ptr node_with_queue = defs->findAbsNode(path_to_node_with_queue_); - if (node_with_queue) { - - QueueAttr& queue_attr = node_with_queue->findQueue(name_); - if (queue_attr.empty()) { - std::stringstream ss; - ss << "QueueCmd:: Could not find queue of name " << name_ << ", on input node " - << path_to_node_with_queue_; - return PreAllocatedReply::error_cmd(ss.str()); - } - - result = handle_queue(queue_attr); - } - else { - std::stringstream ss; - ss << "QueueCmd:: Could not find node at path " << path_to_node_with_queue_; - return PreAllocatedReply::error_cmd(ss.str()); - } - } - } - else { - bool fnd_queue = false; - QueueAttr& queue_attr = submittable_->findQueue(name_); - if (queue_attr.empty()) { - Node* parent = submittable_->parent(); - while (parent) { - QueueAttr& queue_attr1 = parent->findQueue(name_); - if (!queue_attr1.empty()) { - fnd_queue = true; - result = handle_queue(queue_attr1); - break; - } - parent = parent->parent(); - } - } - else { - fnd_queue = true; - result = handle_queue(queue_attr); - } - - if (!fnd_queue) { - std::stringstream ss; - ss << "QueueCmd:: Could not find queue " << name_ << " Up the node hierarchy"; - return PreAllocatedReply::error_cmd(ss.str()); - } - } - } - - // Do job submission in case any triggers dependent on QueueAttr - as->increment_job_generation_count(); - - if (result.empty()) - return PreAllocatedReply::ok_cmd(); - return PreAllocatedReply::string_cmd(result); -} - -std::string QueueCmd::handle_queue(QueueAttr& queue_attr) const { - if (queue_attr.empty()) { - std::stringstream ss; - ss << "QueueCmd:: Could not find queue of name " << name_ << " . Program error !"; - throw std::runtime_error(ss.str()); - } - - if (action_ == "active") - return queue_attr.active(); // return current index and value, make active, update index - if (action_ == "complete") - queue_attr.complete(step_); - if (action_ == "aborted") - queue_attr.aborted(step_); - if (action_ == "no_of_aborted") - return queue_attr.no_of_aborted(); - if (action_ == "reset") - queue_attr.reset_index_to_first_queued_or_aborted(); - - return std::string(); -} - -const char* QueueCmd::arg() { - return TaskApi::queue_arg(); -} -const char* QueueCmd::desc() { - return "QueueCmd. For use in the '.ecf' script file *only*\n" - "Hence the context is supplied via environment variables\n" - " arg1(string) = queue-name:\n" - " arg2(string) = action: [active | aborted | complete | no_of_aborted | reset ]\n" - " active: returns the first queued/aborted step, the return string is the queue value from the " - "definition\n" - " no_of_aborted: returns number of aborted steps as a string, i.e 10\n" - " reset: sets the index to the first queued/aborted step. Allows steps to be reprocessed for errors\n" - " arg2(string) = step: value returned from step=$(ecflow_client --queue=queue_name active)\n" - " This is only valid for complete and aborted steps\n" - " arg4(string) = path: (optional). The path where the queue is defined.\n" - " By default we search for the queue up the node tree.\n\n" - "If this child command is a zombie, then the default action will be to *block*,\n" - "The default can be overridden by using zombie attributes." - "If the path to the queue is not defined, then this command will\n" - "search for the queue up the node hierarchy. If no queue found, command fails\n\n" - "Usage:\n" - "step=\"\"\n" - "QNAME=\"my_queue_name\"\n" - "while [1 == 1 ] ; do\n" - " # this return the first queued/aborted step, then increments to next step, return when all steps " - "processed\n" - " step=$(ecflow_client --queue=$QNAME active) # of the form string i.e \"003\". this step is now active\n" - " if [[ $step == \"\" ]] ; then\n" - " break;\n" - " fi\n" - " ...\n" - " ecflow_client --queue=$QNAME complete $step # tell ecflow this step completed\n" - "done\n" - "\n" - "trap() { ecflow_client --queue=$QNAME aborted $step # tell ecflow this step failed }\n"; -} - -void QueueCmd::addOption(boost::program_options::options_description& desc) const { - desc.add_options()(QueueCmd::arg(), po::value>()->multitoken(), QueueCmd::desc()); -} -void QueueCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { - vector args = vm[arg()].as>(); - - if (clientEnv->debug()) { - dumpVecArgs(QueueCmd::arg(), args); - cout << " QueueCmd::create " << QueueCmd::arg() << " task_path(" << clientEnv->task_path() << ") password(" - << clientEnv->jobs_password() << ") remote_id(" << clientEnv->process_or_remote_id() << ") try_no(" - << clientEnv->task_try_no() << ")\n"; - } - - // expect: - // [active | aborted | complete | no_of_aborted | reset ] step - std::string queue_name, step; - std::string path_to_node_with_queue, action; - for (size_t i = 0; i < args.size(); i++) { - if (i == 0) - queue_name = args[i]; - else { - if (args[i] == "active" || args[i] == "aborted" || args[i] == "complete" || args[i] == "no_of_aborted" || - args[i] == "reset") { - action = args[i]; - } - else if (args[i].find('/') != std::string::npos) - path_to_node_with_queue = args[i]; - else - step = args[i]; - } - } - if (clientEnv->debug()) { - cout << " QueueCmd::create " - << "queue-name:(" << queue_name << ") action:(" << action << ") step:(" << step - << ") path_to_node_with_queue:(" << path_to_node_with_queue << ")\n"; - } - - if (args.size() == 4 && path_to_node_with_queue.empty()) { - std::stringstream ss; - ss << "QueueCmd: The fourth argument if specified must provide a path to a node where the queue resides.\n" - << "No path specified. " << args[3]; - throw std::runtime_error(ss.str()); - } - - if (args.empty() || queue_name.empty() || action.empty()) { - std::stringstream ss; - ss << "QueueCmd: incorrect argument specified, expected at least two arguments but found " << args.size() - << " Please specify [active | aborted | complete | no_of_aborted | reset ] step (optional) i.e\n" - << "--queue=name active # active does not need a step\n" - << "--queue=name active /path/to/node/with/queue\n" - << "--queue=name aborted $step\n" - << "--queue=name complete $step\n" - << "--queue=name no_of_aborted\n" - << "--queue=name reset\n"; - throw std::runtime_error(ss.str()); - } - if ((action == "complete" || action == "aborted") && step.empty()) { - std::stringstream ss; - ss << "QueueCmd: when --queue=name complete || --queue=name aborted is used a step must be provided e.g.\n" - << " ecflow_client --queue=name aborted $step\n" - << " ecflow_client --queue=name complete $step\n" - << "where step is value returned from active, such as\n" - << " step=$(ecflow_client --queue=name active)\n"; - throw std::runtime_error(ss.str()); - } - if ((action == "active" || action == "reset" || action == "no_of_aborted") && !step.empty()) { - throw std::runtime_error("QueueCmd: step should not be used with active, reset or no_of_aborted."); - } - - string msg; - if (!Str::valid_name(queue_name, msg)) { - throw std::runtime_error("QueueCmd: Invalid queue name : " + msg); - } - - std::string errorMsg; - if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { - throw std::runtime_error("QueueCmd: " + errorMsg); - } - - cmd = std::make_shared(clientEnv->task_path(), - clientEnv->jobs_password(), - clientEnv->process_or_remote_id(), - clientEnv->task_try_no(), - queue_name, - action, - step, - path_to_node_with_queue); -} - -std::ostream& operator<<(std::ostream& os, const InitCmd& c) { - std::string ret; - c.print(ret); - os << ret; - return os; -} -std::ostream& operator<<(std::ostream& os, const EventCmd& c) { - std::string ret; - c.print(ret); - os << ret; - return os; -} -std::ostream& operator<<(std::ostream& os, const MeterCmd& c) { - std::string ret; - c.print(ret); - os << ret; - return os; -} -std::ostream& operator<<(std::ostream& os, const LabelCmd& c) { - std::string ret; - c.print(ret); - os << ret; - return os; -} -std::ostream& operator<<(std::ostream& os, const AbortCmd& c) { - std::string ret; - c.print(ret); - os << ret; - return os; -} -std::ostream& operator<<(std::ostream& os, const CompleteCmd& c) { - std::string ret; - c.print(ret); - os << ret; - return os; -} -std::ostream& operator<<(std::ostream& os, const CtsWaitCmd& c) { - std::string ret; - c.print(ret); - os << ret; - return os; -} -std::ostream& operator<<(std::ostream& os, const QueueCmd& c) { - std::string ret; - c.print(ret); - os << ret; - return os; -} diff --git a/Base/src/ecflow/base/cts/task/AbortCmd.cpp b/Base/src/ecflow/base/cts/task/AbortCmd.cpp new file mode 100644 index 000000000..0a254bad7 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/AbortCmd.cpp @@ -0,0 +1,134 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "ecflow/base/cts/task/AbortCmd.hpp" + +#include + +#include "ecflow/attribute/QueueAttr.hpp" +#include "ecflow/base/AbstractClientEnv.hpp" +#include "ecflow/base/AbstractServer.hpp" +#include "ecflow/base/cts/task/TaskApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" +#include "ecflow/core/Log.hpp" +#include "ecflow/core/Str.hpp" +#include "ecflow/node/Expression.hpp" +#include "ecflow/node/Submittable.hpp" +#include "ecflow/node/SuiteChanged.hpp" + +using namespace ecf; +using namespace std; +using namespace boost; +namespace po = boost::program_options; + +AbortCmd::AbortCmd(const std::string& pathToTask, + const std::string& jobsPassword, + const std::string& process_or_remote_id, + int try_no, + const std::string& reason) + : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), + reason_(reason) { + if (!reason_.empty()) { + // Do not use "\n" | ';' in Submittable::abr_, as this can mess up, --migrate output + // Which would then affect --load. + Str::replace(reason_, "\n", ""); + Str::replace(reason_, ";", " "); + } +} + +void AbortCmd::print(std::string& os) const { + os += Str::CHILD_CMD(); + os += "abort "; + os += path_to_node(); + os += " "; + os += reason_; +} + +bool AbortCmd::equals(ClientToServerCmd* rhs) const { + auto* the_rhs = dynamic_cast(rhs); + if (!the_rhs) + return false; + if (reason_ != the_rhs->reason()) + return false; + return TaskCmd::equals(rhs); +} + +STC_Cmd_ptr AbortCmd::doHandleRequest(AbstractServer* as) const { + as->update_stats().task_abort_++; + + assert(isWrite()); // isWrite used in handleRequest() to control check pointing + + { + /// If there is an associated zombie, remove from the list + as->zombie_ctrl().remove(submittable_); + + // update suite change numbers before job submission, submittable_ setup during authentication + SuiteChanged1 changed(submittable_->suite()); + + string theReason = reason_; + if (theReason.empty()) + theReason = "Trap raised in job file"; + + submittable_->aborted(theReason); // will set task->set_state(NState::ABORTED); + } + + // Do job submission in case any triggers dependent on NState::ABORTED + // If task try number is less than ECF_TRIES we attempt to re-submit the job.(ie if still in limit) + as->increment_job_generation_count(); + return PreAllocatedReply::ok_cmd(); +} + +const char* AbortCmd::arg() { + return TaskApi::abortArg(); +} +const char* AbortCmd::desc() { + return "Mark task as aborted. For use in the '.ecf' script file *only*\n" + "Hence the context is supplied via environment variables\n" + " arg1 = (optional) string(reason)\n" + " Optionally provide a reason why the abort was raised\n\n" + "If this child command is a zombie, then the default action will be to *block*.\n" + "The default can be overridden by using zombie attributes.\n" + "Otherwise the blocking period is defined by ECF_TIMEOUT.\n\n" + "Usage:\n" + " ecflow_client --abort=reasonX"; +} + +void AbortCmd::addOption(boost::program_options::options_description& desc) const { + desc.add_options()(AbortCmd::arg(), po::value()->implicit_value(string()), AbortCmd::desc()); +} +void AbortCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { + std::string reason = vm[arg()].as(); + + if (clientEnv->debug()) + cout << " AbortCmd::create " << AbortCmd::arg() << " task_path(" << clientEnv->task_path() << ") password(" + << clientEnv->jobs_password() << ") remote_id(" << clientEnv->process_or_remote_id() << ") try_no(" + << clientEnv->task_try_no() << ") reason(" << reason << ")\n"; + + std::string errorMsg; + if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { + throw std::runtime_error("AbortCmd: " + errorMsg); + } + + cmd = std::make_shared(clientEnv->task_path(), + clientEnv->jobs_password(), + clientEnv->process_or_remote_id(), + clientEnv->task_try_no(), + reason); +} + +std::ostream& operator<<(std::ostream& os, const AbortCmd& c) { + std::string ret; + c.print(ret); + os << ret; + return os; +} + +CEREAL_REGISTER_TYPE(AbortCmd) +CEREAL_REGISTER_DYNAMIC_INIT(AbortCmd) diff --git a/Base/src/ecflow/base/cts/task/AbortCmd.hpp b/Base/src/ecflow/base/cts/task/AbortCmd.hpp new file mode 100644 index 000000000..8ad41a8da --- /dev/null +++ b/Base/src/ecflow/base/cts/task/AbortCmd.hpp @@ -0,0 +1,54 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_task_AbortCmd_HPP +#define ecflow_base_cts_task_AbortCmd_HPP + +#include "ecflow/base/cts/task/TaskCmd.hpp" + +class AbortCmd final : public TaskCmd { +public: + AbortCmd(const std::string& pathToTask, + const std::string& jobsPassword, + const std::string& process_or_remote_id, + int try_no = 1, + const std::string& reason = ""); + AbortCmd() : TaskCmd() {} + + const std::string& reason() const { return reason_; } + + void print(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + ecf::Child::CmdType child_type() const override { return ecf::Child::ABORT; } + + std::string reason_; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(reason_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const AbortCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(AbortCmd) + +#endif /* ecflow_base_cts_task_AbortCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/task/CompleteCmd.cpp b/Base/src/ecflow/base/cts/task/CompleteCmd.cpp new file mode 100644 index 000000000..15fa81aa8 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/CompleteCmd.cpp @@ -0,0 +1,124 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "ecflow/base/cts/task/CompleteCmd.hpp" + +#include + +#include "ecflow/base/AbstractClientEnv.hpp" +#include "ecflow/base/AbstractServer.hpp" +#include "ecflow/base/cts/task/TaskApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" +#include "ecflow/core/Str.hpp" +#include "ecflow/node/Submittable.hpp" +#include "ecflow/node/SuiteChanged.hpp" + +using namespace ecf; +using namespace std; +using namespace boost; +namespace po = boost::program_options; + +void CompleteCmd::print(std::string& os) const { + os += Str::CHILD_CMD(); + os += "complete "; + os += path_to_node(); + if (!var_to_del_.empty()) { + os += " --remove"; + for (const auto& var_to_del : var_to_del_) { + os += " "; + os += var_to_del; + } + } +} + +bool CompleteCmd::equals(ClientToServerCmd* rhs) const { + auto* the_rhs = dynamic_cast(rhs); + if (!the_rhs) + return false; + if (var_to_del_ != the_rhs->variables_to_delete()) + return false; + return TaskCmd::equals(rhs); +} + +STC_Cmd_ptr CompleteCmd::doHandleRequest(AbstractServer* as) const { + as->update_stats().task_complete_++; + + { + /// If there is an associated zombie, remove from the list. Must match, + /// Do this before task->complete(), since that clears password & process id + /// remove(..) uses password/ process id to match the right zombie + as->zombie_ctrl().remove(submittable_); + + // update suite change numbers before job submission, submittable_ setup during authentication + SuiteChanged1 changed(submittable_->suite()); + submittable_->complete(); // will set task->set_state(NState::COMPLETE); + + for (const auto& var_to_delete : var_to_del_) { + submittable_->delete_variable_no_error(var_to_delete); + } + } + + // Do job submission in case any triggers dependent on NState::COMPLETE + as->increment_job_generation_count(); + return PreAllocatedReply::ok_cmd(); +} + +const char* CompleteCmd::arg() { + return TaskApi::completeArg(); +} +const char* CompleteCmd::desc() { + return "Mark task as complete. For use in the '.ecf' script file *only*\n" + "Hence the context is supplied via environment variables\n\n" + "If this child command is a zombie, then the default action will be to *block*.\n" + "The default can be overridden by using zombie attributes.\n" + "Otherwise the blocking period is defined by ECF_TIMEOUT.\n" + "The init command allows variables to be added, and complete command\n" + "allows for them to be removed.\n" + " arg1(--remove)(optional) = a list of variables to removed from this task\n\n" + "Usage:\n" + " ecflow_client --complete\n" + " ecflow_client --complete --remove name1 name2 # delete variables name1 and name2 on the task"; +} + +void CompleteCmd::addOption(boost::program_options::options_description& desc) const { + desc.add_options()(CompleteCmd::arg(), CompleteCmd::desc())( + "remove", po::value>()->multitoken(), "remove variables i.e name name2"); +} +void CompleteCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { + if (clientEnv->debug()) + cout << " CompleteCmd::create " << CompleteCmd::arg() << " task_path(" << clientEnv->task_path() + << ") password(" << clientEnv->jobs_password() << ") remote_id(" << clientEnv->process_or_remote_id() + << ") try_no(" << clientEnv->task_try_no() << ")\n"; + + std::string errorMsg; + if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { + throw std::runtime_error("CompleteCmd: " + errorMsg); + } + + std::vector variable_vec; + if (vm.count("remove")) + variable_vec = vm["remove"].as>(); + + cmd = std::make_shared(clientEnv->task_path(), + clientEnv->jobs_password(), + clientEnv->process_or_remote_id(), + clientEnv->task_try_no(), + variable_vec); +} + +std::ostream& operator<<(std::ostream& os, const CompleteCmd& c) { + std::string ret; + c.print(ret); + os << ret; + return os; +} + +CEREAL_REGISTER_TYPE(CompleteCmd) +CEREAL_REGISTER_DYNAMIC_INIT(CompleteCmd) diff --git a/Base/src/ecflow/base/cts/task/CompleteCmd.hpp b/Base/src/ecflow/base/cts/task/CompleteCmd.hpp new file mode 100644 index 000000000..5b3cb3658 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/CompleteCmd.hpp @@ -0,0 +1,59 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_task_CompleteCmd_HPP +#define ecflow_base_cts_task_CompleteCmd_HPP + +#include "ecflow/base/cts/task/TaskCmd.hpp" +#include "ecflow/core/cereal_optional_nvp.hpp" + +class CompleteCmd final : public TaskCmd { +public: + CompleteCmd(const std::string& pathToTask, + const std::string& jobsPassword, + const std::string& process_or_remote_id = "", + int try_no = 1, + const std::vector& vec = std::vector()) + : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), + var_to_del_(vec) {} + CompleteCmd() : TaskCmd() {} + + const std::vector& variables_to_delete() const { return var_to_del_; } + + void print(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + ecf::Child::CmdType child_type() const override { return ecf::Child::COMPLETE; } + +private: + std::vector var_to_del_; // variables to delete on task + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this)); + CEREAL_OPTIONAL_NVP(ar, var_to_del_, [this]() { return !var_to_del_.empty(); }); // conditionally save + } +}; + +std::ostream& operator<<(std::ostream& os, const CompleteCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(CompleteCmd) + +#endif /* ecflow_base_cts_task_CompleteCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/task/CtsWaitCmd.cpp b/Base/src/ecflow/base/cts/task/CtsWaitCmd.cpp new file mode 100644 index 000000000..011e65a27 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/CtsWaitCmd.cpp @@ -0,0 +1,439 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "ecflow/base/cts/task/CtsWaitCmd.hpp" + +#include + +#include "ecflow/base/AbstractClientEnv.hpp" +#include "ecflow/base/AbstractServer.hpp" +#include "ecflow/base/cts/task/TaskApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" +#include "ecflow/core/Log.hpp" +#include "ecflow/core/Str.hpp" +#include "ecflow/node/Defs.hpp" +#include "ecflow/node/Expression.hpp" +#include "ecflow/node/Submittable.hpp" +#include "ecflow/node/SuiteChanged.hpp" + +using namespace ecf; +using namespace std; +using namespace boost; +namespace po = boost::program_options; + +// #define DEBUG_ZOMBIE 1 + +bool TaskCmd::equals(ClientToServerCmd* rhs) const { + auto* the_rhs = dynamic_cast(rhs); + if (!the_rhs) + return false; + if (path_to_submittable_ != the_rhs->path_to_node()) + return false; + if (jobs_password_ != the_rhs->jobs_password()) + return false; + if (process_or_remote_id_ != the_rhs->process_or_remote_id()) + return false; + if (try_no_ != the_rhs->try_no()) + return false; + return ClientToServerCmd::equals(rhs); +} + +// ********************************************************************************** +// IMPORTANT: In the current SMS/ECF only the init child command, passes the +// process_or_remote_id_, for *ALL* other child commands this is empty. +// The Automated tests get round this via setting ECF_RID in the header/tail job includes +// However since this may not be in .sms/.ecf files, we cannot rely on it +// Hence we need to be able to handle *EMPTY* process_or_remote_id_ for child commands +// +// Task State | Child | Log | Explanation +// ------------------------------------------------------------------------------------------------------------- +// SUBMITTED | child!=INIT | ERR | Script has child command in back ground, Bug in script, out of order +// ACTIVE | INIT & pid & passwd match | WAR | two init commands, overloaded server, or 2*init in script, *FOB*. +// Forgive ACTIVE | INIT & pid & passwd !match | ERR | two init commands, Task started again by user. COMPLETE | +// COMPLETE | WAR | two complete, zombie, overloaded server. *FOB*, Allow job to complete. Forgive. +// COMPLETE | child != COMPLETE | ERR | zombie +// ABORTED | ABORTED | WAR | two aborted, zombie, overloaded server. *FOB*, allow process to +// exit. Forgive ABORTED | child != ABORTED | WAR | zombie +// ------------------------------------------------------------------------------------------------------------------- +// +// zombie type | PID | password | explanation +// --------------------------------------------------------------------------------------------------------------- +// ECF_PID | X | matches | PID miss-match, but password matches. Job scheduled twice. Check submitter +// ECF_PID_PASSWD | X | X | Both PID and password miss-match. Re-queue & submit of active job +// ECF_PASSWD | matches | X | Password miss-match, PID matches, system has re-cycled PID or hacked job file? +// ECF | matches | matches | overloaded server,Two init commands or task complete or aborted but receives +// another child cmd USER | ? | ? | User initiated zombie whilst task was active or submitted, +// command is recorded in zombie PATH | n/a | n/a | Task not found. Nodes replaced whilst jobs were +// running +// ---------------------------------------------------------------------------------------------------------------- +bool TaskCmd::authenticate(AbstractServer* as, STC_Cmd_ptr& theReply) const { +#ifdef DEBUG_ZOMBIE + std::cout << " TaskCmd::authenticate " << Child::to_string(child_type()); + std::cout << " " << path_to_submittable_ << " " << process_or_remote_id_ << " " << jobs_password_ << " " << try_no_; + const Zombie& zombie = as->zombie_ctrl().find(path_to_submittable_, process_or_remote_id_, jobs_password_); + if (!zombie.empty()) + std::cout << " " << zombie; + else { + const Zombie& zombiep = as->zombie_ctrl().find_by_path_only(path_to_submittable_); + if (!zombiep.empty()) + std::cout << " find_by_path_only: " << zombiep; + } +#endif + /// *************************************************************************** + /// Task based cmd have their own authentication mechanism, hence we + /// Don't need to call the base class authenticate + /// ************************************************************************** + + if (!as->allowTaskCommunication()) { + // This is not an Error, hence we don't throw exception + theReply = PreAllocatedReply::block_client_server_halted_cmd(); + return false; + } + + submittable_ = get_submittable(as); + if (!submittable_) { +#ifdef DEBUG_ZOMBIE + std::cout << ": PATH Zombie\n"; +#endif + // Create path zombie, if not already created: + std::string action_taken; + static_cast(as->zombie_ctrl().handle_path_zombie(as, this, action_taken, theReply)); + + // distinguish output by using *path* + std::stringstream ss; + ss << " zombie(*path*) : chd:" << ecf::Child::to_string(child_type()) << " : " << path_to_submittable_ << " : " + << process_or_remote_id_ << " : " << jobs_password_ << " : action(" << action_taken << ")"; + log(Log::ERR, ss.str()); + return false; + } + + // If the CMD *WAS* created with Submittable::DUMMY_JOBS_PASSWORD then we were testing + // This will be copied from client to server, hence we want to avoid same check in the + // server. when handleRequest is called() + // DO NOT place #ifdef DEBUG otherwise test will start failing for the release build + if (jobs_password_ == Submittable::DUMMY_JOBS_PASSWORD()) { + return true; + } + + SuiteChanged1 changed(submittable_->suite()); + + /// Check if User wants to explicitly bypass password checking + /// This can be done via AlterCmd by adding a variable on the task, ECF_PASS with value + /// Submittable::FREE_JOBS_PASSWORD Note: this *does not* look for the variable up the node tree, only on the task. + std::string ecf_pass_value; + if (submittable_->findVariableValue(Str::ECF_PASS(), ecf_pass_value)) { + + if (ecf_pass_value == Submittable::FREE_JOBS_PASSWORD()) { + submittable_->flag().clear(ecf::Flag::ZOMBIE); + return true; + } + } + + /// Handle corner case ,where we have two jobs with different process id, but same password + /// Can happen if jobs is started externally, + /// or via test(i.e submit 1,submit 2(force)) before job1 active its password is overridden + bool submittable_allready_aborted = false; + bool submittable_allready_active = false; + bool submittable_allready_complete = false; + password_missmatch_ = false; + pid_missmatch_ = false; + + /// *** In complete state, the password is empty. + if (submittable_->jobsPassword() != jobs_password_) { +#ifdef DEBUG_ZOMBIE + std::cout << ": submittable pass(" << submittable_->jobsPassword() << ") != jobs_password_(" << jobs_password_ + << ")"; +#endif + password_missmatch_ = true; + } + + /// When state is in SUBMITTED, its process/remote_id is EMPTY. It will be set by the INIT child command. + /// Hence we can *NOT* mark it as pid_missmatch. + /// + /// *** See Note above: Not all child commands pass a process_id. *** + /// *** Hence this test for zombies is ONLY valid if process sets the process_or_remote_id_ **** + /// *** User should really set ECF_RID in the scripts + /// *** In submitted state, the pid is empty. hence clear pid_missmatch_ below + /// *** In complete state, the pid is empty. hence clear pid_missmatch_ below + if (!submittable_->process_or_remote_id().empty() && !process_or_remote_id_.empty() && + submittable_->process_or_remote_id() != process_or_remote_id_) { +#ifdef DEBUG_ZOMBIE + std::cout << ":task pid(" << submittable_->process_or_remote_id() << ") != process pid(" + << process_or_remote_id_ << ")"; +#endif + pid_missmatch_ = true; + } + + switch (submittable_->state()) { + case NState::SUBMITTED: { + // The pid on the task will be empty + if (child_type() != Child::INIT) { + std::stringstream ss; + ss << path_to_submittable_ + << " When a node is submitted, expected next child command to be INIT but received " + << Child::to_string(child_type()); + log(Log::ERR, ss.str()); + } + break; + } + + case NState::ACTIVE: { + if (child_type() == Child::INIT) { +#ifdef DEBUG_ZOMBIE + std::cout << ":(child_type() == Child::INIT) && submittable_->state() == NState::ACTIVE)"; +#endif + // *IF* password and pid matches be more forgiving. How can this case arise: + // i.e server is heavily overloaded, client calls init, which times out because server is busy + // Client then sends init again. In this case rather than treating it as a zombie, we will let it + // through providing the password and pid matches. + if (!password_missmatch_ && !pid_missmatch_) { + string ret = " [ overloaded || --init*2 ](pid and passwd match) : chd:"; + ret += ecf::Child::to_string(child_type()); + ret += " : "; + ret += path_to_submittable_; + ret += " : already active : action(fob)"; + log(Log::WAR, ret); + theReply = PreAllocatedReply::ok_cmd(); + return false; + } + submittable_allready_active = true; + } + break; + } + + case NState::COMPLETE: { +#ifdef DEBUG_ZOMBIE + std::cout << ": submittable_->state() == NState::COMPLETE)"; +#endif + if (child_type() == Child::COMPLETE) { + // Note: when a node completes, we clear tasks password and pid, to save memory on checkpt & network + // bandwidth (We could choose not to clear, This would allow us to disambiguate between 2/ and 3/ + // below). HOWEVER: + // + // How can this situation arise: + // 1/ Two calls to --complete (rare) + // 2/ Overloaded server. Client send --complete to server, but it is overload and does not respond, + // the client then + // times out. Server handles the request. When client tries again we get here. (possible) + // 3/ Zombie, two separate process. (possible, typically done by user action) + // + // For all three it should be safe to just fob: + // 1/ Two calls to --complete # Be forgiving + // 2/ Overloaded server # The correct course of action + // 3/ zombie # The zombie has completed anyway, don't bother blocking it + + submittable_->flag().clear(ecf::Flag::ZOMBIE); + as->zombie_ctrl().remove_by_path(path_to_submittable_); + + string ret = " [ overloaded || zombie || --complete*2 ] : chd:"; + ret += ecf::Child::to_string(child_type()); + ret += " : "; + ret += path_to_submittable_; + ret += " : already complete : action(fob)"; + log(Log::WAR, ret); + theReply = PreAllocatedReply::ok_cmd(); + return false; + } + + // If Task state is complete, and we receive **any** child command then it is a zombie + submittable_allready_complete = true; + password_missmatch_ = false; + pid_missmatch_ = false; + break; + } + + case NState::ABORTED: { +#ifdef DEBUG_ZOMBIE + std::cout << ": submittable_->state() == NState::ABORTED)"; +#endif + + if (child_type() == Child::ABORT) { + + if (!password_missmatch_ && !pid_missmatch_) { + + as->zombie_ctrl().remove(submittable_); + + string ret = " [ overloaded || --abort*2 ] (pid and passwd match) : chd:"; + ret += ecf::Child::to_string(child_type()); + ret += " : "; + ret += path_to_submittable_; + ret += " : already aborted : action(fob)"; + log(Log::WAR, ret); + theReply = PreAllocatedReply::ok_cmd(); + return false; + } + } + + // If Task state is aborted, and we receive **any** child command then it is a zombie + submittable_allready_aborted = true; + password_missmatch_ = false; + pid_missmatch_ = false; + break; + } + case NState::QUEUED: + break; // WTF + case NState::UNKNOWN: + break; // WTF + } + +#ifdef DEBUG_ZOMBIE + std::cout << "\n"; +#endif + + if (password_missmatch_ || pid_missmatch_ || submittable_allready_active || submittable_allready_complete || + submittable_allready_aborted) { + /// If the task has adopted we return true, and carry on as normal + std::string action_taken; + if (!as->zombie_ctrl().handle_zombie(submittable_, this, action_taken, theReply)) { + + // LOG failure: Include type of zombie. + // ** NOTE **: the zombie may have been removed by user actions. i.e if fob and child cmd is abort | + // complete, etc + std::stringstream ss; + ss << " zombie"; + const Zombie& theZombie = + as->zombie_ctrl().find(path_to_submittable_, process_or_remote_id_, jobs_password_); + if (!theZombie.empty()) + ss << "(" << theZombie.type_str() << ")"; + + ss << " : chd:" << ecf::Child::to_string(child_type()); + ss << " : " << path_to_submittable_ << "(" << NState::toString(submittable_->state()) << ")"; + ss << " : " << process_or_remote_id_ << " : " << jobs_password_; + if (submittable_allready_active) + ss << " : already active"; + if (submittable_allready_complete) + ss << " : already complete"; + if (submittable_allready_aborted) + ss << " : already aborted"; + if (password_missmatch_) + ss << " : passwd != [ task:" << submittable_->jobsPassword() << " child:" << jobs_password_ << " ]"; + if (pid_missmatch_) + ss << " : pid != [ task:" << submittable_->process_or_remote_id() << " child:" << process_or_remote_id_ + << " ]"; + ss << " : action(" << action_taken << ")"; + log(Log::ERR, ss.str()); + return false; + } + } + return true; +} + +Submittable* TaskCmd::get_submittable(AbstractServer* as) const { + node_ptr node = as->defs()->findAbsNode(path_to_submittable_); + if (!node.get()) { + return nullptr; + } + + return node->isSubmittable(); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +CtsWaitCmd::CtsWaitCmd(const std::string& pathToTask, + const std::string& jobsPassword, + const std::string& process_or_remote_id, + int try_no, + const std::string& expression) + : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), + expression_(expression) { + // Parse expression to make sure its valid + static_cast(Expression::parse(expression, "CtsWaitCmd:")); // will throw for errors +} + +void CtsWaitCmd::print(std::string& os) const { + os += Str::CHILD_CMD(); + os += "wait "; + os += expression_; + os += " "; + os += path_to_node(); +} + +bool CtsWaitCmd::equals(ClientToServerCmd* rhs) const { + auto* the_rhs = dynamic_cast(rhs); + if (!the_rhs) + return false; + if (expression_ != the_rhs->expression()) + return false; + return TaskCmd::equals(rhs); +} + +STC_Cmd_ptr CtsWaitCmd::doHandleRequest(AbstractServer* as) const { + as->update_stats().task_wait_++; + + SuiteChanged1 changed(submittable_->suite()); + + // Parse the expression, should not fail since client should have already check expression parses + // The complete expression have been parsed and we have created the abstract syntax tree + // We now need CHECK the AST for path nodes, event and meter. repeats,etc. + // *** This will also set the Node pointers *** + // If the expression references paths that don't exist throw an error + // This can be captured in the ecf script, which should then abort the task + // Otherwise we will end up blocking indefinitely + std::unique_ptr ast = + submittable_->parse_and_check_expressions(expression_, true, "CtsWaitCmd:"); // will throw for errors + + // Evaluate the expression + if (ast->evaluate()) { + + submittable_->flag().clear(ecf::Flag::WAIT); + + // expression evaluates, return OK + return PreAllocatedReply::ok_cmd(); + } + + submittable_->flag().set(ecf::Flag::WAIT); + + // Block/wait while expression is false + return PreAllocatedReply::block_client_on_home_server_cmd(); +} + +const char* CtsWaitCmd::arg() { + return TaskApi::waitArg(); +} +const char* CtsWaitCmd::desc() { + return "Evaluates an expression, and block while the expression is false.\n" + "For use in the '.ecf' file *only*, hence the context is supplied via environment variables\n" + " arg1 = string(expression)\n\n" + "Usage:\n" + " ecflow_client --wait=\"/suite/taskx == complete\""; +} + +void CtsWaitCmd::addOption(boost::program_options::options_description& desc) const { + desc.add_options()(CtsWaitCmd::arg(), po::value(), CtsWaitCmd::desc()); +} +void CtsWaitCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { + std::string expression = vm[arg()].as(); + + if (clientEnv->debug()) + cout << " CtsWaitCmd::create " << CtsWaitCmd::arg() << " task_path(" << clientEnv->task_path() << ") password(" + << clientEnv->jobs_password() << ") remote_id(" << clientEnv->process_or_remote_id() << ") try_no(" + << clientEnv->task_try_no() << ") expression(" << expression << ")\n"; + + std::string errorMsg; + if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { + throw std::runtime_error("CtsWaitCmd: " + errorMsg); + } + + cmd = std::make_shared(clientEnv->task_path(), + clientEnv->jobs_password(), + clientEnv->process_or_remote_id(), + clientEnv->task_try_no(), + expression); +} + +std::ostream& operator<<(std::ostream& os, const CtsWaitCmd& c) { + std::string ret; + c.print(ret); + os << ret; + return os; +} + +CEREAL_REGISTER_TYPE(CtsWaitCmd) +CEREAL_REGISTER_DYNAMIC_INIT(CtsWaitCmd) diff --git a/Base/src/ecflow/base/cts/task/CtsWaitCmd.hpp b/Base/src/ecflow/base/cts/task/CtsWaitCmd.hpp new file mode 100644 index 000000000..a2d407c62 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/CtsWaitCmd.hpp @@ -0,0 +1,56 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_task_CtsWaitCmd_HPP +#define ecflow_base_cts_task_CtsWaitCmd_HPP + +#include "ecflow/base/cts/task/TaskCmd.hpp" + +/// A child command that evaluates a expression. If the expression is false. +/// Then client invoker will block. +class CtsWaitCmd final : public TaskCmd { +public: + CtsWaitCmd(const std::string& pathToTask, + const std::string& jobsPassword, + const std::string& process_or_remote_id, + int try_no, + const std::string& expression); + CtsWaitCmd() : TaskCmd() {} + + const std::string& expression() const { return expression_; } + + void print(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + ecf::Child::CmdType child_type() const override { return ecf::Child::WAIT; } + + std::string expression_; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(expression_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const CtsWaitCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(CtsWaitCmd) + +#endif /* ecflow_base_cts_task_CtsWaitCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/task/EventCmd.cpp b/Base/src/ecflow/base/cts/task/EventCmd.cpp new file mode 100644 index 000000000..22edb8762 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/EventCmd.cpp @@ -0,0 +1,141 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "ecflow/base/cts/task/EventCmd.hpp" + +#include + +#include "ecflow/base/AbstractClientEnv.hpp" +#include "ecflow/base/AbstractServer.hpp" +#include "ecflow/base/cts/task/TaskApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" +#include "ecflow/core/Log.hpp" +#include "ecflow/core/Str.hpp" +#include "ecflow/node/Submittable.hpp" +#include "ecflow/node/SuiteChanged.hpp" + +using namespace ecf; +using namespace std; +using namespace boost; +namespace po = boost::program_options; + +bool EventCmd::equals(ClientToServerCmd* rhs) const { + auto* the_rhs = dynamic_cast(rhs); + if (!the_rhs) + return false; + if (name_ != the_rhs->name()) + return false; + if (value_ != the_rhs->value()) + return false; + return TaskCmd::equals(rhs); +} + +void EventCmd::print(std::string& os) const { + os += Str::CHILD_CMD(); + os += "event "; + os += name_; + os += " "; + if (value_) + os += "1 "; + else + os += "0 "; + os += path_to_node(); +} + +STC_Cmd_ptr EventCmd::doHandleRequest(AbstractServer* as) const { + as->update_stats().task_event_++; + + { // update suite change numbers before job submission, task_ setup during authentication + SuiteChanged1 changed(submittable_->suite()); + + // The name could either be "string" or an integer either way it should be unique + if (!submittable_->set_event(name_, value_)) { + std::string ss; + ss = "Event request failed as event '"; + ss += name_; + ss += "' does not exist on task "; + ss += path_to_node(); + ecf::log(Log::ERR, ss); + return PreAllocatedReply::ok_cmd(); + } + } + + // Do job submission in case any triggers dependent on events + as->increment_job_generation_count(); + return PreAllocatedReply::ok_cmd(); +} + +const char* EventCmd::arg() { + return TaskApi::eventArg(); +} +const char* EventCmd::desc() { + return "Change event. For use in the '.ecf' script file *only*\n" + "Hence the context is supplied via environment variables\n" + " arg1(string | int) = event-name\n\n" + " arg2(string)(optional) = [ set | clear] default value is set\n\n" + "If this child command is a zombie, then the default action will be to *fob*,\n" + "i.e allow the ecflow client command to complete without an error\n" + "The default can be overridden by using zombie attributes.\n\n" + "Usage:\n" + " ecflow_client --event=ev # set the event, default since event initial value is clear\n" + " ecflow_client --event=ev set # set the event, explicit\n" + " ecflow_client --event=ev clear # clear the event, use when event initial value is set\n"; +} + +void EventCmd::addOption(boost::program_options::options_description& desc) const { + desc.add_options()(EventCmd::arg(), po::value>()->multitoken(), EventCmd::desc()); +} +void EventCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { + vector args = vm[arg()].as>(); + std::string event; + if (args.size() >= 1) + event = args[0]; + + bool value = true; + if (args.size() == 2) { + if (args[1] == "set") + value = true; + else if (args[1] == "clear") + value = false; + else { + std::stringstream ss; + ss << "EventCmd: The second argument must be [ set | clear ] but found " << args[1]; + throw std::runtime_error(ss.str()); + } + } + + if (clientEnv->debug()) + cout << " EventCmd::create " << EventCmd::arg() << " task_path(" << clientEnv->task_path() << ") password(" + << clientEnv->jobs_password() << ") remote_id(" << clientEnv->process_or_remote_id() << ") try_no(" + << clientEnv->task_try_no() << ") event(" << event << ")" + << ") value(" << value << ")\n"; + + std::string errorMsg; + if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { + throw std::runtime_error("EventCmd: " + errorMsg); + } + + cmd = std::make_shared(clientEnv->task_path(), + clientEnv->jobs_password(), + clientEnv->process_or_remote_id(), + clientEnv->task_try_no(), + event, + value); +} + +std::ostream& operator<<(std::ostream& os, const EventCmd& c) { + std::string ret; + c.print(ret); + os << ret; + return os; +} + +CEREAL_REGISTER_TYPE(EventCmd) +CEREAL_REGISTER_DYNAMIC_INIT(EventCmd) diff --git a/Base/src/ecflow/base/cts/task/EventCmd.hpp b/Base/src/ecflow/base/cts/task/EventCmd.hpp new file mode 100644 index 000000000..a99d8012d --- /dev/null +++ b/Base/src/ecflow/base/cts/task/EventCmd.hpp @@ -0,0 +1,63 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_task_EventCmd_HPP +#define ecflow_base_cts_task_EventCmd_HPP + +#include "ecflow/base/cts/task/TaskCmd.hpp" +#include "ecflow/core/cereal_optional_nvp.hpp" + +class EventCmd final : public TaskCmd { +public: + EventCmd(const std::string& pathToTask, + const std::string& jobsPassword, + const std::string& process_or_remote_id, + int try_no, + const std::string& eventName, + bool value = true) // true = set(default), false = clear + : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), + name_(eventName), + value_(value) {} + EventCmd() : TaskCmd() {} + + const std::string& name() const { return name_; } + bool value() const { return value_; } + + void print(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + ecf::Child::CmdType child_type() const override { return ecf::Child::EVENT; } + +private: + std::string name_; // the events name + bool value_{true}; // true = set(default), false = clear + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(name_)); + CEREAL_OPTIONAL_NVP(ar, value_, [this]() { return !value_; }); // conditionally save if value is false + } +}; + +std::ostream& operator<<(std::ostream& os, const EventCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(EventCmd) + +#endif /* ecflow_base_cts_task_EventCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/task/InitCmd.cpp b/Base/src/ecflow/base/cts/task/InitCmd.cpp new file mode 100644 index 000000000..b0dbada26 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/InitCmd.cpp @@ -0,0 +1,154 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "ecflow/base/cts/task/InitCmd.hpp" + +#include + +#include + +#include "ecflow/base/AbstractClientEnv.hpp" +#include "ecflow/base/AbstractServer.hpp" +#include "ecflow/base/cts/task/TaskApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" +#include "ecflow/core/Str.hpp" +#include "ecflow/node/Submittable.hpp" +#include "ecflow/node/SuiteChanged.hpp" + +using namespace ecf; +using namespace std; +using namespace boost; +namespace po = boost::program_options; + +void InitCmd::print(std::string& os) const { + os += Str::CHILD_CMD(); + os += "init "; + os += path_to_node(); + if (!var_to_add_.empty()) { + os += " --add"; + for (const auto& var_to_add : var_to_add_) { + os += " "; + os += var_to_add.name(); + os += "="; + os += var_to_add.theValue(); + } + } +} + +bool InitCmd::equals(ClientToServerCmd* rhs) const { + auto* the_rhs = dynamic_cast(rhs); + if (!the_rhs) + return false; + if (var_to_add_ != the_rhs->variables_to_add()) + return false; + return TaskCmd::equals(rhs); +} + +STC_Cmd_ptr InitCmd::doHandleRequest(AbstractServer* as) const { + as->update_stats().task_init_++; + + { // update suite change numbers before job submission. submittable_ setup during authentication + SuiteChanged1 changed(submittable_->suite()); + submittable_->init(process_or_remote_id()); // will set task->set_state(NState::ACTIVE); + + for (const auto& var_to_add : var_to_add_) { + submittable_->addVariable(var_to_add); // will update or add variable + } + } + + // Do job submission in case any triggers dependent on NState::ACTIVE + as->increment_job_generation_count(); + return PreAllocatedReply::ok_cmd(); +} + +const char* InitCmd::arg() { + return TaskApi::initArg(); +} +const char* InitCmd::desc() { + return "Mark task as started(active). For use in the '.ecf' script file *only*\n" + "Hence the context is supplied via environment variables.\n" + " arg1(string) = process_or_remote_id The process id of the job or remote_id\n" + " Using remote id allows the jobs to be killed\n" + " arg2(--add)(optional)= add/update variables as name value pairs\n\n" + "If this child command is a zombie, then the default action will be to *block*.\n" + "The default can be overridden by using zombie attributes.\n" + "Otherwise the blocking period is defined by ECF_TIMEOUT.\n\n" + "Usage:\n" + " ecflow_client --init=$$\n" + " ecflow_client --init=$$ --add name=value name2=value2 # add/update variables to task"; +} + +void InitCmd::addOption(boost::program_options::options_description& desc) const { + desc.add_options()(InitCmd::arg(), po::value(), InitCmd::desc())( + "add", + po::value>()->multitoken(), + "Add variables e.g. name1=value1 name2=value2. Can only be used in combination with --init command."); +} + +void InitCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { + std::string process_or_remote_id = vm[arg()].as(); + + if (clientEnv->debug()) + cout << " InitCmd::create " << InitCmd::arg() << " clientEnv->task_path(" << clientEnv->task_path() + << ") clientEnv->jobs_password(" << clientEnv->jobs_password() << ") clientEnv->process_or_remote_id(" + << clientEnv->process_or_remote_id() << ") clientEnv->task_try_no(" << clientEnv->task_try_no() + << ") process_or_remote_id(" << process_or_remote_id << ") clientEnv->under_test(" + << clientEnv->under_test() << ")\n"; + + std::string errorMsg; + if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { + throw std::runtime_error("InitCmd: " + errorMsg); + } + + /// if ECF_RID is specified then it *MUST* be the same as input argument + /// On cca we ECF_RID can be specified under test, and therefore fail this check, hence we use + /// clientEnv->under_test() + if (!clientEnv->under_test() && !clientEnv->process_or_remote_id().empty() && + clientEnv->process_or_remote_id() != process_or_remote_id) { + std::stringstream ss; + ss << "remote id(" << process_or_remote_id + << ") passed as an argument, not the same the client environment ECF_RID(" + << clientEnv->process_or_remote_id() << ")"; + throw std::runtime_error(ss.str()); + } + + std::vector variable_vec; + if (vm.count("add")) { + vector var_args = vm["add"].as>(); + if (!var_args.empty()) { + variable_vec.reserve(var_args.size()); + for (const auto& v : var_args) { + std::vector tokens; + Str::split(v, tokens, "="); + if (tokens.size() != 2) { + throw std::runtime_error( + "Could not parse variable provided to --add; Expected var1=value1 var2=value2 but found " + v); + } + variable_vec.emplace_back(tokens[0], tokens[1]); + } + } + } + + cmd = std::make_shared(clientEnv->task_path(), + clientEnv->jobs_password(), + process_or_remote_id, + clientEnv->task_try_no(), + variable_vec); +} + +std::ostream& operator<<(std::ostream& os, const InitCmd& c) { + std::string ret; + c.print(ret); + os << ret; + return os; +} + +CEREAL_REGISTER_TYPE(InitCmd) +CEREAL_REGISTER_DYNAMIC_INIT(InitCmd) diff --git a/Base/src/ecflow/base/cts/task/InitCmd.hpp b/Base/src/ecflow/base/cts/task/InitCmd.hpp new file mode 100644 index 000000000..31242a6d4 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/InitCmd.hpp @@ -0,0 +1,60 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_task_InitCmd_HPP +#define ecflow_base_cts_task_InitCmd_HPP + +#include "ecflow/base/cts/task/TaskCmd.hpp" +#include "ecflow/core/cereal_optional_nvp.hpp" + +class InitCmd final : public TaskCmd { +public: + InitCmd(const std::string& pathToTask, + const std::string& jobsPassword, + const std::string& process_or_remote_id, + int try_no, + const std::vector& vec = {}) + : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), + var_to_add_(vec) {} + + InitCmd() : TaskCmd() {} + + const std::vector& variables_to_add() const { return var_to_add_; } + + void print(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + ecf::Child::CmdType child_type() const override { return ecf::Child::INIT; } + +private: + std::vector var_to_add_; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this)); + CEREAL_OPTIONAL_NVP(ar, var_to_add_, [this]() { return !var_to_add_.empty(); }); // conditionally save + } +}; + +std::ostream& operator<<(std::ostream& os, const InitCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(InitCmd) + +#endif /* ecflow_base_cts_task_InitCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/task/LabelCmd.cpp b/Base/src/ecflow/base/cts/task/LabelCmd.cpp new file mode 100644 index 000000000..4f4f49f61 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/LabelCmd.cpp @@ -0,0 +1,138 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "ecflow/base/cts/task/LabelCmd.hpp" + +#include + +#include "ecflow/base/AbstractClientEnv.hpp" +#include "ecflow/base/AbstractServer.hpp" +#include "ecflow/base/cts/task/TaskApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" +#include "ecflow/core/Log.hpp" +#include "ecflow/core/Str.hpp" +#include "ecflow/node/Submittable.hpp" +#include "ecflow/node/SuiteChanged.hpp" + +using namespace ecf; +using namespace std; +using namespace boost; +namespace po = boost::program_options; + +bool LabelCmd::equals(ClientToServerCmd* rhs) const { + auto* the_rhs = dynamic_cast(rhs); + if (!the_rhs) + return false; + if (name_ != the_rhs->name()) + return false; + if (label_ != the_rhs->label()) + return false; + return TaskCmd::equals(rhs); +} + +void LabelCmd::print(std::string& os) const { + os += Str::CHILD_CMD(); + os += "label "; + os += name_; + os += " '"; + os += label_; + os += "' "; + os += path_to_node(); +} + +STC_Cmd_ptr LabelCmd::doHandleRequest(AbstractServer* as) const { + as->update_stats().task_label_++; + + assert(isWrite()); // isWrite used in handleRequest() to control check pointing + + // submittable_ setup during authentication + if (submittable_->findLabel(name_)) { + + SuiteChanged1 changed(submittable_->suite()); + submittable_->changeLabel(name_, label_); + } + // else { + // // ECFLOW-175, avoid filling up log file. Can get thousands of these messages, especially form MARS + // std::string ss; + // ss = "Label request failed as label '"; ss += name_; ss += "' does not exist on task "; ss += path_to_node(); + // ecf::log(Log::ERR,ss); + //} + + // Note: reclaiming memory for label_ earlier make *no* difference to performance of server + + return PreAllocatedReply::ok_cmd(); +} + +const char* LabelCmd::arg() { + return TaskApi::labelArg(); +} +const char* LabelCmd::desc() { + return "Change Label. For use in the '.ecf' script file *only*\n" + "Hence the context is supplied via environment variables\n" + " arg1 = label-name\n" + " arg2 = The new label value\n" + " The labels values can be single or multi-line(space separated quoted strings)\n\n" + "If this child command is a zombie, then the default action will be to *fob*,\n" + "i.e allow the ecflow client command to complete without an error\n" + "The default can be overridden by using zombie attributes.\n\n" + "Usage:\n" + " ecflow_client --label=progressed merlin"; +} + +void LabelCmd::addOption(boost::program_options::options_description& desc) const { + desc.add_options()(LabelCmd::arg(), po::value>()->multitoken(), LabelCmd::desc()); +} +void LabelCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { + vector args = vm[arg()].as>(); + + if (clientEnv->debug()) { + dumpVecArgs(LabelCmd::arg(), args); + cout << " LabelCmd::create " << LabelCmd::arg() << " task_path(" << clientEnv->task_path() << ") password(" + << clientEnv->jobs_password() << ") remote_id(" << clientEnv->process_or_remote_id() << ") try_no(" + << clientEnv->task_try_no() << ")\n"; + } + + if (args.size() < 2) { + std::stringstream ss; + ss << "LabelCmd: At least 2 arguments expected. Please specify: \n"; + throw std::runtime_error(ss.str()); + } + + std::string labelName = args[0]; + args.erase(args.begin()); // remove name from vector of strings + std::string labelValue; + for (size_t i = 0; i < args.size(); i++) { + if (i != 0) + labelValue += " "; + labelValue += args[i]; + } + + std::string errorMsg; + if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { + throw std::runtime_error("LabelCmd: " + errorMsg); + } + + cmd = std::make_shared(clientEnv->task_path(), + clientEnv->jobs_password(), + clientEnv->process_or_remote_id(), + clientEnv->task_try_no(), + labelName, + labelValue); +} + +std::ostream& operator<<(std::ostream& os, const LabelCmd& c) { + std::string ret; + c.print(ret); + os << ret; + return os; +} + +CEREAL_REGISTER_TYPE(LabelCmd) +CEREAL_REGISTER_DYNAMIC_INIT(LabelCmd) diff --git a/Base/src/ecflow/base/cts/task/LabelCmd.hpp b/Base/src/ecflow/base/cts/task/LabelCmd.hpp new file mode 100644 index 000000000..dca2045a3 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/LabelCmd.hpp @@ -0,0 +1,61 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_task_LabelCmd_HPP +#define ecflow_base_cts_task_LabelCmd_HPP + +#include "ecflow/base/cts/task/TaskCmd.hpp" + +class LabelCmd final : public TaskCmd { +public: + LabelCmd(const std::string& pathToTask, + const std::string& jobsPassword, + const std::string& process_or_remote_id, + int try_no, + const std::string& name, + const std::string& label) + : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), + name_(name), + label_(label) {} + LabelCmd() : TaskCmd() {} + + const std::string& name() const { return name_; } + const std::string& label() const { return label_; } + + void print(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + ecf::Child::CmdType child_type() const override { return ecf::Child::LABEL; } + +private: + std::string name_; // the label name + std::string label_; // a single label, or multi-line label + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(name_), CEREAL_NVP(label_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const LabelCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(LabelCmd) + +#endif /* ecflow_base_cts_task_LabelCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/task/MeterCmd.cpp b/Base/src/ecflow/base/cts/task/MeterCmd.cpp new file mode 100644 index 000000000..963e43b0f --- /dev/null +++ b/Base/src/ecflow/base/cts/task/MeterCmd.cpp @@ -0,0 +1,155 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "ecflow/base/cts/task/MeterCmd.hpp" + +#include + +#include "ecflow/base/AbstractClientEnv.hpp" +#include "ecflow/base/AbstractServer.hpp" +#include "ecflow/base/cts/task/TaskApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" +#include "ecflow/core/Converter.hpp" +#include "ecflow/core/Log.hpp" +#include "ecflow/core/Str.hpp" +#include "ecflow/node/Submittable.hpp" +#include "ecflow/node/SuiteChanged.hpp" + +using namespace ecf; +using namespace std; +using namespace boost; +namespace po = boost::program_options; + +bool MeterCmd::equals(ClientToServerCmd* rhs) const { + auto* the_rhs = dynamic_cast(rhs); + if (!the_rhs) + return false; + if (name_ != the_rhs->name()) + return false; + if (value_ != the_rhs->value()) + return false; + return TaskCmd::equals(rhs); +} + +void MeterCmd::print(std::string& os) const { + os += Str::CHILD_CMD(); + os += "meter "; + os += name_; + os += " "; + os += ecf::convert_to(value_); + os += " "; + os += path_to_node(); +} + +STC_Cmd_ptr MeterCmd::doHandleRequest(AbstractServer* as) const { + as->update_stats().task_meter_++; + + { // Added scope for SuiteChanged1 changed: i.e update suite change numbers before job submission + // submittable_ setup during authentication + SuiteChanged1 changed(submittable_->suite()); + + /// Allow meter to set any valid value that is in range because: + /// - When we have a network failure, and restoration. The meter tasks, will come in random, order. + /// - When task is executed without a requee the meter value will less than maximum + /// + /// This has *IMPLICATION*, if the meter is used in a trigger, using a equality + /// operator, then the trigger will always hold. hence suite designers need to + /// aware of this. + try { + + Meter& the_meter = submittable_->find_meter(name_); + if (the_meter.empty()) { + LOG(Log::ERR, + "MeterCmd::doHandleRequest: failed as meter '" << name_ << "' does not exist on task " + << path_to_node()); + return PreAllocatedReply::ok_cmd(); + } + + /// Invalid meter values(out or range) will raise exceptions. + /// Just ignore the request rather than failing client cmd + the_meter.set_value(value_); + } + catch (std::exception& e) { + LOG(Log::ERR, "MeterCmd::doHandleRequest: failed for task " << path_to_node() << ". " << e.what()); + return PreAllocatedReply::ok_cmd(); + } + } + + // Do job submission in case any triggers dependent on meters + as->increment_job_generation_count(); + return PreAllocatedReply::ok_cmd(); +} + +const char* MeterCmd::arg() { + return TaskApi::meterArg(); +} +const char* MeterCmd::desc() { + return "Change meter. For use in the '.ecf' script file *only*\n" + "Hence the context is supplied via environment variables\n" + " arg1(string) = meter-name\n" + " arg2(int) = the new meter value\n\n" + "If this child command is a zombie, then the default action will be to *fob*,\n" + "i.e allow the ecflow client command to complete without an error\n" + "The default can be overridden by using zombie attributes.\n\n" + "Usage:\n" + " ecflow_client --meter=my_meter 20"; +} + +void MeterCmd::addOption(boost::program_options::options_description& desc) const { + desc.add_options()(MeterCmd::arg(), po::value>()->multitoken(), MeterCmd::desc()); +} +void MeterCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { + vector args = vm[arg()].as>(); + + if (clientEnv->debug()) { + dumpVecArgs(MeterCmd::arg(), args); + cout << " MeterCmd::create " << MeterCmd::arg() << " task_path(" << clientEnv->task_path() << ") password(" + << clientEnv->jobs_password() << ") remote_id(" << clientEnv->process_or_remote_id() << ") try_no(" + << clientEnv->task_try_no() << ")\n"; + } + + if (args.size() != 2) { + std::stringstream ss; + ss << "MeterCmd: Two arguments expected, found " << args.size() + << " Please specify , ie --meter=name 100\n"; + throw std::runtime_error(ss.str()); + } + + int value = 0; + try { + std::string strVal = args[1]; + value = ecf::convert_to(strVal); + } + catch (const ecf::bad_conversion&) { + throw std::runtime_error("MeterCmd: Second argument must be a integer, i.e. --meter=name 100\n"); + } + + std::string errorMsg; + if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { + throw std::runtime_error("MeterCmd: " + errorMsg); + } + + cmd = std::make_shared(clientEnv->task_path(), + clientEnv->jobs_password(), + clientEnv->process_or_remote_id(), + clientEnv->task_try_no(), + args[0], + value); +} + +std::ostream& operator<<(std::ostream& os, const MeterCmd& c) { + std::string ret; + c.print(ret); + os << ret; + return os; +} + +CEREAL_REGISTER_TYPE(MeterCmd) +CEREAL_REGISTER_DYNAMIC_INIT(MeterCmd) diff --git a/Base/src/ecflow/base/cts/task/MeterCmd.hpp b/Base/src/ecflow/base/cts/task/MeterCmd.hpp new file mode 100644 index 000000000..dd5bdd225 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/MeterCmd.hpp @@ -0,0 +1,61 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_task_MeterCmd_HPP +#define ecflow_base_cts_task_MeterCmd_HPP + +#include "ecflow/base/cts/task/TaskCmd.hpp" + +class MeterCmd final : public TaskCmd { +public: + MeterCmd(const std::string& pathToTask, + const std::string& jobsPassword, + const std::string& process_or_remote_id, + int try_no, + const std::string& meterName, + int meterValue) + : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), + name_(meterName), + value_(meterValue) {} + MeterCmd() : TaskCmd() {} + + const std::string& name() const { return name_; } + int value() const { return value_; } + + void print(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + ecf::Child::CmdType child_type() const override { return ecf::Child::METER; } + +private: + std::string name_; // the meters name + int value_{0}; // the meters value + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(name_), CEREAL_NVP(value_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const MeterCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(MeterCmd) + +#endif /* ecflow_base_cts_task_MeterCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/task/QueueCmd.cpp b/Base/src/ecflow/base/cts/task/QueueCmd.cpp new file mode 100644 index 000000000..e4fbc9de1 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/QueueCmd.cpp @@ -0,0 +1,292 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "ecflow/base/cts/task/QueueCmd.hpp" + +#include + +#include "ecflow/attribute/QueueAttr.hpp" +#include "ecflow/base/AbstractClientEnv.hpp" +#include "ecflow/base/AbstractServer.hpp" +#include "ecflow/base/cts/task/TaskApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" +#include "ecflow/core/Log.hpp" +#include "ecflow/core/Str.hpp" +#include "ecflow/node/Defs.hpp" +#include "ecflow/node/Submittable.hpp" +#include "ecflow/node/SuiteChanged.hpp" + +using namespace ecf; +using namespace std; +using namespace boost; +namespace po = boost::program_options; + +bool QueueCmd::equals(ClientToServerCmd* rhs) const { + auto* the_rhs = dynamic_cast(rhs); + if (!the_rhs) + return false; + if (name_ != the_rhs->name()) + return false; + if (action_ != the_rhs->action()) + return false; + if (step_ != the_rhs->step()) + return false; + if (path_to_node_with_queue_ != the_rhs->path_to_node_with_queue()) + return false; + return TaskCmd::equals(rhs); +} + +void QueueCmd::print(std::string& os) const { + os += Str::CHILD_CMD(); + os += TaskApi::queue_arg(); + os += " "; + os += name_; + os += " "; + os += action_; + os += " "; + os += step_; + os += " "; + if (path_to_node_with_queue_.empty()) { + os += path_to_node(); + return; + } + + os += path_to_node_with_queue_; + os += " "; + os += path_to_node(); +} + +STC_Cmd_ptr QueueCmd::doHandleRequest(AbstractServer* as) const { + as->update_stats().task_queue_++; + std::string result; + + ////////////////////////////////////////////////////////////////////////////// + // Return the current string value, and then increment the index + ////////////////////////////////////////////////////////////////////////////// + { // Added scope for SuiteChanged1 changed: i.e update suite change numbers before job submission + // submittable_ setup during authentication + SuiteChanged1 changed(submittable_->suite()); + + if (!path_to_node_with_queue_.empty()) { + Defs* defs = submittable_->defs(); + if (defs) { + node_ptr node_with_queue = defs->findAbsNode(path_to_node_with_queue_); + if (node_with_queue) { + + QueueAttr& queue_attr = node_with_queue->findQueue(name_); + if (queue_attr.empty()) { + std::stringstream ss; + ss << "QueueCmd:: Could not find queue of name " << name_ << ", on input node " + << path_to_node_with_queue_; + return PreAllocatedReply::error_cmd(ss.str()); + } + + result = handle_queue(queue_attr); + } + else { + std::stringstream ss; + ss << "QueueCmd:: Could not find node at path " << path_to_node_with_queue_; + return PreAllocatedReply::error_cmd(ss.str()); + } + } + } + else { + bool fnd_queue = false; + QueueAttr& queue_attr = submittable_->findQueue(name_); + if (queue_attr.empty()) { + Node* parent = submittable_->parent(); + while (parent) { + QueueAttr& queue_attr1 = parent->findQueue(name_); + if (!queue_attr1.empty()) { + fnd_queue = true; + result = handle_queue(queue_attr1); + break; + } + parent = parent->parent(); + } + } + else { + fnd_queue = true; + result = handle_queue(queue_attr); + } + + if (!fnd_queue) { + std::stringstream ss; + ss << "QueueCmd:: Could not find queue " << name_ << " Up the node hierarchy"; + return PreAllocatedReply::error_cmd(ss.str()); + } + } + } + + // Do job submission in case any triggers dependent on QueueAttr + as->increment_job_generation_count(); + + if (result.empty()) + return PreAllocatedReply::ok_cmd(); + return PreAllocatedReply::string_cmd(result); +} + +std::string QueueCmd::handle_queue(QueueAttr& queue_attr) const { + if (queue_attr.empty()) { + std::stringstream ss; + ss << "QueueCmd:: Could not find queue of name " << name_ << " . Program error !"; + throw std::runtime_error(ss.str()); + } + + if (action_ == "active") + return queue_attr.active(); // return current index and value, make active, update index + if (action_ == "complete") + queue_attr.complete(step_); + if (action_ == "aborted") + queue_attr.aborted(step_); + if (action_ == "no_of_aborted") + return queue_attr.no_of_aborted(); + if (action_ == "reset") + queue_attr.reset_index_to_first_queued_or_aborted(); + + return std::string(); +} + +const char* QueueCmd::arg() { + return TaskApi::queue_arg(); +} +const char* QueueCmd::desc() { + return "QueueCmd. For use in the '.ecf' script file *only*\n" + "Hence the context is supplied via environment variables\n" + " arg1(string) = queue-name:\n" + " arg2(string) = action: [active | aborted | complete | no_of_aborted | reset ]\n" + " active: returns the first queued/aborted step, the return string is the queue value from the " + "definition\n" + " no_of_aborted: returns number of aborted steps as a string, i.e 10\n" + " reset: sets the index to the first queued/aborted step. Allows steps to be reprocessed for errors\n" + " arg2(string) = step: value returned from step=$(ecflow_client --queue=queue_name active)\n" + " This is only valid for complete and aborted steps\n" + " arg4(string) = path: (optional). The path where the queue is defined.\n" + " By default we search for the queue up the node tree.\n\n" + "If this child command is a zombie, then the default action will be to *block*,\n" + "The default can be overridden by using zombie attributes." + "If the path to the queue is not defined, then this command will\n" + "search for the queue up the node hierarchy. If no queue found, command fails\n\n" + "Usage:\n" + "step=\"\"\n" + "QNAME=\"my_queue_name\"\n" + "while [1 == 1 ] ; do\n" + " # this return the first queued/aborted step, then increments to next step, return when all steps " + "processed\n" + " step=$(ecflow_client --queue=$QNAME active) # of the form string i.e \"003\". this step is now active\n" + " if [[ $step == \"\" ]] ; then\n" + " break;\n" + " fi\n" + " ...\n" + " ecflow_client --queue=$QNAME complete $step # tell ecflow this step completed\n" + "done\n" + "\n" + "trap() { ecflow_client --queue=$QNAME aborted $step # tell ecflow this step failed }\n"; +} + +void QueueCmd::addOption(boost::program_options::options_description& desc) const { + desc.add_options()(QueueCmd::arg(), po::value>()->multitoken(), QueueCmd::desc()); +} +void QueueCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const { + vector args = vm[arg()].as>(); + + if (clientEnv->debug()) { + dumpVecArgs(QueueCmd::arg(), args); + cout << " QueueCmd::create " << QueueCmd::arg() << " task_path(" << clientEnv->task_path() << ") password(" + << clientEnv->jobs_password() << ") remote_id(" << clientEnv->process_or_remote_id() << ") try_no(" + << clientEnv->task_try_no() << ")\n"; + } + + // expect: + // [active | aborted | complete | no_of_aborted | reset ] step + std::string queue_name, step; + std::string path_to_node_with_queue, action; + for (size_t i = 0; i < args.size(); i++) { + if (i == 0) + queue_name = args[i]; + else { + if (args[i] == "active" || args[i] == "aborted" || args[i] == "complete" || args[i] == "no_of_aborted" || + args[i] == "reset") { + action = args[i]; + } + else if (args[i].find('/') != std::string::npos) + path_to_node_with_queue = args[i]; + else + step = args[i]; + } + } + if (clientEnv->debug()) { + cout << " QueueCmd::create " + << "queue-name:(" << queue_name << ") action:(" << action << ") step:(" << step + << ") path_to_node_with_queue:(" << path_to_node_with_queue << ")\n"; + } + + if (args.size() == 4 && path_to_node_with_queue.empty()) { + std::stringstream ss; + ss << "QueueCmd: The fourth argument if specified must provide a path to a node where the queue resides.\n" + << "No path specified. " << args[3]; + throw std::runtime_error(ss.str()); + } + + if (args.empty() || queue_name.empty() || action.empty()) { + std::stringstream ss; + ss << "QueueCmd: incorrect argument specified, expected at least two arguments but found " << args.size() + << " Please specify [active | aborted | complete | no_of_aborted | reset ] step (optional) i.e\n" + << "--queue=name active # active does not need a step\n" + << "--queue=name active /path/to/node/with/queue\n" + << "--queue=name aborted $step\n" + << "--queue=name complete $step\n" + << "--queue=name no_of_aborted\n" + << "--queue=name reset\n"; + throw std::runtime_error(ss.str()); + } + if ((action == "complete" || action == "aborted") && step.empty()) { + std::stringstream ss; + ss << "QueueCmd: when --queue=name complete || --queue=name aborted is used a step must be provided e.g.\n" + << " ecflow_client --queue=name aborted $step\n" + << " ecflow_client --queue=name complete $step\n" + << "where step is value returned from active, such as\n" + << " step=$(ecflow_client --queue=name active)\n"; + throw std::runtime_error(ss.str()); + } + if ((action == "active" || action == "reset" || action == "no_of_aborted") && !step.empty()) { + throw std::runtime_error("QueueCmd: step should not be used with active, reset or no_of_aborted."); + } + + string msg; + if (!Str::valid_name(queue_name, msg)) { + throw std::runtime_error("QueueCmd: Invalid queue name : " + msg); + } + + std::string errorMsg; + if (!clientEnv->checkTaskPathAndPassword(errorMsg)) { + throw std::runtime_error("QueueCmd: " + errorMsg); + } + + cmd = std::make_shared(clientEnv->task_path(), + clientEnv->jobs_password(), + clientEnv->process_or_remote_id(), + clientEnv->task_try_no(), + queue_name, + action, + step, + path_to_node_with_queue); +} + +std::ostream& operator<<(std::ostream& os, const QueueCmd& c) { + std::string ret; + c.print(ret); + os << ret; + return os; +} + +CEREAL_REGISTER_TYPE(QueueCmd) +CEREAL_REGISTER_DYNAMIC_INIT(QueueCmd) diff --git a/Base/src/ecflow/base/cts/task/QueueCmd.hpp b/Base/src/ecflow/base/cts/task/QueueCmd.hpp new file mode 100644 index 000000000..d7ece7974 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/QueueCmd.hpp @@ -0,0 +1,75 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_task_QueueCmd_HPP +#define ecflow_base_cts_task_QueueCmd_HPP + +#include "ecflow/base/cts/task/TaskCmd.hpp" + +class QueueAttr; + +class QueueCmd final : public TaskCmd { +public: + QueueCmd(const std::string& pathToTask, + const std::string& jobsPassword, + const std::string& process_or_remote_id, + int try_no, + const std::string& queueName, + const std::string& action, + const std::string& step = "", + const std::string& path_to_node_with_queue = "") // if empty search for queue up node tree + : TaskCmd(pathToTask, jobsPassword, process_or_remote_id, try_no), + name_(queueName), + action_(action), + step_(step), + path_to_node_with_queue_(path_to_node_with_queue) {} + QueueCmd() : TaskCmd() {} + + const std::string& name() const { return name_; } + const std::string& action() const { return action_; } + const std::string& step() const { return step_; } + const std::string& path_to_node_with_queue() const { return path_to_node_with_queue_; } + + void print(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + ecf::Child::CmdType child_type() const override { return ecf::Child::QUEUE; } + + std::string handle_queue(QueueAttr& queue_attr) const; + +private: + std::string name_; // the queue name + std::string action_; // [ active | aborted | complete | no_of_aborted ] + std::string step_; // will be empty when action is [ active | no_of_aborted] + std::string path_to_node_with_queue_; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), + CEREAL_NVP(name_), + CEREAL_NVP(action_), + CEREAL_NVP(step_), + CEREAL_NVP(path_to_node_with_queue_)); + } +}; + +CEREAL_FORCE_DYNAMIC_INIT(QueueCmd) + +#endif /* ecflow_base_cts_task_QueueCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/TaskApi.cpp b/Base/src/ecflow/base/cts/task/TaskApi.cpp similarity index 98% rename from Base/src/ecflow/base/cts/TaskApi.cpp rename to Base/src/ecflow/base/cts/task/TaskApi.cpp index 02c492e27..aa67c8ad3 100644 --- a/Base/src/ecflow/base/cts/TaskApi.cpp +++ b/Base/src/ecflow/base/cts/task/TaskApi.cpp @@ -8,7 +8,7 @@ * nor does it submit to any jurisdiction. */ -#include "ecflow/base/cts/TaskApi.hpp" +#include "ecflow/base/cts/task/TaskApi.hpp" std::string TaskApi::init(const std::string& process_id) { std::string ret = "--init="; diff --git a/Base/src/ecflow/base/cts/TaskApi.hpp b/Base/src/ecflow/base/cts/task/TaskApi.hpp similarity index 93% rename from Base/src/ecflow/base/cts/TaskApi.hpp rename to Base/src/ecflow/base/cts/task/TaskApi.hpp index f03a4b95c..5b53fb608 100644 --- a/Base/src/ecflow/base/cts/TaskApi.hpp +++ b/Base/src/ecflow/base/cts/task/TaskApi.hpp @@ -8,8 +8,8 @@ * nor does it submit to any jurisdiction. */ -#ifndef ecflow_base_cts_TaskApi_HPP -#define ecflow_base_cts_TaskApi_HPP +#ifndef ecflow_base_cts_task_TaskApi_HPP +#define ecflow_base_cts_task_TaskApi_HPP #include #include @@ -48,4 +48,4 @@ class TaskApi { static const char* waitArg(); }; -#endif /* ecflow_base_cts_TaskApi_HPP */ +#endif /* ecflow_base_cts_task_TaskApi_HPP */ diff --git a/Base/src/ecflow/base/cts/task/TaskCmd.cpp b/Base/src/ecflow/base/cts/task/TaskCmd.cpp new file mode 100644 index 000000000..61c17dc44 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/TaskCmd.cpp @@ -0,0 +1,330 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "ecflow/base/cts/task/TaskCmd.hpp" + +#include "ecflow/base/AbstractServer.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" +#include "ecflow/core/Log.hpp" +#include "ecflow/node/Defs.hpp" +#include "ecflow/node/Submittable.hpp" +#include "ecflow/node/SuiteChanged.hpp" + +using namespace ecf; +using namespace std; +using namespace boost; +namespace po = boost::program_options; + +// #define DEBUG_ZOMBIE 1 + +//////////////////////////////////////////////////////////////////////////////////////////////// +bool TaskCmd::equals(ClientToServerCmd* rhs) const { + auto* the_rhs = dynamic_cast(rhs); + if (!the_rhs) + return false; + if (path_to_submittable_ != the_rhs->path_to_node()) + return false; + if (jobs_password_ != the_rhs->jobs_password()) + return false; + if (process_or_remote_id_ != the_rhs->process_or_remote_id()) + return false; + if (try_no_ != the_rhs->try_no()) + return false; + return ClientToServerCmd::equals(rhs); +} + +// ********************************************************************************** +// IMPORTANT: In the current SMS/ECF only the init child command, passes the +// process_or_remote_id_, for *ALL* other child commands this is empty. +// The Automated tests get round this via setting ECF_RID in the header/tail job includes +// However since this may not be in .sms/.ecf files, we cannot rely on it +// Hence we need to be able to handle *EMPTY* process_or_remote_id_ for child commands +// +// Task State | Child | Log | Explanation +// ------------------------------------------------------------------------------------------------------------- +// SUBMITTED | child!=INIT | ERR | Script has child command in back ground, Bug in script, out of order +// ACTIVE | INIT & pid & passwd match | WAR | two init commands, overloaded server, or 2*init in script, *FOB*. +// Forgive ACTIVE | INIT & pid & passwd !match | ERR | two init commands, Task started again by user. COMPLETE | +// COMPLETE | WAR | two complete, zombie, overloaded server. *FOB*, Allow job to complete. Forgive. +// COMPLETE | child != COMPLETE | ERR | zombie +// ABORTED | ABORTED | WAR | two aborted, zombie, overloaded server. *FOB*, allow process to +// exit. Forgive ABORTED | child != ABORTED | WAR | zombie +// ------------------------------------------------------------------------------------------------------------------- +// +// zombie type | PID | password | explanation +// --------------------------------------------------------------------------------------------------------------- +// ECF_PID | X | matches | PID miss-match, but password matches. Job scheduled twice. Check submitter +// ECF_PID_PASSWD | X | X | Both PID and password miss-match. Re-queue & submit of active job +// ECF_PASSWD | matches | X | Password miss-match, PID matches, system has re-cycled PID or hacked job file? +// ECF | matches | matches | overloaded server,Two init commands or task complete or aborted but receives +// another child cmd USER | ? | ? | User initiated zombie whilst task was active or submitted, +// command is recorded in zombie PATH | n/a | n/a | Task not found. Nodes replaced whilst jobs were +// running +// ---------------------------------------------------------------------------------------------------------------- +bool TaskCmd::authenticate(AbstractServer* as, STC_Cmd_ptr& theReply) const { +#ifdef DEBUG_ZOMBIE + std::cout << " TaskCmd::authenticate " << Child::to_string(child_type()); + std::cout << " " << path_to_submittable_ << " " << process_or_remote_id_ << " " << jobs_password_ << " " << try_no_; + const Zombie& zombie = as->zombie_ctrl().find(path_to_submittable_, process_or_remote_id_, jobs_password_); + if (!zombie.empty()) + std::cout << " " << zombie; + else { + const Zombie& zombiep = as->zombie_ctrl().find_by_path_only(path_to_submittable_); + if (!zombiep.empty()) + std::cout << " find_by_path_only: " << zombiep; + } +#endif + /// *************************************************************************** + /// Task based cmd have their own authentication mechanism, hence we + /// Don't need to call the base class authenticate + /// ************************************************************************** + + if (!as->allowTaskCommunication()) { + // This is not an Error, hence we don't throw exception + theReply = PreAllocatedReply::block_client_server_halted_cmd(); + return false; + } + + submittable_ = get_submittable(as); + if (!submittable_) { +#ifdef DEBUG_ZOMBIE + std::cout << ": PATH Zombie\n"; +#endif + // Create path zombie, if not already created: + std::string action_taken; + static_cast(as->zombie_ctrl().handle_path_zombie(as, this, action_taken, theReply)); + + // distinguish output by using *path* + std::stringstream ss; + ss << " zombie(*path*) : chd:" << ecf::Child::to_string(child_type()) << " : " << path_to_submittable_ << " : " + << process_or_remote_id_ << " : " << jobs_password_ << " : action(" << action_taken << ")"; + log(Log::ERR, ss.str()); + return false; + } + + // If the CMD *WAS* created with Submittable::DUMMY_JOBS_PASSWORD then we were testing + // This will be copied from client to server, hence we want to avoid same check in the + // server. when handleRequest is called() + // DO NOT place #ifdef DEBUG otherwise test will start failing for the release build + if (jobs_password_ == Submittable::DUMMY_JOBS_PASSWORD()) { + return true; + } + + SuiteChanged1 changed(submittable_->suite()); + + /// Check if User wants to explicitly bypass password checking + /// This can be done via AlterCmd by adding a variable on the task, ECF_PASS with value + /// Submittable::FREE_JOBS_PASSWORD Note: this *does not* look for the variable up the node tree, only on the task. + std::string ecf_pass_value; + if (submittable_->findVariableValue(Str::ECF_PASS(), ecf_pass_value)) { + + if (ecf_pass_value == Submittable::FREE_JOBS_PASSWORD()) { + submittable_->flag().clear(ecf::Flag::ZOMBIE); + return true; + } + } + + /// Handle corner case ,where we have two jobs with different process id, but same password + /// Can happen if jobs is started externally, + /// or via test(i.e submit 1,submit 2(force)) before job1 active its password is overridden + bool submittable_allready_aborted = false; + bool submittable_allready_active = false; + bool submittable_allready_complete = false; + password_missmatch_ = false; + pid_missmatch_ = false; + + /// *** In complete state, the password is empty. + if (submittable_->jobsPassword() != jobs_password_) { +#ifdef DEBUG_ZOMBIE + std::cout << ": submittable pass(" << submittable_->jobsPassword() << ") != jobs_password_(" << jobs_password_ + << ")"; +#endif + password_missmatch_ = true; + } + + /// When state is in SUBMITTED, its process/remote_id is EMPTY. It will be set by the INIT child command. + /// Hence we can *NOT* mark it as pid_missmatch. + /// + /// *** See Note above: Not all child commands pass a process_id. *** + /// *** Hence this test for zombies is ONLY valid if process sets the process_or_remote_id_ **** + /// *** User should really set ECF_RID in the scripts + /// *** In submitted state, the pid is empty. hence clear pid_missmatch_ below + /// *** In complete state, the pid is empty. hence clear pid_missmatch_ below + if (!submittable_->process_or_remote_id().empty() && !process_or_remote_id_.empty() && + submittable_->process_or_remote_id() != process_or_remote_id_) { +#ifdef DEBUG_ZOMBIE + std::cout << ":task pid(" << submittable_->process_or_remote_id() << ") != process pid(" + << process_or_remote_id_ << ")"; +#endif + pid_missmatch_ = true; + } + + switch (submittable_->state()) { + case NState::SUBMITTED: { + // The pid on the task will be empty + if (child_type() != Child::INIT) { + std::stringstream ss; + ss << path_to_submittable_ + << " When a node is submitted, expected next child command to be INIT but received " + << Child::to_string(child_type()); + log(Log::ERR, ss.str()); + } + break; + } + + case NState::ACTIVE: { + if (child_type() == Child::INIT) { +#ifdef DEBUG_ZOMBIE + std::cout << ":(child_type() == Child::INIT) && submittable_->state() == NState::ACTIVE)"; +#endif + // *IF* password and pid matches be more forgiving. How can this case arise: + // i.e server is heavily overloaded, client calls init, which times out because server is busy + // Client then sends init again. In this case rather than treating it as a zombie, we will let it + // through providing the password and pid matches. + if (!password_missmatch_ && !pid_missmatch_) { + string ret = " [ overloaded || --init*2 ](pid and passwd match) : chd:"; + ret += ecf::Child::to_string(child_type()); + ret += " : "; + ret += path_to_submittable_; + ret += " : already active : action(fob)"; + log(Log::WAR, ret); + theReply = PreAllocatedReply::ok_cmd(); + return false; + } + submittable_allready_active = true; + } + break; + } + + case NState::COMPLETE: { +#ifdef DEBUG_ZOMBIE + std::cout << ": submittable_->state() == NState::COMPLETE)"; +#endif + if (child_type() == Child::COMPLETE) { + // Note: when a node completes, we clear tasks password and pid, to save memory on checkpt & network + // bandwidth (We could choose not to clear, This would allow us to disambiguate between 2/ and 3/ + // below). HOWEVER: + // + // How can this situation arise: + // 1/ Two calls to --complete (rare) + // 2/ Overloaded server. Client send --complete to server, but it is overload and does not respond, + // the client then + // times out. Server handles the request. When client tries again we get here. (possible) + // 3/ Zombie, two separate process. (possible, typically done by user action) + // + // For all three it should be safe to just fob: + // 1/ Two calls to --complete # Be forgiving + // 2/ Overloaded server # The correct course of action + // 3/ zombie # The zombie has completed anyway, don't bother blocking it + + submittable_->flag().clear(ecf::Flag::ZOMBIE); + as->zombie_ctrl().remove_by_path(path_to_submittable_); + + string ret = " [ overloaded || zombie || --complete*2 ] : chd:"; + ret += ecf::Child::to_string(child_type()); + ret += " : "; + ret += path_to_submittable_; + ret += " : already complete : action(fob)"; + log(Log::WAR, ret); + theReply = PreAllocatedReply::ok_cmd(); + return false; + } + + // If Task state is complete, and we receive **any** child command then it is a zombie + submittable_allready_complete = true; + password_missmatch_ = false; + pid_missmatch_ = false; + break; + } + + case NState::ABORTED: { +#ifdef DEBUG_ZOMBIE + std::cout << ": submittable_->state() == NState::ABORTED)"; +#endif + + if (child_type() == Child::ABORT) { + + if (!password_missmatch_ && !pid_missmatch_) { + + as->zombie_ctrl().remove(submittable_); + + string ret = " [ overloaded || --abort*2 ] (pid and passwd match) : chd:"; + ret += ecf::Child::to_string(child_type()); + ret += " : "; + ret += path_to_submittable_; + ret += " : already aborted : action(fob)"; + log(Log::WAR, ret); + theReply = PreAllocatedReply::ok_cmd(); + return false; + } + } + + // If Task state is aborted, and we receive **any** child command then it is a zombie + submittable_allready_aborted = true; + password_missmatch_ = false; + pid_missmatch_ = false; + break; + } + case NState::QUEUED: + break; // WTF + case NState::UNKNOWN: + break; // WTF + } + +#ifdef DEBUG_ZOMBIE + std::cout << "\n"; +#endif + + if (password_missmatch_ || pid_missmatch_ || submittable_allready_active || submittable_allready_complete || + submittable_allready_aborted) { + /// If the task has adopted we return true, and carry on as normal + std::string action_taken; + if (!as->zombie_ctrl().handle_zombie(submittable_, this, action_taken, theReply)) { + + // LOG failure: Include type of zombie. + // ** NOTE **: the zombie may have been removed by user actions. i.e if fob and child cmd is abort | + // complete, etc + std::stringstream ss; + ss << " zombie"; + const Zombie& theZombie = + as->zombie_ctrl().find(path_to_submittable_, process_or_remote_id_, jobs_password_); + if (!theZombie.empty()) + ss << "(" << theZombie.type_str() << ")"; + + ss << " : chd:" << ecf::Child::to_string(child_type()); + ss << " : " << path_to_submittable_ << "(" << NState::toString(submittable_->state()) << ")"; + ss << " : " << process_or_remote_id_ << " : " << jobs_password_; + if (submittable_allready_active) + ss << " : already active"; + if (submittable_allready_complete) + ss << " : already complete"; + if (submittable_allready_aborted) + ss << " : already aborted"; + if (password_missmatch_) + ss << " : passwd != [ task:" << submittable_->jobsPassword() << " child:" << jobs_password_ << " ]"; + if (pid_missmatch_) + ss << " : pid != [ task:" << submittable_->process_or_remote_id() << " child:" << process_or_remote_id_ + << " ]"; + ss << " : action(" << action_taken << ")"; + log(Log::ERR, ss.str()); + return false; + } + } + return true; +} + +Submittable* TaskCmd::get_submittable(AbstractServer* as) const { + node_ptr node = as->defs()->findAbsNode(path_to_submittable_); + if (!node.get()) { + return nullptr; + } + + return node->isSubmittable(); +} diff --git a/Base/src/ecflow/base/cts/task/TaskCmd.hpp b/Base/src/ecflow/base/cts/task/TaskCmd.hpp new file mode 100644 index 000000000..b7119c673 --- /dev/null +++ b/Base/src/ecflow/base/cts/task/TaskCmd.hpp @@ -0,0 +1,90 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_task_TaskCmd_HPP +#define ecflow_base_cts_task_TaskCmd_HPP + +#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/core/Child.hpp" + +//================================================================================= +// Task Command +// ================================================================================ +class TaskCmd : public ClientToServerCmd { +protected: + TaskCmd(const std::string& pathToSubmittable, + const std::string& jobsPassword, + const std::string& process_or_remote_id, + int try_no) + : submittable_(nullptr), + path_to_submittable_(pathToSubmittable), + jobs_password_(jobsPassword), + process_or_remote_id_(process_or_remote_id), + try_no_(try_no) { + assert(!hostname().empty()); + } + + TaskCmd() = default; + +public: + bool isWrite() const override { return true; } + int timeout() const override { return 190; } // ECFLOW-157 80 -> 190 + + const std::string& path_to_node() const { return path_to_submittable_; } + const std::string& jobs_password() const { return jobs_password_; } + const std::string& process_or_remote_id() const { return process_or_remote_id_; } + int try_no() const { return try_no_; } + virtual ecf::Child::CmdType child_type() const = 0; + + bool equals(ClientToServerCmd*) const override; + bool task_cmd() const override { return true; } + bool connect_to_different_servers() const override { return true; } + + bool password_missmatch() const { return password_missmatch_; } + bool pid_missmatch() const { return pid_missmatch_; } + +protected: + /// Overridden to do nothing since Task based commands don't need _user_ based authentication + void setup_user_authentification(const std::string& /*user*/, const std::string& /*passwd*/) override {} + bool setup_user_authentification(AbstractClientEnv&) override { return true; } + void setup_user_authentification() override {} + + bool authenticate(AbstractServer*, + STC_Cmd_ptr&) const override; /// Task have their own mechanism,can throw std::runtime_error + Submittable* get_submittable(AbstractServer* as) const; // can throw std::runtime_error + +protected: + mutable Submittable* submittable_{ + nullptr}; // stored during authentication and re-used handle request, not persisted, server side only + +private: + std::string path_to_submittable_; + std::string jobs_password_; + std::string process_or_remote_id_; + int try_no_{0}; + +private: + mutable bool password_missmatch_{ + false}; // stored during authentication and re-used handle request, not persisted, server side only + mutable bool pid_missmatch_{ + false}; // stored during authentication and re-used handle request, not persisted, server side only + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), + CEREAL_NVP(path_to_submittable_), + CEREAL_NVP(jobs_password_), + CEREAL_NVP(process_or_remote_id_), + CEREAL_NVP(try_no_)); + } +}; + +#endif /* ecflow_base_cts_task_TaskCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/AlterCmd.cpp b/Base/src/ecflow/base/cts/user/AlterCmd.cpp similarity index 99% rename from Base/src/ecflow/base/cts/AlterCmd.cpp rename to Base/src/ecflow/base/cts/user/AlterCmd.cpp index 317cc7416..de1fab151 100644 --- a/Base/src/ecflow/base/cts/AlterCmd.cpp +++ b/Base/src/ecflow/base/cts/user/AlterCmd.cpp @@ -8,22 +8,24 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/AlterCmd.hpp" + #include +#include "ecflow/attribute/GenericAttr.hpp" #include "ecflow/attribute/LateAttr.hpp" +#include "ecflow/attribute/QueueAttr.hpp" #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/core/Extract.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/core/Str.hpp" #include "ecflow/node/Defs.hpp" -#include "ecflow/node/ExprAst.hpp" #include "ecflow/node/Expression.hpp" #include "ecflow/node/Limit.hpp" -#include "ecflow/node/MiscAttrs.hpp" +#include "ecflow/node/Node.hpp" #include "ecflow/node/Suite.hpp" #include "ecflow/node/SuiteChanged.hpp" @@ -1861,3 +1863,6 @@ std::ostream& operator<<(std::ostream& os, const AlterCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(AlterCmd) +CEREAL_REGISTER_DYNAMIC_INIT(AlterCmd) diff --git a/Base/src/ecflow/base/cts/user/AlterCmd.hpp b/Base/src/ecflow/base/cts/user/AlterCmd.hpp new file mode 100644 index 000000000..44874953d --- /dev/null +++ b/Base/src/ecflow/base/cts/user/AlterCmd.hpp @@ -0,0 +1,247 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_AlterCmd_HPP +#define ecflow_base_cts_user_AlterCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" +#include "ecflow/node/Flag.hpp" + +class AlterCmd final : public UserCmd { +public: + enum Delete_attr_type { + DEL_VARIABLE, + DEL_TIME, + DEL_TODAY, + DEL_DATE, + DEL_DAY, + DEL_CRON, + DEL_EVENT, + DEL_METER, + DEL_LABEL, + DEL_TRIGGER, + DEL_COMPLETE, + DEL_REPEAT, + DEL_LIMIT, + DEL_LIMIT_PATH, + DEL_INLIMIT, + DEL_ZOMBIE, + DELETE_ATTR_ND, + DEL_LATE, + DEL_QUEUE, + DEL_GENERIC + }; + + enum Change_attr_type { + VARIABLE, + CLOCK_TYPE, + CLOCK_DATE, + CLOCK_GAIN, + EVENT, + METER, + LABEL, + TRIGGER, + COMPLETE, + REPEAT, + LIMIT_MAX, + LIMIT_VAL, + DEFSTATUS, + CHANGE_ATTR_ND, + CLOCK_SYNC, + LATE, + TIME, + TODAY + }; + + enum Add_attr_type { + ADD_TIME, + ADD_TODAY, + ADD_DATE, + ADD_DAY, + ADD_ZOMBIE, + ADD_VARIABLE, + ADD_ATTR_ND, + ADD_LATE, + ADD_LIMIT, + ADD_INLIMIT, + ADD_LABEL + }; + + // Python + AlterCmd(const std::vector& paths, + const std::string& alterType, /* one of [ add | change | delete | set_flag | clear_flag ] */ + const std::string& attrType, + const std::string& name, + const std::string& value); + // add + AlterCmd(const std::string& path, Add_attr_type attr, const std::string& name, const std::string& value = "") + : paths_(std::vector(1, path)), + name_(name), + value_(value), + add_attr_type_(attr) {} + AlterCmd(const std::vector& paths, + Add_attr_type attr, + const std::string& name, + const std::string& value = "") + : paths_(paths), + name_(name), + value_(value), + add_attr_type_(attr) {} + // delete + AlterCmd(const std::string& path, Delete_attr_type del, const std::string& name = "", const std::string& value = "") + : paths_(std::vector(1, path)), + name_(name), + value_(value), + del_attr_type_(del) {} + AlterCmd(const std::vector& paths, + Delete_attr_type del, + const std::string& name = "", + const std::string& value = "") + : paths_(paths), + name_(name), + value_(value), + del_attr_type_(del) {} + // change + AlterCmd(const std::string& path, Change_attr_type attr, const std::string& name, const std::string& value = "") + : paths_(std::vector(1, path)), + name_(name), + value_(value), + change_attr_type_(attr) {} + AlterCmd(const std::vector& paths, + Change_attr_type attr, + const std::string& name, + const std::string& value = "") + : paths_(paths), + name_(name), + value_(value), + change_attr_type_(attr) {} + // flag + AlterCmd(const std::string& path, ecf::Flag::Type ft, bool flag) + : paths_(std::vector(1, path)), + flag_type_(ft), + flag_(flag) {} + AlterCmd(const std::vector& paths, ecf::Flag::Type ft, bool flag) + : paths_(paths), + flag_type_(ft), + flag_(flag) {} + // sort + AlterCmd(const std::string& path, const std::string& name, const std::string& value) + : paths_(std::vector(1, path)), + name_(name), + value_(value) {} + AlterCmd(const std::vector& paths, const std::string& name, const std::string& value) + : paths_(paths), + name_(name), + value_(value) {} + + AlterCmd() = default; + + // Uses by equals only + const std::vector& paths() const { return paths_; } + const std::string& name() const { return name_; } + const std::string& value() const { return value_; } + Delete_attr_type delete_attr_type() const { return del_attr_type_; } + Change_attr_type change_attr_type() const { return change_attr_type_; } + Add_attr_type add_attr_type() const { return add_attr_type_; } + ecf::Flag::Type flag_type() const { return flag_type_; } + bool flag() const { return flag_; } + + bool isWrite() const override { return true; } + void print(std::string&) const override; + void print(std::string& os, const std::string& path) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + STC_Cmd_ptr alter_server_state(AbstractServer*) const; + bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; + void cleanup() override { std::vector().swap(paths_); } /// run in the server, after doHandleRequest + + void my_print(std::string& os, const std::vector& paths) const; + + Add_attr_type get_add_attr_type(const std::string&) const; + void createAdd(Cmd_ptr& cmd, std::vector& options, std::vector& paths) const; + void extract_name_and_value_for_add(Add_attr_type, + std::string& name, + std::string& value, + std::vector& options, + std::vector& paths) const; + void check_for_add(Add_attr_type, const std::string& name, const std::string& value) const; + + Delete_attr_type get_delete_attr_type(const std::string&) const; + void + createDelete(Cmd_ptr& cmd, const std::vector& options, const std::vector& paths) const; + void extract_name_and_value_for_delete(Delete_attr_type, + std::string& name, + std::string& value, + const std::vector& options, + const std::vector& paths) const; + void check_for_delete(Delete_attr_type, const std::string& name, const std::string& value) const; + + Change_attr_type get_change_attr_type(const std::string&) const; + void createChange(Cmd_ptr& cmd, std::vector& options, std::vector& paths) const; + void extract_name_and_value_for_change(Change_attr_type, + std::string& name, + std::string& value, + std::vector& options, + std::vector& paths) const; + void check_for_change(Change_attr_type, const std::string& name, const std::string& value) const; + + ecf::Flag::Type get_flag_type(const std::string&) const; + void create_flag(Cmd_ptr& cmd, + const std::vector& options, + const std::vector& paths, + bool flag) const; + + void check_sort_attr_type(const std::string&) const; + void create_sort_attributes(Cmd_ptr& cmd, + const std::vector& options, + const std::vector& paths) const; + + void alter_and_attr_type(std::string& alter_type, std::string& attr_type) const; + +private: + std::vector paths_; + std::string name_; + std::string value_; + Add_attr_type add_attr_type_{ADD_ATTR_ND}; + Delete_attr_type del_attr_type_{DELETE_ATTR_ND}; + Change_attr_type change_attr_type_{CHANGE_ATTR_ND}; + ecf::Flag::Type flag_type_{ecf::Flag::NOT_SET}; + bool flag_{false}; // true means set false means clear + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), + CEREAL_NVP(paths_), + CEREAL_NVP(name_), + CEREAL_NVP(value_), + CEREAL_NVP(add_attr_type_), + CEREAL_NVP(del_attr_type_), + CEREAL_NVP(change_attr_type_), + CEREAL_NVP(flag_type_), + CEREAL_NVP(flag_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const AlterCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(AlterCmd) + +#endif /* ecflow_base_cts_user_AlterCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/BeginCmd.cpp b/Base/src/ecflow/base/cts/user/BeginCmd.cpp similarity index 97% rename from Base/src/ecflow/base/cts/BeginCmd.cpp rename to Base/src/ecflow/base/cts/user/BeginCmd.cpp index 78b55802d..85c0c1877 100644 --- a/Base/src/ecflow/base/cts/BeginCmd.cpp +++ b/Base/src/ecflow/base/cts/user/BeginCmd.cpp @@ -8,14 +8,13 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/BeginCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" -#include "ecflow/core/Str.hpp" -#include "ecflow/node/Defs.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" #include "ecflow/node/Submittable.hpp" #include "ecflow/node/Suite.hpp" @@ -185,3 +184,6 @@ std::ostream& operator<<(std::ostream& os, const BeginCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(BeginCmd) +CEREAL_REGISTER_DYNAMIC_INIT(BeginCmd) diff --git a/Base/src/ecflow/base/cts/user/BeginCmd.hpp b/Base/src/ecflow/base/cts/user/BeginCmd.hpp new file mode 100644 index 000000000..ce0ccdb12 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/BeginCmd.hpp @@ -0,0 +1,58 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_BeginCmd_HPP +#define ecflow_base_cts_user_BeginCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" +#include "ecflow/node/Defs.hpp" + +// class Begin: if suiteName is empty we will begin all suites +class BeginCmd final : public UserCmd { +public: + explicit BeginCmd(const std::string& suiteName, bool force = false); + BeginCmd() = default; + + const std::string& suiteName() const { return suiteName_; } + bool force() const { return force_; } + + int timeout() const override { return 80; } + + bool isWrite() const override { return true; } + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + +private: + std::string suiteName_; + bool force_{false}; // reset begin status on suites & bypass checks, can create zombies, used in test only + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(suiteName_), CEREAL_NVP(force_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const BeginCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(BeginCmd) + +#endif /* ecflow_base_cts_user_BeginCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/CFileCmd.cpp b/Base/src/ecflow/base/cts/user/CFileCmd.cpp similarity index 98% rename from Base/src/ecflow/base/cts/CFileCmd.cpp rename to Base/src/ecflow/base/cts/user/CFileCmd.cpp index 2c5f92603..47ca30231 100644 --- a/Base/src/ecflow/base/cts/CFileCmd.cpp +++ b/Base/src/ecflow/base/cts/user/CFileCmd.cpp @@ -8,16 +8,17 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/CFileCmd.hpp" + #include #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/core/File.hpp" -#include "ecflow/core/Str.hpp" #include "ecflow/node/EcfFile.hpp" #include "ecflow/node/Submittable.hpp" @@ -378,3 +379,6 @@ std::ostream& operator<<(std::ostream& os, const CFileCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(CFileCmd) +CEREAL_REGISTER_DYNAMIC_INIT(CFileCmd) diff --git a/Base/src/ecflow/base/cts/user/CFileCmd.hpp b/Base/src/ecflow/base/cts/user/CFileCmd.hpp new file mode 100644 index 000000000..a16cfcb5b --- /dev/null +++ b/Base/src/ecflow/base/cts/user/CFileCmd.hpp @@ -0,0 +1,69 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_CFileCmd_HPP +#define ecflow_base_cts_user_CFileCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +//================================================================================ +// Paired with SStringCmd +// Client---(CFileCmd)---->Server-----(SStringCmd)--->client: +//================================================================================ +class CFileCmd final : public UserCmd { +public: + enum File_t { ECF, JOB, JOBOUT, MANUAL, KILL, STAT }; + CFileCmd(const std::string& pathToNode, File_t file, size_t max_lines) + : file_(file), + pathToNode_(pathToNode), + max_lines_(max_lines) {} + CFileCmd(const std::string& pathToNode, const std::string& file_type, const std::string& max_lines); + CFileCmd() = default; + + // Uses by equals only + const std::string& pathToNode() const { return pathToNode_; } + File_t fileType() const { return file_; } + size_t max_lines() const { return max_lines_; } + + static std::vector fileTypesVec(); + static std::string toString(File_t); + + bool handleRequestIsTestable() const override { return false; } + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; + + File_t file_{ECF}; + std::string pathToNode_; + size_t max_lines_{0}; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(file_), CEREAL_NVP(pathToNode_), CEREAL_NVP(max_lines_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const CFileCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(CFileCmd) + +#endif /* ecflow_base_cts_user_CFileCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/CSyncCmd.cpp b/Base/src/ecflow/base/cts/user/CSyncCmd.cpp similarity index 97% rename from Base/src/ecflow/base/cts/CSyncCmd.cpp rename to Base/src/ecflow/base/cts/user/CSyncCmd.cpp index 85516d637..ca2f9a0a7 100644 --- a/Base/src/ecflow/base/cts/CSyncCmd.cpp +++ b/Base/src/ecflow/base/cts/user/CSyncCmd.cpp @@ -8,12 +8,14 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/CSyncCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/node/Defs.hpp" @@ -227,3 +229,6 @@ std::ostream& operator<<(std::ostream& os, const CSyncCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(CSyncCmd) +CEREAL_REGISTER_DYNAMIC_INIT(CSyncCmd) diff --git a/Base/src/ecflow/base/cts/user/CSyncCmd.hpp b/Base/src/ecflow/base/cts/user/CSyncCmd.hpp new file mode 100644 index 000000000..a663b7d49 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/CSyncCmd.hpp @@ -0,0 +1,78 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_CSyncCmd_HPP +#define ecflow_base_cts_user_CSyncCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +// Client---(CSyncCmd::SYNC_FULL)---->Server-----(SSyncCmd)--->client: +// Client---(CSyncCmd::SYNC)--------->Server-----(SSyncCmd)--->client: +// Client---(CSyncCmd::SYNC_CLOCK)--->Server-----(SSyncCmd)--->client: +// Client---(CSyncCmd::NEWS)--------->Server-----(SNewsCmd)--->client: +class CSyncCmd final : public UserCmd { +public: + enum Api { NEWS, SYNC, SYNC_FULL, SYNC_CLOCK }; + + CSyncCmd(Api a, + unsigned int client_handle, + unsigned int client_state_change_no, + unsigned int client_modify_change_no) + : api_(a), + client_handle_(client_handle), + client_state_change_no_(client_state_change_no), + client_modify_change_no_(client_modify_change_no) {} + explicit CSyncCmd(unsigned int client_handle) : api_(SYNC_FULL), client_handle_(client_handle) {} + CSyncCmd() = default; + + Api api() const { return api_; } + int client_state_change_no() const { return client_state_change_no_; } + int client_modify_change_no() const { return client_modify_change_no_; } + int client_handle() const { return client_handle_; } + + void set_client_handle(int client_handle) override { client_handle_ = client_handle; } // used by group_cmd + void print(std::string&) const override; + std::string print_short() const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + int timeout() const override; + + const char* theArg() const override; + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + /// Custom handling of command logging to add additional debug on same line + /// makes it easier to debug errors in syncing. + void do_log(AbstractServer*) const override; + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + + Api api_{SYNC}; + int client_handle_{0}; + int client_state_change_no_{0}; + int client_modify_change_no_{0}; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), + CEREAL_NVP(api_), + CEREAL_NVP(client_handle_), + CEREAL_NVP(client_state_change_no_), + CEREAL_NVP(client_modify_change_no_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const CSyncCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(CSyncCmd) + +#endif /* ecflow_base_cts_user_CSyncCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/CheckPtCmd.cpp b/Base/src/ecflow/base/cts/user/CheckPtCmd.cpp similarity index 97% rename from Base/src/ecflow/base/cts/CheckPtCmd.cpp rename to Base/src/ecflow/base/cts/user/CheckPtCmd.cpp index 97a5e6d7c..b832f41c5 100644 --- a/Base/src/ecflow/base/cts/CheckPtCmd.cpp +++ b/Base/src/ecflow/base/cts/user/CheckPtCmd.cpp @@ -8,12 +8,14 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/CheckPtCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/Converter.hpp" using namespace ecf; @@ -228,3 +230,6 @@ std::ostream& operator<<(std::ostream& os, const CheckPtCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(CheckPtCmd) +CEREAL_REGISTER_DYNAMIC_INIT(CheckPtCmd) diff --git a/Base/src/ecflow/base/cts/user/CheckPtCmd.hpp b/Base/src/ecflow/base/cts/user/CheckPtCmd.hpp new file mode 100644 index 000000000..5d048b063 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/CheckPtCmd.hpp @@ -0,0 +1,60 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_CheckPtCmd_HPP +#define ecflow_base_cts_user_CheckPtCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" +#include "ecflow/core/CheckPt.hpp" + +class CheckPtCmd final : public UserCmd { +public: + CheckPtCmd(ecf::CheckPt::Mode m, int interval, int checkpt_save_time_alarm) + : mode_(m), + check_pt_interval_(interval), + check_pt_save_time_alarm_(checkpt_save_time_alarm) {} + CheckPtCmd() = default; + + ecf::CheckPt::Mode mode() const { return mode_; } + int check_pt_interval() const { return check_pt_interval_; } + int check_pt_save_time_alarm() const { return check_pt_save_time_alarm_; } + + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + bool isWrite() const override; + bool is_mutable() const override; + const char* theArg() const override; + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + +private: + ecf::CheckPt::Mode mode_{ecf::CheckPt::UNDEFINED}; + int check_pt_interval_{0}; + int check_pt_save_time_alarm_{0}; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), + CEREAL_NVP(mode_), + CEREAL_NVP(check_pt_interval_), + CEREAL_NVP(check_pt_save_time_alarm_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const CheckPtCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(CheckPtCmd) + +#endif /* ecflow_base_cts_user_CheckPtCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/ClientHandleCmd.cpp b/Base/src/ecflow/base/cts/user/ClientHandleCmd.cpp similarity index 98% rename from Base/src/ecflow/base/cts/ClientHandleCmd.cpp rename to Base/src/ecflow/base/cts/user/ClientHandleCmd.cpp index 4dfa4a14f..d902e487d 100644 --- a/Base/src/ecflow/base/cts/ClientHandleCmd.cpp +++ b/Base/src/ecflow/base/cts/user/ClientHandleCmd.cpp @@ -8,12 +8,15 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/ClientHandleCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/cts/user/GroupCTSCmd.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/node/Defs.hpp" @@ -509,3 +512,6 @@ std::ostream& operator<<(std::ostream& os, const ClientHandleCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(ClientHandleCmd) +CEREAL_REGISTER_DYNAMIC_INIT(ClientHandleCmd) diff --git a/Base/src/ecflow/base/cts/user/ClientHandleCmd.hpp b/Base/src/ecflow/base/cts/user/ClientHandleCmd.hpp new file mode 100644 index 000000000..1b80ca7a2 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/ClientHandleCmd.hpp @@ -0,0 +1,85 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_ClientHandleCmd_HPP +#define ecflow_base_cts_user_ClientHandleCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +class ClientHandleCmd final : public UserCmd { +public: + enum Api { REGISTER, DROP, DROP_USER, ADD, REMOVE, AUTO_ADD, SUITES }; + + explicit ClientHandleCmd(Api api = AUTO_ADD) : api_(api) {} + + ClientHandleCmd(int client_handle, const std::vector& suites, bool add_add_new_suites) + : api_(REGISTER), + client_handle_(client_handle), + suites_(suites), + auto_add_new_suites_(add_add_new_suites) {} + + explicit ClientHandleCmd(int client_handle) : api_(DROP), client_handle_(client_handle) {} + + explicit ClientHandleCmd(const std::string& drop_user) : api_(DROP_USER), drop_user_(drop_user) {} + + ClientHandleCmd(int client_handle, const std::vector& suites, Api api) + : api_(api), // Must be ADD or REMOVE + client_handle_(client_handle), + suites_(suites) {} + + ClientHandleCmd(int client_handle, bool add_add_new_suites) + : api_(AUTO_ADD), + client_handle_(client_handle), + auto_add_new_suites_(add_add_new_suites) {} + + Api api() const { return api_; } + const std::string& drop_user() const { return drop_user_; } + + bool cmd_updates_defs() const override; + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override; + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + + // called in the server + void set_group_cmd(const GroupCTSCmd* cmd) override { group_cmd_ = cmd; } + +private: + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + + Api api_; + int client_handle_{0}; + std::string drop_user_; + std::vector suites_; + bool auto_add_new_suites_{false}; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), + CEREAL_NVP(api_), + CEREAL_NVP(client_handle_), + CEREAL_NVP(drop_user_), + CEREAL_NVP(suites_), + CEREAL_NVP(auto_add_new_suites_)); + } + +private: + const GroupCTSCmd* group_cmd_ = nullptr; // not persisted only used in server +}; + +std::ostream& operator<<(std::ostream& os, const ClientHandleCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(ClientHandleCmd) + +#endif /* ecflow_base_cts_user_ClientHandleCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/CtsApi.cpp b/Base/src/ecflow/base/cts/user/CtsApi.cpp similarity index 99% rename from Base/src/ecflow/base/cts/CtsApi.cpp rename to Base/src/ecflow/base/cts/user/CtsApi.cpp index b29b0c3eb..d17e58ca2 100644 --- a/Base/src/ecflow/base/cts/CtsApi.cpp +++ b/Base/src/ecflow/base/cts/user/CtsApi.cpp @@ -8,7 +8,7 @@ * nor does it submit to any jurisdiction. */ -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" #include diff --git a/Base/src/ecflow/base/cts/CtsApi.hpp b/Base/src/ecflow/base/cts/user/CtsApi.hpp similarity index 99% rename from Base/src/ecflow/base/cts/CtsApi.hpp rename to Base/src/ecflow/base/cts/user/CtsApi.hpp index b75479144..a860a6dff 100644 --- a/Base/src/ecflow/base/cts/CtsApi.hpp +++ b/Base/src/ecflow/base/cts/user/CtsApi.hpp @@ -8,8 +8,8 @@ * nor does it submit to any jurisdiction. */ -#ifndef ecflow_base_cts_CtsApi_HPP -#define ecflow_base_cts_CtsApi_HPP +#ifndef ecflow_base_cts_user_CtsApi_HPP +#define ecflow_base_cts_user_CtsApi_HPP /// /// \brief (C)lient (t)o (s)erver API @@ -283,4 +283,4 @@ class CtsApi { static const char* queryArg(); }; -#endif /* ecflow_base_cts_CtsApi_HPP */ +#endif /* ecflow_base_cts_user_CtsApi_HPP */ diff --git a/Base/src/ecflow/base/cts/CtsCmd.cpp b/Base/src/ecflow/base/cts/user/CtsCmd.cpp similarity index 99% rename from Base/src/ecflow/base/cts/CtsCmd.cpp rename to Base/src/ecflow/base/cts/user/CtsCmd.cpp index 0bced10d8..edf4d48cd 100644 --- a/Base/src/ecflow/base/cts/CtsCmd.cpp +++ b/Base/src/ecflow/base/cts/user/CtsCmd.cpp @@ -8,13 +8,15 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/CtsCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" #include "ecflow/base/Gnuplot.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Jobs.hpp" @@ -783,3 +785,6 @@ std::ostream& operator<<(std::ostream& os, const CtsCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(CtsCmd) +CEREAL_REGISTER_DYNAMIC_INIT(CtsCmd) diff --git a/Base/src/ecflow/base/cts/user/CtsCmd.hpp b/Base/src/ecflow/base/cts/user/CtsCmd.hpp new file mode 100644 index 000000000..2c6041a43 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/CtsCmd.hpp @@ -0,0 +1,88 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_CtsCmd_HPP +#define ecflow_base_cts_user_CtsCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +// This command is used to encapsulate all commands that are +// simple signals to the server. This helps to cut down on the +// number of global symbols used by boost serialisation. +// ========================================================================= +// *** IMPORTANT ***: If any of these commands in the future need arguments, +// *** then ensure to place a DUMMY enum in its place. +// *** This will allow a *newer* development client to still send message to a older server. +// *** i.e like terminating the server +// *** IMPORTANT: For any new commands, must be added to the end, for each major release +// *** - STATS_RESET was introduced in release 4.0.5 +// ========================================================================= +class CtsCmd final : public UserCmd { +public: + enum Api { + NO_CMD, + RESTORE_DEFS_FROM_CHECKPT, + RESTART_SERVER, + SHUTDOWN_SERVER, + HALT_SERVER, + TERMINATE_SERVER, + RELOAD_WHITE_LIST_FILE, + FORCE_DEP_EVAL, + PING, + GET_ZOMBIES, + STATS, + SUITES, + DEBUG_SERVER_ON, + DEBUG_SERVER_OFF, + SERVER_LOAD, + STATS_RESET, + RELOAD_PASSWD_FILE, + STATS_SERVER, + RELOAD_CUSTOM_PASSWD_FILE + }; + + explicit CtsCmd(Api a) : api_(a) {} + CtsCmd() = default; + + Api api() const { return api_; } + + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + bool isWrite() const override; + bool cmd_updates_defs() const override; + bool terminate_cmd() const override { return api_ == TERMINATE_SERVER; } + bool ping_cmd() const override { return api_ == PING; } + int timeout() const override; + + bool handleRequestIsTestable() const override; + + const char* theArg() const override; + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + + Api api_{NO_CMD}; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(api_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const CtsCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(CtsCmd) + +#endif /* ecflow_base_cts_user_CtsCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/CtsNodeCmd.cpp b/Base/src/ecflow/base/cts/user/CtsNodeCmd.cpp similarity index 98% rename from Base/src/ecflow/base/cts/CtsNodeCmd.cpp rename to Base/src/ecflow/base/cts/user/CtsNodeCmd.cpp index 4be1a140d..729445b6a 100644 --- a/Base/src/ecflow/base/cts/CtsNodeCmd.cpp +++ b/Base/src/ecflow/base/cts/user/CtsNodeCmd.cpp @@ -8,12 +8,14 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/CtsNodeCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/JobCreationCtrl.hpp" #include "ecflow/node/Jobs.hpp" @@ -380,3 +382,6 @@ std::ostream& operator<<(std::ostream& os, const CtsNodeCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(CtsNodeCmd) +CEREAL_REGISTER_DYNAMIC_INIT(CtsNodeCmd) diff --git a/Base/src/ecflow/base/cts/user/CtsNodeCmd.hpp b/Base/src/ecflow/base/cts/user/CtsNodeCmd.hpp new file mode 100644 index 000000000..246eac9ca --- /dev/null +++ b/Base/src/ecflow/base/cts/user/CtsNodeCmd.hpp @@ -0,0 +1,74 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_CtsNodeCmd_HPP +#define ecflow_base_cts_user_CtsNodeCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +// Collection of commands, that all take a abs node path as their only arg +// Reduce number of global symbols caused by boost serialisation +// Previously they were all separate commands +// +// Client---(CtsNodeCmd(GET))---->Server-----(DefsCmd | SNodeCmd )--->client: +// When doHandleRequest is called in the server it will return DefsCmd +// The DefsCmd is used to transport the node tree hierarchy to/from the server +// +// CHECK_JOB_GEN_ONLY command will traverse hierarchically from the given node path +// and force generation of jobs. (i.e independently of dependencies). +// This is used in *testing* only, so that we can compare/test/verify +// job generation with the old version. +// if absNodepath is empty we will generate jobs for all tasks +class CtsNodeCmd final : public UserCmd { +public: + enum Api { NO_CMD, JOB_GEN, CHECK_JOB_GEN_ONLY, GET, WHY, GET_STATE, MIGRATE }; + CtsNodeCmd(Api a, const std::string& absNodePath) : api_(a), absNodePath_(absNodePath) {} + explicit CtsNodeCmd(Api a) : api_(a) { assert(a != NO_CMD); } + CtsNodeCmd() = default; + + Api api() const { return api_; } + const std::string& absNodePath() const { return absNodePath_; } + + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + PrintStyle::Type_t show_style() const override; + + int timeout() const override; + bool isWrite() const override; + bool handleRequestIsTestable() const override { return !terminate_cmd(); } + bool why_cmd(std::string& nodePath) const override; + bool get_cmd() const override { return api_ == GET; } + + const char* theArg() const override; + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; + +private: + Api api_{NO_CMD}; + std::string absNodePath_; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(api_), CEREAL_NVP(absNodePath_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const CtsNodeCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(CtsNodeCmd) + +#endif /* ecflow_base_cts_user_UserCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/DeleteCmd.cpp b/Base/src/ecflow/base/cts/user/DeleteCmd.cpp similarity index 96% rename from Base/src/ecflow/base/cts/DeleteCmd.cpp rename to Base/src/ecflow/base/cts/user/DeleteCmd.cpp index 6657402aa..6c777664f 100644 --- a/Base/src/ecflow/base/cts/DeleteCmd.cpp +++ b/Base/src/ecflow/base/cts/user/DeleteCmd.cpp @@ -8,12 +8,15 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/DeleteCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/cts/user/GroupCTSCmd.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Task.hpp" @@ -218,3 +221,6 @@ std::ostream& operator<<(std::ostream& os, const DeleteCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(DeleteCmd) +CEREAL_REGISTER_DYNAMIC_INIT(DeleteCmd) diff --git a/Base/src/ecflow/base/cts/user/DeleteCmd.hpp b/Base/src/ecflow/base/cts/user/DeleteCmd.hpp new file mode 100644 index 000000000..1ebf2d26a --- /dev/null +++ b/Base/src/ecflow/base/cts/user/DeleteCmd.hpp @@ -0,0 +1,66 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_DeleteCmd_HPP +#define ecflow_base_cts_user_DeleteCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +// DELETE If paths_ empty will delete all suites (beware) else will delete the chosen nodes. +class DeleteCmd final : public UserCmd { +public: + explicit DeleteCmd(const std::vector& paths, bool force = false) + : group_cmd_(nullptr), + paths_(paths), + force_(force) {} + explicit DeleteCmd(const std::string& absNodePath, bool force = false); + DeleteCmd() = default; + + const std::vector& paths() const { return paths_; } + bool force() const { return force_; } + + void print(std::string&) const override; + void print_only(std::string&) const override; + void print(std::string& os, const std::string& path) const override; + + bool equals(ClientToServerCmd*) const override; + bool isWrite() const override { return true; } + + const char* theArg() const override; + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + + // called in the server + void set_group_cmd(const GroupCTSCmd* cmd) override { group_cmd_ = cmd; } + + static void check_for_active_or_submitted_tasks(AbstractServer* as, Node* theNodeToDelete); + +private: + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; + void cleanup() override { std::vector().swap(paths_); } /// run in the server, after handlerequest + +private: + const GroupCTSCmd* group_cmd_{nullptr}; // not persisted only used in server + std::vector paths_; + bool force_{false}; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(paths_), CEREAL_NVP(force_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const DeleteCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(DeleteCmd) + +#endif /* ecflow_base_cts_user_DeleteCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/EditScriptCmd.cpp b/Base/src/ecflow/base/cts/user/EditScriptCmd.cpp similarity index 98% rename from Base/src/ecflow/base/cts/EditScriptCmd.cpp rename to Base/src/ecflow/base/cts/user/EditScriptCmd.cpp index 2f9fa6314..9d846bc8a 100644 --- a/Base/src/ecflow/base/cts/EditScriptCmd.cpp +++ b/Base/src/ecflow/base/cts/user/EditScriptCmd.cpp @@ -8,12 +8,14 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/EditScriptCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/File.hpp" #include "ecflow/node/Alias.hpp" #include "ecflow/node/EcfFile.hpp" @@ -465,3 +467,6 @@ std::ostream& operator<<(std::ostream& os, const EditScriptCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(EditScriptCmd) +CEREAL_REGISTER_DYNAMIC_INIT(EditScriptCmd) diff --git a/Base/src/ecflow/base/cts/user/EditScriptCmd.hpp b/Base/src/ecflow/base/cts/user/EditScriptCmd.hpp new file mode 100644 index 000000000..7588bc1fc --- /dev/null +++ b/Base/src/ecflow/base/cts/user/EditScriptCmd.hpp @@ -0,0 +1,102 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_EditScriptCmd_HPP +#define ecflow_base_cts_user_EditScriptCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +//================================================================================ +// Paired with SStringCmd +// Client---(EditScriptCmd)---->Server-----(SStringCmd)--->client: +//================================================================================ +class EditScriptCmd final : public UserCmd { +public: + enum EditType { EDIT, PREPROCESS, SUBMIT, PREPROCESS_USER_FILE, SUBMIT_USER_FILE }; + EditScriptCmd(const std::string& path_to_node, EditType et) // EDIT or PREPROCESS + : edit_type_(et), + path_to_node_(path_to_node) {} + + EditScriptCmd(const std::string& path_to_node, const NameValueVec& user_variables) + : edit_type_(SUBMIT), + path_to_node_(path_to_node), + user_variables_(user_variables) {} + + EditScriptCmd(const std::string& path_to_node, const std::vector& user_file_contents) + : edit_type_(PREPROCESS_USER_FILE), + path_to_node_(path_to_node), + user_file_contents_(user_file_contents) {} + + EditScriptCmd(const std::string& path_to_node, + const NameValueVec& user_variables, + const std::vector& user_file_contents, + bool create_alias, + bool run_alias) + : edit_type_(SUBMIT_USER_FILE), + path_to_node_(path_to_node), + user_file_contents_(user_file_contents), + user_variables_(user_variables), + alias_(create_alias), + run_(run_alias) {} + + EditScriptCmd() = default; + + // Uses by equals only + const std::string& path_to_node() const { return path_to_node_; } + EditType edit_type() const { return edit_type_; } + bool alias() const { return alias_; } + bool run() const { return run_; } + + bool handleRequestIsTestable() const override { return false; } + bool isWrite() const override; + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; + void cleanup() override { + std::vector().swap(user_file_contents_); + } /// run in the server, after doHandleRequest + +private: + EditType edit_type_{EDIT}; + std::string path_to_node_; + mutable std::vector user_file_contents_; + NameValueVec user_variables_; + bool alias_{false}; + bool run_{false}; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), + CEREAL_NVP(edit_type_), + CEREAL_NVP(path_to_node_), + CEREAL_NVP(user_file_contents_), + CEREAL_NVP(user_variables_), + CEREAL_NVP(alias_), + CEREAL_NVP(run_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const EditScriptCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(EditScriptCmd) + +#endif /* ecflow_base_cts_user_EditScriptCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/ForceCmd.cpp b/Base/src/ecflow/base/cts/user/ForceCmd.cpp similarity index 98% rename from Base/src/ecflow/base/cts/ForceCmd.cpp rename to Base/src/ecflow/base/cts/user/ForceCmd.cpp index fc07a04fe..152a7dff7 100644 --- a/Base/src/ecflow/base/cts/ForceCmd.cpp +++ b/Base/src/ecflow/base/cts/user/ForceCmd.cpp @@ -8,12 +8,13 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/ForceCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/core/Extract.hpp" #include "ecflow/core/Str.hpp" @@ -315,3 +316,6 @@ std::ostream& operator<<(std::ostream& os, const ForceCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(ForceCmd) +CEREAL_REGISTER_DYNAMIC_INIT(ForceCmd) diff --git a/Base/src/ecflow/base/cts/user/ForceCmd.hpp b/Base/src/ecflow/base/cts/user/ForceCmd.hpp new file mode 100644 index 000000000..bd2481a3a --- /dev/null +++ b/Base/src/ecflow/base/cts/user/ForceCmd.hpp @@ -0,0 +1,87 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_ForceCmd_HPP +#define ecflow_base_cts_user_ForceCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +// Set the state on the affected node ONLY. +// If recursive is used set the state, on node and _ALL_ nodes _beneath +// setRepeatToLastValue set, only make sense when used with recursive. +// stateOrEvent, string is one of: +// < unknown | suspended | complete | queued | submitted | active | aborted | clear | set > +class ForceCmd final : public UserCmd { +public: + ForceCmd(const std::vector& paths, + const std::string& stateOrEvent, + bool recursive, + bool setRepeatToLastValue) + : paths_(paths), + stateOrEvent_(stateOrEvent), + recursive_(recursive), + setRepeatToLastValue_(setRepeatToLastValue) {} + ForceCmd(const std::string& path, const std::string& stateOrEvent, bool recursive, bool setRepeatToLastValue) + : paths_(std::vector(1, path)), + stateOrEvent_(stateOrEvent), + recursive_(recursive), + setRepeatToLastValue_(setRepeatToLastValue) {} + ForceCmd() = default; + + // Uses by equals only + const std::vector paths() const { return paths_; } + const std::string& stateOrEvent() const { return stateOrEvent_; } + bool recursive() const { return recursive_; } + bool setRepeatToLastValue() const { return setRepeatToLastValue_; } + + bool isWrite() const override { return true; } + void print(std::string&) const override; + std::string print_short() const override; + void print_only(std::string&) const override; + void print(std::string& os, const std::string& path) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; + void cleanup() override { std::vector().swap(paths_); } /// run in the server, after doHandleRequest + + void my_print(std::string& os, const std::vector& paths) const; + void my_print_only(std::string& os, const std::vector& paths) const; + +private: + std::vector paths_; + std::string stateOrEvent_; + bool recursive_{false}; + bool setRepeatToLastValue_{false}; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), + CEREAL_NVP(paths_), + CEREAL_NVP(stateOrEvent_), + CEREAL_NVP(recursive_), + CEREAL_NVP(setRepeatToLastValue_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const ForceCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(ForceCmd) + +#endif /* ecflow_base_cts_user_ForceCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/FreeDepCmd.cpp b/Base/src/ecflow/base/cts/user/FreeDepCmd.cpp similarity index 97% rename from Base/src/ecflow/base/cts/FreeDepCmd.cpp rename to Base/src/ecflow/base/cts/user/FreeDepCmd.cpp index 5411009bf..92112ece2 100644 --- a/Base/src/ecflow/base/cts/FreeDepCmd.cpp +++ b/Base/src/ecflow/base/cts/user/FreeDepCmd.cpp @@ -8,12 +8,13 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/FreeDepCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/node/Node.hpp" #include "ecflow/node/SuiteChanged.hpp" @@ -177,3 +178,6 @@ std::ostream& operator<<(std::ostream& os, const FreeDepCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(FreeDepCmd) +CEREAL_REGISTER_DYNAMIC_INIT(FreeDepCmd) diff --git a/Base/src/ecflow/base/cts/user/FreeDepCmd.hpp b/Base/src/ecflow/base/cts/user/FreeDepCmd.hpp new file mode 100644 index 000000000..1b83a4429 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/FreeDepCmd.hpp @@ -0,0 +1,93 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_FreeDepCmd_HPP +#define ecflow_base_cts_user_FreeDepCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +// Free Dependencies +class FreeDepCmd final : public UserCmd { +public: + explicit FreeDepCmd(const std::vector& paths, + bool trigger = true, + bool all = false, // day, date, time, today, trigger, cron + bool date = false, + bool time = false // includes time, day, date, today, cron + ) + : paths_(paths), + trigger_(trigger), + all_(all), + date_(date), + time_(time) {} + + explicit FreeDepCmd(const std::string& path, + bool trigger = true, + bool all = false, // day, date, time, today, trigger, cron + bool date = false, + bool time = false // includes time, day, date, today, cron + ) + : paths_(std::vector(1, path)), + trigger_(trigger), + all_(all), + date_(date), + time_(time) {} + + FreeDepCmd() = default; + + // Uses by equals only + const std::vector& paths() const { return paths_; } + bool trigger() const { return trigger_; } + bool all() const { return all_; } + bool date() const { return date_; } + bool time() const { return time_; } + + bool isWrite() const override { return true; } + void print(std::string&) const override; + void print_only(std::string&) const override; + void print(std::string& os, const std::string& path) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; + void cleanup() override { std::vector().swap(paths_); } /// run in the server, after doHandleRequest + +private: + std::vector paths_; + bool trigger_{true}; + bool all_{false}; + bool date_{false}; + bool time_{false}; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), + CEREAL_NVP(paths_), + CEREAL_NVP(trigger_), + CEREAL_NVP(all_), + CEREAL_NVP(date_), + CEREAL_NVP(time_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const FreeDepCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(FreeDepCmd) + +#endif /* ecflow_base_cts_user_FreeDepCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/GroupCTSCmd.cpp b/Base/src/ecflow/base/cts/user/GroupCTSCmd.cpp similarity index 98% rename from Base/src/ecflow/base/cts/GroupCTSCmd.cpp rename to Base/src/ecflow/base/cts/user/GroupCTSCmd.cpp index a45ce86b1..faed3963e 100644 --- a/Base/src/ecflow/base/cts/GroupCTSCmd.cpp +++ b/Base/src/ecflow/base/cts/user/GroupCTSCmd.cpp @@ -8,6 +8,8 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/GroupCTSCmd.hpp" + #include #include #include @@ -15,11 +17,11 @@ #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" #include "ecflow/base/ClientOptionsParser.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" #include "ecflow/base/cts/CtsCmdRegistry.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" #include "ecflow/base/stc/ErrorCmd.hpp" #include "ecflow/base/stc/GroupSTCCmd.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/CommandLine.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/core/Str.hpp" @@ -401,3 +403,6 @@ std::ostream& operator<<(std::ostream& os, const GroupCTSCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(GroupCTSCmd) +CEREAL_REGISTER_DYNAMIC_INIT(GroupCTSCmd) diff --git a/Base/src/ecflow/base/cts/user/GroupCTSCmd.hpp b/Base/src/ecflow/base/cts/user/GroupCTSCmd.hpp new file mode 100644 index 000000000..9674f3543 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/GroupCTSCmd.hpp @@ -0,0 +1,85 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_GroupCTSCmd_HPP +#define ecflow_base_cts_user_GroupCTSCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +// The group command allows a series of commands to be be executed: +// +// Client---(GroupCTSCmd)---->Server-----(GroupSTCCmd | StcCmd(OK) | Error )--->client: +// +// If client->server contains GetDefs cmd and log file commands, then a group +// command will be created for returning to the client +// +// If group command contains multiple [ CtsNodeCmd(GET) | LogCmd --get ] commands then +// all the results are returned back to the client, HOWEVER when client calls +// Cmd::defs() | Cmd::get_string() only the first data is returned. +// +class GroupCTSCmd final : public UserCmd { +public: + GroupCTSCmd(const std::string& list_of_commands, AbstractClientEnv* clientEnv); + explicit GroupCTSCmd(Cmd_ptr cmd) : cli_(false) { addChild(cmd); } + GroupCTSCmd() = default; + + bool isWrite() const override; + bool cmd_updates_defs() const override; + + PrintStyle::Type_t show_style() const override; + bool get_cmd() const override; + bool task_cmd() const override; + bool terminate_cmd() const override; + bool why_cmd(std::string&) const override; + bool group_cmd() const override { return true; } + + void set_client_handle(int client_handle) const; // used in group sync with client register + + void print(std::string&) const override; + std::string print_short() const override; + bool equals(ClientToServerCmd*) const override; + + void addChild(Cmd_ptr childCmd); + const std::vector& cmdVec() const { return cmdVec_; } + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + + void add_edit_history(Defs*) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + void setup_user_authentification(const std::string& user, const std::string& passwd) override; + bool setup_user_authentification(AbstractClientEnv&) override; + void setup_user_authentification() override; + + bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + void cleanup() override; // cleanup all children + +private: + std::vector cmdVec_; + bool cli_{true}; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(cmdVec_), CEREAL_NVP(cli_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const GroupCTSCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(GroupCTSCmd) + +#endif /* ecflow_base_cts_user_GroupCTSCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/LoadDefsCmd.cpp b/Base/src/ecflow/base/cts/user/LoadDefsCmd.cpp similarity index 97% rename from Base/src/ecflow/base/cts/LoadDefsCmd.cpp rename to Base/src/ecflow/base/cts/user/LoadDefsCmd.cpp index 564fd5fb8..cf1d0c538 100644 --- a/Base/src/ecflow/base/cts/LoadDefsCmd.cpp +++ b/Base/src/ecflow/base/cts/user/LoadDefsCmd.cpp @@ -8,15 +8,16 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/LoadDefsCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/Filesystem.hpp" #include "ecflow/core/Log.hpp" -#include "ecflow/core/PrintStyle.hpp" #include "ecflow/node/Defs.hpp" using namespace ecf; @@ -225,3 +226,6 @@ std::ostream& operator<<(std::ostream& os, const LoadDefsCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(LoadDefsCmd) +CEREAL_REGISTER_DYNAMIC_INIT(LoadDefsCmd) diff --git a/Base/src/ecflow/base/cts/user/LoadDefsCmd.hpp b/Base/src/ecflow/base/cts/user/LoadDefsCmd.hpp new file mode 100644 index 000000000..966bcfb50 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/LoadDefsCmd.hpp @@ -0,0 +1,71 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_LoadDefsCmd_HPP +#define ecflow_base_cts_user_LoadDefsCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +// Will *load* the suites, into the server. +// Additionally the server will try to resolve extern's. The extern are references +// to Node, events, meters, limits, variables defined on another suite. +class LoadDefsCmd final : public UserCmd { +public: + explicit LoadDefsCmd(const defs_ptr& defs, bool force = false); + explicit LoadDefsCmd(const std::string& defs_filename, + bool force = false, + bool check_only = false /* not persisted */, + bool print = false /* not persisted */, + bool stats = false /* not persisted */, + const std::vector>& client_env = + std::vector>()); + LoadDefsCmd() = default; + + // Uses by equals only + const std::string& defs_as_string() const { return defs_; } + + bool isWrite() const override { return true; } + int timeout() const override { return time_out_for_load_sync_and_get(); } + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + static Cmd_ptr create(const std::string& defs_filename, + bool force, + bool check_only, + bool print, + bool stats, + AbstractClientEnv* clientEnv); + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the command as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + + bool force_{false}; + std::string defs_; + std::string defs_filename_; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(force_), CEREAL_NVP(defs_), CEREAL_NVP(defs_filename_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const LoadDefsCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(LoadDefsCmd) + +#endif /* ecflow_base_cts_user_LoadDefsCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/LogCmd.cpp b/Base/src/ecflow/base/cts/user/LogCmd.cpp similarity index 98% rename from Base/src/ecflow/base/cts/LogCmd.cpp rename to Base/src/ecflow/base/cts/user/LogCmd.cpp index 0d398e96d..b5d7a4fed 100644 --- a/Base/src/ecflow/base/cts/LogCmd.cpp +++ b/Base/src/ecflow/base/cts/user/LogCmd.cpp @@ -8,12 +8,14 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/LogCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/core/Str.hpp" @@ -298,3 +300,6 @@ std::ostream& operator<<(std::ostream& os, const LogCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(LogCmd) +CEREAL_REGISTER_DYNAMIC_INIT(LogCmd) diff --git a/Base/src/ecflow/base/cts/user/LogCmd.hpp b/Base/src/ecflow/base/cts/user/LogCmd.hpp new file mode 100644 index 000000000..3665c155b --- /dev/null +++ b/Base/src/ecflow/base/cts/user/LogCmd.hpp @@ -0,0 +1,62 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_LogCmd_HPP +#define ecflow_base_cts_user_LogCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +/// The LogCmd is paired with SStringCmd +/// Client---(LogCmd)---->Server-----(SStringCmd)--->client: +/// When doHandleRequest is called in the server it will return SStringCmd +/// The SStringCmd is used to transport the log file contents to the client +class LogCmd final : public UserCmd { +public: + enum LogApi { GET, CLEAR, FLUSH, NEW, PATH }; + explicit LogCmd(LogApi a, + int get_last_n_lines = 0); // for zero we take default from log. Avoid adding dependency on log.hpp + explicit LogCmd(const std::string& path); // NEW + LogCmd(); + + LogApi api() const { return api_; } + int get_last_n_lines() const { return get_last_n_lines_; } + const std::string& new_path() const { return new_path_; } + + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + bool isWrite() const override; + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + + LogApi api_{LogCmd::GET}; + int get_last_n_lines_; // default to 100 -> ECFLOW-174 + std::string new_path_; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(api_), CEREAL_NVP(get_last_n_lines_), CEREAL_NVP(new_path_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const LogCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(LogCmd) + +#endif /* ecflow_base_cts_user_LogCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/LogMessageCmd.cpp b/Base/src/ecflow/base/cts/user/LogMessageCmd.cpp similarity index 90% rename from Base/src/ecflow/base/cts/LogMessageCmd.cpp rename to Base/src/ecflow/base/cts/user/LogMessageCmd.cpp index 044c1f068..2e86085d8 100644 --- a/Base/src/ecflow/base/cts/LogMessageCmd.cpp +++ b/Base/src/ecflow/base/cts/user/LogMessageCmd.cpp @@ -8,13 +8,14 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/LogMessageCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/Stats.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" using namespace ecf; using namespace std; @@ -71,3 +72,6 @@ std::ostream& operator<<(std::ostream& os, const LogMessageCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(LogMessageCmd) +CEREAL_REGISTER_DYNAMIC_INIT(LogMessageCmd) diff --git a/Base/src/ecflow/base/cts/user/LogMessageCmd.hpp b/Base/src/ecflow/base/cts/user/LogMessageCmd.hpp new file mode 100644 index 000000000..955353204 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/LogMessageCmd.hpp @@ -0,0 +1,50 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_LogMessageCmd_HPP +#define ecflow_base_cts_user_LogMessageCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +/// Simply writes the message to the log file +class LogMessageCmd final : public UserCmd { +public: + explicit LogMessageCmd(const std::string& msg) : msg_(msg) {} + LogMessageCmd() = default; + + const std::string& msg() const { return msg_; } + + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + std::string msg_; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(msg_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const LogMessageCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(LogMessageCmd) + +#endif /* ecflow_base_cts_user_LogMessageCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/user/MoveCmd.cpp b/Base/src/ecflow/base/cts/user/MoveCmd.cpp new file mode 100644 index 000000000..0399a9aa9 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/MoveCmd.cpp @@ -0,0 +1,203 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "ecflow/base/cts/user/MoveCmd.hpp" + +#include + +#include "ecflow/base/AbstractServer.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" +#include "ecflow/node/Defs.hpp" +#include "ecflow/node/Suite.hpp" +#include "ecflow/node/SuiteChanged.hpp" +#ifdef ECF_OPENSSL + #include "ecflow/base/Openssl.hpp" + #include "ecflow/base/SslClient.hpp" +#endif + +using namespace ecf; +using namespace std; +using namespace boost; +namespace po = boost::program_options; + +namespace { + +/// Class to manage locking: Only unlock if acquired the lock, +class Lock { +public: + Lock(const std::string& user, AbstractServer* as) : as_(as) { ok_ = as->lock(user); } + ~Lock() { + if (ok_) + as_->unlock(); + } + bool ok() const { return ok_; } + +private: + bool ok_; + AbstractServer* as_; +}; + +} // namespace + +//======================================================================================= + +MoveCmd::MoveCmd(const std::pair& host_port, Node* src, const std::string& dest) + : src_node_(src->print(PrintStyle::NET)), + src_host_(host_port.first), + src_port_(host_port.second), + src_path_(src->absNodePath()), + dest_(dest) { +} + +MoveCmd::MoveCmd() = default; +MoveCmd::~MoveCmd() = default; + +bool MoveCmd::equals(ClientToServerCmd* rhs) const { + auto* the_rhs = dynamic_cast(rhs); + if (!the_rhs) + return false; + if (dest_ != the_rhs->dest()) { + return false; + } + if (src_node_ != the_rhs->src_node()) { + return false; + } + return UserCmd::equals(rhs); +} + +void MoveCmd::print(std::string& os) const { + std::string ss; + ss += "Plug(Move) source("; + ss += src_host_; + ss += ":"; + ss += src_port_; + ss += ":"; + ss += src_path_; + ss += ") destination("; + ss += dest_; + ss += ")"; + user_cmd(os, ss); +} + +bool MoveCmd::check_source() const { + return !src_node_.empty(); +} + +STC_Cmd_ptr MoveCmd::doHandleRequest(AbstractServer* as) const { + Defs* defs = as->defs().get(); + + Lock lock(user(), as); + if (!lock.ok()) { + std::string errorMsg = "Plug(Move) command failed. User "; + errorMsg += as->lockedUser(); + errorMsg += " already has an exclusive lock"; + throw std::runtime_error(errorMsg); + } + + if (!check_source()) { + throw std::runtime_error("Plug(Move) command failed. No source specified"); + } + + std::string error_msg; + node_ptr src_node = Node::create(src_node_, error_msg); + if (!error_msg.empty() || !src_node) { + throw std::runtime_error("Plug(Move) command failed. Error in source:\n" + error_msg); + } + + // destNode can be NULL when we are moving a suite + node_ptr destNode; + if (!dest_.empty()) { + + destNode = defs->findAbsNode(dest_); + if (!destNode.get()) { + std::string errorMsg = "Plug(Move) command failed. The destination path "; + errorMsg += dest_; + errorMsg += " does not exist on server"; + throw std::runtime_error(errorMsg); + } + } + else { + if (!src_node->isSuite()) { + throw std::runtime_error("::Destination path can only be empty when moving a whole suite to a new server"); + } + } + + if (destNode.get()) { + + // The destNode containing suite may be in a handle + SuiteChanged0 suiteChanged(destNode); + + // If the destination is task, replace with its parent + Node* thedestNode = destNode.get(); + if (thedestNode->isTask()) + thedestNode = thedestNode->parent(); + + // check its ok to add + std::string errorMsg; + if (!thedestNode->isAddChildOk(src_node.get(), errorMsg)) { + std::string msg = "Plug(Move) command failed. "; + msg += errorMsg; + throw std::runtime_error(msg); + } + + // pass ownership + if (!thedestNode->addChild(src_node)) { + // This should never fail !!!! else we have lost/ and leaked source node !!!! + throw std::runtime_error("Fatal error plug(move) command failed. cannot addChild"); + } + + add_node_for_edit_history(destNode); + } + else { + + if (!src_node->isSuite()) + throw std::runtime_error("plug(move): Source node was expected to be a suite"); + + // convert node_ptr to suite_ptr + suite_ptr the_source_suite = std::dynamic_pointer_cast(src_node); + + // The sourceSuite may be in a handle or pre-registered suite + SuiteChanged suiteChanged(the_source_suite); + + defs->addSuite(the_source_suite); + + add_node_for_edit_history(the_source_suite); + } + + defs->set_most_significant_state(); + + // Ownership for src_node has been passed on. + return PreAllocatedReply::ok_cmd(); +} + +const char* MoveCmd::arg() { + return "move"; +} +const char* MoveCmd::desc() { + return "The move command is an internal cmd, Called by the plug cmd. Does not appear on public api."; +} + +void MoveCmd::addOption(boost::program_options::options_description& desc) const { + desc.add_options()(MoveCmd::arg(), po::value>()->multitoken(), MoveCmd::desc()); +} + +void MoveCmd::create(Cmd_ptr&, boost::program_options::variables_map&, AbstractClientEnv*) const { + assert(false); +} + +std::ostream& operator<<(std::ostream& os, const MoveCmd& c) { + std::string ret; + c.print(ret); + os << ret; + return os; +} + +CEREAL_REGISTER_TYPE(MoveCmd) +CEREAL_REGISTER_DYNAMIC_INIT(MoveCmd) diff --git a/Base/src/ecflow/base/cts/user/MoveCmd.hpp b/Base/src/ecflow/base/cts/user/MoveCmd.hpp new file mode 100644 index 000000000..51679d067 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/MoveCmd.hpp @@ -0,0 +1,67 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_MoveCmd_HPP +#define ecflow_base_cts_user_MoveCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +class MoveCmd final : public UserCmd { +public: + MoveCmd(const std::pair& host_port, Node* src, const std::string& dest); + MoveCmd(); + ~MoveCmd() override; + + Node* source() const; + const std::string& src_node() const { return src_node_; } + const std::string& dest() const { return dest_; } + + bool handleRequestIsTestable() const override { return false; } + bool isWrite() const override { return true; } + + void print(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + + bool check_source() const; + +private: + std::string src_node_; + std::string src_host_; + std::string src_port_; + std::string src_path_; + std::string dest_; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), + CEREAL_NVP(src_node_), + CEREAL_NVP(src_host_), + CEREAL_NVP(src_port_), + CEREAL_NVP(src_path_), + CEREAL_NVP(dest_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const MoveCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(MoveCmd) + +#endif /* ecflow_base_cts_user_MoveCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/OrderNodeCmd.cpp b/Base/src/ecflow/base/cts/user/OrderNodeCmd.cpp similarity index 96% rename from Base/src/ecflow/base/cts/OrderNodeCmd.cpp rename to Base/src/ecflow/base/cts/user/OrderNodeCmd.cpp index 2c67e5029..3f70656ab 100644 --- a/Base/src/ecflow/base/cts/OrderNodeCmd.cpp +++ b/Base/src/ecflow/base/cts/user/OrderNodeCmd.cpp @@ -8,12 +8,13 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/OrderNodeCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Node.hpp" @@ -124,3 +125,6 @@ std::ostream& operator<<(std::ostream& os, const OrderNodeCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(OrderNodeCmd) +CEREAL_REGISTER_DYNAMIC_INIT(OrderNodeCmd) diff --git a/Base/src/ecflow/base/cts/user/OrderNodeCmd.hpp b/Base/src/ecflow/base/cts/user/OrderNodeCmd.hpp new file mode 100644 index 000000000..2ba749c7f --- /dev/null +++ b/Base/src/ecflow/base/cts/user/OrderNodeCmd.hpp @@ -0,0 +1,56 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_OrderNodeCmd_HPP +#define ecflow_base_cts_user_OrderNodeCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" +#include "ecflow/core/NOrder.hpp" + +class OrderNodeCmd final : public UserCmd { +public: + OrderNodeCmd(const std::string& absNodepath, NOrder::Order op) : absNodepath_(absNodepath), option_(op) {} + OrderNodeCmd() = default; + + const std::string& absNodepath() const { return absNodepath_; } + NOrder::Order option() const { return option_; } + + bool isWrite() const override { return true; } + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; + +private: + std::string absNodepath_; + NOrder::Order option_{NOrder::TOP}; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(absNodepath_), CEREAL_NVP(option_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const OrderNodeCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(OrderNodeCmd) + +#endif /* ecflow_base_cts_user_OrderNodeCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/PathsCmd.cpp b/Base/src/ecflow/base/cts/user/PathsCmd.cpp similarity index 99% rename from Base/src/ecflow/base/cts/PathsCmd.cpp rename to Base/src/ecflow/base/cts/user/PathsCmd.cpp index 3b230455e..eca879c3b 100644 --- a/Base/src/ecflow/base/cts/PathsCmd.cpp +++ b/Base/src/ecflow/base/cts/user/PathsCmd.cpp @@ -8,13 +8,16 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/PathsCmd.hpp" + #include #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/cts/user/DeleteCmd.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/node/Defs.hpp" @@ -681,3 +684,6 @@ std::ostream& operator<<(std::ostream& os, const PathsCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(PathsCmd) +CEREAL_REGISTER_DYNAMIC_INIT(PathsCmd) diff --git a/Base/src/ecflow/base/cts/user/PathsCmd.hpp b/Base/src/ecflow/base/cts/user/PathsCmd.hpp new file mode 100644 index 000000000..b7c0f2f4b --- /dev/null +++ b/Base/src/ecflow/base/cts/user/PathsCmd.hpp @@ -0,0 +1,69 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_PathsCmd_HPP +#define ecflow_base_cts_user_PathsCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +// DELETE If paths_ empty will delete all suites (beware) else will delete the chosen nodes. +class PathsCmd final : public UserCmd { +public: + enum Api { NO_CMD, SUSPEND, RESUME, KILL, STATUS, CHECK, EDIT_HISTORY, ARCHIVE, RESTORE }; + + PathsCmd(Api api, const std::vector& paths, bool force = false) + : api_(api), + paths_(paths), + force_(force) {} + PathsCmd(Api api, const std::string& absNodePath, bool force = false); + explicit PathsCmd(Api api) : api_(api) { assert(api != NO_CMD); } + PathsCmd() = default; + + Api api() const { return api_; } + const std::vector& paths() const { return paths_; } + bool force() const { return force_; } + + void print(std::string&) const override; + std::string print_short() const override; + void print_only(std::string&) const override; + void print(std::string& os, const std::string& path) const override; + + bool equals(ClientToServerCmd*) const override; + bool isWrite() const override; + + const char* theArg() const override; + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; + void cleanup() override { std::vector().swap(paths_); } /// run in the server, after handlerequest + + void my_print(std::string& os, const std::vector& paths) const; + void my_print_only(std::string& os, const std::vector& paths) const; + +private: + Api api_{NO_CMD}; + std::vector paths_; + bool force_{false}; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(api_), CEREAL_NVP(paths_), CEREAL_NVP(force_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const PathsCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(PathsCmd) + +#endif /* ecflow_base_cts_user_PathsCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/PlugCmd.cpp b/Base/src/ecflow/base/cts/user/PlugCmd.cpp similarity index 69% rename from Base/src/ecflow/base/cts/PlugCmd.cpp rename to Base/src/ecflow/base/cts/user/PlugCmd.cpp index 6d1dac2df..4c64c0a48 100644 --- a/Base/src/ecflow/base/cts/PlugCmd.cpp +++ b/Base/src/ecflow/base/cts/user/PlugCmd.cpp @@ -8,17 +8,19 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/PlugCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" #include "ecflow/base/Client.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/cts/user/MoveCmd.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/NodePath.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Family.hpp" -#include "ecflow/node/Suite.hpp" #include "ecflow/node/SuiteChanged.hpp" #ifdef ECF_OPENSSL #include "ecflow/base/Openssl.hpp" @@ -30,28 +32,7 @@ using namespace std; using namespace boost; namespace po = boost::program_options; -//======================================================================================= - -bool PlugCmd::equals(ClientToServerCmd* rhs) const { - auto* the_rhs = dynamic_cast(rhs); - if (!the_rhs) - return false; - if (source_ != the_rhs->source()) { - return false; - } - if (dest_ != the_rhs->dest()) { - return false; - } - return UserCmd::equals(rhs); -} - -void PlugCmd::print(std::string& os) const { - user_cmd(os, CtsApi::to_string(CtsApi::plug(source_, dest_))); -} - -void PlugCmd::print_only(std::string& os) const { - os += CtsApi::to_string(CtsApi::plug(source_, dest_)); -} +namespace { /// Class to manage locking: Only unlock if acquired the lock, class Lock { @@ -78,6 +59,31 @@ static void restore(NodeContainer* container) { } } +} // namespace + +//======================================================================================= + +bool PlugCmd::equals(ClientToServerCmd* rhs) const { + auto* the_rhs = dynamic_cast(rhs); + if (!the_rhs) + return false; + if (source_ != the_rhs->source()) { + return false; + } + if (dest_ != the_rhs->dest()) { + return false; + } + return UserCmd::equals(rhs); +} + +void PlugCmd::print(std::string& os) const { + user_cmd(os, CtsApi::to_string(CtsApi::plug(source_, dest_))); +} + +void PlugCmd::print_only(std::string& os) const { + os += CtsApi::to_string(CtsApi::plug(source_, dest_)); +} + STC_Cmd_ptr PlugCmd::doHandleRequest(AbstractServer* as) const { as->update_stats().plug_++; Defs* defs = as->defs().get(); @@ -292,161 +298,5 @@ void PlugCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, Ab cmd = std::make_shared(sourceNode, destNode); } -// =================================================================================== - -MoveCmd::MoveCmd(const std::pair& host_port, Node* src, const std::string& dest) - : src_node_(src->print(PrintStyle::NET)), - src_host_(host_port.first), - src_port_(host_port.second), - src_path_(src->absNodePath()), - dest_(dest) { -} - -MoveCmd::MoveCmd() = default; -MoveCmd::~MoveCmd() = default; - -bool MoveCmd::equals(ClientToServerCmd* rhs) const { - auto* the_rhs = dynamic_cast(rhs); - if (!the_rhs) - return false; - if (dest_ != the_rhs->dest()) { - return false; - } - if (src_node_ != the_rhs->src_node()) { - return false; - } - return UserCmd::equals(rhs); -} - -void MoveCmd::print(std::string& os) const { - std::string ss; - ss += "Plug(Move) source("; - ss += src_host_; - ss += ":"; - ss += src_port_; - ss += ":"; - ss += src_path_; - ss += ") destination("; - ss += dest_; - ss += ")"; - user_cmd(os, ss); -} - -bool MoveCmd::check_source() const { - return !src_node_.empty(); -} - -STC_Cmd_ptr MoveCmd::doHandleRequest(AbstractServer* as) const { - Defs* defs = as->defs().get(); - - Lock lock(user(), as); - if (!lock.ok()) { - std::string errorMsg = "Plug(Move) command failed. User "; - errorMsg += as->lockedUser(); - errorMsg += " already has an exclusive lock"; - throw std::runtime_error(errorMsg); - } - - if (!check_source()) { - throw std::runtime_error("Plug(Move) command failed. No source specified"); - } - - std::string error_msg; - node_ptr src_node = Node::create(src_node_, error_msg); - if (!error_msg.empty() || !src_node) { - throw std::runtime_error("Plug(Move) command failed. Error in source:\n" + error_msg); - } - - // destNode can be NULL when we are moving a suite - node_ptr destNode; - if (!dest_.empty()) { - - destNode = defs->findAbsNode(dest_); - if (!destNode.get()) { - std::string errorMsg = "Plug(Move) command failed. The destination path "; - errorMsg += dest_; - errorMsg += " does not exist on server"; - throw std::runtime_error(errorMsg); - } - } - else { - if (!src_node->isSuite()) { - throw std::runtime_error("::Destination path can only be empty when moving a whole suite to a new server"); - } - } - - if (destNode.get()) { - - // The destNode containing suite may be in a handle - SuiteChanged0 suiteChanged(destNode); - - // If the destination is task, replace with its parent - Node* thedestNode = destNode.get(); - if (thedestNode->isTask()) - thedestNode = thedestNode->parent(); - - // check its ok to add - std::string errorMsg; - if (!thedestNode->isAddChildOk(src_node.get(), errorMsg)) { - std::string msg = "Plug(Move) command failed. "; - msg += errorMsg; - throw std::runtime_error(msg); - } - - // pass ownership - if (!thedestNode->addChild(src_node)) { - // This should never fail !!!! else we have lost/ and leaked source node !!!! - throw std::runtime_error("Fatal error plug(move) command failed. cannot addChild"); - } - - add_node_for_edit_history(destNode); - } - else { - - if (!src_node->isSuite()) - throw std::runtime_error("plug(move): Source node was expected to be a suite"); - - // convert node_ptr to suite_ptr - suite_ptr the_source_suite = std::dynamic_pointer_cast(src_node); - - // The sourceSuite may be in a handle or pre-registered suite - SuiteChanged suiteChanged(the_source_suite); - - defs->addSuite(the_source_suite); - - add_node_for_edit_history(the_source_suite); - } - - defs->set_most_significant_state(); - - // Ownership for src_node has been passed on. - return PreAllocatedReply::ok_cmd(); -} - -const char* MoveCmd::arg() { - return "move"; -} -const char* MoveCmd::desc() { - return "The move command is an internal cmd, Called by the plug cmd. Does not appear on public api."; -} - -void MoveCmd::addOption(boost::program_options::options_description& desc) const { - desc.add_options()(MoveCmd::arg(), po::value>()->multitoken(), MoveCmd::desc()); -} - -void MoveCmd::create(Cmd_ptr&, boost::program_options::variables_map&, AbstractClientEnv*) const { - assert(false); -} - -std::ostream& operator<<(std::ostream& os, const PlugCmd& c) { - std::string ret; - c.print(ret); - os << ret; - return os; -} -std::ostream& operator<<(std::ostream& os, const MoveCmd& c) { - std::string ret; - c.print(ret); - os << ret; - return os; -} +CEREAL_REGISTER_TYPE(PlugCmd) +CEREAL_REGISTER_DYNAMIC_INIT(PlugCmd) diff --git a/Base/src/ecflow/base/cts/user/PlugCmd.hpp b/Base/src/ecflow/base/cts/user/PlugCmd.hpp new file mode 100644 index 000000000..6d5cfbf6d --- /dev/null +++ b/Base/src/ecflow/base/cts/user/PlugCmd.hpp @@ -0,0 +1,57 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_PlugCmd_HPP +#define ecflow_base_cts_user_PlugCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +class PlugCmd final : public UserCmd { +public: + PlugCmd(const std::string& source, const std::string& dest) : source_(source), dest_(dest) {} + PlugCmd() = default; + + // Uses by equals only + const std::string& source() const { return source_; } + const std::string& dest() const { return dest_; } + + int timeout() const override { return 120; } + bool handleRequestIsTestable() const override { return false; } + bool isWrite() const override { return true; } + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + +private: + std::string source_; + std::string dest_; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(source_), CEREAL_NVP(dest_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const PlugCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(PlugCmd) + +#endif /* ecflow_base_cts_user_PlugCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/QueryCmd.cpp b/Base/src/ecflow/base/cts/user/QueryCmd.cpp similarity index 98% rename from Base/src/ecflow/base/cts/QueryCmd.cpp rename to Base/src/ecflow/base/cts/user/QueryCmd.cpp index 6063bacb5..4182e0612 100644 --- a/Base/src/ecflow/base/cts/QueryCmd.cpp +++ b/Base/src/ecflow/base/cts/user/QueryCmd.cpp @@ -8,13 +8,15 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/QueryCmd.hpp" + #include #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/core/Extract.hpp" #include "ecflow/node/Defs.hpp" @@ -331,3 +333,6 @@ std::ostream& operator<<(std::ostream& os, const QueryCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(QueryCmd) +CEREAL_REGISTER_DYNAMIC_INIT(QueryCmd) diff --git a/Base/src/ecflow/base/cts/user/QueryCmd.hpp b/Base/src/ecflow/base/cts/user/QueryCmd.hpp new file mode 100644 index 000000000..d29e03718 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/QueryCmd.hpp @@ -0,0 +1,71 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_QueryCmd_HPP +#define ecflow_base_cts_user_QueryCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +class QueryCmd final : public UserCmd { +public: + QueryCmd(const std::string& query_type, + const std::string& path_to_attribute, + const std::string& attribute, + const std::string& path_to_task) + : query_type_(query_type), + path_to_attribute_(path_to_attribute), + attribute_(attribute), + path_to_task_(path_to_task) {} + QueryCmd() : UserCmd() {} + ~QueryCmd() override; + + const std::string& query_type() const { return query_type_; } + const std::string& path_to_attribute() const { return path_to_attribute_; } + const std::string& attribute() const { return attribute_; } + const std::string& path_to_task() const { return path_to_task_; } + + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + bool handleRequestIsTestable() const override { return false; } + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + +private: + std::string query_type_; // [ state | dstate | event | meter | label | trigger ] + std::string path_to_attribute_; + std::string attribute_; // [ event_name | meter_name | label_name | variable_name | trigger expression] empty for + // state and dstate + std::string path_to_task_; // The task the invoked this command, needed for logging + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), + CEREAL_NVP(query_type_), + CEREAL_NVP(path_to_attribute_), + CEREAL_NVP(attribute_), + CEREAL_NVP(path_to_task_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const QueryCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(QueryCmd) + +#endif /* ecflow_base_cts_user_QueryCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/ReplaceNodeCmd.cpp b/Base/src/ecflow/base/cts/user/ReplaceNodeCmd.cpp similarity index 98% rename from Base/src/ecflow/base/cts/ReplaceNodeCmd.cpp rename to Base/src/ecflow/base/cts/user/ReplaceNodeCmd.cpp index ac860eefd..a8c2f3c59 100644 --- a/Base/src/ecflow/base/cts/ReplaceNodeCmd.cpp +++ b/Base/src/ecflow/base/cts/user/ReplaceNodeCmd.cpp @@ -8,12 +8,13 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/ReplaceNodeCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Suite.hpp" @@ -236,3 +237,6 @@ std::ostream& operator<<(std::ostream& os, const ReplaceNodeCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(ReplaceNodeCmd) +CEREAL_REGISTER_DYNAMIC_INIT(ReplaceNodeCmd) diff --git a/Base/src/ecflow/base/cts/user/ReplaceNodeCmd.hpp b/Base/src/ecflow/base/cts/user/ReplaceNodeCmd.hpp new file mode 100644 index 000000000..249a7704e --- /dev/null +++ b/Base/src/ecflow/base/cts/user/ReplaceNodeCmd.hpp @@ -0,0 +1,71 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_ReplaceNodeCmd_HPP +#define ecflow_base_cts_user_ReplaceNodeCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +class ReplaceNodeCmd final : public UserCmd { +public: + ReplaceNodeCmd(const std::string& node_path, bool createNodesAsNeeded, defs_ptr client_defs, bool force); + ReplaceNodeCmd(const std::string& node_path, bool createNodesAsNeeded, const std::string& path_to_defs, bool force); + ReplaceNodeCmd() = default; + + const std::string& the_client_defs() const { return clientDefs_; } + const std::string& pathToNode() const { return pathToNode_; } + const std::string& path_to_defs() const { return path_to_defs_; } + bool createNodesAsNeeded() const { return createNodesAsNeeded_; } + bool force() const { return force_; } + + bool isWrite() const override { return true; } + int timeout() const override { return 300; } + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + + // void set_client_env(const std::vector >& env ) { client_env_ = env;} // only + // used in test + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; + void cleanup() override { std::string().swap(clientDefs_); } /// run in the server, after command send to client + + bool createNodesAsNeeded_{false}; + bool force_{false}; + std::string pathToNode_; + std::string path_to_defs_; // Can be empty if defs loaded in memory via python api + std::string clientDefs_; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), + CEREAL_NVP(createNodesAsNeeded_), + CEREAL_NVP(force_), + CEREAL_NVP(pathToNode_), + CEREAL_NVP(path_to_defs_), + CEREAL_NVP(clientDefs_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const ReplaceNodeCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(ReplaceNodeCmd) + +#endif /* ecflow_base_cts_user_ReplaceNodeCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/RequeueNodeCmd.cpp b/Base/src/ecflow/base/cts/user/RequeueNodeCmd.cpp similarity index 97% rename from Base/src/ecflow/base/cts/RequeueNodeCmd.cpp rename to Base/src/ecflow/base/cts/user/RequeueNodeCmd.cpp index aa9b77a38..9808bdb59 100644 --- a/Base/src/ecflow/base/cts/RequeueNodeCmd.cpp +++ b/Base/src/ecflow/base/cts/user/RequeueNodeCmd.cpp @@ -8,12 +8,14 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/RequeueNodeCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/node/Suite.hpp" #include "ecflow/node/SuiteChanged.hpp" @@ -252,3 +254,6 @@ std::ostream& operator<<(std::ostream& os, const RequeueNodeCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(RequeueNodeCmd) +CEREAL_REGISTER_DYNAMIC_INIT(RequeueNodeCmd) diff --git a/Base/src/ecflow/base/cts/user/RequeueNodeCmd.hpp b/Base/src/ecflow/base/cts/user/RequeueNodeCmd.hpp new file mode 100644 index 000000000..2c2299b5d --- /dev/null +++ b/Base/src/ecflow/base/cts/user/RequeueNodeCmd.hpp @@ -0,0 +1,66 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_RequeueNodeCmd_HPP +#define ecflow_base_cts_user_RequeueNodeCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +class RequeueNodeCmd final : public UserCmd { +public: + enum Option { NO_OPTION, ABORT, FORCE }; + + explicit RequeueNodeCmd(const std::vector& paths, Option op = NO_OPTION) + : paths_(paths), + option_(op) {} + + explicit RequeueNodeCmd(const std::string& absNodepath, Option op = NO_OPTION) + : paths_(std::vector(1, absNodepath)), + option_(op) {} + + RequeueNodeCmd() = default; + + const std::vector& paths() const { return paths_; } + Option option() const { return option_; } + + bool isWrite() const override { return true; } + void print(std::string&) const override; + void print_only(std::string&) const override; + void print(std::string& os, const std::string& path) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; + void cleanup() override { std::vector().swap(paths_); } /// run in the server, after doHandleRequest + +private: + mutable std::vector paths_; // mutable to allow swap to clear & reclaim memory, as soon as possible + Option option_{NO_OPTION}; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(paths_), CEREAL_NVP(option_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const RequeueNodeCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(RequeueNodeCmd) + +#endif /* ecflow_base_cts_user_RequeueNodeCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/RunNodeCmd.cpp b/Base/src/ecflow/base/cts/user/RunNodeCmd.cpp similarity index 97% rename from Base/src/ecflow/base/cts/RunNodeCmd.cpp rename to Base/src/ecflow/base/cts/user/RunNodeCmd.cpp index 23a4e02e4..0e15d00eb 100644 --- a/Base/src/ecflow/base/cts/RunNodeCmd.cpp +++ b/Base/src/ecflow/base/cts/user/RunNodeCmd.cpp @@ -8,12 +8,14 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/RunNodeCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/node/JobsParam.hpp" #include "ecflow/node/Suite.hpp" @@ -180,3 +182,6 @@ std::ostream& operator<<(std::ostream& os, const RunNodeCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(RunNodeCmd) +CEREAL_REGISTER_DYNAMIC_INIT(RunNodeCmd) diff --git a/Base/src/ecflow/base/cts/user/RunNodeCmd.hpp b/Base/src/ecflow/base/cts/user/RunNodeCmd.hpp new file mode 100644 index 000000000..00b688659 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/RunNodeCmd.hpp @@ -0,0 +1,68 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_RunNodeCmd_HPP +#define ecflow_base_cts_user_RunNodeCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +// The absNodepath must be provided +class RunNodeCmd final : public UserCmd { +public: + RunNodeCmd(const std::string& absNodepath, bool force, bool test = false) + : paths_(std::vector(1, absNodepath)), + force_(force), + test_(test) {} + + RunNodeCmd(const std::vector& paths, bool force, bool test = false) + : paths_(paths), + force_(force), + test_(test) {} + + RunNodeCmd() = default; + + const std::vector& paths() const { return paths_; } + bool force() const { return force_; } + + bool isWrite() const override { return true; } + void print(std::string&) const override; + void print_only(std::string&) const override; + void print(std::string& os, const std::string& path) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; + void cleanup() override { std::vector().swap(paths_); } /// run in the server, after doHandleRequest + +private: + std::vector paths_; + bool force_{false}; + bool test_{false}; // only for test, hence we don't serialise this + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(paths_), CEREAL_NVP(force_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const RunNodeCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(RunNodeCmd) + +#endif /* ecflow_base_cts_user_RunNodeCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/ServerVersionCmd.cpp b/Base/src/ecflow/base/cts/user/ServerVersionCmd.cpp similarity index 90% rename from Base/src/ecflow/base/cts/ServerVersionCmd.cpp rename to Base/src/ecflow/base/cts/user/ServerVersionCmd.cpp index 966e46213..134543f5d 100644 --- a/Base/src/ecflow/base/cts/ServerVersionCmd.cpp +++ b/Base/src/ecflow/base/cts/user/ServerVersionCmd.cpp @@ -8,10 +8,12 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/ServerVersionCmd.hpp" + #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/Version.hpp" using namespace ecf; @@ -71,3 +73,6 @@ std::ostream& operator<<(std::ostream& os, const ServerVersionCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(ServerVersionCmd) +CEREAL_REGISTER_DYNAMIC_INIT(ServerVersionCmd) diff --git a/Base/src/ecflow/base/cts/user/ServerVersionCmd.hpp b/Base/src/ecflow/base/cts/user/ServerVersionCmd.hpp new file mode 100644 index 000000000..ce288d245 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/ServerVersionCmd.hpp @@ -0,0 +1,45 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_ServerVersionCmd_HPP +#define ecflow_base_cts_user_ServerVersionCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +// ======================================================================== +// This Command should NEVER be changed +// This will allow new client to ask OLD server about its version +// ======================================================================== +class ServerVersionCmd final : public UserCmd { +public: + ServerVersionCmd() = default; + + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + const char* theArg() const override; + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this)); + } +}; + +std::ostream& operator<<(std::ostream& os, const ServerVersionCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(ServerVersionCmd) + +#endif /* ecflow_base_cts_user_ServerVersionCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/ShowCmd.cpp b/Base/src/ecflow/base/cts/user/ShowCmd.cpp similarity index 96% rename from Base/src/ecflow/base/cts/ShowCmd.cpp rename to Base/src/ecflow/base/cts/user/ShowCmd.cpp index 992fa814a..852395e1f 100644 --- a/Base/src/ecflow/base/cts/ShowCmd.cpp +++ b/Base/src/ecflow/base/cts/user/ShowCmd.cpp @@ -8,11 +8,13 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/ShowCmd.hpp" + #include #include #include "ecflow/base/AbstractClientEnv.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" using namespace ecf; using namespace std; @@ -107,3 +109,6 @@ std::ostream& operator<<(std::ostream& os, const ShowCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(ShowCmd) +CEREAL_REGISTER_DYNAMIC_INIT(ShowCmd) diff --git a/Base/src/ecflow/base/cts/user/ShowCmd.hpp b/Base/src/ecflow/base/cts/user/ShowCmd.hpp new file mode 100644 index 000000000..12205c375 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/ShowCmd.hpp @@ -0,0 +1,58 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_ShowCmd_HPP +#define ecflow_base_cts_user_ShowCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" + +// Does Nothing in the server, however allows client code to display the +// returned Defs in different showStyles +// This class has no need for persistence, i.e client side only +class ShowCmd final : public UserCmd { +public: + explicit ShowCmd(PrintStyle::Type_t s = PrintStyle::DEFS) : style_(s) {} + + // returns the showStyle + bool show_cmd() const override { return true; } + PrintStyle::Type_t show_style() const override { return style_; } + + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override { return arg(); } + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + static const char* arg(); // used for argument parsing + static const char* desc(); // The description of the argument as provided to user + + // The Show Cmd is processed on the client side, + // Likewise the doHandleRequest does nothing, + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + + PrintStyle::Type_t style_; + + // Persistence is still required since show command can be *USED* in a *GROUP* command + // However its ONLY used on the client side, hence no need to serialise data members + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this)); + } +}; + +std::ostream& operator<<(std::ostream& os, const ShowCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(ShowCmd) + +#endif /* ecflow_base_cts_user_ShowCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/UserCmd.cpp b/Base/src/ecflow/base/cts/user/UserCmd.cpp similarity index 99% rename from Base/src/ecflow/base/cts/UserCmd.cpp rename to Base/src/ecflow/base/cts/user/UserCmd.cpp index 2681d28b9..87d8271a3 100644 --- a/Base/src/ecflow/base/cts/UserCmd.cpp +++ b/Base/src/ecflow/base/cts/user/UserCmd.cpp @@ -8,13 +8,13 @@ * nor does it submit to any jurisdiction. */ -#include /* tolower */ +#include "ecflow/base/cts/user/UserCmd.hpp" + #include #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" #include "ecflow/core/Str.hpp" #include "ecflow/core/User.hpp" diff --git a/Base/src/ecflow/base/cts/user/UserCmd.hpp b/Base/src/ecflow/base/cts/user/UserCmd.hpp new file mode 100644 index 000000000..975fe9bec --- /dev/null +++ b/Base/src/ecflow/base/cts/user/UserCmd.hpp @@ -0,0 +1,65 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_UserCmd_HPP +#define ecflow_base_cts_user_UserCmd_HPP + +#include "ecflow/base/Cmd.hpp" +#include "ecflow/base/cts/ClientToServerCmd.hpp" + +//================================================================================= +// User Command +// ================================================================================ +class UserCmd : public ClientToServerCmd { +public: + UserCmd() = default; + + const std::string& user() const { return user_; } + const std::string& passwd() const { return pswd_; } + + void setup_user_authentification(const std::string& user, const std::string& passwd) override; + bool setup_user_authentification(AbstractClientEnv&) override; + void setup_user_authentification() override; + +protected: + bool equals(ClientToServerCmd*) const override; + bool authenticate(AbstractServer*, STC_Cmd_ptr&) const override; + bool do_authenticate(AbstractServer* as, STC_Cmd_ptr&, const std::string& path) const; + bool do_authenticate(AbstractServer* as, STC_Cmd_ptr&, const std::vector& paths) const; + + /// Prompt the user for confirmation: If user responds with no, will exit client + static void prompt_for_confirmation(const std::string& prompt); + + /// All user commands will be pre_fixed with "--" and post_fixed with :user@host + void user_cmd(std::string& os, const std::string& the_cmd) const; + + static int time_out_for_load_sync_and_get(); + + // The order is preserved during the split. Paths assumed to start with '/' char + static void split_args_to_options_and_paths(const std::vector& args, + std::vector& options, + std::vector& paths, + bool treat_colon_in_path_as_path = false); + +private: + std::string user_; + std::string pswd_; + bool cu_ = false; // custom user, i.e used set_user_name() || ECF_USER || --user -> only check this password + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), CEREAL_NVP(user_)); + CEREAL_OPTIONAL_NVP(ar, pswd_, [this]() { return !pswd_.empty(); }); // conditionally save + CEREAL_OPTIONAL_NVP(ar, cu_, [this]() { return cu_; }); // conditionally save + } +}; + +#endif /* ecflow_base_cts_user_UserCmd_HPP */ diff --git a/Base/src/ecflow/base/cts/ZombieCmd.cpp b/Base/src/ecflow/base/cts/user/ZombieCmd.cpp similarity index 98% rename from Base/src/ecflow/base/cts/ZombieCmd.cpp rename to Base/src/ecflow/base/cts/user/ZombieCmd.cpp index 71dff9a4f..38a0a5413 100644 --- a/Base/src/ecflow/base/cts/ZombieCmd.cpp +++ b/Base/src/ecflow/base/cts/user/ZombieCmd.cpp @@ -8,12 +8,14 @@ * nor does it submit to any jurisdiction. */ +#include "ecflow/base/cts/user/ZombieCmd.hpp" + #include #include "ecflow/base/AbstractClientEnv.hpp" #include "ecflow/base/AbstractServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Task.hpp" @@ -341,3 +343,6 @@ std::ostream& operator<<(std::ostream& os, const ZombieCmd& c) { os << ret; return os; } + +CEREAL_REGISTER_TYPE(ZombieCmd) +CEREAL_REGISTER_DYNAMIC_INIT(ZombieCmd) diff --git a/Base/src/ecflow/base/cts/user/ZombieCmd.hpp b/Base/src/ecflow/base/cts/user/ZombieCmd.hpp new file mode 100644 index 000000000..90a9a43b4 --- /dev/null +++ b/Base/src/ecflow/base/cts/user/ZombieCmd.hpp @@ -0,0 +1,65 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#ifndef ecflow_base_cts_user_ZombieCmd_HPP +#define ecflow_base_cts_user_ZombieCmd_HPP + +#include "ecflow/base/cts/user/UserCmd.hpp" +#include "ecflow/core/User.hpp" + +class ZombieCmd final : public UserCmd { +public: + ZombieCmd(ecf::User::Action uc, + const std::vector& paths, + const std::string& process_id, + const std::string& password) + : user_action_(uc), + process_id_(process_id), + password_(password), + paths_(paths) {} + explicit ZombieCmd(ecf::User::Action uc = ecf::User::BLOCK) : user_action_(uc) {} + + const std::vector& paths() const { return paths_; } + const std::string& process_or_remote_id() const { return process_id_; } + const std::string& password() const { return password_; } + + void print(std::string&) const override; + void print_only(std::string&) const override; + bool equals(ClientToServerCmd*) const override; + + const char* theArg() const override; + void addOption(boost::program_options::options_description& desc) const override; + void create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, AbstractClientEnv* clientEnv) const override; + +private: + STC_Cmd_ptr doHandleRequest(AbstractServer*) const override; + void cleanup() override { std::vector().swap(paths_); } /// run in the server, after handlerequest + + ecf::User::Action user_action_; + std::string process_id_; // should be empty for multiple paths and when using CLI + std::string password_; // should be empty for multiple paths and when using CLI + std::vector paths_; + + friend class cereal::access; + template + void serialize(Archive& ar, std::uint32_t const /*version*/) { + ar(cereal::base_class(this), + CEREAL_NVP(user_action_), + CEREAL_NVP(process_id_), + CEREAL_NVP(password_), + CEREAL_NVP(paths_)); + } +}; + +std::ostream& operator<<(std::ostream& os, const ZombieCmd&); + +CEREAL_FORCE_DYNAMIC_INIT(ZombieCmd) + +#endif /* ecflow_base_cts_user_ZombieCmd_HPP */ diff --git a/Base/test/TestAlterCmd.cpp b/Base/test/TestAlterCmd.cpp index 5a0b3ad6b..57e6e4fb1 100644 --- a/Base/test/TestAlterCmd.cpp +++ b/Base/test/TestAlterCmd.cpp @@ -13,7 +13,9 @@ #include "TestHelper.hpp" #include "ecflow/attribute/GenericAttr.hpp" #include "ecflow/attribute/LateAttr.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/AlterCmd.hpp" +#include "ecflow/base/cts/user/PathsCmd.hpp" +#include "ecflow/base/cts/user/RequeueNodeCmd.hpp" #include "ecflow/core/Ecf.hpp" #include "ecflow/core/Str.hpp" #include "ecflow/node/Defs.hpp" diff --git a/Base/test/TestArchiveAndRestoreCmd.cpp b/Base/test/TestArchiveAndRestoreCmd.cpp index 7922a8ae4..8299319dd 100644 --- a/Base/test/TestArchiveAndRestoreCmd.cpp +++ b/Base/test/TestArchiveAndRestoreCmd.cpp @@ -11,7 +11,10 @@ #include #include "TestHelper.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/BeginCmd.hpp" +#include "ecflow/base/cts/user/DeleteCmd.hpp" +#include "ecflow/base/cts/user/PathsCmd.hpp" +#include "ecflow/base/cts/user/RequeueNodeCmd.hpp" #include "ecflow/core/File.hpp" #include "ecflow/core/Pid.hpp" #include "ecflow/core/Str.hpp" diff --git a/Base/test/TestClientHandleCmd.cpp b/Base/test/TestClientHandleCmd.cpp index 184756151..7af470560 100644 --- a/Base/test/TestClientHandleCmd.cpp +++ b/Base/test/TestClientHandleCmd.cpp @@ -11,7 +11,8 @@ #include #include "TestHelper.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/ClientHandleCmd.hpp" +#include "ecflow/base/cts/user/OrderNodeCmd.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/node/Suite.hpp" diff --git a/Base/test/TestCmd.cpp b/Base/test/TestCmd.cpp index 1cec27c87..23534d149 100644 --- a/Base/test/TestCmd.cpp +++ b/Base/test/TestCmd.cpp @@ -14,6 +14,9 @@ #include #include "TestHelper.hpp" +#include "ecflow/base/cts/task/AbortCmd.hpp" +#include "ecflow/base/cts/user/BeginCmd.hpp" +#include "ecflow/base/cts/user/CtsCmd.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/core/Str.hpp" #include "ecflow/node/Defs.hpp" diff --git a/Base/test/TestDeleteNodeCmd.cpp b/Base/test/TestDeleteNodeCmd.cpp index d9667c990..3cbc94749 100644 --- a/Base/test/TestDeleteNodeCmd.cpp +++ b/Base/test/TestDeleteNodeCmd.cpp @@ -12,7 +12,8 @@ #include "MockServer.hpp" #include "MyDefsFixture.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/DeleteCmd.hpp" +#include "ecflow/base/cts/user/PathsCmd.hpp" #include "ecflow/base/stc/ServerToClientCmd.hpp" #include "ecflow/core/Str.hpp" diff --git a/Base/test/TestECFLOW-189.cpp b/Base/test/TestECFLOW-189.cpp index 33438ecf2..a258a1e35 100644 --- a/Base/test/TestECFLOW-189.cpp +++ b/Base/test/TestECFLOW-189.cpp @@ -11,7 +11,7 @@ #include #include "TestHelper.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/PathsCmd.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Family.hpp" #include "ecflow/node/Suite.hpp" diff --git a/Base/test/TestForceCmd.cpp b/Base/test/TestForceCmd.cpp index daaf5a5de..d5272813e 100644 --- a/Base/test/TestForceCmd.cpp +++ b/Base/test/TestForceCmd.cpp @@ -15,7 +15,9 @@ #include "MockServer.hpp" #include "MyDefsFixture.hpp" #include "TestHelper.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/BeginCmd.hpp" +#include "ecflow/base/cts/user/ForceCmd.hpp" +#include "ecflow/base/cts/user/RequeueNodeCmd.hpp" #include "ecflow/base/stc/ServerToClientCmd.hpp" #include "ecflow/core/PrintStyle.hpp" // IWYU pragma: keep #include "ecflow/node/System.hpp" diff --git a/Base/test/TestFreeDepCmd.cpp b/Base/test/TestFreeDepCmd.cpp index 74adf4b83..d315d84fa 100644 --- a/Base/test/TestFreeDepCmd.cpp +++ b/Base/test/TestFreeDepCmd.cpp @@ -13,7 +13,7 @@ #include #include "MockServer.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/FreeDepCmd.hpp" #include "ecflow/base/stc/ServerToClientCmd.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Expression.hpp" diff --git a/Base/test/TestInLimitAndLimit.cpp b/Base/test/TestInLimitAndLimit.cpp index 1164dc250..57f739fab 100644 --- a/Base/test/TestInLimitAndLimit.cpp +++ b/Base/test/TestInLimitAndLimit.cpp @@ -15,6 +15,9 @@ #include #include "TestHelper.hpp" +#include "ecflow/base/cts/user/BeginCmd.hpp" +#include "ecflow/base/cts/user/ForceCmd.hpp" +#include "ecflow/base/cts/user/RequeueNodeCmd.hpp" #include "ecflow/core/PrintStyle.hpp" // IWYU pragma: keep #include "ecflow/node/Defs.hpp" #include "ecflow/node/Family.hpp" diff --git a/Base/test/TestLogCmd.cpp b/Base/test/TestLogCmd.cpp index 3b9dd7058..ae6538021 100644 --- a/Base/test/TestLogCmd.cpp +++ b/Base/test/TestLogCmd.cpp @@ -11,7 +11,7 @@ #include #include "TestHelper.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/LogCmd.hpp" #include "ecflow/core/File.hpp" #include "ecflow/core/Log.hpp" #include "ecflow/core/Str.hpp" diff --git a/Base/test/TestMeterCmd.cpp b/Base/test/TestMeterCmd.cpp index 370cc84ab..e35edff15 100644 --- a/Base/test/TestMeterCmd.cpp +++ b/Base/test/TestMeterCmd.cpp @@ -14,6 +14,7 @@ #include #include "TestHelper.hpp" +#include "ecflow/base/cts/task/MeterCmd.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Family.hpp" #include "ecflow/node/Suite.hpp" diff --git a/Base/test/TestQueryCmd.cpp b/Base/test/TestQueryCmd.cpp index 3b79a2dd8..b42afaed7 100644 --- a/Base/test/TestQueryCmd.cpp +++ b/Base/test/TestQueryCmd.cpp @@ -13,6 +13,9 @@ #include #include "TestHelper.hpp" +#include "ecflow/base/cts/task/LabelCmd.hpp" +#include "ecflow/base/cts/user/PathsCmd.hpp" +#include "ecflow/base/cts/user/QueryCmd.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Family.hpp" #include "ecflow/node/Limit.hpp" diff --git a/Base/test/TestQueueCmd.cpp b/Base/test/TestQueueCmd.cpp index cc4b30e36..2e73f49a7 100644 --- a/Base/test/TestQueueCmd.cpp +++ b/Base/test/TestQueueCmd.cpp @@ -15,6 +15,7 @@ #include "TestHelper.hpp" #include "ecflow/attribute/QueueAttr.hpp" +#include "ecflow/base/cts/task/QueueCmd.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Family.hpp" #include "ecflow/node/Suite.hpp" diff --git a/Base/test/TestRequest.cpp b/Base/test/TestRequest.cpp index 05c72a220..ac7332cd0 100644 --- a/Base/test/TestRequest.cpp +++ b/Base/test/TestRequest.cpp @@ -13,6 +13,39 @@ #include "MyDefsFixture.hpp" #include "TestHelper.hpp" #include "ecflow/base/ServerToClientResponse.hpp" +#include "ecflow/base/cts/task/AbortCmd.hpp" +#include "ecflow/base/cts/task/CompleteCmd.hpp" +#include "ecflow/base/cts/task/CtsWaitCmd.hpp" +#include "ecflow/base/cts/task/EventCmd.hpp" +#include "ecflow/base/cts/task/InitCmd.hpp" +#include "ecflow/base/cts/task/LabelCmd.hpp" +#include "ecflow/base/cts/task/MeterCmd.hpp" +#include "ecflow/base/cts/task/QueueCmd.hpp" +#include "ecflow/base/cts/user/AlterCmd.hpp" +#include "ecflow/base/cts/user/BeginCmd.hpp" +#include "ecflow/base/cts/user/CFileCmd.hpp" +#include "ecflow/base/cts/user/CSyncCmd.hpp" +#include "ecflow/base/cts/user/CheckPtCmd.hpp" +#include "ecflow/base/cts/user/ClientHandleCmd.hpp" +#include "ecflow/base/cts/user/CtsCmd.hpp" +#include "ecflow/base/cts/user/CtsNodeCmd.hpp" +#include "ecflow/base/cts/user/EditScriptCmd.hpp" +#include "ecflow/base/cts/user/ForceCmd.hpp" +#include "ecflow/base/cts/user/FreeDepCmd.hpp" +#include "ecflow/base/cts/user/GroupCTSCmd.hpp" +#include "ecflow/base/cts/user/LoadDefsCmd.hpp" +#include "ecflow/base/cts/user/LogCmd.hpp" +#include "ecflow/base/cts/user/LogMessageCmd.hpp" +#include "ecflow/base/cts/user/OrderNodeCmd.hpp" +#include "ecflow/base/cts/user/PathsCmd.hpp" +#include "ecflow/base/cts/user/PlugCmd.hpp" +#include "ecflow/base/cts/user/QueryCmd.hpp" +#include "ecflow/base/cts/user/ReplaceNodeCmd.hpp" +#include "ecflow/base/cts/user/RequeueNodeCmd.hpp" +#include "ecflow/base/cts/user/RunNodeCmd.hpp" +#include "ecflow/base/cts/user/ServerVersionCmd.hpp" +#include "ecflow/base/cts/user/ShowCmd.hpp" +#include "ecflow/base/cts/user/ZombieCmd.hpp" #include "ecflow/base/stc/BlockClientZombieCmd.hpp" #include "ecflow/base/stc/DefsCmd.hpp" #include "ecflow/base/stc/ErrorCmd.hpp" diff --git a/Base/test/TestRequeueNodeCmd.cpp b/Base/test/TestRequeueNodeCmd.cpp index 304313c8f..274661952 100644 --- a/Base/test/TestRequeueNodeCmd.cpp +++ b/Base/test/TestRequeueNodeCmd.cpp @@ -11,7 +11,10 @@ #include #include "TestHelper.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/AlterCmd.hpp" +#include "ecflow/base/cts/user/ForceCmd.hpp" +#include "ecflow/base/cts/user/PathsCmd.hpp" +#include "ecflow/base/cts/user/RequeueNodeCmd.hpp" #include "ecflow/core/CalendarUpdateParams.hpp" #include "ecflow/core/File.hpp" #include "ecflow/core/Pid.hpp" diff --git a/Base/test/TestResolveDependencies.cpp b/Base/test/TestResolveDependencies.cpp index 720d347de..f4d15a46e 100644 --- a/Base/test/TestResolveDependencies.cpp +++ b/Base/test/TestResolveDependencies.cpp @@ -13,6 +13,9 @@ #include #include "TestHelper.hpp" +#include "ecflow/base/cts/task/MeterCmd.hpp" +#include "ecflow/base/cts/user/BeginCmd.hpp" +#include "ecflow/base/cts/user/CtsCmd.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/ExprAst.hpp" #include "ecflow/node/Family.hpp" diff --git a/Base/test/TestSSyncCmd.cpp b/Base/test/TestSSyncCmd.cpp index 2df443aef..8967d801e 100644 --- a/Base/test/TestSSyncCmd.cpp +++ b/Base/test/TestSSyncCmd.cpp @@ -13,7 +13,10 @@ #include "MockServer.hpp" #include "MyDefsFixture.hpp" #include "TestHelper.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/AlterCmd.hpp" +#include "ecflow/base/cts/user/DeleteCmd.hpp" +#include "ecflow/base/cts/user/OrderNodeCmd.hpp" +#include "ecflow/base/cts/user/PlugCmd.hpp" #include "ecflow/base/stc/SNewsCmd.hpp" #include "ecflow/base/stc/SSyncCmd.hpp" #include "ecflow/core/CalendarUpdateParams.hpp" diff --git a/Base/test/TestSSyncCmdOrder.cpp b/Base/test/TestSSyncCmdOrder.cpp index ae63feee3..5c1d6b0f4 100644 --- a/Base/test/TestSSyncCmdOrder.cpp +++ b/Base/test/TestSSyncCmdOrder.cpp @@ -12,7 +12,8 @@ #include "MockServer.hpp" #include "TestHelper.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/ClientHandleCmd.hpp" +#include "ecflow/base/cts/user/OrderNodeCmd.hpp" #include "ecflow/base/stc/SSyncCmd.hpp" #include "ecflow/core/Ecf.hpp" #include "ecflow/core/TestUtil.hpp" diff --git a/Base/test/TestSSyncCmd_CH1.cpp b/Base/test/TestSSyncCmd_CH1.cpp index 574a26ef6..e8be19815 100644 --- a/Base/test/TestSSyncCmd_CH1.cpp +++ b/Base/test/TestSSyncCmd_CH1.cpp @@ -12,7 +12,8 @@ #include "MockServer.hpp" #include "TestHelper.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/AlterCmd.hpp" +#include "ecflow/base/cts/user/ClientHandleCmd.hpp" #include "ecflow/base/stc/SNewsCmd.hpp" #include "ecflow/base/stc/SSyncCmd.hpp" #include "ecflow/core/Converter.hpp" diff --git a/Base/test/TestStatsCmd.cpp b/Base/test/TestStatsCmd.cpp index 7e7693529..37991f42c 100644 --- a/Base/test/TestStatsCmd.cpp +++ b/Base/test/TestStatsCmd.cpp @@ -13,6 +13,7 @@ #include "MockServer.hpp" #include "MyDefsFixture.hpp" #include "ecflow/base/ClientToServerRequest.hpp" +#include "ecflow/base/cts/user/CtsCmd.hpp" #include "ecflow/base/stc/ServerToClientCmd.hpp" #include "ecflow/core/Log.hpp" diff --git a/Client/src/ecflow/client/ClientCmdCache.hpp b/Client/src/ecflow/client/ClientCmdCache.hpp index 152a03854..0ff1b855c 100644 --- a/Client/src/ecflow/client/ClientCmdCache.hpp +++ b/Client/src/ecflow/client/ClientCmdCache.hpp @@ -11,7 +11,7 @@ #ifndef ecflow_client_ClientCmdCache_HPP #define ecflow_client_ClientCmdCache_HPP -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/CSyncCmd.hpp" class ClientCmdCache { public: diff --git a/Client/src/ecflow/client/ClientInvoker.cpp b/Client/src/ecflow/client/ClientInvoker.cpp index a3b228260..76f34aa6c 100644 --- a/Client/src/ecflow/client/ClientInvoker.cpp +++ b/Client/src/ecflow/client/ClientInvoker.cpp @@ -17,6 +17,39 @@ #include // requires boost date and time lib #include "ecflow/base/Client.hpp" +#include "ecflow/base/cts/task/AbortCmd.hpp" +#include "ecflow/base/cts/task/CompleteCmd.hpp" +#include "ecflow/base/cts/task/CtsWaitCmd.hpp" +#include "ecflow/base/cts/task/EventCmd.hpp" +#include "ecflow/base/cts/task/InitCmd.hpp" +#include "ecflow/base/cts/task/LabelCmd.hpp" +#include "ecflow/base/cts/task/MeterCmd.hpp" +#include "ecflow/base/cts/task/QueueCmd.hpp" +#include "ecflow/base/cts/user/AlterCmd.hpp" +#include "ecflow/base/cts/user/BeginCmd.hpp" +#include "ecflow/base/cts/user/CFileCmd.hpp" +#include "ecflow/base/cts/user/CSyncCmd.hpp" +#include "ecflow/base/cts/user/CheckPtCmd.hpp" +#include "ecflow/base/cts/user/ClientHandleCmd.hpp" +#include "ecflow/base/cts/user/CtsCmd.hpp" +#include "ecflow/base/cts/user/CtsNodeCmd.hpp" +#include "ecflow/base/cts/user/DeleteCmd.hpp" +#include "ecflow/base/cts/user/EditScriptCmd.hpp" +#include "ecflow/base/cts/user/ForceCmd.hpp" +#include "ecflow/base/cts/user/FreeDepCmd.hpp" +#include "ecflow/base/cts/user/GroupCTSCmd.hpp" +#include "ecflow/base/cts/user/LoadDefsCmd.hpp" +#include "ecflow/base/cts/user/LogCmd.hpp" +#include "ecflow/base/cts/user/LogMessageCmd.hpp" +#include "ecflow/base/cts/user/OrderNodeCmd.hpp" +#include "ecflow/base/cts/user/PathsCmd.hpp" +#include "ecflow/base/cts/user/PlugCmd.hpp" +#include "ecflow/base/cts/user/QueryCmd.hpp" +#include "ecflow/base/cts/user/ReplaceNodeCmd.hpp" +#include "ecflow/base/cts/user/RequeueNodeCmd.hpp" +#include "ecflow/base/cts/user/RunNodeCmd.hpp" +#include "ecflow/base/cts/user/ServerVersionCmd.hpp" +#include "ecflow/base/cts/user/ZombieCmd.hpp" #include "ecflow/core/Converter.hpp" #ifdef ECF_OPENSSL #include "ecflow/base/SslClient.hpp" diff --git a/Client/src/ecflow/client/ClientInvoker.hpp b/Client/src/ecflow/client/ClientInvoker.hpp index 49c73ef75..515fe8acd 100644 --- a/Client/src/ecflow/client/ClientInvoker.hpp +++ b/Client/src/ecflow/client/ClientInvoker.hpp @@ -15,8 +15,8 @@ #include "ecflow/base/Cmd.hpp" #include "ecflow/base/ServerReply.hpp" -#include "ecflow/base/cts/CtsApi.hpp" -#include "ecflow/base/cts/TaskApi.hpp" +#include "ecflow/base/cts/task/TaskApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" #include "ecflow/client/ClientEnvironment.hpp" #include "ecflow/client/ClientOptions.hpp" #include "ecflow/core/NOrder.hpp" diff --git a/Client/src/ecflow/client/ClientOptions.cpp b/Client/src/ecflow/client/ClientOptions.cpp index 4d7c64ca0..91b11ff22 100644 --- a/Client/src/ecflow/client/ClientOptions.cpp +++ b/Client/src/ecflow/client/ClientOptions.cpp @@ -20,7 +20,7 @@ #include #include "ecflow/base/ClientOptionsParser.hpp" -#include "ecflow/base/cts/TaskApi.hpp" +#include "ecflow/base/cts/task/TaskApi.hpp" #include "ecflow/client/ClientEnvironment.hpp" #include "ecflow/client/Help.hpp" #include "ecflow/core/CommandLine.hpp" diff --git a/Client/test/TestClientInterface.cpp b/Client/test/TestClientInterface.cpp index 5904575be..dceb96646 100644 --- a/Client/test/TestClientInterface.cpp +++ b/Client/test/TestClientInterface.cpp @@ -15,7 +15,7 @@ #include -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/CFileCmd.hpp" #include "ecflow/client/ClientInvoker.hpp" #include "ecflow/core/Child.hpp" #include "ecflow/core/File.hpp" diff --git a/Client/test/TestClientOptions.cpp b/Client/test/TestClientOptions.cpp index 1b60d7ca7..55c0d3086 100644 --- a/Client/test/TestClientOptions.cpp +++ b/Client/test/TestClientOptions.cpp @@ -10,7 +10,7 @@ #include -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/AlterCmd.hpp" #include "ecflow/client/ClientEnvironment.hpp" #include "ecflow/client/ClientInvoker.hpp" #include "ecflow/client/ClientOptions.hpp" diff --git a/Client/test/TestInitAddVariables.cpp b/Client/test/TestInitAddVariables.cpp index c5090b5e1..bcfc6fb52 100644 --- a/Client/test/TestInitAddVariables.cpp +++ b/Client/test/TestInitAddVariables.cpp @@ -13,6 +13,10 @@ #include #include "TestHelper.hpp" +#include "ecflow/base/cts/task/CompleteCmd.hpp" +#include "ecflow/base/cts/task/InitCmd.hpp" +#include "ecflow/base/cts/user/BeginCmd.hpp" +#include "ecflow/base/cts/user/CtsCmd.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Family.hpp" #include "ecflow/node/Suite.hpp" diff --git a/Client/test/TestLifeCycle.cpp b/Client/test/TestLifeCycle.cpp index fd9a7a1d0..b40f7d55c 100644 --- a/Client/test/TestLifeCycle.cpp +++ b/Client/test/TestLifeCycle.cpp @@ -13,6 +13,12 @@ #include #include "TestHelper.hpp" +#include "ecflow/base/cts/task/CompleteCmd.hpp" +#include "ecflow/base/cts/task/EventCmd.hpp" +#include "ecflow/base/cts/task/InitCmd.hpp" +#include "ecflow/base/cts/task/MeterCmd.hpp" +#include "ecflow/base/cts/user/BeginCmd.hpp" +#include "ecflow/base/cts/user/CtsCmd.hpp" #include "ecflow/core/File.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Jobs.hpp" diff --git a/Client/test/TestLoadDefsCmd.cpp b/Client/test/TestLoadDefsCmd.cpp index fae9bd90a..2932a9c16 100644 --- a/Client/test/TestLoadDefsCmd.cpp +++ b/Client/test/TestLoadDefsCmd.cpp @@ -15,10 +15,8 @@ #include #include "InvokeServer.hpp" -#include "MockServer.hpp" #include "SCPort.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" -#include "ecflow/base/stc/ServerToClientCmd.hpp" +#include "ecflow/base/cts/user/LoadDefsCmd.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Family.hpp" #include "ecflow/node/Suite.hpp" diff --git a/Client/test/TestPlugCmd.cpp b/Client/test/TestPlugCmd.cpp index e89c34948..3411003f8 100644 --- a/Client/test/TestPlugCmd.cpp +++ b/Client/test/TestPlugCmd.cpp @@ -13,9 +13,9 @@ #include #include "InvokeServer.hpp" -#include "MockServer.hpp" #include "SCPort.hpp" -#include "TestHelper.hpp" +#include "ecflow/base/cts/user/ClientHandleCmd.hpp" +#include "ecflow/base/cts/user/PlugCmd.hpp" #include "ecflow/client/ClientInvoker.hpp" #include "ecflow/core/File.hpp" #include "ecflow/node/Defs.hpp" diff --git a/Server/src/ecflow/server/CheckPtSaver.cpp b/Server/src/ecflow/server/CheckPtSaver.cpp index aac3dd824..7dbc301bc 100644 --- a/Server/src/ecflow/server/CheckPtSaver.cpp +++ b/Server/src/ecflow/server/CheckPtSaver.cpp @@ -10,7 +10,7 @@ #include "ecflow/server/CheckPtSaver.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" #include "ecflow/core/DurationTimer.hpp" #include "ecflow/core/Ecf.hpp" #include "ecflow/core/Filesystem.hpp" diff --git a/Server/src/ecflow/server/TcpBaseServer.cpp b/Server/src/ecflow/server/TcpBaseServer.cpp index b8346aab3..20028d6e8 100644 --- a/Server/src/ecflow/server/TcpBaseServer.cpp +++ b/Server/src/ecflow/server/TcpBaseServer.cpp @@ -12,6 +12,42 @@ #include +#include "ecflow/base/cts/task/AbortCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/task/CompleteCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/task/CtsWaitCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/task/EventCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/task/InitCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/task/LabelCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/task/MeterCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/task/QueueCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/AlterCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/BeginCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/CFileCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/CSyncCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/CheckPtCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/ClientHandleCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/CtsCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/CtsNodeCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/DeleteCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/EditScriptCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/ForceCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/FreeDepCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/GroupCTSCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/LoadDefsCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/LogCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/LogMessageCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/MoveCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/OrderNodeCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/PathsCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/PlugCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/QueryCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/ReplaceNodeCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/RequeueNodeCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/RunNodeCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/ServerVersionCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/ShowCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/cts/user/ZombieCmd.hpp" // required to enforce cereal registration +#include "ecflow/base/stc/PreAllocatedReply.hpp" #include "ecflow/core/Version.hpp" #include "ecflow/server/BaseServer.hpp" #include "ecflow/server/ServerEnvironment.hpp" diff --git a/Test/TestAlias.cpp b/Test/TestAlias.cpp index a5bbb9a0c..c6d4f85f9 100644 --- a/Test/TestAlias.cpp +++ b/Test/TestAlias.cpp @@ -17,7 +17,7 @@ #include "ServerTestHarness.hpp" #include "TestFixture.hpp" #include "ecflow/attribute/VerifyAttr.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/CFileCmd.hpp" #include "ecflow/core/AssertTimer.hpp" #include "ecflow/core/DurationTimer.hpp" #include "ecflow/core/NOrder.hpp" diff --git a/Test/TestFileCmd.cpp b/Test/TestFileCmd.cpp index 6ceca905a..fc6dafa48 100644 --- a/Test/TestFileCmd.cpp +++ b/Test/TestFileCmd.cpp @@ -15,10 +15,9 @@ #include "ServerTestHarness.hpp" #include "TestFixture.hpp" #include "ecflow/attribute/VerifyAttr.hpp" -#include "ecflow/base/cts/ClientToServerCmd.hpp" +#include "ecflow/base/cts/user/CFileCmd.hpp" #include "ecflow/core/Converter.hpp" #include "ecflow/core/DurationTimer.hpp" -#include "ecflow/core/File.hpp" #include "ecflow/node/Defs.hpp" #include "ecflow/node/Family.hpp" #include "ecflow/node/Suite.hpp" diff --git a/Test/src/TestFixture.cpp b/Test/src/TestFixture.cpp index 48fb5cc75..c924cb07c 100644 --- a/Test/src/TestFixture.cpp +++ b/Test/src/TestFixture.cpp @@ -15,7 +15,7 @@ #include #include "TestHelper.hpp" -#include "ecflow/base/cts/CtsApi.hpp" +#include "ecflow/base/cts/user/CtsApi.hpp" #include "ecflow/client/ClientEnvironment.hpp" // needed for static ClientEnvironment::hostSpecified(); ONLY #include "ecflow/client/Rtt.hpp" #include "ecflow/core/EcfPortLock.hpp"