diff mod_http_host_status_check/mod_http_host_status_check.lua @ 2219:5fcf9d558250

Three new modules: mod_host_status_check, mod_host_status_heartbeat and mod_http_host_status_check
author Matthew Wild <mwild1@gmail.com>
date Tue, 28 Jun 2016 22:33:13 +0100
parents
children 7356d722e180
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_http_host_status_check/mod_http_host_status_check.lua	Tue Jun 28 22:33:13 2016 +0100
@@ -0,0 +1,101 @@
+local heartbeats = module:shared("/*/host_status_check/heartbeats");
+local events = module:shared("/*/host_status_check/connection_events");
+
+local time = require "socket".gettime;
+local template = require "util.interpolation".new("%b{}", function (s) return s end)
+local st = require "util.stanza";
+
+module:depends "http"
+
+local threshold = module:get_option_number("status_check_heartbeat_threshold", 5);
+
+local function status_string(status, duration, comment)
+	local string_timestamp = "";
+	if duration then
+		string_timestamp = ("(%0.2fs%s)"):format(duration, comment or "");
+	elseif comment then
+		string_timestamp = ("(%s)"):format(comment);
+	else
+		return status and "UP" or "DOWN";
+	end
+	return (status and "UP " or "DOWN ")..string_timestamp;
+end
+
+local function string_pad(s, len)
+	return s..(" "):rep(len-#s);
+end
+
+local status_page_template = [[
+STATUS {status}
+{host_statuses%HOST {item} {idx}
+}]];
+
+function status_page()
+	local host_statuses = {};
+	local current_time = time();
+
+	local all_ok = true;
+
+	for host in pairs(hosts) do
+		local last_heartbeat_time = heartbeats[host];
+		
+		local ok, status_text = true, "OK";
+		
+		local is_component = hosts[host].type == "component" and hosts[host].modules.component;
+		
+		if is_component then
+			local current_status = hosts[host].modules.component.connected;
+			if events[host] then
+				local tracked_status = events[host].connected;
+				if tracked_status == current_status then
+					status_text = status_string(current_status, time() - events[host].timestamp);
+				else
+					status_text = status_string(current_status, nil, "!");
+				end
+			else
+				status_text = status_string(current_status, nil, "?");
+			end
+			if not current_status then
+				ok = false;
+			end
+		else
+			local event_info = events[host];
+			local connected = true;
+			if event_info then
+				connected = event_info.connected;
+			end
+			status_text = status_string(connected, event_info and (time() - events[host].timestamp), not event_info and "?");
+		end
+
+		if last_heartbeat_time then
+			local time_since_heartbeat = current_time - last_heartbeat_time;
+			if ok then
+				if time_since_heartbeat > threshold then
+					status_text = ("TIMEOUT (%0.2fs)"):format(time_since_heartbeat);
+					ok = false;
+				else
+					status_text = status_text:gsub("^%S+", "GOOD");
+				end
+			end
+		end
+
+		if not ok then
+			all_ok = false;
+		end
+		
+		if not ok or is_component or last_heartbeat_time then
+			host_statuses[host] = string_pad(status_text, 20);
+		end
+	end
+	local page = template(status_page_template, {
+		status = all_ok and "OK" or "FAIL";
+		host_statuses = host_statuses;
+	});
+	return page;
+end
+
+module:provides("http", {
+	route = {
+		GET = status_page;
+	};
+})