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; }; })