#!/usr/bin/env lua --[[ Writing Nagios plugins - https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/3/en/pluginapi.html https://nagios-plugins.org/doc/guidelines.html http://users.telenet.be/mydotcom/howto/nagios/pluginsudo.html ]] 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({...}) APT.mirrors = loadfile("results/mirrors.lua")() -- Nagios result codes local OK = 0 local WARNING = 1 local CRITICAL = 2 local UNKNOWN = 3 -- Result variables. local status = "UNKNOWN: something went horribly wrong with apt-panopticon-nagios.lua." local extra = "" local returnCode = UNKNOWN local perfData = {} local perfCount = 0 -- For collecting debugging text for verbosity level 3. local debugText = "\nDebugging output -\n" -- Misc functions. ------------------------------------------------------------------------------- -- Wrapper coz I'm a C coder. local function printf(...) io.write(string.format(...)) end -- Command line argument handling. ------------------------------------------------------------------------------- -- A bunch of functions for parsing argument values. local function parseVersion(arguments, token, value) arguments[token].value = true return false end local function parseHelp(arguments, token, value) arguments["version"].value = true arguments[token].value = 2 return false end local function parseVerbose(arguments, token, value) if nil == arguments[token].value then arguments[token].value = 0 end arguments[token].value = arguments[token].value + 1 if arguments[token].value > 3 then arguments[token].value = 3 end return false end local function parseTimeout(arguments, token, value) if nil == value then return true end -- The "+ 0" part forces this to be converted to a number. Coz the comparison wont coerce it automaticaly, which Lua normally does. arguments[token].value = value + 0 if arguments[token].value > 120 then arguments[token].value = 120 end return true end local function parseString(arguments, token, value) if nil == value then return true end arguments[token].value = value return true end local function parseRange(arguments, token, value) if nil == value then return true end -- TODO - actually parse this. -- Threshhold and ranges, meaning that we can set what is classified as the various thresholds and ranges for the three result codes. return true end -- Lua doesn't have any sort of C like structure, this is how to fake it. local function addArgument(short, default, help, parser) return { short = short, default = default, help = help, parser = parser } end local arguments = { -- Reserved arguments. version = addArgument("V", false, "Print version string, then exit.", parseVersion), help = addArgument("h", 0, "Print version string, verbose help, then exit.", parseHelp), verbose = addArgument("v", 0, "Be verbose. Can be up to three of these to increase the verbosity.", parseVerbose), timeout = addArgument("t", 50, "Timeout to wait for the actual results to arrive.", parseTimeout), ok = addArgument("o", {}, "What range of thresholds counts as OK.", parseRange), warning = addArgument("w", {}, "What range of thresholds counts as WARNING.", parseRange), critical = addArgument("c", {}, "What range of thresholds counts as CRITICAL.", parseRange), --Standard, but optional arguments. -- community = addArgument("C", "", "SNMP community.", parseString), -- logname = addArgument("l", "", "User name.", parseString), -- username = addArgument("u", "", "User name.", parseString), -- authentication = addArgument("a", "", "Password.", parseString), -- password = addArgument("p", "", "Password.", parseString), -- passwd = addArgument("p", "", "Password.", parseString), -- We can combine hostname, port, and URL. Avoids duplicate short options. hostname = addArgument("H", "", "Host name or complete URL, including port number.", parseString), -- url = addArgument("u", "", "URL.", parseString), -- port = addArgument("p", -1, "Port number.", parseString), -- Our non standard arguments. } -- Parse the arguments. if nil ~= arg[1] then local lastArg = nil local expectValue = false for i, token in ipairs(arg) do if 0 < i then if not expectValue then if "--" == token:sub(1, 2) then token = token:sub(3) if nil ~= arguments[token] then lastArg = token expectValue = arguments[token].parser(arguments, lastArg, nil) else arguments["version"].value = true arguments["help"].value = 1 lastArg = nil expectValue = false end elseif "-" == token:sub(1, 1) then token = token:sub(2) -- Scan through the arguments table looking for short token. for k, v in pairs(arguments) do if token == arguments[k].short then lastArg = k expectValue = arguments[k].parser(arguments, lastArg, nil) end end end elseif nil ~= lastArg then arguments[lastArg].parser(arguments, lastArg, token) lastArg = nil expectValue = false else arguments["version"].value = true arguments["help"].value = 1 lastArg = nil expectValue = false end end end end -- Fill in default values if needed. for k, v in pairs(arguments) do if nil == arguments[k].value then arguments[k].value = arguments[k].default end end -- Deal with the various help and version variations. ------------------------------------------------------------------------------- if arguments["version"].value then print("apt-panopticon-nagios.lua v0.1") end if arguments["help"].value >= 1 then printf("Usage:\n apt-panopticon-nagios.lua ") for k, v in pairs(arguments) do printf("[-%s] ", arguments[k].short, k) end print("") end if arguments["help"].value == 2 then print("\nThis Nagios plugin is a generic wrapper around stdio based mini checker scripts that can be written in any language.\n") print("Options:") -- TODO - should sort this somehow, it's coming out in hash order. for k, v in pairs(arguments) do printf(" -%s, --%s\n", arguments[k].short, k) printf(" %s\n", arguments[k].help) end end -- All the help and version variations don't actually run the checker script, just output stuff and exit. if arguments["version"].value or arguments["help"].value >= 1 then os.exit(OK) end local function readFile(name) local file = io.open(name, 'r') local output = file:read('*a') file:close() return output end -- Actually calculate the results. ------------------------------------------------------------------------------- if "" == arguments["hostname"].value then status = "UNKNOWN: no host specified." returnCode = UNKNOWN else local host = arguments["hostname"].value if APT.checkFile('results/' .. host .. '.lua') then local results = APT.collateAll(APT.mirrors, 'results', host) local e = 0 local w = 0 local t = 0 for h, typ in pairs(APT.protocols) do if nil ~= results[typ] then e = e + results[typ].errors w = w + results[typ].warnings t = t + results[typ].timeouts for k, v in pairs(results[typ]) do if ("table" == type(v)) and ('redirects' ~= k) then if 0 <= v.errors then e = e + v.errors end if 0 <= v.warnings then w = w + v.warnings end if 0 <= v.timeouts then t = t + v.timeouts 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.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 end perfData['errors'] = e perfData['warnings'] = w perfData['timeouts'] = t perfCount = perfCount + 3 if 0 < e then returnCode = CRITICAL; status = 'CRITICAL' elseif 0 < w then returnCode = WARNING; status = 'WARNING' -- elseif 0 < t then returnCode = UNKNOWN; status = 'UNKNOWN' else returnCode = OK; status = 'OK' end if arguments["verbose"].value == 1 then status = status .. ': ' .. APT.plurals(e, w, t) else extra = '\n' .. APT.plurals(e, w, t) end else status = "UNKNOWN: no records for host " .. host returnCode = UNKNOWN end end -- Send the results to Nagios. ------------------------------------------------------------------------------- --[[ Verbosity levels mean - 0 Single line, minimal output. Summary 1 Single line, additional information (eg list processes that fail) 2 Multi line, configuration debug output (eg ps command used) 3 Lots of detail for plugin problem diagnosis ]] printf(status) if arguments["verbose"].value > 0 then printf(extra) end -- Performance data can be a complex format, or a simple format. if perfCount > 0 then printf(" | ") for k, v in pairs(perfData) do printf("'%s'=%s\n", k, v) end else print() end if arguments["verbose"].value > 1 then print("\nCheckGeneric.lua arguments -") for k, v in pairs(arguments) do if type(v.value) == "table" then APT.dumpTable(v.value, " --" .. k) elseif type(v.value) == "boolean" then if (v.value) then printf(" --%s: true\n", k) else printf(" --%s: false\n", k) end else printf(" --%s: %s\n", k, v.value) end end end if arguments["verbose"].value == 3 then print(debugText) end os.exit(returnCode)