diff --git a/doc/openapi.yaml b/doc/openapi.yaml
index 22d503110..4d774b5b6 100644
--- a/doc/openapi.yaml
+++ b/doc/openapi.yaml
@@ -58,7 +58,46 @@ paths:
examples:
example1:
value:
- { test: 1 }
+ state: running
+ version: v0.10.0
+ release: 1.node_uuid_unique_debug.20201015git335440d
+ build_id: v0.10.0-335440d-debug
+ build_date: '20201015'
+ hostname: ernie
+ uuid: c9d64cc7-c6e1-4dd4-8873-126318e9d42c
+ time_now: 1602765814.9240997
+ time_started: 1602765814.3103526
+ timezone:
+ name: CEST
+ offset: -3600
+ dst: true
+ kernel:
+ sysname: Linux
+ nodename: ernie
+ release: 5.6.17-rt10
+ version: "#5 SMP Fri Jul 10 14:02:33 CEST 2020"
+ machine: x86_64
+ domainname: "(none)"
+ system:
+ cores_configured: 28
+ cores: 28
+ procecces: 780
+ uptime: 1379600
+ load:
+ - 1.66259765625
+ - 1.271484375
+ - 1.18701171875
+ ram:
+ total: 269994606592
+ free: 262204465152
+ shared: 44191744
+ buffer: 130211840
+ swap:
+ total: 4294963200
+ free: 4294963200
+ highmem:
+ total: 0
+ free: 0
"/capabilities":
get:
diff --git a/include/villas/super_node.hpp b/include/villas/super_node.hpp
index b5613ad78..4451be1d8 100644
--- a/include/villas/super_node.hpp
+++ b/include/villas/super_node.hpp
@@ -78,6 +78,10 @@ protected:
struct Task task; /**< Task for periodic stats output */
+ uuid_t uuid; /**< A globally unique identifier of the instance */
+
+ struct timespec started; /**< The time at which the instance has been started. */
+
std::string uri; /**< URI of configuration */
Config config; /** The configuration file. */
@@ -156,6 +160,16 @@ public:
return state;
}
+ void getUUID(uuid_t out) const
+ {
+ uuid_copy(out, uuid);
+ }
+
+ struct timespec getStartTime() const
+ {
+ return started;
+ }
+
#ifdef WITH_API
Api * getApi()
{
diff --git a/lib/api.cpp b/lib/api.cpp
index 1050266ff..bca1e68e1 100644
--- a/lib/api.cpp
+++ b/lib/api.cpp
@@ -89,7 +89,7 @@ void Api::worker()
logger->info("Started worker");
/* Process pending requests */
- while (!pending.empty() && running) {
+ while (running) {
Session *s = pending.pop();
if (s) {
/* Check that the session is still alive */
diff --git a/lib/api/requests/capabiltities.cpp b/lib/api/requests/capabiltities.cpp
index 52097c1b1..c4b71240d 100644
--- a/lib/api/requests/capabiltities.cpp
+++ b/lib/api/requests/capabiltities.cpp
@@ -74,8 +74,7 @@ public:
}
#endif
- auto *json_capabilities = json_pack("{ s: s, s: o, s: o, s: o, s: o }",
- "build", PROJECT_BUILD_ID,
+ auto *json_capabilities = json_pack("{ s: o, s: o, s: o, s: o }",
"hooks", json_hooks,
"node-types", json_nodes,
"apis", json_apis,
diff --git a/lib/api/requests/status.cpp b/lib/api/requests/status.cpp
index d58b565f1..336522670 100644
--- a/lib/api/requests/status.cpp
+++ b/lib/api/requests/status.cpp
@@ -20,27 +20,49 @@
* along with this program. If not, see .
*********************************************************************************/
-#ifdef LWS_WITH_SERVER_STATUS
-
-#include
+#include
+#include
+#include
+#include
+#include
#include
#include
+#ifndef UUID_STR_LEN
+ #define UUID_STR_LEN 37
+#endif
+
namespace villas {
namespace node {
namespace api {
class StatusRequest : public Request {
+protected:
+
+#ifdef LWS_WITH_SERVER_STATUS
+ json_t * getLwsStatus()
+ {
+ int ret;
+
+ struct lws_context *ctx = lws_get_context(session->wsi);
+ char buf[4096];
+
+ ret = lws_json_dump_context(ctx, buf, sizeof(buf), 0);
+ if (ret)
+ throw Error();
+
+ return json_loads(buf, 0, nullptr);
+ }
+#endif /* LWS_WITH_SERVER_STATUS */
+
public:
using Request::Request;
virtual Response * execute()
{
int ret;
- struct lws_context *ctx = lws_get_context(s->wsi);
- char buf[4096];
if (method != Session::Method::GET)
throw InvalidMethod(this);
@@ -48,11 +70,83 @@ public:
if (body != nullptr)
throw BadRequest("Status endpoint does not accept any body data");
- ret = lws_json_dump_context(ctx, buf, sizeof(buf), 0);
- if (ret)
- throw Error();
+ auto *sn = session->getSuperNode();
- auto *json_status = json_loads(buf, 0, nullptr);
+ uuid_t uuid;
+ struct utsname uts;
+ struct sysinfo sinfo;
+ char hname[128];
+ char uuid_str[UUID_STR_LEN];
+
+ sn->getUUID(uuid);
+ uuid_unparse_lower(uuid, uuid_str);
+
+ auto now = time_now();
+ auto started = sn->getStartTime();
+
+ ret = gethostname(hname, sizeof(hname));
+ if (ret)
+ throw Error(HTTP_STATUS_INTERNAL_SERVER_ERROR, "Failed to get system hostname");
+
+ ret = uname(&uts);
+ if (ret)
+ throw Error(HTTP_STATUS_INTERNAL_SERVER_ERROR, "Failed to get kernel information");
+
+ ret = sysinfo(&sinfo);
+ if (ret)
+ throw Error(HTTP_STATUS_INTERNAL_SERVER_ERROR, "Failed to get system information");
+
+ float f_load = 1.f / (1 << SI_LOAD_SHIFT);
+
+ json_error_t err;
+ json_t *json_status = json_pack_ex(&err, 0, "{ s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: f, s: f, s: { s: s, s: I, s: b }, s: { s: s, s: s, s: s, s: s, s: s, s: s}, s: { s: i, s: i, s: I, s: I, s: [ f, f, f ], s: { s: I, s, I, s: I, s: I }, s: { s: I, s: I }, s: { s: I, s: I } } }",
+ "state", state_print(sn->getState()),
+ "version", PROJECT_VERSION_STR,
+ "release", PROJECT_RELEASE,
+ "build_id", PROJECT_BUILD_ID,
+ "build_date", PROJECT_BUILD_DATE,
+ "hostname", hname,
+ "uuid", uuid_str,
+ "time_now", time_to_double(&now),
+ "time_started", time_to_double(&started),
+ "timezone",
+ "name", tzname[daylight],
+ "offset", (json_int_t) timezone,
+ "dst", daylight,
+ "kernel",
+ "sysname", uts.sysname,
+ "nodename", uts.nodename,
+ "release", uts.release,
+ "version", uts.version,
+ "machine", uts.machine,
+ "domainname", uts.domainname,
+ "system",
+ "cores_configured", get_nprocs_conf(),
+ "cores", get_nprocs(),
+ "procecces", (json_int_t) sinfo.procs,
+ "uptime", (json_int_t) sinfo.uptime,
+ "load",
+ f_load * sinfo.loads[0],
+ f_load * sinfo.loads[1],
+ f_load * sinfo.loads[2],
+ "ram",
+ "total", (json_int_t) (sinfo.totalram * sinfo.mem_unit),
+ "free", (json_int_t) (sinfo.freeram * sinfo.mem_unit),
+ "shared", (json_int_t) (sinfo.sharedram * sinfo.mem_unit),
+ "buffer", (json_int_t) (sinfo.bufferram * sinfo.mem_unit),
+ "swap",
+ "total", (json_int_t) (sinfo.totalswap * sinfo.mem_unit),
+ "free", (json_int_t) (sinfo.freeswap * sinfo.mem_unit),
+ "highmem",
+ "total", (json_int_t) (sinfo.totalhigh * sinfo.mem_unit),
+ "free", (json_int_t) (sinfo.freehigh * sinfo.mem_unit)
+ );
+ if (!json_status)
+ throw Error(HTTP_STATUS_INTERNAL_SERVER_ERROR, "Failed to prepare response: {}", err.text);
+
+#ifdef LWS_WITH_SERVER_STATUS
+ json_object_set(json_status, "lws", getLwsStatus());
+#endif /* LWS_WITH_SERVER_STATUS */
return new Response(session, json_status);
}
@@ -68,4 +162,3 @@ static RequestPlugin p;
} /* namespace node */
} /* namespace villas */
-#endif /* LWS_WITH_SERVER_STATUS */
diff --git a/lib/super_node.cpp b/lib/super_node.cpp
index b7e4bc93c..5af5ea95c 100644
--- a/lib/super_node.cpp
+++ b/lib/super_node.cpp
@@ -34,6 +34,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -48,7 +49,7 @@ using namespace villas::utils;
SuperNode::SuperNode() :
state(State::INITIALIZED),
- idleStop(true),
+ idleStop(-1),
#ifdef WITH_API
api(this),
#endif
@@ -63,10 +64,18 @@ SuperNode::SuperNode() :
affinity(0),
hugepages(DEFAULT_NR_HUGEPAGES),
statsRate(1.0),
- task(CLOCK_REALTIME)
+ task(CLOCK_REALTIME),
+ started(time_now())
{
int ret;
+ char hname[128];
+ ret = gethostname(hname, sizeof(hname));
+ if (ret)
+ throw SystemError("Failed to determine hostname");
+
+ uuid_generate_from_str(uuid, hname);
+
ret = vlist_init(&nodes);
if (ret)
throw RuntimeError("Failed to initialize list");
@@ -95,6 +104,8 @@ void SuperNode::parse(json_t *root)
assert(state != State::STARTED);
+ const char *uuid_str = nullptr;
+
json_t *json_nodes = nullptr;
json_t *json_paths = nullptr;
json_t *json_logging = nullptr;
@@ -102,7 +113,9 @@ void SuperNode::parse(json_t *root)
json_error_t err;
- ret = json_unpack_ex(root, &err, 0, "{ s?: F, s?: o, s?: o, s?: o, s?: o, s?: i, s?: i, s?: i, s?: b }",
+ idleStop = 1;
+
+ ret = json_unpack_ex(root, &err, 0, "{ s?: F, s?: o, s?: o, s?: o, s?: o, s?: i, s?: i, s?: i, s?: b, s?: s }",
"stats", &statsRate,
"http", &json_http,
"logging", &json_logging,
@@ -111,11 +124,18 @@ void SuperNode::parse(json_t *root)
"hugepages", &hugepages,
"affinity", &affinity,
"priority", &priority,
- "idle_stop", &idleStop
+ "idle_stop", &idleStop,
+ "uuid", &uuid_str
);
if (ret)
throw ConfigError(root, err, "node-config", "Unpacking top-level config failed");
+ if (uuid_str) {
+ ret = uuid_parse(uuid_str, uuid);
+ if (ret)
+ throw ConfigError(root, "node-config-uuid", "Failed to parse UUID: {}", uuid_str);
+ }
+
#ifdef WITH_WEB
if (json_http)
web.parse(json_http);
@@ -502,7 +522,7 @@ int SuperNode::periodic()
}
}
- if (idleStop && state == State::STARTED && started == 0) {
+ if (idleStop > 0 && state == State::STARTED && started == 0) {
info("No more active paths. Stopping super-node");
return -1;