#!/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"].IPs["deb.roundr.devuan.org"][IP] then return "DNS-RR" 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" ) for k, v in orderedPairs(mirrors) do local results = loadfile("results/" .. k .. ".lua")() file:write(" ") 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 = "[skip]" local updated = "[skip]" -- DNS-RR test. if "deb.devuan.org" ~= k 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 = "DNS-RR: [no]" else dns = "DNS-RR:" .. dns end end file:write("\n") end file:write( "
    " .. k .. "http: " .. http .. " https: " .. https .. " " .. dns .. " Protocol: " .. protocol .. " URL-sanity: " .. sanity .. " Integrity: " .. integrity .. " Updated: " .. updated .. "
    \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