changeset 4791:b86282953663

mod_s2s_status: Module to track status of s2s connections by domain
author Matthew Wild <mwild1@gmail.com>
date Fri, 26 Nov 2021 10:10:02 +0000 (2021-11-26)
parents bb66e87a3604
children 9d57aa79c5d9
files mod_s2s_status/README.markdown mod_s2s_status/mod_s2s_status.lua
diffstat 2 files changed, 132 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_s2s_status/README.markdown	Fri Nov 26 10:10:02 2021 +0000
@@ -0,0 +1,42 @@
+---
+labels:
+- 'Stage-Alpha'
+summary: 'Track the status and health of s2s connections'
+...
+
+Introduction
+============
+
+Prosody already gives some insight into current s2s connections, e.g. via
+the `s2s:show()` command in the console. This will tell you about all current
+s2s connections.
+
+However sometimes this is not enough. For example if an s2s connection fails
+to establish, it won't show up - you have to go digging through the log file
+looking for the errors instead.
+
+This module maintains a record of recent connection attempts to a remote
+domain. You can use this module to answer questions such as:
+
+- Why did the last connection attempt to `example.com` fail?
+- When did I last have a successful connection with `example.com`?
+- Are my s2s connections generally stable?
+
+**Note:** At the time of writing, this module is not yet finished, and should
+be considered a proof-of-concept.
+
+# Configuration
+
+Just load the module as normal:
+
+``` {.lua}
+modules_enabled = {
+  ...
+  "s2s_status";
+  ...
+}
+```
+
+# Compatibility
+
+trunk (0.12) and later, e.g. due to [60676b607b6d](https://hg.prosody.im/trunk/rev/60676b607b6d).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_s2s_status/mod_s2s_status.lua	Fri Nov 26 10:10:02 2021 +0000
@@ -0,0 +1,90 @@
+local status_out = module:shared("out");
+
+local errors = require "util.error";
+
+local function get_session_info(session)
+	local direction, peer_host = session.direction;
+	if direction == "outgoing" then
+		peer_host = session.to_host;
+	elseif direction == "incoming" then
+		peer_host = session.from_host;
+	end
+	return peer_host, direction, session.id;
+end
+
+local function get_domain_log_out(peer_domain)
+	local domain_log = status_out[peer_domain];
+	if not domain_log then
+		domain_log = {};
+		status_out[peer_domain] = domain_log;
+	end
+end
+
+local function get_connection_record(domain_log, id)
+	for _, record in ipairs(domain_log) do
+		if record.id == id then
+			return record;
+		end
+	end
+	-- No record for this connection yet, create it
+	local record = { id = id };
+	table.insert(domain_log, 1, record);
+	return record;
+end
+
+local function log_new_connection_out(peer_domain, id)
+	local domain_log = get_domain_log_out(peer_domain);
+	local record = get_connection_record(domain_log, id);
+	record.status, record.time_started = "connecting", os.time();
+end
+
+local function log_successful_connection_out(peer_domain, id)
+	local domain_log = get_domain_log_out(peer_domain);
+	local record = get_connection_record(domain_log, id);
+	record.status, record.time_connected = "connected", os.time();
+end
+
+local function log_ended_connection_out(peer_domain, id, reason)
+	local domain_log = get_domain_log_out(peer_domain);
+	local record = get_connection_record(domain_log, id);
+
+	if record.status == "connecting" then
+		record.status = "failed";
+	elseif record.status == "connected" then
+		record.status = "disconnected";
+	end
+	if reason then
+		local e_reason = errors.new(reason);
+		record.error = {
+			type = e_reason.type;
+			condition = e_reason.condition;
+			text = e_reason.text;
+		};
+		if not record.error.text and type(reason) == "string" then
+			record.error.text = reason;
+		end
+	end
+	local now = os.time();
+	record.time_ended = now;
+end
+
+local function s2sout_established(event)
+	local peer_domain, _, id = get_session_info(event.session);
+	log_successful_connection_out(peer_domain, id);
+end
+
+local function s2sout_destroyed(event)
+	local peer_domain, _, id = get_session_info(event.session);
+	log_ended_connection_out(peer_domain, id);
+end
+
+local function s2s_created(event)
+	local peer_domain, direction, id = get_session_info(event.session);
+	if direction == "outgoing" then
+		log_new_connection_out(peer_domain, id);
+	end
+end
+
+module:hook("s2s-created", s2s_created);
+module:hook("s2sout-established", s2sout_established);
+module:hook("s2sout-destroyed", s2sout_destroyed);