diff --git a/include/villas/api/node_request.hpp b/include/villas/api/node_request.hpp index ded6cb4b2..2988a59a0 100644 --- a/include/villas/api/node_request.hpp +++ b/include/villas/api/node_request.hpp @@ -1,4 +1,4 @@ -/** API Request. +/** API Request for nodes. * * @file * @author Steffen Vogel @@ -21,12 +21,51 @@ * along with this program. If not, see . *********************************************************************************/ -#pragma once +#include #include +/* Forward declarations */ +struct node; + +namespace villas { +namespace node { +namespace api { + +/* Forward declarations */ +class Session; +class Response; +class RequestFactory; + class NodeRequest : public Request { public: + using Request::Request; + struct node *node; + + virtual void + prepare() + { + int ret; + + auto *nodes = session->getSuperNode()->getNodes(); + + uuid_t uuid; + ret = uuid_parse(matches[1].str().c_str(), uuid); + if (ret) { + node = vlist_lookup_name(nodes, matches[1].str()); + if (!node) + throw BadRequest("Unknown node"); + } + else { + node = vlist_lookup_uuid(nodes, uuid); + if (!node) + throw BadRequest("No node found with with matching UUID"); + } + } }; + +} /* namespace api */ +} /* namespace node */ +} /* namespace villas */ \ No newline at end of file diff --git a/include/villas/api/path_request.hpp b/include/villas/api/path_request.hpp new file mode 100644 index 000000000..6b0b640e8 --- /dev/null +++ b/include/villas/api/path_request.hpp @@ -0,0 +1,66 @@ +/** API Request for paths. + * + * @file + * @author Steffen Vogel + * @copyright 2014-2020, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *********************************************************************************/ + +#include + +#include + +/* Forward declarations */ +struct vpath; + +namespace villas { +namespace node { +namespace api { + +/* Forward declarations */ +class Session; +class Response; +class RequestFactory; + +class PathRequest : public Request { + +public: + using Request::Request; + + struct vpath *path; + + virtual void + prepare() + { + int ret; + uuid_t uuid; + + ret = uuid_parse(matches[1].str().c_str(), uuid); + if (ret) + throw BadRequest("Invalid UUID"); + + auto *paths = session->getSuperNode()->getPaths(); + path = vlist_lookup_uuid(paths, uuid); + if (!path) + throw BadRequest("No path found with with matching UUID"); + } +}; + +} /* namespace api */ +} /* namespace node */ +} /* namespace villas */ \ No newline at end of file diff --git a/include/villas/api/request.hpp b/include/villas/api/request.hpp index cebedd0a3..427be1535 100644 --- a/include/villas/api/request.hpp +++ b/include/villas/api/request.hpp @@ -24,11 +24,14 @@ #pragma once #include - #include #include #include +#include +#include + +#define REGEX_UUID "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" namespace villas { namespace node { @@ -82,6 +85,9 @@ public: virtual Response * execute() = 0; + virtual void prepare() + { } + void setBody(json_t *j) { body = j; @@ -110,8 +116,6 @@ protected: std::regex regex; public: - using RequestFactory::RequestFactory; - RequestPlugin() : RequestFactory(), regex(re) diff --git a/lib/api/CMakeLists.txt b/lib/api/CMakeLists.txt index 29d83b2f1..0a160fe6a 100644 --- a/lib/api/CMakeLists.txt +++ b/lib/api/CMakeLists.txt @@ -32,11 +32,13 @@ set(API_SRC requests/shutdown.cpp requests/restart.cpp requests/nodes.cpp + requests/node_info.cpp requests/node_action.cpp requests/node_stats.cpp requests/node_stats_reset.cpp requests/node_file.cpp requests/paths.cpp + requests/path_info.cpp requests/path_action.cpp ) diff --git a/lib/api/request.cpp b/lib/api/request.cpp index f673ee8e6..f8436c02f 100644 --- a/lib/api/request.cpp +++ b/lib/api/request.cpp @@ -35,14 +35,14 @@ Request * RequestFactory::make(Session *s, const std::string &uri, int meth) if (not rf->match(uri, mr)) continue; - auto *r = rf->make(s); + auto *p = rf->make(s); - r->uri = uri; - r->method = meth; - r->matches = mr; - r->factory = rf; + p->matches = mr; + p->factory = rf; + p->method = meth; + p->uri = uri; - return r; + return p; } throw BadRequest("Unknown API request"); diff --git a/lib/api/requests/node_action.cpp b/lib/api/requests/node_action.cpp index 25f78068f..b57ef2b5c 100644 --- a/lib/api/requests/node_action.cpp +++ b/lib/api/requests/node_action.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include namespace villas { @@ -36,10 +36,10 @@ namespace node { namespace api { template -class NodeActionRequest : public Request { +class NodeActionRequest : public NodeRequest { public: - using Request::Request; + using NodeRequest::NodeRequest; virtual Response * execute() { @@ -49,15 +49,7 @@ public: if (body != nullptr) throw BadRequest("Node endpoints do not accept any body data"); - const auto &nodeName = matches[1].str(); - - struct vlist *nodes = session->getSuperNode()->getNodes(); - struct node *n = (struct node *) vlist_lookup(nodes, nodeName.c_str()); - - if (!n) - throw Error(HTTP_STATUS_NOT_FOUND, "Node not found"); - - A(n); + A(node); return new Response(session); } @@ -66,27 +58,27 @@ public: /* Register API requests */ static char n1[] = "node/start"; -static char r1[] = "/node/([^/]+)/start"; +static char r1[] = "/node/(" REGEX_NODE_NAME "|" REGEX_UUID ")/start"; static char d1[] = "start a node"; static RequestPlugin, n1, r1, d1> p1; static char n2[] = "node/stop"; -static char r2[] = "/node/([^/]+)/stop"; +static char r2[] = "/node/(" REGEX_NODE_NAME "|" REGEX_UUID ")/stop"; static char d2[] = "stop a node"; static RequestPlugin, n2, r2, d2> p2; static char n3[] = "node/pause"; -static char r3[] = "/node/([^/]+)/pause"; +static char r3[] = "/node/(" REGEX_NODE_NAME "|" REGEX_UUID ")/pause"; static char d3[] = "pause a node"; static RequestPlugin, n3, r3, d3> p3; static char n4[] = "node/resume"; -static char r4[] = "/node/([^/]+)/resume"; +static char r4[] = "/node/(" REGEX_NODE_NAME "|" REGEX_UUID ")/resume"; static char d4[] = "resume a node"; static RequestPlugin, n4, r4, d4> p4; static char n5[] = "node/restart"; -static char r5[] = "/node/([^/]+)/restart"; +static char r5[] = "/node/(" REGEX_NODE_NAME "|" REGEX_UUID ")/restart"; static char d5[] = "restart a node"; static RequestPlugin, n5, r5, d5> p5; diff --git a/lib/api/requests/node_file.cpp b/lib/api/requests/node_file.cpp index 5fddd0926..06c851635 100644 --- a/lib/api/requests/node_file.cpp +++ b/lib/api/requests/node_file.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include @@ -32,10 +32,10 @@ namespace villas { namespace node { namespace api { -class FileRequest : public Request { +class FileRequest : public NodeRequest { public: - using Request::Request; + using NodeRequest::NodeRequest; virtual Response * execute() { @@ -45,21 +45,14 @@ public: if (body != nullptr) throw BadRequest("File endpoint does not accept any body data"); - const auto &nodeName = matches[1].str(); - - struct vlist *nodes = session->getSuperNode()->getNodes(); - struct node *n = (struct node *) vlist_lookup(nodes, nodeName.c_str()); - if (!n) - throw BadRequest("Invalid node"); - struct node_type *vt = node_type_lookup("file"); - if (n->_vt != vt) + if (node->_vt != vt) throw BadRequest("This node is not a file node"); - struct file *f = (struct file *) n->_vd; + struct file *f = (struct file *) node->_vd; - if (matches[1].str() == "rewind") + if (matches[2].str() == "rewind") io_rewind(&f->io); return new Response(session); @@ -67,8 +60,8 @@ public: }; /* Register API request */ -static char n[] = "file"; -static char r[] = "/node/([^/]+)/file(?:/([^/]+))?"; +static char n[] = "node/file"; +static char r[] = "/node/(" REGEX_NODE_NAME "|" REGEX_UUID ")/file(?:/([^/]+))?"; static char d[] = "control instances of 'file' node-type"; static RequestPlugin p; diff --git a/lib/api/requests/node_info.cpp b/lib/api/requests/node_info.cpp new file mode 100644 index 000000000..308f07aa7 --- /dev/null +++ b/lib/api/requests/node_info.cpp @@ -0,0 +1,63 @@ +/** The "node" API ressource. + * + * @author Steffen Vogel + * @copyright 2014-2020, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *********************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace villas { +namespace node { +namespace api { + +class NodeInfoRequest : public NodeRequest { + +public: + using NodeRequest::NodeRequest; + + virtual Response * execute() + { + if (method != Method::GET) + throw InvalidMethod(this); + + if (body != nullptr) + throw BadRequest("Nodes endpoint does not accept any body data"); + + return new Response(session, node_to_json(node)); + } +}; + +/* Register API request */ +static char n[] = "node"; +static char r[] = "/node/(" REGEX_NODE_NAME "|" REGEX_UUID ")"; +static char d[] = "retrieve info of a node"; +static RequestPlugin p; + +} /* namespace api */ +} /* namespace node */ +} /* namespace villas */ diff --git a/lib/api/requests/node_stats.cpp b/lib/api/requests/node_stats.cpp index 3b3ab3ed1..91b9bf2ac 100644 --- a/lib/api/requests/node_stats.cpp +++ b/lib/api/requests/node_stats.cpp @@ -52,7 +52,7 @@ public: const auto &nodeName = matches[1].str(); struct vlist *nodes = session->getSuperNode()->getNodes(); - struct node *node = (struct node *) vlist_lookup(nodes, nodeName.c_str()); + struct node *node = vlist_lookup_name(nodes, nodeName.c_str()); if (!node) throw BadRequest("Unknown node"); @@ -66,7 +66,7 @@ public: /* Register API requests */ static char n[] = "stats"; -static char r[] = "/node/([^/]+)/stats"; +static char r[] = "/node/(" REGEX_NODE_NAME "|" REGEX_UUID ")/stats"; static char d[] = "get internal statistics counters"; static RequestPlugin p; diff --git a/lib/api/requests/node_stats_reset.cpp b/lib/api/requests/node_stats_reset.cpp index c89831b2e..c4dd3394d 100644 --- a/lib/api/requests/node_stats_reset.cpp +++ b/lib/api/requests/node_stats_reset.cpp @@ -80,8 +80,8 @@ public: }; /* Register API requests */ -static char n[] = "stats/reset"; -static char r[] = "/node/([^/]+)/stats/reset"; +static char n[] = "node/stats/reset"; +static char r[] = "/node/(" REGEX_NODE_NAME "|" REGEX_UUID ")/stats/reset"; static char d[] = "reset internal statistics counters"; static RequestPlugin p; diff --git a/lib/api/requests/path_action.cpp b/lib/api/requests/path_action.cpp index 5329cdeef..724e2a8c1 100644 --- a/lib/api/requests/path_action.cpp +++ b/lib/api/requests/path_action.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include namespace villas { @@ -36,10 +36,10 @@ namespace node { namespace api { template -class PathActionRequest : public Request { +class PathActionRequest : public PathRequest { public: - using Request::Request; + using PathRequest::PathRequest; virtual Response * execute() { @@ -49,22 +49,7 @@ public: if (body != nullptr) throw BadRequest("Path endpoints do not accept any body data"); - unsigned long pathIndex; - const auto &pathIndexStr = matches[1].str(); - - try { - pathIndex = std::stoul(pathIndexStr); - } catch (const std::invalid_argument &e) { - throw BadRequest("Invalid argument"); - } - - struct vlist *paths = session->getSuperNode()->getPaths(); - struct vpath *p = (struct vpath *) vlist_at_safe(paths, pathIndex); - - if (!p) - throw Error(HTTP_STATUS_NOT_FOUND, "Path not found"); - - A(p); + A(path); return new Response(session); } @@ -73,12 +58,12 @@ public: /* Register API requests */ static char n1[] = "path/start"; -static char r1[] = "/path/([^/]+)/start"; +static char r1[] = "/path/(" REGEX_UUID ")/start"; static char d1[] = "start a path"; static RequestPlugin, n1, r1, d1> p1; static char n2[] = "path/stop"; -static char r2[] = "/path/([^/]+)/stop"; +static char r2[] = "/path/(" REGEX_UUID ")/stop"; static char d2[] = "stop a path"; static RequestPlugin, n2, r2, d2> p2; diff --git a/lib/api/requests/path_info.cpp b/lib/api/requests/path_info.cpp new file mode 100644 index 000000000..f2a025079 --- /dev/null +++ b/lib/api/requests/path_info.cpp @@ -0,0 +1,63 @@ +/** The "path" API ressource. + * + * @author Steffen Vogel + * @copyright 2014-2020, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *********************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace villas { +namespace node { +namespace api { + +class PathInfoRequest : public PathRequest { + +public: + using PathRequest::PathRequest; + + virtual Response * execute() + { + if (method != Method::GET) + throw InvalidMethod(this); + + if (body != nullptr) + throw BadRequest("Endpoint does not accept any body data"); + + return new Response(session, path_to_json(path)); + } +}; + +/* Register API request */ +static char n[] = "path"; +static char r[] = "/path/(" REGEX_UUID ")"; +static char d[] = "retrieve info of a path"; +static RequestPlugin p; + +} /* namespace api */ +} /* namespace node */ +} /* namespace villas */ diff --git a/lib/api/session.cpp b/lib/api/session.cpp index fdd50bb26..26b6bc1be 100644 --- a/lib/api/session.cpp +++ b/lib/api/session.cpp @@ -77,7 +77,8 @@ void Session::execute() logger->debug("Running API request: uri={}, method={}", r->uri, r->method); try { - response = req->execute(); + r->prepare(); + response = r->execute(); logger->debug("Completed API request: request={}", r->uri, r->method); } catch (const Error &e) {