Changeset

4576:cade5dac1003

mod_http_admin_api: Add endpoints for server maintenance
author Jonas Schäfer <jonas@wielicki.name>
date Thu, 27 May 2021 16:18:10 +0200
parents 4575:5b4f43b90766
children 4577:253df0798996
files mod_http_admin_api/mod_http_admin_api.lua mod_http_admin_api/openapi.yaml
diffstat 2 files changed, 147 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/mod_http_admin_api/mod_http_admin_api.lua	Tue May 25 19:01:54 2021 +0200
+++ b/mod_http_admin_api/mod_http_admin_api.lua	Thu May 27 16:18:10 2021 +0200
@@ -3,9 +3,11 @@
 local json = require "util.json";
 local st = require "util.stanza";
 local array = require "util.array";
+local statsmanager = require "core.statsmanager";
 
 module:depends("http");
 
+local announce = module:depends("announce");
 local invites = module:depends("invites");
 local tokens = module:depends("tokenauth");
 local mod_pep = module:depends("pep");
@@ -578,6 +580,83 @@
 	});
 end
 
+local function maybe_export_plain_gauge(mf)
+	if mf == nil then
+		return nil
+	end
+	return mf.data.value
+end
+
+local function maybe_export_plain_counter(mf)
+	if mf == nil then
+		return nil
+	end
+	return {
+		since = mf.data._created,
+		value = mf.data.value,
+	}
+end
+
+local function maybe_export_summed_gauge(mf)
+	if mf == nil then
+		return nil
+	end
+	local sum = 0;
+	for _, metric in mf:iter_metrics() do
+		sum = sum + metric.value;
+	end
+	return sum;
+end
+
+local function get_server_metrics(event)
+	event.response.headers["Content-Type"] = json_content_type;
+	local result = {};
+	local families = statsmanager.get_metric_registry():get_metric_families();
+	result.memory = maybe_export_plain_gauge(families.process_resident_memory_bytes);
+	result.cpu = maybe_export_plain_counter(families.process_cpu_seconds);
+	result.c2s = maybe_export_summed_gauge(families["prosody_mod_c2s/connections"])
+	return json.encode(result);
+end
+
+local function post_server_announcement(event)
+	local request = event.request;
+	if request.headers.content_type ~= json_content_type
+	or (not request.body or #request.body == 0) then
+		return 400;
+	end
+	local body = json.decode(event.request.body);
+	if not body then
+		return 400;
+	end
+
+	if type(body.recipients) ~= "table" and body.recipients ~= "online" and body.recipients ~= "all" then
+		return 400;
+	end
+
+	if not body.body or #body.body == 0 then
+		return 400;
+	end
+
+	local message = st.message():tag("body"):text(body.body):up();
+	local host = module.host
+	message.attr.from = host
+	if body.recipients == "online" then
+		announce.send_to_online(message, host);
+	elseif body.recipients == "all" then
+		for username in usermanager.users(host) do
+			message.attr.to = username .. "@" .. host
+			module:send(st.clone(message))
+		end
+	else
+		for _, addr in ipairs(body.recipients) do
+			message.attr.to = addr
+			module:send(message)
+		end
+	end
+
+	return 201;
+end
+
 module:provides("http", {
 	route = check_auth {
 		["GET /invites"] = list_invites;
@@ -597,5 +676,8 @@
 		["DELETE /groups/*"] = delete_group;
 
 		["GET /server/info"] = get_server_info;
+
+		["GET /server/metrics"] = get_server_metrics;
+		["POST /server/announcement"] = post_server_announcement;
 	};
 });
--- a/mod_http_admin_api/openapi.yaml	Tue May 25 19:01:54 2021 +0200
+++ b/mod_http_admin_api/openapi.yaml	Thu May 27 16:18:10 2021 +0200
@@ -437,6 +437,35 @@
             application/json:
               schema:
                 $ref: '#/components/schemas/ServerInfo'
+  /server/metrics:
+    get:
+      tags:
+      - server
+      summary: Get metrics from the running server
+      operationId: getServerMetrics
+      responses:
+        200:
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ServerMetrics'
+  /server/announcement:
+    post:
+      tags:
+      - server
+      summary: Post an announcement to some or all users
+      operationId: postServerAnnouncement
+      requestBody:
+        description: Announcement parameters
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/Announcement"
+      responses:
+        201:
+          description: Announcement has been sent
 components:
   schemas:
     UserList:
@@ -735,4 +764,39 @@
           description: A friendly name for the service
         version:
           type: string
-          description: A human-readable version string
\ No newline at end of file
+          description: A human-readable version string
+    ServerMetrics:
+      type: object
+      description: A selection of instantaneous metrics of the prosody server
+      properties:
+        memory:
+          type: integer
+          description: RSS in bytes
+        cpu:
+          type: object
+          description: CPU time counter
+          required:
+          - value
+          - since
+          properties:
+            since:
+              type: float
+              description: The metric epoch as UNIX timestamp
+            value:
+              type: float
+              description: Seconds of CPU time used since the metric epoch
+        c2s:
+          type: integer
+          description: Number of active c2s sessions
+    Announcement:
+      type: object
+      description: An announcemen to post to users on the server
+      required:
+      - body
+      - recipients
+      properties:
+        body:
+          type: string
+          description: The message body to send
+        recipients:
+          description: List of recipients or one of the strings "online" or "all"