Files
infix/patches/rousette/0002-Add-optional-timeout-option.patch
T

249 lines
12 KiB
Diff

commit c83585452819b6e06008aba49237f46977a73a9a
Author: Mattias Walström <lazzer@gmail.com>
Date: Wed Jun 26 11:04:36 2024 +0200
Add optional timeout option
Since rousette will fail applying a new configuration
on really slow systems and a lot of sysrepo callbacks.
Change-Id: I61d92a1f64e4c3617587e544cfd9d6a4269e0d35
diff --git a/src/restconf/Server.cpp b/src/restconf/Server.cpp
index 5327b52..685bf78 100644
--- a/src/restconf/Server.cpp
+++ b/src/restconf/Server.cpp
@@ -202,14 +202,13 @@ DataFormat chooseDataEncoding(const nghttp2::asio_http2::header_map& headers)
return {resContentType, resAccept ? *resAccept : libyang::DataFormat::JSON};
}
-bool dataExists(sysrepo::Session session, const std::string& path)
+bool dataExists(sysrepo::Session session, const std::string& path, std::chrono::milliseconds timeout)
{
- if (auto data = session.getData(path)) {
- if (data->findPath(path)) {
+ if (auto data = session.getData(path, 0, sysrepo::GetOptions::Default,timeout)) {
+ if (data->findPath(path))
return true;
}
- }
- return false;
+ return false;
}
/** @brief Checks if node is a key node in a maybeList node list */
@@ -261,7 +260,7 @@ struct RequestContext {
std::string payload;
};
-void processActionOrRPC(std::shared_ptr<RequestContext> requestCtx)
+void processActionOrRPC(std::shared_ptr<RequestContext> requestCtx, std::chrono::milliseconds timeout)
{
requestCtx->sess.switchDatastore(sysrepo::Datastore::Operational);
auto ctx = requestCtx->sess.getContext();
@@ -276,7 +275,7 @@ void processActionOrRPC(std::shared_ptr<RequestContext> requestCtx)
if (rpcSchemaNode.nodeType() == libyang::NodeType::Action) {
// FIXME: This is race-prone: we check for existing action data node but before we send the RPC the node may be gone
auto [pathToParent, pathSegment] = asLibyangPathSplit(ctx, requestCtx->req.uri().path);
- if (!dataExists(requestCtx->sess, pathToParent)) {
+ if (!dataExists(requestCtx->sess, pathToParent,timeout)) {
throw ErrorResponse(400, "application", "operation-failed", "Action data node '" + requestCtx->lyPathOriginal + "' does not exist.");
}
}
@@ -287,7 +286,7 @@ void processActionOrRPC(std::shared_ptr<RequestContext> requestCtx)
rpcNode->parseOp(requestCtx->payload, *requestCtx->dataFormat.request, libyang::OperationType::RpcRestconf);
}
- auto rpcReply = requestCtx->sess.sendRPC(*rpcNode);
+ auto rpcReply = requestCtx->sess.sendRPC(*rpcNode, timeout);
if (rpcReply.immediateChildren().empty()) {
requestCtx->res.write_head(204, {{"access-control-allow-origin", {"*", false}}});
@@ -330,7 +329,7 @@ void processActionOrRPC(std::shared_ptr<RequestContext> requestCtx)
}
}
-void processPost(std::shared_ptr<RequestContext> requestCtx)
+void processPost(std::shared_ptr<RequestContext> requestCtx, std::chrono::milliseconds timeout)
{
try {
auto ctx = requestCtx->sess.getContext();
@@ -374,7 +373,7 @@ void processPost(std::shared_ptr<RequestContext> requestCtx)
createdNodes.begin()->newMeta(*mod, "operation", "create"); // FIXME: check no other nc:operations in the tree
requestCtx->sess.editBatch(*edit, sysrepo::DefaultOperation::Merge);
- requestCtx->sess.applyChanges();
+ requestCtx->sess.applyChanges(timeout);
requestCtx->res.write_head(201,
{
@@ -402,7 +401,7 @@ void processPost(std::shared_ptr<RequestContext> requestCtx)
}
}
-void processPut(std::shared_ptr<RequestContext> requestCtx)
+void processPut(std::shared_ptr<RequestContext> requestCtx, std::chrono::milliseconds timeout)
{
try {
auto ctx = requestCtx->sess.getContext();
@@ -410,7 +409,7 @@ void processPut(std::shared_ptr<RequestContext> requestCtx)
// PUT / means replace everything
if (requestCtx->lyPathOriginal == "/") {
auto edit = ctx.parseData(requestCtx->payload, *requestCtx->dataFormat.request, libyang::ParseOptions::Strict | libyang::ParseOptions::NoState | libyang::ParseOptions::ParseOnly);
- requestCtx->sess.replaceConfig(edit);
+ requestCtx->sess.replaceConfig(edit, std::nullopt, timeout);
requestCtx->res.write_head(edit ? 201 : 204,
{
@@ -422,7 +421,7 @@ void processPut(std::shared_ptr<RequestContext> requestCtx)
}
auto mod = ctx.getModuleImplemented("ietf-netconf");
- bool nodeExisted = dataExists(requestCtx->sess, requestCtx->lyPathOriginal);
+ bool nodeExisted = dataExists(requestCtx->sess, requestCtx->lyPathOriginal, timeout);
std::optional<libyang::DataNode> edit;
std::optional<libyang::DataNode> replacementNode;
@@ -471,7 +470,7 @@ void processPut(std::shared_ptr<RequestContext> requestCtx)
replacementNode->newMeta(*mod, "operation", "replace"); // FIXME: check no other nc:operations in the tree
requestCtx->sess.editBatch(*edit, sysrepo::DefaultOperation::Merge);
- requestCtx->sess.applyChanges();
+ requestCtx->sess.applyChanges(timeout);
requestCtx->res.write_head(nodeExisted ? 204 : 201,
{
@@ -551,7 +550,7 @@ Server::~Server()
server->join();
}
-Server::Server(sysrepo::Connection conn, const std::string& address, const std::string& port)
+Server::Server(sysrepo::Connection conn, const std::string& address, const std::string& port, long timeout_seconds)
: nacm(conn)
, server{std::make_unique<nghttp2::asio_http2::server::http2>()}
, dwdmEvents{std::make_unique<sr::OpticalEvents>(conn.sessionStart())}
@@ -562,6 +561,8 @@ Server::Server(sysrepo::Connection conn, const std::string& address, const std::
}
}
+ std::chrono::milliseconds timeout(timeout_seconds * 1000);
+
// RFC 8527, we must implement at least ietf-yang-library@2019-01-04 and support operational DS
if (auto mod = conn.sessionStart().getContext().getModuleImplemented("ietf-yang-library"); !mod || mod->revision() < "2019-01-04") {
throw std::runtime_error("Module ietf-yang-library of revision at least 2019-01-04 is not implemented");
@@ -627,7 +628,7 @@ Server::Server(sysrepo::Connection conn, const std::string& address, const std::
});
server->handle(restconfRoot,
- [conn /* intentionally by value, otherwise conn gets destroyed when the ctor returns */, this](const auto& req, const auto& res) mutable {
+ [conn /* intentionally by value, otherwise conn gets destroyed when the ctor returns */, this, timeout](const auto& req, const auto& res) mutable {
const auto& peer = http::peer_from_request(req);
spdlog::info("{}: {} {}", peer, req.method(), req.uri().raw_path);
@@ -665,7 +666,7 @@ Server::Server(sysrepo::Connection conn, const std::string& address, const std::
case RestconfRequest::Type::GetData:
sess.switchDatastore(restconfRequest.datastore.value_or(sysrepo::Datastore::Operational));
- if (auto data = sess.getData(restconfRequest.path); data) {
+ if (auto data = sess.getData(restconfRequest.path, 0, sysrepo::GetOptions::Default,timeout); data) {
res.write_head(
200,
{
@@ -692,16 +693,16 @@ Server::Server(sysrepo::Connection conn, const std::string& address, const std::
auto requestCtx = std::make_shared<RequestContext>(req, res, dataFormat, sess, restconfRequest.path);
- req.on_data([requestCtx, restconfRequest /* intentional copy */](const uint8_t* data, std::size_t length) {
+ req.on_data([requestCtx, restconfRequest /* intentional copy */, timeout](const uint8_t* data, std::size_t length) {
if (length > 0) { // there are still some data to be read
requestCtx->payload.append(reinterpret_cast<const char*>(data), length);
return;
}
if (restconfRequest.type == RestconfRequest::Type::CreateOrReplaceThisNode) {
- processPut(requestCtx);
+ processPut(requestCtx, timeout);
} else {
- processPost(requestCtx);
+ processPost(requestCtx, timeout);
}
});
break;
@@ -725,7 +726,7 @@ Server::Server(sysrepo::Connection conn, const std::string& address, const std::
}
sess.editBatch(*edit, sysrepo::DefaultOperation::Merge);
- sess.applyChanges();
+ sess.applyChanges(timeout);
} catch (const sysrepo::ErrorWithCode& e) {
if (e.code() == sysrepo::ErrorCode::Unauthorized) {
throw ErrorResponse(403, "application", "access-denied", "Access denied.", restconfRequest.path);
@@ -747,11 +748,11 @@ Server::Server(sysrepo::Connection conn, const std::string& address, const std::
case RestconfRequest::Type::Execute: {
auto requestCtx = std::make_shared<RequestContext>(req, res, dataFormat, sess, restconfRequest.path);
- req.on_data([requestCtx](const uint8_t* data, std::size_t length) {
+ req.on_data([requestCtx,timeout](const uint8_t* data, std::size_t length) {
if (length > 0) {
requestCtx->payload.append(reinterpret_cast<const char*>(data), length);
} else {
- processActionOrRPC(requestCtx);
+ processActionOrRPC(requestCtx,timeout);
}
});
break;
diff --git a/src/restconf/Server.h b/src/restconf/Server.h
index bf6fecc..b2c42f4 100644
--- a/src/restconf/Server.h
+++ b/src/restconf/Server.h
@@ -24,11 +24,10 @@ class OpticalEvents;
namespace restconf {
std::optional<std::string> as_subtree_path(const std::string& path);
-
/** @short A RESTCONF-ish server */
class Server {
public:
- explicit Server(sysrepo::Connection conn, const std::string& address, const std::string& port);
+ explicit Server(sysrepo::Connection conn, const std::string& address, const std::string& port, long timeout);
~Server();
private:
diff --git a/src/restconf/main.cpp b/src/restconf/main.cpp
index 2bc9a86..e295f63 100644
--- a/src/restconf/main.cpp
+++ b/src/restconf/main.cpp
@@ -27,9 +27,10 @@
static const char usage[] =
R"(Rousette - RESTCONF server
Usage:
- rousette [--syslog] [--help]
+ rousette [--syslog] [--timeout <SECONDS>] [--help]
Options:
-h --help Show this screen.
+ -t --timeout <SECONDS> Change default timeout in sysrepo (if not set, use sysrepo internal).
--syslog Log to syslog.
)";
#ifdef HAVE_SYSTEMD
@@ -74,6 +75,10 @@ public:
int main(int argc, char* argv [])
{
auto args = docopt::docopt(usage, {argv + 1, argv + argc}, true,""/* version */, true);
+ long timeout = 0;
+
+ if (args["--timeout"])
+ timeout = args["--timeout"].asLong();
if (args["--syslog"].asBool()) {
auto syslog_sink = std::make_shared<spdlog::sinks::syslog_sink_mt>("rousette", LOG_PID, LOG_USER, true);
@@ -100,8 +105,7 @@ int main(int argc, char* argv [])
}
auto conn = sysrepo::Connection{};
- auto server = rousette::restconf::Server{conn, "::1", "10080"};
-
+ auto server = rousette::restconf::Server{conn, "::1", "10080", timeout};
signal(SIGTERM, [](int) {});
signal(SIGINT, [](int) {});
pause();