mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-30 12:33:02 +02:00
249 lines
12 KiB
Diff
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();
|