From b81e5c82e5fc32246a2da0cf656baa7d1dc635da Mon Sep 17 00:00:00 2001 From: onefang Date: Thu, 26 Dec 2019 00:36:28 +1000 Subject: Add a Icinga/Nagios script. --- apt-panopticon-nagios.lua | 320 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 320 insertions(+) create mode 100755 apt-panopticon-nagios.lua (limited to 'apt-panopticon-nagios.lua') diff --git a/apt-panopticon-nagios.lua b/apt-panopticon-nagios.lua new file mode 100755 index 0000000..1cc9bbc --- /dev/null +++ b/apt-panopticon-nagios.lua @@ -0,0 +1,320 @@ +#!/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) -- cgit v1.1