Skip to content

Commit

Permalink
Add support for Aviso/Mirror in Python
Browse files Browse the repository at this point in the history
  • Loading branch information
marcosbento committed May 30, 2024
1 parent 4f767e1 commit f7175b8
Show file tree
Hide file tree
Showing 15 changed files with 397 additions and 1 deletion.
29 changes: 29 additions & 0 deletions libs/node/src/ecflow/node/AvisoAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,33 @@ void AvisoAttr::finish() const {
stop_controller(aviso_path);
}

bool operator==(const AvisoAttr& lhs, const AvisoAttr& rhs) {
return lhs.name() == rhs.name() && lhs.listener() == rhs.listener() && lhs.url() == rhs.url() &&
lhs.schema() == rhs.schema() && lhs.polling() == rhs.polling() && lhs.revision() == rhs.revision() &&
lhs.auth() == rhs.auth() && lhs.reason() == rhs.reason();
}

std::string to_python_string(const AvisoAttr& aviso) {
std::string s;
s += "AvisoAttr(";
s += "name=";
s += aviso.name();
s += ", listener=";
s += aviso.listener();
s += ", url=";
s += aviso.url();
s += ", schema=";
s += aviso.schema();
s += ", polling=";
s += aviso.polling();
s += ", revision=";
s += aviso.revision();
s += ", auth=";
s += aviso.auth();
s += ", reason=";
s += aviso.reason();
s += ")";
return s;
}

} // namespace ecf
4 changes: 4 additions & 0 deletions libs/node/src/ecflow/node/AvisoAttr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ class AvisoAttr {
mutable controller_ptr_t controller_;
};

bool operator==(const AvisoAttr& lhs, const AvisoAttr& rhs);

std::string to_python_string(const AvisoAttr& aviso);

template <class Archive>
void serialize(Archive& ar, AvisoAttr& aviso, [[maybe_unused]] std::uint32_t version) {
ar & aviso.parent_path_;
Expand Down
7 changes: 7 additions & 0 deletions libs/node/src/ecflow/node/MirrorAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,11 @@ void MirrorAttr::stop_controller() const {
}
}

bool operator==(const MirrorAttr& lhs, const MirrorAttr& rhs) {
return lhs.name() == rhs.name() && lhs.remote_path() == rhs.remote_path() &&
lhs.remote_host() == rhs.remote_host() && lhs.remote_port() == rhs.remote_port() &&
lhs.polling() == rhs.polling() && lhs.ssl() == rhs.ssl() && lhs.auth() == rhs.auth() &&
lhs.reason() == rhs.reason();
}

} // namespace ecf
2 changes: 2 additions & 0 deletions libs/node/src/ecflow/node/MirrorAttr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ class MirrorAttr {
mutable controller_ptr_t controller_;
};

bool operator==(const MirrorAttr& lhs, const MirrorAttr& rhs);

template <class Archive>
void serialize(Archive& ar, MirrorAttr& aviso, [[maybe_unused]] std::uint32_t version) {
ar & aviso.name_;
Expand Down
4 changes: 4 additions & 0 deletions libs/node/src/ecflow/node/Node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,10 @@ class Node : public std::enable_shared_from_this<Node> {
std::vector<Event>::const_iterator event_end() const { return events_.end(); }
std::vector<Label>::const_iterator label_begin() const { return labels_.begin(); }
std::vector<Label>::const_iterator label_end() const { return labels_.end(); }
std::vector<ecf::AvisoAttr>::const_iterator aviso_begin() const { return avisos_.begin(); }
std::vector<ecf::AvisoAttr>::const_iterator aviso_end() const { return avisos_.end(); }
std::vector<ecf::MirrorAttr>::const_iterator mirror_begin() const { return mirrors_.begin(); }
std::vector<ecf::MirrorAttr>::const_iterator mirror_end() const { return mirrors_.end(); }
std::vector<ecf::TimeAttr>::const_iterator time_begin() const { return times_.begin(); }
std::vector<ecf::TimeAttr>::const_iterator time_end() const { return times_.end(); }
std::vector<ecf::TodayAttr>::const_iterator today_begin() const { return todays_.begin(); }
Expand Down
2 changes: 2 additions & 0 deletions libs/pyext/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ set(u_tests
py_u_TestAddDeleteFunc
py_u_TestAddNodeFunc
py_u_TestAutoAddExtern
py_u_TestAviso
py_u_TestCopy
py_u_TestDefs
py_u_TestDefsCheck
Expand All @@ -105,6 +106,7 @@ set(u_tests
py_u_TestFlag
py_u_TestGetAllTasks
py_u_TestJobGeneration
py_u_TestMirror
py_u_TestParent
py_u_TestRemove
py_u_TestRepeatArithmetic
Expand Down
8 changes: 8 additions & 0 deletions libs/pyext/src/ecflow/python/DefsDoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,14 @@ const char* DefsDoc::add_label_doc() {
" ecflow_client --label=Joe ninety\n";
}

const char* DefsDoc::add_aviso_doc() {
return "Adds an `aviso`_ to a `node`_. See :py:class:`ecflow.Aviso`\n";
}

const char* DefsDoc::add_mirror_doc() {
return "Adds a `mirror`_ to a `node`_. See :py:class:`ecflow.Mirror`\n";
}

const char* DefsDoc::add_limit_doc() {
return "Adds a `limit`_ to a `node`_ for simple load management. See :py:class:`ecflow.Limit`\n\n"
"Multiple limits can be added, however the limit name must be unique.\n"
Expand Down
2 changes: 2 additions & 0 deletions libs/pyext/src/ecflow/python/DefsDoc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class DefsDoc {
static const char* trigger();
static const char* add_variable_doc();
static const char* add_label_doc();
static const char* add_aviso_doc();
static const char* add_mirror_doc();
static const char* add_limit_doc();
static const char* add_inlimit_doc();
static const char* node_doc();
Expand Down
17 changes: 17 additions & 0 deletions libs/pyext/src/ecflow/python/ExportNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
#include "ecflow/attribute/LateAttr.hpp"
#include "ecflow/client/ClientInvoker.hpp"
#include "ecflow/node/AutoRestoreAttr.hpp"
#include "ecflow/node/AvisoAttr.hpp"
#include "ecflow/node/Defs.hpp"
#include "ecflow/node/Expression.hpp"
#include "ecflow/node/Limit.hpp"
#include "ecflow/node/MirrorAttr.hpp"
#include "ecflow/node/MiscAttrs.hpp"
#include "ecflow/node/Node.hpp"
#include "ecflow/node/NodeContainer.hpp"
Expand Down Expand Up @@ -116,6 +118,17 @@ node_ptr add_label_1(node_ptr self, const Label& label) {
self->addLabel(label);
return self;
}

node_ptr add_aviso(node_ptr self, const ecf::AvisoAttr& attr) {
self->addAviso(attr);
return self;
}

node_ptr add_mirror(node_ptr self, const ecf::MirrorAttr& attr) {
self->addMirror(attr);
return self;
}

node_ptr add_limit(node_ptr self, const std::string& name, int limit) {
self->addLimit(Limit(name, limit));
return self;
Expand Down Expand Up @@ -564,6 +577,8 @@ void export_Node() {
.def("add_variable", &NodeUtil::add_variable_dict)
.def("add_label", &add_label, DefsDoc::add_label_doc())
.def("add_label", &add_label_1)
.def("add_aviso", &add_aviso, DefsDoc::add_aviso_doc())
.def("add_mirror", &add_mirror, DefsDoc::add_mirror_doc())
.def("add_limit", &add_limit, DefsDoc::add_limit_doc())
.def("add_limit", &add_limit_1)
.def("add_inlimit",
Expand Down Expand Up @@ -746,6 +761,8 @@ void export_Node() {
bp::range(&Node::variable_begin, &Node::variable_end),
"Returns a list of user defined `variable`_\\ s")
.add_property("labels", bp::range(&Node::label_begin, &Node::label_end), "Returns a list of `label`_\\ s")
.add_property("avisos", bp::range(&Node::aviso_begin, &Node::aviso_end), "Returns a list of `aviso`_\\ s")
.add_property("mirrors", bp::range(&Node::mirror_begin, &Node::mirror_end), "Returns a list of `mirror`_\\ s")
.add_property("limits", bp::range(&Node::limit_begin, &Node::limit_end), "Returns a list of `limit`_\\ s")
.add_property(
"inlimits", bp::range(&Node::inlimit_begin, &Node::inlimit_end), "Returns a list of `inlimit`_\\ s")
Expand Down
78 changes: 78 additions & 0 deletions libs/pyext/src/ecflow/python/ExportNodeAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@
#include "ecflow/attribute/ZombieAttr.hpp"
#include "ecflow/node/Attr.hpp"
#include "ecflow/node/AutoRestoreAttr.hpp"
#include "ecflow/node/AvisoAttr.hpp"
#include "ecflow/node/Expression.hpp"
#include "ecflow/node/Flag.hpp"
#include "ecflow/node/InLimit.hpp"
#include "ecflow/node/JobCreationCtrl.hpp"
#include "ecflow/node/Limit.hpp"
#include "ecflow/node/MirrorAttr.hpp"
#include "ecflow/python/BoostPythonUtil.hpp"
#include "ecflow/python/DefsDoc.hpp"
#include "ecflow/python/NodeAttrDoc.hpp"
Expand Down Expand Up @@ -281,6 +283,27 @@ static job_creation_ctrl_ptr makeJobCreationCtrl() {
return std::make_shared<JobCreationCtrl>();
}

static std::shared_ptr<AvisoAttr> aviso_init(const std::string& name,
const std::string& listener,
const std::string& url,
const std::string& schema,
const std::string& polling,
const std::string& auth) {
auto attr = std::make_shared<AvisoAttr>(nullptr, name, listener, url, schema, polling, 0, auth, "");
return attr;
}

static std::shared_ptr<MirrorAttr> mirror_init(const std::string& name,
const std::string& path,
const std::string& host,
const std::string& port,
const std::string& polling,
bool ssl,
const std::string& auth) {
auto attr = std::make_shared<MirrorAttr>(nullptr, name, path, host, port, polling, ssl, auth, "");
return attr;
}

void export_NodeAttr() {
// Trigger & Complete thin wrapper over Expression, allows us to call: Task("a").add(Trigger("a=1"),Complete("b=1"))
class_<Trigger, std::shared_ptr<Trigger>>("Trigger", DefsDoc::trigger(), init<std::string>())
Expand Down Expand Up @@ -992,4 +1015,59 @@ void export_NodeAttr() {
#if ECF_ENABLE_PYTHON_PTR_REGISTER
bp::register_ptr_to_python<std::shared_ptr<ClockAttr>>(); // needed for mac and boost 1.6
#endif

class_<ecf::AvisoAttr>("AvisoAttr", NodeAttrDoc::aviso_doc())
.def("__init__", make_constructor(&aviso_init))
.def(self == self) // __eq__
.def("__str__", &ecf::to_python_string) // __str__
.def("__copy__", copyObject<AvisoAttr>) // __copy__ uses copy constructor
.def("name",
&AvisoAttr::name,
return_value_policy<copy_const_reference>(),
"Returns the name of the Aviso attribute")
.def("listener",
&AvisoAttr::listener,
return_value_policy<copy_const_reference>(),
"Returns the Aviso listener configuration")
.def("url",
&AvisoAttr::url,
return_value_policy<copy_const_reference>(),
"Returns the URL used to contact the Aviso server")
.def("schema",
&AvisoAttr::schema,
return_value_policy<copy_const_reference>(),
"Returns the path to the schema used to contact the Aviso server")
.def("polling", &AvisoAttr::polling, "Returns polling interval used to contact the Aviso server")
.def("auth",
&AvisoAttr::auth,
return_value_policy<copy_const_reference>(),
"Returns the path to Authentication credentials used to contact the Aviso server");

class_<MirrorAttr>("MirrorAttr", NodeAttrDoc::mirror_doc())
.def("__init__", make_constructor(&mirror_init))
.def(self == self) // __eq__
.def("__str__", &ecf::to_python_string) // __str__
.def("__copy__", copyObject<MirrorAttr>) // __copy__ uses copy constructor
.def("name",
&MirrorAttr::name,
return_value_policy<copy_const_reference>(),
"Returns the name of the Mirror attribute")
.def("remote_path",
&MirrorAttr::remote_path,
return_value_policy<copy_const_reference>(),
"Returns the path on the remote ecFlow server")
.def("remote_host",
&MirrorAttr::remote_host,
return_value_policy<copy_const_reference>(),
"Returns the host of the remote ecFlow server")
.def("remote_port",
&MirrorAttr::remote_port,
return_value_policy<copy_const_reference>(),
"Returns the port of the remote ecFlow server")
.def("ssl", &MirrorAttr::ssl, "Returns a boolean, where true means that SSL is enabled")
.def("polling", &MirrorAttr::polling, "Returns the polling interval used to contact the remove ecFlow server")
.def("auth",
&MirrorAttr::auth,
return_value_policy<copy_const_reference>(),
"Returns the path to Authentication credentials used to contact the remote ecFlow server");
}
51 changes: 51 additions & 0 deletions libs/pyext/src/ecflow/python/NodeAttrDoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -705,3 +705,54 @@ const char* NodeAttrDoc::clock_doc() {
" clock.set_gain(1,10,True)\n"
" suite.add_clock(clock)\n";
}

const char* NodeAttrDoc::aviso_doc() {
return "An `aviso`_ attribute, assigned to a `node`_, represents an external trigger holding the node queued until"
"an Aviso notification matching the attribute configuration is detected.\n"
"\n"
"Although `aviso`_ attributes can be set at any level (Suite, Family, Task), it only makes sense to assign "
"aviso attributes to tasks, and only one aviso attribute per node is allowed.\n"
"\n"
"\nConstructor::\n\n"
" AvisoAttr(name, listener, ...)\n"
" string name: The Aviso attribute name\n"
" string listener: The Aviso listener configuration (in JSON format)\n"
" string url: The URL used to contact the Aviso server\n"
" string schema: The path to the Aviso schema\n"
" string polling: The polling interval used to contact the Aviso server\n"
" string auth: The path to the Aviso Authentication credentials\n"
"\n"
"\nUsage:\n\n"
".. code-block:: python\n\n"
" t1 = Task('t1',\n"
" AvisoAttr('name', '{...}', 'http://aviso.com', '60', '/path/to/auth'))\n"
"\n"
" t2 = Task('t2')\n"
" t2.add_aviso('name', '{...}', 'http://aviso.com', '60', '/path/to/auth')\n";
}

const char* NodeAttrDoc::mirror_doc() {
return "A `mirror`_ attribute, assigned to a `node`_, enables establishing an external link and "
"locally replicate the state of a node executing on a remote ecFlow server.\n"
"\n"
"Although `mirror`_ attributes can be set at any level (Suite, Family, Task), it only makes sense to assign "
"mirror attributes to Tasks, and only one mirror attribute per node is allowed.\n"
"\n"
"\nConstructor::\n\n"
" MirrorAttr(name, remote_path, ...)\n"
" string name: The Mirror attribute name\n"
" string remote_path: The path on the remote ecFlow server to the node being replicated\n"
" string remote_host: The host of the remote ecFlow server\n"
" string remote_port: The port of the remote ecFlow server\n"
" string polling: The polling interval used to contact the remote ecFlow server\n"
" Bool ssl: `true`, when using SSL to contact the remote ecFlow server; `false`, otherwise\n"
" string auth: The path to the Mirror Authentication credentials\n"
"\n"
"\nUsage:\n\n"
".. code-block:: python\n\n"
" t1 = Task('t1',\n"
" MirrorAttr('name', '/remote/task', 'remote-ecflow', '3141', '60', True, '/path/to/auth'))\n"
"\n"
" t2 = Task('t2')\n"
" t2.add_aviso('name', '/remote/task', 'remote-ecflow', '3141', '60', True, '/path/to/auth')\n";
}
2 changes: 2 additions & 0 deletions libs/pyext/src/ecflow/python/NodeAttrDoc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class NodeAttrDoc {
static const char* repeat_day_doc();
static const char* cron_doc();
static const char* clock_doc();
static const char* aviso_doc();
static const char* mirror_doc();
};

#endif /* ecflow_python_NodeAttrDoc_HPP */
11 changes: 10 additions & 1 deletion libs/pyext/src/ecflow/python/NodeUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@
#include "ecflow/attribute/ZombieAttr.hpp"
#include "ecflow/node/Attr.hpp"
#include "ecflow/node/AutoRestoreAttr.hpp"
#include "ecflow/node/AvisoAttr.hpp"
#include "ecflow/node/Defs.hpp"
#include "ecflow/node/Flag.hpp"
#include "ecflow/node/InLimit.hpp"
#include "ecflow/node/Limit.hpp"
#include "ecflow/node/MirrorAttr.hpp"
#include "ecflow/node/Suite.hpp"
#include "ecflow/node/Task.hpp"
#include "ecflow/python/BoostPythonUtil.hpp"
Expand Down Expand Up @@ -163,8 +165,15 @@ object NodeUtil::do_add(node_ptr self, const bp::object& arg) {
throw std::runtime_error("ExportNode::add() : Can only add a clock to a suite");
self->isSuite()->addClock(extract<ClockAttr>(arg));
}
else if (extract<Variable>(arg).check())
else if (extract<Variable>(arg).check()) {
self->addVariable(extract<Variable>(arg));
}
else if (auto attr = extract<ecf::AvisoAttr>(arg); attr.check()) {
self->addAviso(attr);
}
else if (auto attr = extract<ecf::MirrorAttr>(arg); attr.check()) {
self->addMirror(attr);
}
else if (extract<dict>(arg).check()) {
dict d = extract<dict>(arg);
add_variable_dict(self, d);
Expand Down
Loading

0 comments on commit f7175b8

Please sign in to comment.