A Lua script to check the health of Devuan Linux package mirrors. The master repo is at https://sledjhamr.org/cgit/apt-panopticon/ and the master issues tracker is at https://sledjhamr.org/mantisbt/project_page.php?project_id=13
https://sledjhamr.org/cgit/apt-panopticon/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
632 lines
24 KiB
632 lines
24 KiB
#!/usr/bin/env luajit
|
|
|
|
local APT = require 'apt-panopticommon'
|
|
local D = APT.D
|
|
local I = APT.I
|
|
local T = APT.T
|
|
local W = APT.W
|
|
local E = APT.E
|
|
local C = APT.C
|
|
local arg, sendArgs = APT.parseArgs({...})
|
|
|
|
|
|
local results = {}
|
|
APT.mirrors = loadfile("results/mirrors.lua")()
|
|
APT.debians = loadfile("results/debians.lua")()
|
|
|
|
|
|
local revDNS = function(hosts, dom, IP)
|
|
if APT.options.roundRobin.value ~= dom then
|
|
if nil ~= hosts[APT.options.roundRobin.value] then
|
|
if nil ~= hosts[APT.options.roundRobin.value].IPs[APT.options.roundRobinCname.value] then
|
|
if nil ~= hosts[APT.options.roundRobin.value].IPs[APT.options.roundRobinCname.value][IP] then
|
|
if APT.html then
|
|
return "<font color='purple'><b>DNS-RR</b></font>"
|
|
else
|
|
return "DNS-RR"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
else
|
|
for k, v in pairs(hosts) do
|
|
if (APT.options.roundRobin.value ~= k) and (nil ~= v.IPs) 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(hosts, host, results, typ)
|
|
local result = ""
|
|
local e = 0
|
|
local w = 0
|
|
local t = 0
|
|
local d = 0
|
|
local s = nil ~= hosts[host].Protocols[typ]
|
|
local to = results.timeout
|
|
if not APT.search(APT.protocols, typ) then s = true end
|
|
if nil ~= results[typ] then
|
|
d = results[typ].tested
|
|
e = results[typ].errors
|
|
w = results[typ].warnings
|
|
t = results[typ].timeouts
|
|
for k, v in pairs(results[typ]) do
|
|
if ("table" == type(v)) and ('redirects' ~= k) then
|
|
if 0 <= v.tested then d = d + v.tested else to = true end
|
|
if 0 <= v.errors then e = e + v.errors else to = true end
|
|
if 0 <= v.warnings then w = w + v.warnings else to = true end
|
|
if 0 <= v.timeouts then t = t + v.timeouts else to = true end
|
|
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
|
|
if 0 <= u.tested then d = d + u.tested end
|
|
if 0 <= u.errors then e = e + u.errors end
|
|
if 0 <= u.warnings then w = w + u.warnings end
|
|
if 0 <= u.timeouts then t = t + u.timeouts end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if to then
|
|
result = "TIMEOUT"
|
|
hosts[host].passed = false;
|
|
if not s then result = result .. "*" end
|
|
if APT.html then
|
|
if s then
|
|
result = "<font color='aqua'><b>TIMEOUT</b></font>"
|
|
else
|
|
result = "<font color='blue'><b>TIMEOUT*</b></font>"
|
|
end
|
|
end
|
|
if APT.html then
|
|
faulty = faulty .. host .. " (" .. typ .. ")<br>\n"
|
|
else
|
|
faulty = faulty .. host .. " (" .. typ .. ")\n"
|
|
end
|
|
elseif 0 < e then
|
|
result = "FAILED"
|
|
hosts[host].passed = false;
|
|
if not s then result = result .. "*" end
|
|
if APT.html then
|
|
if s then
|
|
result = "<font color='red'><b>FAILED</b></font>"
|
|
else
|
|
result = "<font color='darkred'><b>FAILED*</b></font>"
|
|
end
|
|
end
|
|
if APT.html then
|
|
faulty = faulty .. host .. " (" .. typ .. ")<br>\n"
|
|
else
|
|
faulty = faulty .. host .. " (" .. typ .. ")\n"
|
|
end
|
|
elseif 0 < d then
|
|
result = "OK"
|
|
if not s then result = result .. "*" end
|
|
if APT.html then
|
|
if s then
|
|
result = "<font color='lime'><b>OK</b></font>"
|
|
else
|
|
result = "<font color='darkgreen'><b>OK*</b></font>"
|
|
end
|
|
end
|
|
else
|
|
result = "untested"
|
|
if not s then result = result .. "*" end
|
|
if APT.html then
|
|
if s then
|
|
result = "<font color='grey'><b>untested</b></font>"
|
|
else
|
|
result = "<font color='dimgrey'><b>untested*</b></font>"
|
|
end
|
|
end
|
|
end
|
|
return result .. APT.plurals(e, w, t)
|
|
end
|
|
|
|
local m = {}
|
|
|
|
local logCount = function(domain, ip)
|
|
local nm = "LOG_" .. domain
|
|
local log = ""
|
|
local extra = ""
|
|
local errors = 0
|
|
local warnings = 0
|
|
local timeouts = 0
|
|
if nil ~= ip then nm = nm .. "_" .. ip end
|
|
nm = nm .. ".html"
|
|
local rfile, e = io.open("results/" .. nm, "r")
|
|
if nil ~= rfile then
|
|
for l in rfile:lines() do
|
|
if nil ~= l:match("><b>ERROR ") then errors = errors + 1 end
|
|
if nil ~= l:match("><b>WARNING ") then warnings = warnings + 1 end
|
|
if nil ~= l:match("><b>TIMEOUT ") then timeouts = timeouts + 1 end
|
|
end
|
|
rfile:close()
|
|
end
|
|
if APT.html then
|
|
if nil == ip then
|
|
log = "<a href='" .. nm .. "'>" .. domain .. "</a>"
|
|
else
|
|
log = "<a href='" .. nm .. "'>" .. ip .. "</a>"
|
|
end
|
|
end
|
|
log = log .. APT.plurals(errors, warnings, timeouts)
|
|
return log
|
|
end
|
|
|
|
local redirs = function(hosts, host)
|
|
local results = APT.collateAll(hosts, 'results', host)
|
|
local rdr = {}
|
|
local redirs = ''
|
|
for p, pt in pairs(APT.protocols) do
|
|
if 0 ~= #(results[pt].redirects) then
|
|
table.sort(results[pt].redirects)
|
|
for r, rd in pairs(results[pt].redirects) do
|
|
rdr[rd] = rd
|
|
end
|
|
end
|
|
end
|
|
for r, rd in pairs(rdr) do
|
|
redirs = redirs .. ',   ' .. rd
|
|
end
|
|
if '' ~= redirs then redirs = '<br>\n (Redirects some packages to - ' .. redirs:sub(3) .. ')' end
|
|
return redirs
|
|
end
|
|
|
|
|
|
local DNSrrTest = function(hosts, k)
|
|
local dns = ''
|
|
local space = ' '
|
|
local no = 'no'
|
|
if APT.html then
|
|
space = ' '
|
|
no = "<font color='grey'><b>no</b></font>"
|
|
end
|
|
if (APT.options.roundRobin.value ~= k) and (nil ~= hosts[APT.options.roundRobin.value]) and (nil ~= hosts[k].IPs) and ("no" ~= hosts[k].DNSRR) then
|
|
APT.allpairs(hosts[k].IPs,
|
|
function(i, w, k, v)
|
|
-- if nil ~= hosts[APT.options.roundRobin.value].IPs[APT.options.roundRobinCname.value] then
|
|
-- if nil ~= hosts[APT.options.roundRobin.value].IPs[APT.options.roundRobinCname.value][i] then
|
|
local log = logCount(APT.options.roundRobin.value, i)
|
|
local inRR = "<font color='green'><b>✅</b></font>"
|
|
if nil ~= log:find("<font color='") then inRR = "<font color='red'>❌</font>" end
|
|
if "" ~= log then
|
|
if "" == dns then dns = " " else dns = dns .. space end
|
|
dns = dns .. inRR .. logCount(APT.options.roundRobin.value, i)
|
|
else
|
|
if "" == dns then dns = " " else dns = dns .. space end
|
|
if APT.html then i = "<font color='maroon'><b>" .. i .. "</b></font>" end
|
|
dns = dns .. inRR .. i
|
|
end
|
|
-- end
|
|
-- end
|
|
end
|
|
)
|
|
end
|
|
if "" == dns then dns = no end
|
|
return dns
|
|
end
|
|
|
|
|
|
local copyHTMLbit = function(web, file)
|
|
local rfile, e = io.open(file, "r")
|
|
if nil == rfile then W("opening " .. file .. " file - " .. e) else
|
|
for line in rfile:lines("*l") do
|
|
web:write(line .. '\n')
|
|
end
|
|
end
|
|
end
|
|
|
|
local makeTable = function(web, hosts)
|
|
copyHTMLbit(web, "Report-web_TABLE.html")
|
|
local bg = ''
|
|
for k, v in APT.orderedPairs(hosts) do
|
|
if '' == bg then bg = " style='background-color:#111111'" else bg = '' end
|
|
local results = APT.collateAll(hosts, 'results', k)
|
|
local active = ""
|
|
if "yes" == v.Active then
|
|
web:write(" <tr" .. bg .. "><th>" .. k .. "</th> ")
|
|
else
|
|
if nil == v.Active then active = 'nil' else active = v.Active end
|
|
web:write(" <tr style='background-color:dimgrey'><th>" .. k .. "</th> ")
|
|
end
|
|
hosts[k].passed = true;
|
|
local inRR = "<font color='green'><b>✅</b></font>"
|
|
local ftp = "<font color='grey'><b>skip</b></font>"
|
|
local http = status(hosts, k, results, "http")
|
|
local https = status(hosts, k, results, "https")
|
|
local rsync = "<font color='grey'><b>skip</b></font>"
|
|
local dns = DNSrrTest(hosts, k)
|
|
local protocol = status(hosts, k, results, "Protocol")
|
|
local sanity = status(hosts, k, results, "URLSanity")
|
|
local integrity = status(hosts, k, results, "Integrity")
|
|
local redirects = status(hosts, k, results, "Redirects")
|
|
local updated = status(hosts, k, results, "Updated")
|
|
local rate = v.Rate
|
|
local min = tonumber(results.speed.min)
|
|
local max = tonumber(results.speed.max)
|
|
local spd = '<td></td><td></td>'
|
|
local week = '<td> </td><td> </td>'
|
|
if nil == rate then rate = '' end
|
|
|
|
if not hosts[k].passed then inRR = "<font color='red'>❌</font>" end
|
|
|
|
if (APT.options.roundRobin.value ~= k) and (nil ~= hosts[APT.options.roundRobin.value]) then
|
|
if 0 == max then
|
|
spd = '<td></td><td></td>'
|
|
else
|
|
spd = string.format('<td align="right">%d -</td><td align="right">%d</td>', min, max)
|
|
end
|
|
end
|
|
|
|
if (APT.options.roundRobin.value ~= k) then
|
|
local percentUp = '??'
|
|
local percentUpdated = '??'
|
|
if APT.checkFile('rrd/' .. k .. '/Speed/Speed.rrd') then
|
|
local start, step, names, data = APT.rrd.fetch('rrd/' .. k .. '/Speed/Speed.rrd', 'LAST', '-a', '-r', '10m', '-s', '-1w')
|
|
local count, up, down, unknown = 0, 0, 0, 0
|
|
for i, dp in ipairs(data) do
|
|
for j,v in ipairs(dp) do
|
|
if 'max' == names[j] then
|
|
if 'nan' == tostring(v) then
|
|
unknown = unknown + 1
|
|
else
|
|
count = count + 1
|
|
if 0 == v then down = down + 1 else up = up + 1 end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
percentUp = string.format('%.2f', up / count * 100)
|
|
end
|
|
if APT.checkFile('rrd/' .. k .. '/HTTP/Tests.rrd') then
|
|
local start, step, names, data = APT.rrd.fetch('rrd/' .. k .. '/HTTP/Tests.rrd', 'LAST', '-a', '-r', '10m', '-s', '-1w')
|
|
local count, up, down, unknown = 0, 0, 0, 0
|
|
for i,dp in ipairs(data) do
|
|
for j,v in ipairs(dp) do
|
|
if 'UpdatedErrors' == names[j] then
|
|
if 'nan' == tostring(v) then
|
|
unknown = unknown + 1
|
|
else
|
|
count = count + 1
|
|
if 0 == v then down = down + 1 else up = up + 1 end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
percentUpdated = string.format('%.2f', (down / count * 100))
|
|
if '0.00' == percentUp then percentUpdated = '??' end -- We are counting errors, and you can't get an error if you can't check anything.
|
|
-- TODO - try to account for this better, this is just a quick hack.
|
|
end
|
|
week = '<td> ' .. percentUp .. '% up</td><td> ' .. percentUpdated .. '% updated</td>'
|
|
-- if ('100.00' ~= percentUp) or ('100.00' ~= percentUpdated) then inRR = "<font color='red'>❌</font>" end
|
|
end
|
|
if "yes" ~= hosts[k].DNSRR then inRR = " " end
|
|
if "maybe" == hosts[k].DNSRR then inRR = "<font color='red'>❓</font>" end
|
|
|
|
web:write("<td>" .. ftp .. " </td><td>" .. http .. " </td><td>" .. https .. " </td><td>" .. rsync .. " </td><td>" .. inRR .. " " .. dns ..
|
|
" </td><td>" .. protocol .. " </td><td>" .. redirects .. " </td><td>" .. sanity .. " </td><td>" .. integrity .. " </td>" .. '<td>' .. rate ..
|
|
'</td><td>' .. updated .. '</td> ' .. spd .. " " .. week .." </tr>\n")
|
|
if "" ~= active then
|
|
web:write("<tr><td style='background-color:dimgrey'>" .. active .. "</td></tr>\n")
|
|
end
|
|
end
|
|
web:write( "</table>\n<br>\n")
|
|
end
|
|
|
|
local makeIPlist = function(hosts)
|
|
local m = {}
|
|
local adr = ''
|
|
local checkRR = hosts == APT.mirrors;
|
|
local RRbfile, RRgfile
|
|
if APT.options.cgi.value then adr = 'php.cgi/' end
|
|
adr = '/' .. adr .. 'apt-panopticon/apt-panopticon_cgp/host.php?h='
|
|
if checkRR then
|
|
-- TODO - note that an IP can end up in both, which means it failed direct, but worked via DNS-RR, or the other way around.
|
|
-- TODO - They want to use a masterlist instead of the actual DNS to know which should be in either file. Should put this into https://pkgmaster.devuan.org/mirror_list.txt
|
|
RRbfile, e = io.open("results/DNS-RR_bad.txt", "w+")
|
|
if nil == RRbfile then C("opening DNS-RR_bad.txt file - " .. e) end
|
|
RRgfile, e = io.open("results/DNS-RR_good.txt", "w+")
|
|
if nil == RRgfile then C("opening DNS-RR_good.txt file - " .. e) end
|
|
end
|
|
for k, v in pairs(hosts) do
|
|
local log = k
|
|
local n = {}
|
|
log = logCount(k)
|
|
hosts[k].Protocols = nil
|
|
hosts[k].FQDN = nil
|
|
hosts[k].Active = nil
|
|
hosts[k].Rate = nil
|
|
hosts[k].BaseURL = nil
|
|
hosts[k].Country = nil
|
|
hosts[k].Bandwidth = nil
|
|
|
|
if nil ~= hosts[k].IPs then
|
|
for l, w in pairs(hosts[k].IPs) do
|
|
if type(w) == "table" then
|
|
-- Don't output the extra DNS-RR entries that are for admin reasons.
|
|
if ((APT.options.roundRobin.value == k) and (APT.options.roundRobinCname.value == l)) or (APT.options.roundRobin.value ~= k) then
|
|
n[l] = {}
|
|
for i, u in pairs(w) do
|
|
if (APT.testing("IPv6") and ("AAAA" == u)) or ("A" == u) then
|
|
local inRR = ""
|
|
local lc = logCount(k, i)
|
|
if checkRR and ('no' ~= hosts[k].DNSRR) then
|
|
-- If there where errors, warnings, or timeouts, then it'll have that wrapped in font tags.
|
|
inRR = "<font color='green'><b>✅</b></font>"
|
|
if nil ~= lc:find("<font color='") then
|
|
inRR = "<font color='red'>❌</font>"
|
|
if nil ~= RRbfile then
|
|
local f, e = RRbfile:write(i, '\n')
|
|
if f == nil then C("writing DNS-RR_bad.txt file - " .. e) end
|
|
end
|
|
elseif nil ~= RRgfile then
|
|
local f, e = RRgfile:write(i, '\n')
|
|
if f == nil then C("writing DNS-RR_good.txt file - " .. e) end
|
|
end
|
|
end
|
|
if "maybe" == hosts[k].DNSRR then inRR = "<font color='red'>❓</font>" end
|
|
if "no" == hosts[k].DNSRR then inRR = "" end
|
|
local log = '[<a href="' .. adr .. k .. '_' .. i .. '">graphs</a>] '
|
|
if "" == log then n[l][i] = u else n[l][log .. inRR .. ' ' .. revDNS(hosts, k, i) .. ' ' .. lc] = u end
|
|
end
|
|
end
|
|
end
|
|
else
|
|
if (APT.testing("IPv6") and ("AAAA" == w)) or ("A" == w) then
|
|
local inRR = ""
|
|
local lc = logCount(k, l)
|
|
if checkRR and ('no' ~= hosts[k].DNSRR) then
|
|
-- If there where errors, warnings, or timeouts, then it'll have that wrapped in font tags.
|
|
inRR = "<font color='green'><b>✅</b></font>"
|
|
if nil ~= lc:find("<font color='") then
|
|
inRR = "<font color='red'>❌</font>"
|
|
if nil ~= RRbfile then
|
|
local f, e = RRbfile:write(l, '\n')
|
|
if f == nil then C("writing DNS-RR_bad.txt file - " .. e) end
|
|
end
|
|
elseif nil ~= RRgfile then
|
|
local f, e = RRgfile:write(l, '\n')
|
|
if f == nil then C("writing DNS-RR_good.txt file - " .. e) end
|
|
end
|
|
end
|
|
if "maybe" == hosts[k].DNSRR then inRR = "<font color='red'>❓</font>" end
|
|
if "no" == hosts[k].DNSRR then inRR = "" end
|
|
local log = '[<a href="' .. adr .. k .. '_' .. l .. '">graphs</a>] '
|
|
if "" == log then n[l] = w else n[log .. inRR .. ' ' .. revDNS(hosts, k, l) .. ' ' .. lc] = w end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
m['[<a href="' .. adr .. k .. '">graphs</a>] ' .. log .. " DNS entries -" .. redirs(hosts, k)] = n
|
|
end
|
|
if nil ~= RRgfile then
|
|
RRgfile:close()
|
|
os.execute('sort results/DNS-RR_good.txt | uniq > results/DNS-RR_good.txt_ && mv results/DNS-RR_good.txt_ results/DNS-RR_good.txt')
|
|
end
|
|
if nil ~= RRbfile then
|
|
RRbfile:close()
|
|
os.execute('sort results/DNS-RR_bad.txt | uniq > results/DNS-RR_bad.txt_ && mv results/DNS-RR_bad.txt_ results/DNS-RR_bad.txt')
|
|
end
|
|
return m
|
|
end
|
|
|
|
|
|
APT.html = false
|
|
local email, e = io.open("results/Report-email.txt", "w+")
|
|
if nil == email then C("opening mirrors file - " .. e) else
|
|
email:write( "Dear Mirror Admins,\n\n" ..
|
|
"This is a summary of the status of the mirror servers in the \nDevuan package mirror network.\n\n" ..
|
|
"EXPERIMENTAL CODE - double check all results you see here, \nand read the logs if it's important.\n\n" ..
|
|
"The full list of Devuan package mirrors is available at the URL:\n\n" ..
|
|
" https://pkgmaster.devuan.org/mirror_list.txt\n\n" ..
|
|
'Please contact "mirrors@devuan.org" if any of the information \nin the file above needs to be amended.\n\n' ..
|
|
"The full results of the mirror checking is available at the URLs:\n\n" ..
|
|
" https://borta.devuan.dev/apt-panopticon/results/Report-web.html\n (updated once every hour)\n" ..
|
|
" https://sledjhamr.org/apt-panopticon/results/Report-web.html\n (updated once every ten minutes)\n\n" ..
|
|
"Due to the nature of the tests, some errors or warnings will be \ncounted several times. " ..
|
|
"Refer to the logs on the web page for details.\n\n" ..
|
|
"Please see below the current status of the Devuan Package Mirror \nnetwork:\n\n" ..
|
|
"==== package mirror status " .. os.date("!%F %H:%M") .. " GMT ====\n" ..
|
|
"[skip] means that the test hasn't been written yet.\n\n")
|
|
for k, v in APT.orderedPairs(APT.mirrors) do
|
|
email:write(k .. "..\n")
|
|
local results = APT.collateAll(APT.mirrors, 'results', k)
|
|
local ftp = "[skip]"
|
|
local http = status(APT.mirrors, k, results, "http")
|
|
local https = status(APT.mirrors, k, results, "https")
|
|
local rsync = "[skip]"
|
|
local dns = DNSrrTest(APT.mirrors, k)
|
|
local protocol = status(APT.mirrors, k, results, "Protocol")
|
|
local redirects = status(APT.mirrors, k, results, "Redirects")
|
|
local sanity = status(APT.mirrors, k, results, "URLSanity")
|
|
local integrity = status(APT.mirrors, k, results, "Integrity")
|
|
local updated = status(APT.mirrors, k, results, "Updated")
|
|
|
|
-- DNS-RR test.
|
|
if (APT.options.roundRobin.value ~= k) and (nil ~= APT.mirrors[APT.options.roundRobin.value]) then
|
|
dns = " DNS-RR: " .. dns .. "\n"
|
|
end
|
|
|
|
email:write( " ftp: " .. ftp .. "\n" ..
|
|
" http: " .. http .. "\n" ..
|
|
" https: " .. https .. "\n" ..
|
|
" rsync: " .. rsync .. "\n" ..
|
|
dns ..
|
|
" Protocol: " .. protocol .. "\n" ..
|
|
" Redirects: " .. redirects .. "\n" ..
|
|
" URL-sanity: " .. sanity .. "\n" ..
|
|
" Integrity: " .. integrity .. "\n" ..
|
|
" Updated: " .. updated .. "\n")
|
|
end
|
|
email:write( "\n==== faulty mirrors: ====\n" .. faulty)
|
|
email:write( "\n-------------------------\n\n" ..
|
|
"* This means that this protocol isn't actually supported, but the test was run ayway.\n\n" ..
|
|
"Thanks for your precious help in ensuring that Devuan GNU+Linux \nremains a universal, stable, dependable, free operating system.\n\n" ..
|
|
"You can get the source code from https://sledjhamr.org/cgit/apt-panopticon/about/ (main repo)\n" ..
|
|
"and from https://git.devuan.dev/onefang/apt-panopticon' (Devuan repo).\n" ..
|
|
"You can get the cgp graphing source code from https://sledjhamr.org/cgit/apt-panopticon_cgp/about/ (main repo)\n" ..
|
|
"and https://git.devuan.dev/onefang/apt-panopticon_cgp (Devuan repo)\n\n" ..
|
|
"Love\n\n" ..
|
|
"The Dev1Devs\n\n")
|
|
email:close()
|
|
end
|
|
|
|
|
|
local colours =
|
|
{
|
|
'f0000080',
|
|
'0f000080',
|
|
'00f00080',
|
|
'000f0080',
|
|
'0000f080',
|
|
'00000f80',
|
|
'80000080',
|
|
'08000080',
|
|
'00800080',
|
|
'00080080',
|
|
'00008080',
|
|
'00000880',
|
|
'ff000080',
|
|
'0ff00080',
|
|
'00ff0080',
|
|
'000ff080',
|
|
'0000ff80',
|
|
'88000080',
|
|
'08800080',
|
|
'00880080',
|
|
'00088080',
|
|
'00008880',
|
|
'80000080',
|
|
'08000080',
|
|
'00800080',
|
|
'00080080',
|
|
'00008080',
|
|
'00000880',
|
|
'fff00080',
|
|
'0fff0080',
|
|
'00fff080',
|
|
'000fff80',
|
|
'0000fff0',
|
|
}
|
|
local g = {}
|
|
local count = 0
|
|
for k, v in APT.orderedPairs(mirrors) do
|
|
if APT.options.referenceSite.value ~= k then count = count + 1 end
|
|
end
|
|
for i = 1, count do
|
|
end
|
|
|
|
count = 1
|
|
for k, v in APT.orderedPairs(mirrors) do
|
|
if APT.options.roundRobin.value ~= k then
|
|
local c = colours[count]
|
|
local name = string.format('%32s', k)
|
|
if c == nil then c = 'ffffff' end
|
|
if APT.options.referenceSite.value == k then c = 'ffffff' end
|
|
table.insert(g, 'DEF:speedn' .. count .. '=rrd/' .. k .. '/Speed/Speed.rrd:max:MIN')
|
|
table.insert(g, 'DEF:speedx' .. count .. '=rrd/' .. k .. '/Speed/Speed.rrd:max:MAX')
|
|
table.insert(g, 'DEF:speeda' .. count .. '=rrd/' .. k .. '/Speed/Speed.rrd:max:AVERAGE')
|
|
table.insert(g, 'DEF:speedl' .. count .. '=rrd/' .. k .. '/Speed/Speed.rrd:max:LAST')
|
|
table.insert(g, 'VDEF:vspeedn' .. count .. '=speedn' .. count .. ',AVERAGE')
|
|
table.insert(g, 'VDEF:vspeedx' .. count .. '=speedx' .. count .. ',AVERAGE')
|
|
table.insert(g, 'VDEF:vspeeda' .. count .. '=speeda' .. count .. ',AVERAGE')
|
|
table.insert(g, 'VDEF:vspeedl' .. count .. '=speedl' .. count .. ',AVERAGE')
|
|
table.insert(g, 'LINE2:speedx' .. count .. '#' .. c .. ':' .. name .. ' ')
|
|
table.insert(g, 'GPRINT:vspeedn' .. count .. ':Min %5.1lf%s,')
|
|
table.insert(g, 'GPRINT:vspeeda' .. count .. ':Avg %5.1lf%s,')
|
|
table.insert(g, 'GPRINT:vspeedx' .. count .. ':Max %5.1lf%s,')
|
|
table.insert(g, 'GPRINT:vspeedl' .. count .. ':Last %5.1lf%s\\l')
|
|
count = count + 1
|
|
end
|
|
end
|
|
APT.rrd.graph('results/speed.png', '--start', 'now-2w', '--end', 'now', '-t', 'Speed, rough maximum guess.', '-v', 'bytes per second', '-w', '900', '-h', '400', '-Z',
|
|
'-c', 'BACK#000000', '-c', 'CANVAS#000000', '-c', 'FONT#FFFFFF', '-c', 'AXIS#FFFFFF', '-c', 'FRAME#FFFFFF', '-c', 'ARROW#FFFFFF',
|
|
unpack(g))
|
|
|
|
|
|
results = {}
|
|
m = {}
|
|
faulty = ""
|
|
APT.html = true
|
|
local web, e = io.open("results/Report-web.html", "w+")
|
|
if nil == web then C("opening mirrors file - " .. e) else
|
|
copyHTMLbit(web, "Report-web_0.html")
|
|
if 0 < tonumber(APT.options.refresh.value) then
|
|
web:write('<meta http-equiv="refresh" content="' .. APT.options.refresh.value .. '">\n')
|
|
end
|
|
copyHTMLbit(web, "Report-web_1.html")
|
|
if 0 < tonumber(APT.options.refresh.value) then
|
|
web:write( '<p>This page will refresh every ' .. (APT.options.refresh.value / 60) .. ' minutes.</p>')
|
|
end
|
|
copyHTMLbit(web, "Report-web_2.html")
|
|
web:write("\n<h2>==== package mirror status " .. os.date("!%F %H:%M") .. " GMT ====</h2>\n")
|
|
copyHTMLbit(web, "Report-web_3.html")
|
|
makeTable(web, APT.mirrors)
|
|
web:write( "<h2>==== faulty mirrors: ====</h2>\n" .. faulty)
|
|
web:write( "<br>\n<br>\n<h2>==== DNS, links to graphs, and links to logs: ====</h2>\n")
|
|
m = makeIPlist(APT.mirrors)
|
|
web:write( "<p>This lists each mirror, and the DNS entries for that mirror. " ..
|
|
"The IP links point to the testing log files (the overall log is " .. logCount("apt-panopticon") .. ") for each domain name / IP combination that was tested. " ..
|
|
"If a mirror has a CNAME, that CNAME is listed along with that CNAMEs DNS entries. " ..
|
|
"<br>" ..
|
|
APT.options.roundRobin.value .. " is the DNS round robin, which points to the mirrors that are part of the DNS-RR. " ..
|
|
"If an IP is part of the DNS-RR, it is marked with '<font color='purple'><b>DNS-RR</b></font>'," ..
|
|
" if it <a href='DNS-RR_good.txt'>should be</a> it is marked with '<font color='green'><b>✅</b></font>'," ..
|
|
" if it <a href='DNS-RR_bad.txt'>should not be</a> it is marked with '<font color='red'>❌</font>'," ..
|
|
" if it might be but still pending full testing, it is marked with '<font color='red'>❓</font>'. " ..
|
|
"<br>" ..
|
|
APT.options.referenceSite.value .. " is the master mirror, all the others copy files from it. " ..
|
|
"</p>\n"
|
|
)
|
|
web:write(APT.dumpTableHTML(m, ""))
|
|
web:write( "\n<br>\n<br>\n<h2>==== graphs: ====</h2>\n" ..
|
|
"<img src='speed.png'>\n<br>\n<p><a href='../apt-panopticon_cgp/'>More graphs.</a> with greater detail.</p><hr>\n\n")
|
|
|
|
results = {}
|
|
m = {}
|
|
faulty = ""
|
|
web:write( "<hr>\n<h2>==== Debian mirror status ====</h2>\n" ..
|
|
"<p>NOTE - This is not fully probing the Debian mirrors, we just collect some data from any redirects to other servers. " ..
|
|
"So this isn't a full set of tests.   Basically we don't know the shape of the Debian mirror infrastructure.</p>\n" ..
|
|
"<p><font style='background-color:red; color:black'>EXPERIMENTAL CODE - this is even more experimental than the rest.</font></p>\n"
|
|
)
|
|
|
|
makeTable(web, APT.debians)
|
|
web:write( "<br>\n<br>\n<h2>==== Debian DNS, links to graphs, and links to logs: ====</h2>\n")
|
|
|
|
m = makeIPlist(APT.debians)
|
|
web:write(APT.dumpTableHTML(m, ""))
|
|
|
|
web:write( "<hr>\n<hr>\n<p>The <a href='Report-email.txt'>email report</a>. " ..
|
|
"All <a href='../results'>the logs and other output</a>. " ..
|
|
"You can get the <a href='https://sledjhamr.org/cgit/apt-panopticon/about/'>source code here (main repo)</a>" ..
|
|
"and <a href='https://git.devuan.dev/onefang/apt-panopticon'>here (Devuan repo)</a>. " ..
|
|
"You can get the cgp graphing <a href='https://sledjhamr.org/cgit/apt-panopticon_cgp/about/'>source code here (main repo)</a>" ..
|
|
"and <a href='https://git.devuan.dev/onefang/apt-panopticon_cgp'>here (Devuan repo)</a>.</p>\n"
|
|
)
|
|
local whn = APT.exe('TZ="GMT" ls -dl1 --time-style="+%s" results/stamp | cut -d " " -f 6-6'):Do().result:sub(2, -2)
|
|
web:write( "<p>This run took " .. (os.time() - tonumber("0" .. whn)) .. " seconds.     apt-panopticon version " .. APT.version .. " </p>" ..
|
|
"\n</body></html>\n")
|
|
web:close()
|
|
end
|
|
|