From 4bb51520521d9ab612d50dd2bcf904d70cf15735 Mon Sep 17 00:00:00 2001 From: onefang Date: Tue, 10 Dec 2019 15:45:11 +1000 Subject: Move common code to it's own module. Some minor clean ups and test tweaks likely came for the ride. --- apt-panopticommon.lua | 563 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 563 insertions(+) create mode 100644 apt-panopticommon.lua (limited to 'apt-panopticommon.lua') diff --git a/apt-panopticommon.lua b/apt-panopticommon.lua new file mode 100644 index 0000000..b38595c --- /dev/null +++ b/apt-panopticommon.lua @@ -0,0 +1,563 @@ +local APT = {} + +-- https://oss.oetiker.ch/rrdtool/prog/rrdlua.en.html +APT.rrd = require 'rrd' + +verbosity = -1 +APT.origin = false +APT.keep = false +-- TODO - Should actually implement this. +APT.fork = true + +APT.options = +{ + referenceSite = + { + typ = "string", + help = "", + value = "pkgmaster.devuan.org", + }, + roundRobin = + { + typ = "string", + help = "", + value = "deb.devuan.org", + }, + tests = + { + typ = "table", + help = "", + value = + { + "IPv4", + "IPv6", +-- "ftp", + "http", + "https", +-- "rsync", + "DNSRR", + "Protocol", + "URLSanity", + "Integrity", + "Updated", + }, + }, + maxtime = + { + typ = "number", + help = "", + value = 300, + }, + timeout = + { + typ = "number", + help = "", + value = 15, + }, + reports = + { + typ = "table", + help = "", + value = + { + "RRD", -- RRD has to be before web, coz web creates a graph from the RRD data. + "email-web", +-- "Nagios", +-- "Prometheus", + }, + }, +} + +APT.parseArgs = function(args) + local arg = {} + local sendArgs = "" + if 0 ~= #(args) then + local option = "" + for i, a in pairs(args) do + if ("--help" == a) or ("-h" == a) then + print("I should write some docs, huh? Read README.md for instructions.") + elseif "--version" == a then + print("apt-panopticon version 0.1 WIP development version") + elseif "-v" == a then + verbosity = verbosity + 1 + sendArgs = sendArgs .. a .. " " + elseif "-q" == a then + verbosity = -1 + sendArgs = sendArgs .. a .. " " + elseif "-k" == a then + APT.keep = true + elseif "-n" == a then + APT.fork = false + elseif "-o" == a then + APT.origin = true + elseif "--" == a:sub(1, 2) then + local s, e = a:find("=") + if nil == s then e = -1 end + option = a:sub(3, e - 1) + local o = APT.options[option] + if nil == o then + print("Unknown option --" .. option) + option = "" + else + option = a + sendArgs = sendArgs .. a .. " " + local s, e = a:find("=") + if nil == s then e = 0 end + option = a:sub(3, e - 1) + if "table" == APT.options[option].typ then + local result = {} + for t in (a:sub(e + 1) .. ","):gmatch("([+%-]?%w*),") do + local f = t:sub(1, 1) + local n = t:sub(2, -1) + if ("+" ~= f) and ("-" ~= f) then + table.insert(result, t) + end + end + if 0 ~= #result then + APT.options[option].value = result + else + for t in (a:sub(e + 1) .. ","):gmatch("([+%-]?%w*),") do + local f = t:sub(1, 1) + local n = t:sub(2, -1) + if "+" == f then + table.insert(APT.options[option].value, n) + elseif "-" == f then + local r = {} + for i, k in pairs(APT.options[option].value) do + if k ~= n then table.insert(r, k) end + end + APT.options[option].value = r + end + end + end + else + APT.options[option].value = a:sub(e + 1, -1) + end + option = "" + end + elseif "-" == a:sub(1, 1) then + print("Unknown option " .. a) + else + table.insert(arg, a) + end + end + end + return arg, sendArgs +end + +--print(APT.dumpTable(APT.options, "", "options")) + + + +--[[ 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 APT.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. +APT.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 APT.orderedPairs(table) do + if type(v) == "table" then + if " " == space then + r = r .. space .. APT.dumpTableHTML(v, space, k .. "
" .. s .. "
")
+ --[[ Damn os.execute()
+ Lua 5.1 says it returns "a status code, which is system-dependent"
+ Lua 5.2 says it returns true/nil, "exit"/"signal", the status code.
+ I'm getting 7168 or 0. No idea what the fuck that is.
+ local ok, rslt, status = os.execute(s)
+ ]]
+ local f = io.popen(s .. ' ; echo "$?"', 'r')
+ local status = ""
+ local result = ""
+ -- The last line will be the command's returned status, collect everything else in result.
+ for l in f:lines() do
+ result = result .. status .. "\n"
+ status = l
+ end
+ return status, result
+end
+
+APT.fork = function(s)
+ D(" forking " .. s .. "
")
+ os.execute(s .. " &")
+end
+
+APT.checkExes = function (exe)
+ local count = io.popen('ps x | grep "' .. exe .. '" | grep -v " grep " | grep -v "flock -n apt-panopticon.lock " | wc -l'):read("*l")
+ D(count .. " " .. exe .. " commands still running.")
+ return tonumber(count)
+end
+
+APT.checkFile = function(f)
+ local h, e = io.open(f, "r")
+ if nil == h then return false else h:close(); return true end
+end
+
+APT.plurals = function(e, w)
+ local result = ""
+ if 1 == e then
+ result = e .. " error"
+ elseif e ~= 0 then
+ result = e .. " errors"
+ end
+ if ("" ~= result) and APT.html then result = "" .. result .. "" end
+-- result = " " .. result
+ 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
+ if ("" ~= result) and APT.html then result = "" .. result .. "" end
+-- result = " " .. result
+ end
+ if "" ~= result then result = " (" .. result .. ")" end
+ return result
+end
+
+APT.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
+ if ("speed" == k) and (nil ~= results.speed) then
+ if v.min < results.speed.min then results.speed.min = v.min end
+ if v.max > results.speed.max then results.speed.max = v.max end
+ else
+ for i, u in pairs(v) do
+ if "table" == type(u) then
+ for h, t in pairs(u) do
+ local a = results[k][i][h]
+ if nil == a then a = 0 end
+ results[k][i][h] = a + t
+ end
+ else
+ local a = results[k]
+ if nil == a then a = 0; results[k] = {} else a = a[i] end
+ if nil == a then a = 0 end
+ results[k][i] = a + u
+ end
+ end
+ end
+ elseif "timeout" ~= k then
+ local a = results[k]
+ if nil == a then a = 0 end
+ results[k] = a + v
+ end
+ end
+ end
+ return results
+end
+
+
+
+
+APT.now = 0
+local status
+status, APT.now = APT.execute('TZ="GMT" ls -l --time-style="+%s" results/stamp | cut -d " " -f 6-6')
+APT.now = tonumber(APT.now)
+APT.protocols = {'ftp', 'http', 'https', 'rsync'}
+APT.tests = {'raw', 'Integrity', 'Protocol', 'Updated', 'URLSanity', 'Speed'}
+local start = 'now-2week'
+local step = '10min'
+local hb = '150min'
+local DSIe = 'DS:IntegrityErrors:GAUGE:' .. hb .. ':0:U'
+local DSIw = 'DS:IntegrityWarnings:GAUGE:' .. hb .. ':0:U'
+local DSPe = 'DS:ProtocolErrors:GAUGE:' .. hb .. ':0:U'
+local DSPw = 'DS:ProtocolWarnings:GAUGE:' .. hb .. ':0:U'
+local DSUe = 'DS:UpdatedErrors:GAUGE:' .. hb .. ':0:U'
+local DSUw = 'DS:UpdatedWarnings:GAUGE:' .. hb .. ':0:U'
+local DSSe = 'DS:URLSanityErrors:GAUGE:' .. hb .. ':0:U'
+local DSSw = 'DS:URLSanityWarnings:GAUGE:' .. hb .. ':0:U'
+local DSx = 'DS:max:GAUGE:' .. hb .. ':0:U'
+local DSn = 'DS:min:GAUGE:' .. hb .. ':0:U'
+
+-- What Collectd uses.
+local RRAc0 = 'RRA:AVERAGE:0.9:1:1200'
+local RRAc1 = 'RRA:MIN:0.9:1:1200'
+local RRAc2 = 'RRA:MAX:0.9:1:1200'
+local RRAc3 = 'RRA:AVERAGE:0.9:7:1235'
+local RRAc4 = 'RRA:MIN:0.9:7:1235'
+local RRAc5 = 'RRA:MAX:0.9:7:1235'
+local RRAc6 = 'RRA:AVERAGE:0.9:50:1210'
+local RRAc7 = 'RRA:MIN:0.9:50:1210'
+local RRAc8 = 'RRA:MAX:0.9:50:1210'
+local RRAc9 = 'RRA:AVERAGE:0.9:223:1202'
+local RRAc10 = 'RRA:MIN:0.9:223:1202'
+local RRAc11 = 'RRA:MAX:0.9:223:1202'
+local RRAc12 = 'RRA:AVERAGE:0.9:2635:1201'
+local RRAc13 = 'RRA:MIN:0.9:2635:1201'
+local RRAc14 = 'RRA:MAX:0.9:2635:1201'
+
+-- Try LAST.
+local RRAl0 = 'RRA:LAST:0.9:1:1200'
+local RRAl1 = 'RRA:LAST:0.9:7:1235'
+local RRAl2 = 'RRA:LAST:0.9:50:1210'
+local RRAl3 = 'RRA:LAST:0.9:223:1202'
+local RRAl4 = 'RRA:LAST:0.9:2635:1201'
+
+--[[
+/var/lib/collectd/rrd/onefang/interface-eth0/if_packets.rrd
+
+ rrd/host.IP/protocol.rrd test-error count
+ rrd/host.IP/protocol.rrd test-warning count
+
+ rrh/host.IP/protocol-speed.rrd min
+ rrh/host.IP/protocol-speed.rrd max
+]]
+
+APT.createRRD = function(host, ip)
+ if nil ~= ip then host = host .. '_' .. ip end
+ for i, p in pairs(APT.protocols) do
+-- for j, t in pairs(tests) do
+ os.execute( 'mkdir -p rrd/' .. host .. '/' .. p:upper())
+ if not APT.checkFile('rrd/' .. host .. '/' .. p:upper() .. '/Speed.rrd') then
+ D('Creating ' .. 'rrd/' .. host .. '/' .. p:upper() .. '/Speed.rrd')
+-- if 'Speed' == t then
+ APT.rrd.create( 'rrd/' .. host .. '/' .. p:upper() .. '/Speed.rrd', '--start', start, '--step', step, DSx, DSn,
+ RRAc0, RRAc1, RRAc2, RRAl0, RRAc3, RRAc4, RRAc5, RRAl1, RRAc6, RRAc7, RRAc8, RRAl2, RRAc9, RRAc10, RRAc11, RRAl3, RRAc12, RRAc13, RRAc14, RRAl4)
+-- else
+ end
+ if not APT.checkFile('rrd/' .. host .. '/' .. p:upper() .. '/Tests.rrd') then
+ D('Creating ' .. 'rrd/' .. host .. '/' .. p:upper() .. '/Tests.rrd')
+ APT.rrd.create( 'rrd/' .. host .. '/' .. p:upper() .. '/Tests.rrd', '--start', start, '--step', step, DSIe, DSIw, DSPe, DSPw, DSUe, DSUw, DSSe, DSSw,
+ RRAc0, RRAc1, RRAc2, RRAl0, RRAc3, RRAc4, RRAc5, RRAl1, RRAc6, RRAc7, RRAc8, RRAl2, RRAc9, RRAc10, RRAc11, RRAl3, RRAc12, RRAc13, RRAc14, RRAl4)
+-- end
+ -- Start them at 0 so the average has something to work on.
+ APT.rrd.update( 'rrd/' .. host .. '/' .. p:upper() .. '/Speed.rrd', (APT.now - 600) .. ':0:0')
+ APT.rrd.update( 'rrd/' .. host .. '/' .. p:upper() .. '/Tests.rrd', (APT.now - 600) .. ':0:0:0:0:0:0:0:0')
+ end
+-- end
+ end
+end
+
+APT.updateRRD = function(results, host, ip)
+ if nil ~= ip then host = host .. '_' .. ip end
+ for i, p in pairs(APT.protocols) do
+ if nil ~= results.speed then
+ APT.rrd.update( 'rrd/' .. host .. '/' .. p:upper() .. '/Speed.rrd', APT.now .. ':' .. results.speed.max .. ':' .. results.speed.min)
+ end
+ if nil ~= results[p] then
+ APT.rrd.update('rrd/' .. host .. '/' .. p:upper() .. '/Tests.rrd', APT.now .. ':' ..
+ results[p]['Integrity'].errors .. ':' .. results[p]['Integrity'].warnings .. ':' ..
+ results[p]['Protocol'].errors .. ':' .. results[p]['Protocol'].warnings .. ':' ..
+ results[p]['Updated'].errors .. ':' .. results[p]['Updated'].warnings .. ':' ..
+ results[p]['URLSanity'].errors .. ':' .. results[p]['URLSanity'].warnings)
+ else
+ APT.rrd.update('rrd/' .. host .. '/' .. p:upper() .. '/Tests.rrd', APT.now .. ':U:U:U:U:U:U:U:U')
+ end
+ end
+end
+
+
+APT.doRRD = function(l, k, v)
+ if APT.checkFile(l .. "/" .. k .. ".lua") then
+ local results = loadfile(l .. "/" .. k .. ".lua")()
+ APT.createRRD(k)
+ APT.updateRRD(results, k)
+ if til ~= v then
+ local IPs = v.IPs
+ for i, u in pairs(IPs) do
+ if "table" == type(u) then
+ for h, t in pairs(u) do
+ APT.createRRD(k, h)
+ results = APT.collate(k, h, results)
+ APT.updateRRD(results, k, h)
+ end
+ else
+ APT.createRRD(k, i)
+ results = APT.collate(k, i, results)
+ APT.updateRRD(results, k, i)
+ end
+ end
+ end
+ end
+end
+
+
+return APT
--
cgit v1.1