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;