#!/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 [ " deb.roundr.devuan.org " ] [ IP ] then
if APT.html then
return " <font color='purple'><b>DNS-RR</b></font> "
else
return " DNS-RR "
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 "
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 "
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 ) then
APT.allpairs ( hosts [ k ] . IPs ,
function ( i , w , k , v )
if nil ~= hosts [ APT.options . roundRobin.value ] . IPs [ " deb.roundr.devuan.org " ] [ i ] then
local log = logCount ( APT.options . roundRobin.value , i )
if " " ~= log then
if " " == dns then dns = " " else dns = dns .. space end
dns = dns .. 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 .. i
end
end
end
)
if " " == dns then dns = no end
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
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
-- DNS-RR test.
-- if nil == http: find('OK') then inRR = "<font color='red'>❌</font>" end
-- if nil == https: find('OK') then inRR = "<font color='red'>❌</font>" end
-- if nil == protocol: find('OK') then inRR = "<font color='red'>❌</font>" end
-- if nil == redirects: find('OK') then inRR = "<font color='red'>❌</font>" end
-- if nil == sanity: find('OK') then inRR = "<font color='red'>❌</font>" end
-- if nil == integrity: find('OK') then inRR = "<font color='red'>❌</font>" end
-- if nil == updated: find('OK') 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
web : write ( " <td> " .. ftp .. " </td><td> " .. http .. " </td><td> " .. https .. " </td><td> " .. rsync .. " </td><td> " .. 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
-- TODO - don't hard code deb.roundr.devuan.org.
-- Don't output the extra DNS-RR entries that are for admin reasons.
if ( ( APT.options . roundRobin.value == k ) and ( " deb.roundr.devuan.org " == 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 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 " yes " ~= 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 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 " yes " ~= 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 \n Devuan package mirror network. \n \n " ..
" EXPERIMENTAL CODE - double check all results you see here, \n and 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 \n in 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 \n counted 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 \n network: \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 \n remains 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 ' ,
}
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 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>'. " ..
" <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.</p> " ..
" \n </body></html> \n " )
web : close ( )
end