#!/usr/bin/env luajit
local args = {...}
verbosity = -1
local logFile
--[[ Ordered table iterator, allow to iterate on the natural order of the keys of a table.
From http://lua-users.org/wiki/SortedIteration
]]
function __genOrderedIndex( t )
local orderedIndex = {}
for key in pairs(t) do
table.insert( orderedIndex, key )
end
table.sort( orderedIndex )
return orderedIndex
end
function orderedNext(t, state)
-- Equivalent of the next function, but returns the keys in the alphabetic
-- order. We use a temporary ordered key table that is stored in the
-- table being iterated.
local key = nil
--print("orderedNext: state = "..tostring(state) )
if state == nil then
-- the first time, generate the index
t.__orderedIndex = __genOrderedIndex( t )
key = t.__orderedIndex[1]
else
-- fetch the next value
for i = 1,table.getn(t.__orderedIndex) do
if t.__orderedIndex[i] == state then
key = t.__orderedIndex[i+1]
end
end
end
if key then
return key, t[key]
end
-- no more value to return, cleanup
t.__orderedIndex = nil
return
end
function orderedPairs(t)
-- Equivalent of the pairs() function on tables. Allows to iterate
-- in order
return orderedNext, t, nil
end
-- Use this to dump a table to a string, with HTML.
dumpTableHTML = function (table, space, name)
local r = name .. "\n"
r = r .. dumpTableHTMLSub(table, space .. " ")
r = r .. space .. ""
return r
end
dumpTableHTMLSub = function (table, space)
local r = ""
for k, v in orderedPairs(table) do
if type(v) == "table" then
if " " == space then
r = r .. space .. dumpTableHTML(v, space, k .. "
\n"
else
r = r .. "" .. space .. dumpTableHTML(v, space, k .. "\n"
end
else
r = r .. space .. "" .. k .. "\n"
end
end
return r
end
local plurals = function(e, w)
local result = ""
if 1 == e then
result = e .. " error"
elseif e ~= 0 then
result = e .. " errors"
end
if 0 < w then
if 0 < e then result = result .. ", " end
if 1 == w then
result = result .. w .. " warning"
else
result = result .. w .. " warnings"
end
end
if "" ~= result then result = " (" .. result .. ")" end
return result
end
local results = {}
local log = function(v, t, s, prot, test, host)
local x = ""
if nil == prot then prot = "" end
if nil ~= test then x = x .. test else test = "" end
if nil ~= host then
if #x > 0 then x = x .. " " end
x = x .. host
end
if #x > 0 then
t = t .. "(" .. x .. ")"
end
if v <= verbosity then
if 3 <= verbosity then t = os.date() .. " " .. t end
print(t .. ": " .. s)
end
if nil ~= logFile then
logFile:write(os.date() .. " " .. t .. ": " .. s .. "\n")
logFile:flush()
end
end
local D = function(s) log(3, "DEBUG ", s) end
local I = function(s) log(2, "INFO ", s) end
local W = function(s, p, t, h) log(1, "WARNING ", s, p, t, h) end
local E = function(s, p, t, h) log(0, "ERROR ", s, p, t, h) end
local C = function(s) log(-1, "CRITICAL", s) end
local mirrors = loadfile("results/mirrors.lua")()
local revDNS = function(dom, IP)
if "deb.devuan.org" ~= dom then
if nil ~= mirrors["deb.devuan.org"] then
if nil ~= mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][IP] then
return "DNS-RR"
end
end
else
for k, v in pairs(mirrors) do
if "deb.devuan.org" ~= k then
local IPs = v.IPs
for i, u in pairs(IPs) do
if "table" == type(u) then
for h, t in pairs(u) do
if IP == h then return k end
end
else
if IP == i then return k end
end
end
end
end
end
return ""
end
local faulty = ""
local status = function(host, results, typ)
local result = ""
local e = 0
local w = 0
if nil ~= results[typ] then
e = results[typ].errors
w = results[typ].warnings
for k, v in pairs(results[typ]) do
if "table" == type(v) then
e = e + v.errors
w = w + v.warnings
end
end
else
for k, v in pairs(results) do
if "table" == type(v) then
for i, u in pairs(v) do
if "table" == type(u) then
if typ == i then
e = e + u.errors
w = w + u.warnings
end
end
end
end
end
end
if 0 < e then
result = "[FAILED]"
faulty = faulty .. host .. " (" .. typ .. ")
\n"
else
result = "[OK]"
end
return result .. plurals(e, w)
end
local collate = function(host, ip, results)
local f = "results/" .. host .. "_" .. ip .. ".lua"
local rfile, e = io.open(f, "r")
if nil == rfile then I("opening " .. f .. " file - " .. e) else
rfile:close()
local rs = loadfile(f)()
for k, v in pairs(rs) do
if "table" == type(v) then
for i, u in pairs(v) do
if "table" == type(u) then
for h, t in pairs(u) do
local a = results[k][h]
if nil == a then a = 0 end
results[k][h] = a + t
end
else
local a = results[k][i]
if nil == a then a = 0 end
results[k][i] = a + u
end
end
else
local a = results[k]
if nil == a then a = 0 end
results[k] = a + v
end
end
end
return results
end
local m = {}
local logCount = function(domain, ip)
local nm = "LOG_" .. domain
local log = ""
local extra = ""
if nil ~= ip then nm = nm .. "_" .. ip end
nm = nm .. ".html"
local rfile, e = io.open("results/" .. nm, "r")
if nil ~= rfile then
local errors = 0
local warnings = 0
for l in rfile:lines() do
if nil ~= l:match(">ERROR ") then errors = errors + 1 end
if nil ~= l:match(">WARNING ") then warnings = warnings + 1 end
end
rfile:close()
if nil == ip then
log = "" .. domain .. ""
else
log = "" .. ip .. ""
end
log = log .. plurals(errors, warnings)
end
return log
end
local file, e = io.open("results/Report-web.html", "w+")
if nil == file then C("opening mirrors file - " .. e) else
file:write( "apt-panopticon results\n" ..
'' ..
"Welcome to the apt-opticon results page.
\n" ..
"Check time: " .. os.date("!%a %b %d %T %Z %Y") .. "
\n" ..
"The full list of Devuan package mirrors is available at the URL: " ..
"https://pkgmaster.devuan.org/mirror_list.txt
\n" ..
"Due to the nature of the tests, some errors or warnings will be counted several times. " ..
"Refer to the logs below for details.
\n\n" ..
"Please see below the current status of the Devuan Package Mirror network:
\n" ..
"==== mirror status: ====
\n\n" ..
" | HTTP | HTTPS | DNS round robin | Protocol | URL sanity | Integrity | Updated |
\n"
)
for k, v in orderedPairs(mirrors) do
local results = loadfile("results/" .. k .. ".lua")()
file:write(" " .. k .. " | ")
local IPs = v.IPs
for i, u in pairs(IPs) do
if "table" == type(u) then
for h, t in pairs(u) do
results = collate(k, h, results)
end
else
results = collate(k, i, results)
end
end
local http = status(k, results, "http")
local https = status(k, results, "https")
local dns = ""
local protocol = status(k, results, "Protocol")
local sanity = "[skip]"
local integrity = status(k, results, "Integrity")
local updated = "[skip]"
-- DNS-RR test.
if ("deb.devuan.org" ~= k) and (nil ~= mirrors["deb.devuan.org"]) then
for l, w in pairs(mirrors[k].IPs) do
if type(w) == "table" then
for i, u in pairs(w) do
if nil ~= mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][i] then
local log = logCount("deb.devuan.org", i)
if "" ~= log then
if "" == dns then dns = " " else dns = dns .. " " end
dns = dns .. logCount("deb.devuan.org", i)
else
if "" == dns then dns = " " else dns = dns .. " " end
dns = dns .. "" .. i .. ""
end
end
end
else
if nil ~= mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][l] then
local log = logCount("deb.devuan.org", l)
if "" ~= log then
if "" == dns then dns = " " else dns = dns .. " " end
dns = dns .. log
else
if "" == dns then dns = " " else dns = dns .. " " end
dns = dns .. "" .. l .. ""
end
end
end
end
if "" == dns then dns = "[no]" end
end
file:write("" .. http .. " | " .. https .. " | " .. dns ..
" | " .. protocol .. " | " .. sanity ..
" | " .. integrity .. " | " .. updated .. " |
\n")
end
file:write( "
\n
\n==== faulty mirrors: ====
\n" .. faulty)
file:write( "
\n
\n==== DNS and logs: ====
\n")
for k, v in pairs(mirrors) do
local log = k
local n = {}
log = logCount(k)
mirrors[k].Protocols = nil
mirrors[k].FQDN = nil
mirrors[k].Active = nil
mirrors[k].Rate = nil
mirrors[k].BaseURL = nil
mirrors[k].Country = nil
mirrors[k].Bandwidth = nil
for l, w in pairs(mirrors[k].IPs) do
if type(w) == "table" then
n[l] = {}
for i, u in pairs(w) do
local log = logCount(k, i)
if "" == log then n[l][i] = u else n[l][log .. " " .. revDNS(k, i)] = u end
end
else
local log = logCount(k, l)
if "" == log then n[l] = w else n[log .. " " .. revDNS(k, l)] = w end
end
end
m[log .. " DNS entries -"] = n
end
file:write( "This lists each mirror, and the DNS entries for that mirror. " ..
"The links point to the log files for " .. logCount("apt-panopticon") .. " for each FDQN / IP combination that was checked. " ..
"If a mirror has a CNAME, that CNAME is listed along with that CNAMEs DNS entries. " ..
"deb.devuan.org is the DNS round robin, which points to the mirrors that are part of the DNS-RR. " ..
"pkgmaster.devuan.org is the master mirror, all the others sync to it. " ..
"
\n"
)
file:write(dumpTableHTML(m, "", ""))
file:write( "\n
\n\n" ..
"The email report.
" ..
"All the logs and other output.
" ..
"\n")
file:close()
end