aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/apt-panopticon-report-email-web.lua
diff options
context:
space:
mode:
authoronefang2019-12-02 06:28:30 +1000
committeronefang2019-12-02 06:28:30 +1000
commit3829c9b7962f11e760361deb764fc96af1345c62 (patch)
tree5d3dde39d99934cc5a21ca7f60f096603d048e19 /apt-panopticon-report-email-web.lua
parentAdd more report stuff. (diff)
downloadapt-panopticon-3829c9b7962f11e760361deb764fc96af1345c62.zip
apt-panopticon-3829c9b7962f11e760361deb764fc96af1345c62.tar.gz
apt-panopticon-3829c9b7962f11e760361deb764fc96af1345c62.tar.bz2
apt-panopticon-3829c9b7962f11e760361deb764fc96af1345c62.tar.xz
Merge email and web report scripts.
Diffstat (limited to 'apt-panopticon-report-email-web.lua')
-rwxr-xr-xapt-panopticon-report-email-web.lua525
1 files changed, 525 insertions, 0 deletions
diff --git a/apt-panopticon-report-email-web.lua b/apt-panopticon-report-email-web.lua
new file mode 100755
index 0000000..60027d0
--- /dev/null
+++ b/apt-panopticon-report-email-web.lua
@@ -0,0 +1,525 @@
1#!/usr/bin/env luajit
2
3local args = {...}
4
5verbosity = -1
6local logFile
7local html = false
8
9
10--[[ Ordered table iterator, allow to iterate on the natural order of the keys of a table.
11 From http://lua-users.org/wiki/SortedIteration
12 ]]
13function __genOrderedIndex( t )
14 local orderedIndex = {}
15 for key in pairs(t) do
16 table.insert( orderedIndex, key )
17 end
18 table.sort( orderedIndex )
19 return orderedIndex
20end
21function orderedNext(t, state)
22 -- Equivalent of the next function, but returns the keys in the alphabetic
23 -- order. We use a temporary ordered key table that is stored in the
24 -- table being iterated.
25
26 local key = nil
27 --print("orderedNext: state = "..tostring(state) )
28 if state == nil then
29 -- the first time, generate the index
30 t.__orderedIndex = __genOrderedIndex( t )
31 key = t.__orderedIndex[1]
32 else
33 -- fetch the next value
34 for i = 1,table.getn(t.__orderedIndex) do
35 if t.__orderedIndex[i] == state then
36 key = t.__orderedIndex[i+1]
37 end
38 end
39 end
40
41 if key then
42 return key, t[key]
43 end
44
45 -- no more value to return, cleanup
46 t.__orderedIndex = nil
47 return
48end
49function orderedPairs(t)
50 -- Equivalent of the pairs() function on tables. Allows to iterate
51 -- in order
52 return orderedNext, t, nil
53end
54
55-- Use this to dump a table to a string, with HTML.
56dumpTableHTML = function (table, space, name)
57 local r = name .. "\n"
58 r = r .. dumpTableHTMLSub(table, space .. " ")
59 r = r .. space .. ""
60 return r
61end
62dumpTableHTMLSub = function (table, space)
63 local r = ""
64 for k, v in orderedPairs(table) do
65 if type(v) == "table" then
66 if " " == space then
67 r = r .. space .. dumpTableHTML(v, space, k .. "<ul>") .. "</ul>\n"
68 else
69 r = r .. "<li>" .. space .. dumpTableHTML(v, space, k .. "<ul>") .. "</ul></li>\n"
70 end
71 else
72 r = r .. space .. "<li>" .. k .. "</li>\n"
73 end
74 end
75 return r
76end
77
78local plurals = function(e, w)
79 local result = ""
80 if 1 == e then
81 result = e .. " error"
82 elseif e ~= 0 then
83 result = e .. " errors"
84 end
85 if ("" ~= result) and html then result = "<font color='red'><b>" .. result .. "</b></font>" end
86-- result = " " .. result
87 if 0 < w then
88 if 0 < e then result = result .. ", " end
89 if 1 == w then
90 result = result .. w .. " warning"
91 else
92 result = result .. w .. " warnings"
93 end
94 if ("" ~= result) and html then result = "<font color='yellow'><b>" .. result .. "</b></font>" end
95-- result = " " .. result
96 end
97 if "" ~= result then result = " (" .. result .. ")" end
98 return result
99end
100
101local results = {}
102
103local log = function(v, t, s, prot, test, host)
104 local x = ""
105 if nil == prot then prot = "" end
106 if nil ~= test then x = x .. test else test = "" end
107 if nil ~= host then
108 if #x > 0 then x = x .. " " end
109 x = x .. host
110 end
111 if #x > 0 then
112 t = t .. "(" .. x .. ")"
113 end
114 if v <= verbosity then
115 if 3 <= verbosity then t = os.date() .. " " .. t end
116 print(t .. ": " .. s)
117 end
118 if nil ~= logFile then
119 logFile:write(os.date() .. " " .. t .. ": " .. s .. "\n")
120 logFile:flush()
121 end
122end
123local D = function(s) log(3, "DEBUG ", s) end
124local I = function(s) log(2, "INFO ", s) end
125local W = function(s, p, t, h) log(1, "WARNING ", s, p, t, h) end
126local E = function(s, p, t, h) log(0, "ERROR ", s, p, t, h) end
127local C = function(s) log(-1, "CRITICAL", s) end
128
129local mirrors = loadfile("results/mirrors.lua")()
130
131local revDNS = function(dom, IP)
132 if "deb.devuan.org" ~= dom then
133 if nil ~= mirrors["deb.devuan.org"] then
134 if nil ~= mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][IP] then
135 if html then
136 return "<font color='purple'><b>DNS-RR</b></font>"
137 else
138 return "DNS-RR"
139 end
140 end
141 end
142 else
143 for k, v in pairs(mirrors) do
144 if "deb.devuan.org" ~= k then
145 local IPs = v.IPs
146 for i, u in pairs(IPs) do
147 if "table" == type(u) then
148 for h, t in pairs(u) do
149 if IP == h then return k end
150 end
151 else
152 if IP == i then return k end
153 end
154 end
155 end
156 end
157 end
158 return ""
159end
160
161local faulty = ""
162local status = function(host, results, typ)
163 local result = ""
164 local e = 0
165 local w = 0
166 local s = nil ~= mirrors[host].Protocols[typ]
167 local to = false
168 if ('http' ~= typ) and ('https' ~= typ) and ('ftp' ~= typ) and ('rsync' ~= typ) then s = true end
169 if nil ~= results[typ] then
170 e = results[typ].errors
171 w = results[typ].warnings
172--[[
173 for k, v in pairs(results[typ]) do
174 if "table" == type(v) then
175 if 0 <= v.errors then e = e + v.errors else to = true end
176 if 0 <= v.warnings then w = w + v.warnings else to = true end
177 end
178 end
179]]
180 else
181 for k, v in pairs(results) do
182 if "table" == type(v) then
183 for i, u in pairs(v) do
184 if "table" == type(u) then
185 if typ == i then
186 if 0 <= u.errors then e = e + u.errors else to = true end
187 if 0 <= u.warnings then w = w + u.warnings else to = true end
188 end
189 end
190 end
191 end
192 end
193 end
194
195 if to then
196 result = "[TIMEOUT"
197 if not s then result = result .. "*" end
198 if html then
199 if s then
200 result = "[<font color='blue'><b>TIMEOUT</b></font>"
201 else
202 result = "[<font color='darkblue'><b>TIMEOUT*</b></font>"
203 end
204 end
205 elseif 0 < e then
206 result = "[FAILED"
207 if not s then result = result .. "*" end
208 if html then
209 if s then
210 result = "[<font color='red'><b>FAILED</b></font>"
211 else
212 result = "[<font color='darkred'><b>FAILED*</b></font>"
213 end
214 end
215 if html then
216 faulty = faulty .. host .. " (" .. typ .. ")<br>\n"
217 else
218 faulty = faulty .. host .. " (" .. typ .. ")\n"
219 end
220 else
221 result = "[OK"
222 if not s then result = result .. "*" end
223 if html then
224 if s then
225 result = "[<font color='lime'><b>OK</b></font>"
226 else
227 result = "[<font color='darkgreen'><b>OK*</b></font>"
228 end
229 end
230 end
231 return result .. plurals(e, w) .. "]"
232end
233
234local collate = function(host, ip, results)
235 local f = "results/" .. host .. "_" .. ip .. ".lua"
236 local rfile, e = io.open(f, "r")
237 if nil == rfile then I("opening " .. f .. " file - " .. e) else
238 rfile:close()
239 local rs = loadfile(f)()
240 for k, v in pairs(rs) do
241 if "table" == type(v) then
242 for i, u in pairs(v) do
243 if "table" == type(u) then
244 for h, t in pairs(u) do
245 local a = results[k][h]
246 if nil == a then a = 0 end
247 results[k][h] = a + t
248 end
249 else
250 local a = results[k][i]
251 if nil == a then a = 0 end
252 results[k][i] = a + u
253 end
254 end
255 else
256 local a = results[k]
257 if nil == a then a = 0 end
258 results[k] = a + v
259 end
260 end
261 end
262 return results
263end
264
265local m = {}
266
267local logCount = function(domain, ip)
268 local nm = "LOG_" .. domain
269 local log = ""
270 local extra = ""
271 if nil ~= ip then nm = nm .. "_" .. ip end
272 nm = nm .. ".html"
273 local rfile, e = io.open("results/" .. nm, "r")
274 if nil ~= rfile then
275 local errors = 0
276 local warnings = 0
277 for l in rfile:lines() do
278 if nil ~= l:match("><b>ERROR ") then errors = errors + 1 end
279 if nil ~= l:match("><b>WARNING ") then warnings = warnings + 1 end
280 end
281 rfile:close()
282 if html then
283 if nil == ip then
284 log = "<a href='" .. nm .. "'>" .. domain .. "</a>"
285 else
286 log = "<a href='" .. nm .. "'>" .. ip .. "</a>"
287 end
288 end
289 log = log .. plurals(errors, warnings)
290 end
291 return log
292end
293
294
295html = false
296local email, e = io.open("results/Report-email.txt", "w+")
297if nil == email then C("opening mirrors file - " .. e) else
298 email:write( "Dear Mirror Admins,\n\n" ..
299 "This is the status of the mirror servers in the Devuan package mirror network.\n\n" ..
300 "The full list of Devuan package mirrors is available at the URL:\n\n" ..
301 " https://pkgmaster.devuan.org/mirror_list.txt\n\n" ..
302 'Please contact "mirrors@devuan.org" if any of the information \nin the file above needs to be amended. \n\n' ..
303 "The full results of the mirror checking is available at the URL:\n\n" ..
304 " https://sledjhamr.org/apt-panopticon/results/Report-web.html\n\n" ..
305 "Due to the nature of the tests, some errors or warnings will be \ncounted several times. " ..
306 "Refer to the logs on the web page for details.\n\n" ..
307 "Please see below the current status of the Devuan Package Mirror \nnetwork:\n\n" ..
308 "==== package mirror status " .. os.date("!%Y-%m-%d %H:%M") .. " GMT ====\n" ..
309 "[skip] means that the test hasn't been written yet.\n\n")
310 for k, v in orderedPairs(mirrors) do
311 local results = loadfile("results/" .. k .. ".lua")()
312 email:write(k .. "....\n")
313 local IPs = v.IPs
314 for i, u in pairs(IPs) do
315 if "table" == type(u) then
316 for h, t in pairs(u) do
317 results = collate(k, h, results)
318 end
319 else
320 results = collate(k, i, results)
321 end
322 end
323 local ftp = "[skip]"
324 local http = status(k, results, "http")
325 local https = status(k, results, "https")
326 local rsync = "[skip]"
327 local dns = ""
328 local protocol = status(k, results, "Protocol")
329 local sanity = "[skip]"
330 local integrity = status(k, results, "Integrity")
331 local updated = status(k, results, "Updated")
332
333 -- DNS-RR test.
334 if ("deb.devuan.org" ~= k) and (nil ~= mirrors["deb.devuan.org"]) then
335 for l, w in pairs(mirrors[k].IPs) do
336 if type(w) == "table" then
337 for i, u in pairs(w) do
338 if nil ~= mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][i] then
339 local log = logCount("deb.devuan.org", i)
340 if "" ~= log then
341 if "" == dns then dns = " " else dns = dns .. " " end
342 dns = dns .. logCount("deb.devuan.org", i)
343 else
344 if "" == dns then dns = " " else dns = dns .. " " end
345 dns = dns .. i
346 end
347 end
348 end
349 else
350 if nil ~= mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][l] then
351 local log = logCount("deb.devuan.org", l)
352 if "" ~= log then
353 if "" == dns then dns = " " else dns = dns .. " " end
354 dns = dns .. log
355 else
356 if "" == dns then dns = " " else dns = dns .. " " end
357 dns = dns .. l
358 end
359 end
360 end
361 end
362 if "" == dns then dns = "[no]" end
363 dns = " DNS-RR: " .. dns
364 end
365
366 email:write( " ftp: " .. ftp .. " http: " .. http .. " https: " .. https .." rsync: " .. rsync .. "\n" ..
367 " " .. dns .. "\n" ..
368 " Protocol: " .. protocol .. " URL-sanity: " .. sanity .. " Integrity: " .. integrity .. "\n" ..
369 " Updated: " .. updated .. "\n")
370 end
371 email:write( "\n==== faulty mirrors: ====\n" .. faulty)
372 email:write( "\n-------------------------\n\n" ..
373 "* This means that this protocol isn't actually supported, but the test was run ayway.\n\n" ..
374 "Thanks for your precious help in ensuring that Devuan GNU+Linux \nremains a universal, stable, dependable, free operating system.\n\n" ..
375 "You can get the source code from https://sledjhamr.org/cgit/apt-panopticon/about/ .\n\n" ..
376 "Love\n\n" ..
377 "The Dev1Devs\n\n")
378 email:close()
379end
380
381
382results = {}
383m = {}
384faulty = ""
385html = true
386local web, e = io.open("results/Report-web.html", "w+")
387if nil == web then C("opening mirrors file - " .. e) else
388 web:write( "<html><head><title>apt-panopticon results</title>\n" ..
389 '</head><body bgcolor="black" text="white">' ..
390 "<h1>Welcome to the apt-panopticon results page.</h1>\n" ..
391 "<p>This is the status of the mirror servers in the Devuan package mirror network.</p>\n" ..
392 "<p>The full list of Devuan package mirrors is available at the URL: " ..
393 "<a href='https://pkgmaster.devuan.org/mirror_list.txt'>https://pkgmaster.devuan.org/mirror_list.txt</a></p>\n" ..
394 "<p>Due to the nature of the tests, some errors or warnings will be counted several times. &nbsp; " ..
395 "The links in the table and DNS list go to the detailed testing logs.</p>\n\n" ..
396 "<hr>\n<h2>==== package mirror status " .. os.date("!%Y-%m-%d %H:%M") .. " GMT ====</h2>\n<table>\n" ..
397
398 "<p>[<font color='red'><b>FAILED</b></font>] or [<font color='lime'><b>OK</b></font>]" ..
399 " means the tested thing is supported for that mirror.</p>\n" ..
400 "<p>[<font color='darkred'><b>FAILED*</b></font>] or [<font color='darkgreen'><b>OK*</b></font>]" ..
401 " means the tested thing is unsupported for that mirror, but might have been tested anyway.</p>\n" ..
402 "<p>[<font color='blue'><b>TIMEOUT</b></font>] or [<font color='darkblue'><b>TIMEOUT</b></font>]" ..
403 " means the server had too many timeouts, and tests where aborted, so there is no result for this test.</p>" ..
404 "<p>The DNS round robin (DNS-RR) column shows the IPs for that mirror, or [<font color='grey'><b>no</b></font>] if it isn't part of the DNS-RR. &nbsp; " ..
405 "The IPs link to the testing log for that IP accessed via the DNS-RR. &nbsp; " ..
406 "deb.devuan.org is the DNS-RR itself, so it doesn't get tested directly.</p>\n" ..
407 "<p>The time in the Updated column is how often the mirror updates itself.</p>" ..
408 "<p>Mirrors with a <font style='background-color:dimgrey'>grey background</font> are not active (though may be usable as part of the DNS-RR).</p>\n" ..
409 "<p>[<font color='grey'><b>skip</b></font>] means that the test hasn't been written yet.</p>\n" ..
410 "<tr><th></th><th>FTP</th><th>HTTP</th><th>HTTPS</th><th>RSYNC</th><th>DNS round robin</th><th>Protocol</th><th>URL sanity</th><th>Integrity</th><th>Updated</th></tr>\n"
411 )
412 for k, v in orderedPairs(mirrors) do
413 local results = loadfile("results/" .. k .. ".lua")()
414 local active = ""
415 if "yes" == v.Active then
416 web:write(" <tr><th>" .. k .. "</th> ")
417 else
418 if nil == v.Active then active = 'nil' else active = v.Active end
419 web:write(" <tr style='background-color:dimgrey'><th>" .. k .. "</th> ")
420 end
421 local IPs = v.IPs
422 for i, u in pairs(IPs) do
423 if "table" == type(u) then
424 for h, t in pairs(u) do
425 results = collate(k, h, results)
426 end
427 else
428 results = collate(k, i, results)
429 end
430 end
431 local ftp = "[<font color='grey'><b>skip</b></font>]"
432 local http = status(k, results, "http")
433 local https = status(k, results, "https")
434 local rsync = "[<font color='grey'><b>skip</b></font>]"
435 local dns = ""
436 local protocol = status(k, results, "Protocol")
437 local sanity = "[<font color='grey'><b>skip</b></font>]"
438 local integrity = status(k, results, "Integrity")
439 local updated = status(k, results, "Updated")
440 local rate = v.Rate
441 if nil ~= rate then updated = updated .. ' ' .. rate end
442
443 -- DNS-RR test.
444 if ("deb.devuan.org" ~= k) and (nil ~= mirrors["deb.devuan.org"]) then
445 for l, w in pairs(mirrors[k].IPs) do
446 if type(w) == "table" then
447 for i, u in pairs(w) do
448 if nil ~= mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][i] then
449 local log = logCount("deb.devuan.org", i)
450 if "" ~= log then
451 if "" == dns then dns = " " else dns = dns .. " &nbsp; " end
452 dns = dns .. logCount("deb.devuan.org", i)
453 else
454 if "" == dns then dns = " " else dns = dns .. " &nbsp; " end
455 dns = dns .. "<font color='maroon'><b>" .. i .. "</b></font>"
456 end
457 end
458 end
459 else
460 if nil ~= mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][l] then
461 local log = logCount("deb.devuan.org", l)
462 if "" ~= log then
463 if "" == dns then dns = " " else dns = dns .. " &nbsp; " end
464 dns = dns .. log
465 else
466 if "" == dns then dns = " " else dns = dns .. " &nbsp; " end
467 dns = dns .. "<font color='maroon'><b>" .. l .. "</b></font>"
468 end
469 end
470 end
471 end
472 if "" == dns then dns = "[<font color='grey'><b>no</b></font>]" end
473 end
474
475 web:write("<td>" .. ftp .. "&nbsp;</td><td>" .. http .. "&nbsp;</td><td>" .. https .. "&nbsp;</td><td>" .. rsync .. "&nbsp;</td><td>" .. dns ..
476 "&nbsp;</td><td>" .. protocol .. "&nbsp;</td><td>" .. sanity ..
477 "&nbsp;</td><td>" .. integrity .. "&nbsp;</td><td>" .. updated .. "</td></tr>\n")
478 if "" ~= v.Active then
479 web:write("<tr><td style='background-color:dimgrey'>" .. active .. "</td></tr>\n")
480 end
481 end
482 web:write( "</table>\n<br>\n<h2>==== faulty mirrors: ====</h2>\n" .. faulty)
483 web:write( "<br>\n<br>\n<h2>==== DNS and logs: ====</h2>\n")
484
485 for k, v in pairs(mirrors) do
486 local log = k
487 local n = {}
488 log = logCount(k)
489 mirrors[k].Protocols = nil
490 mirrors[k].FQDN = nil
491 mirrors[k].Active = nil
492 mirrors[k].Rate = nil
493 mirrors[k].BaseURL = nil
494 mirrors[k].Country = nil
495 mirrors[k].Bandwidth = nil
496 for l, w in pairs(mirrors[k].IPs) do
497 if type(w) == "table" then
498 n[l] = {}
499 for i, u in pairs(w) do
500 local log = logCount(k, i)
501 if "" == log then n[l][i] = u else n[l][log .. " " .. revDNS(k, i)] = u end
502 end
503 else
504 local log = logCount(k, l)
505 if "" == log then n[l] = w else n[log .. " " .. revDNS(k, l)] = w end
506 end
507 end
508 m[log .. " DNS entries -"] = n
509 end
510 web:write( "<p>This lists each mirror, and the DNS entries for that mirror. &nbsp; " ..
511 "The links point to the testing log files for " .. logCount("apt-panopticon") .. " for each domain name / IP combination that was tested. &nbsp; " ..
512 "If a mirror has a CNAME, that CNAME is listed along with that CNAMEs DNS entries. &nbsp; " ..
513 "deb.devuan.org is the DNS round robin, which points to the mirrors that are part of the DNS-RR. &nbsp; " ..
514 "If an IP is part of the DNS-RR, it is marked with '<font color='purple'><b>DNS-RR</b></font>' &nbsp; " ..
515 "pkgmaster.devuan.org is the master mirror, all the others sync to it. &nbsp; " ..
516 "</p>\n"
517 )
518 web:write(dumpTableHTML(m, "", ""))
519 web:write( "\n<br>\n<hr>\n\n" ..
520 "<p>The <a href='Report-email.txt'>email report</a>. &nbsp; " ..
521 "All <a href='../results'>the logs and other output</a>. &nbsp; " ..
522 "You can get the <a href='https://sledjhamr.org/cgit/apt-panopticon/about/'>source code here</a>.</p>" ..
523 "</body></html>\n")
524 web:close()
525end