A Lua script to check the health of Devuan Linux package mirrors. The master repo is at https://sledjhamr.org/cgit/apt-panopticon/ and the master issues tracker is at https://sledjhamr.org/mantisbt/project_page.php?project_id=13 https://sledjhamr.org/cgit/apt-panopticon/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

321 lines
9.5 KiB

  1. #!/usr/bin/env lua
  2. --[[
  3. Writing Nagios plugins -
  4. https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/3/en/pluginapi.html
  5. https://nagios-plugins.org/doc/guidelines.html
  6. http://users.telenet.be/mydotcom/howto/nagios/pluginsudo.html
  7. ]]
  8. local APT = require 'apt-panopticommon'
  9. local D = APT.D
  10. local I = APT.I
  11. local T = APT.T
  12. local W = APT.W
  13. local E = APT.E
  14. local C = APT.C
  15. --local arg, sendArgs = APT.parseArgs({...})
  16. APT.mirrors = loadfile("results/mirrors.lua")()
  17. -- Nagios result codes
  18. local OK = 0
  19. local WARNING = 1
  20. local CRITICAL = 2
  21. local UNKNOWN = 3
  22. -- Result variables.
  23. local status = "UNKNOWN: something went horribly wrong with apt-panopticon-nagios.lua."
  24. local extra = ""
  25. local returnCode = UNKNOWN
  26. local perfData = {}
  27. local perfCount = 0
  28. -- For collecting debugging text for verbosity level 3.
  29. local debugText = "\nDebugging output -\n"
  30. -- Misc functions.
  31. -------------------------------------------------------------------------------
  32. -- Wrapper coz I'm a C coder.
  33. local function printf(...) io.write(string.format(...)) end
  34. -- Command line argument handling.
  35. -------------------------------------------------------------------------------
  36. -- A bunch of functions for parsing argument values.
  37. local function parseVersion(arguments, token, value)
  38. arguments[token].value = true
  39. return false
  40. end
  41. local function parseHelp(arguments, token, value)
  42. arguments["version"].value = true
  43. arguments[token].value = 2
  44. return false
  45. end
  46. local function parseVerbose(arguments, token, value)
  47. if nil == arguments[token].value then arguments[token].value = 0 end
  48. arguments[token].value = arguments[token].value + 1
  49. if arguments[token].value > 3 then arguments[token].value = 3 end
  50. return false
  51. end
  52. local function parseTimeout(arguments, token, value)
  53. if nil == value then return true end
  54. -- The "+ 0" part forces this to be converted to a number. Coz the comparison wont coerce it automaticaly, which Lua normally does.
  55. arguments[token].value = value + 0
  56. if arguments[token].value > 120 then arguments[token].value = 120 end
  57. return true
  58. end
  59. local function parseString(arguments, token, value)
  60. if nil == value then return true end
  61. arguments[token].value = value
  62. return true
  63. end
  64. local function parseRange(arguments, token, value)
  65. if nil == value then return true end
  66. -- TODO - actually parse this.
  67. -- Threshhold and ranges, meaning that we can set what is classified as the various thresholds and ranges for the three result codes.
  68. return true
  69. end
  70. -- Lua doesn't have any sort of C like structure, this is how to fake it.
  71. local function addArgument(short, default, help, parser)
  72. return { short = short, default = default, help = help, parser = parser }
  73. end
  74. local arguments =
  75. {
  76. -- Reserved arguments.
  77. version = addArgument("V", false, "Print version string, then exit.", parseVersion),
  78. help = addArgument("h", 0, "Print version string, verbose help, then exit.", parseHelp),
  79. verbose = addArgument("v", 0, "Be verbose. Can be up to three of these to increase the verbosity.", parseVerbose),
  80. timeout = addArgument("t", 50, "Timeout to wait for the actual results to arrive.", parseTimeout),
  81. ok = addArgument("o", {}, "What range of thresholds counts as OK.", parseRange),
  82. warning = addArgument("w", {}, "What range of thresholds counts as WARNING.", parseRange),
  83. critical = addArgument("c", {}, "What range of thresholds counts as CRITICAL.", parseRange),
  84. --Standard, but optional arguments.
  85. -- community = addArgument("C", "", "SNMP community.", parseString),
  86. -- logname = addArgument("l", "", "User name.", parseString),
  87. -- username = addArgument("u", "", "User name.", parseString),
  88. -- authentication = addArgument("a", "", "Password.", parseString),
  89. -- password = addArgument("p", "", "Password.", parseString),
  90. -- passwd = addArgument("p", "", "Password.", parseString),
  91. -- We can combine hostname, port, and URL. Avoids duplicate short options.
  92. hostname = addArgument("H", "", "Host name or complete URL, including port number.", parseString),
  93. -- url = addArgument("u", "", "URL.", parseString),
  94. -- port = addArgument("p", -1, "Port number.", parseString),
  95. -- Our non standard arguments.
  96. }
  97. -- Parse the arguments.
  98. if nil ~= arg[1] then
  99. local lastArg = nil
  100. local expectValue = false
  101. for i, token in ipairs(arg) do
  102. if 0 < i then
  103. if not expectValue then
  104. if "--" == token:sub(1, 2) then
  105. token = token:sub(3)
  106. if nil ~= arguments[token] then
  107. lastArg = token
  108. expectValue = arguments[token].parser(arguments, lastArg, nil)
  109. else
  110. arguments["version"].value = true
  111. arguments["help"].value = 1
  112. lastArg = nil
  113. expectValue = false
  114. end
  115. elseif "-" == token:sub(1, 1) then
  116. token = token:sub(2)
  117. -- Scan through the arguments table looking for short token.
  118. for k, v in pairs(arguments) do
  119. if token == arguments[k].short then
  120. lastArg = k
  121. expectValue = arguments[k].parser(arguments, lastArg, nil)
  122. end
  123. end
  124. end
  125. elseif nil ~= lastArg then
  126. arguments[lastArg].parser(arguments, lastArg, token)
  127. lastArg = nil
  128. expectValue = false
  129. else
  130. arguments["version"].value = true
  131. arguments["help"].value = 1
  132. lastArg = nil
  133. expectValue = false
  134. end
  135. end
  136. end
  137. end
  138. -- Fill in default values if needed.
  139. for k, v in pairs(arguments) do
  140. if nil == arguments[k].value then arguments[k].value = arguments[k].default end
  141. end
  142. -- Deal with the various help and version variations.
  143. -------------------------------------------------------------------------------
  144. if arguments["version"].value then print("apt-panopticon-nagios.lua v0.1") end
  145. if arguments["help"].value >= 1 then
  146. printf("Usage:\n apt-panopticon-nagios.lua ")
  147. for k, v in pairs(arguments) do
  148. printf("[-%s] ", arguments[k].short, k)
  149. end
  150. print("")
  151. end
  152. if arguments["help"].value == 2 then
  153. print("\nThis Nagios plugin is a generic wrapper around stdio based mini checker scripts that can be written in any language.\n")
  154. print("Options:")
  155. -- TODO - should sort this somehow, it's coming out in hash order.
  156. for k, v in pairs(arguments) do
  157. printf(" -%s, --%s\n", arguments[k].short, k)
  158. printf(" %s\n", arguments[k].help)
  159. end
  160. end
  161. -- All the help and version variations don't actually run the checker script, just output stuff and exit.
  162. if arguments["version"].value or arguments["help"].value >= 1 then os.exit(OK) end
  163. local function readFile(name)
  164. local file = io.open(name, 'r')
  165. local output = file:read('*a')
  166. file:close()
  167. return output
  168. end
  169. -- Actually calculate the results.
  170. -------------------------------------------------------------------------------
  171. if "" == arguments["hostname"].value then
  172. status = "UNKNOWN: no host specified."
  173. returnCode = UNKNOWN
  174. else
  175. local host = arguments["hostname"].value
  176. if APT.checkFile('results/' .. host .. '.lua') then
  177. local results = APT.collateAll(APT.mirrors, 'results', host)
  178. local e = 0
  179. local w = 0
  180. local t = 0
  181. for h, typ in pairs(APT.protocols) do
  182. if nil ~= results[typ] then
  183. e = e + results[typ].errors
  184. w = w + results[typ].warnings
  185. t = t + results[typ].timeouts
  186. for k, v in pairs(results[typ]) do
  187. if ("table" == type(v)) and ('redirects' ~= k) then
  188. if 0 <= v.errors then e = e + v.errors end
  189. if 0 <= v.warnings then w = w + v.warnings end
  190. if 0 <= v.timeouts then t = t + v.timeouts end
  191. end
  192. end
  193. else
  194. for k, v in pairs(results) do
  195. if "table" == type(v) then
  196. for i, u in pairs(v) do
  197. if "table" == type(u) then
  198. if typ == i then
  199. if 0 <= u.errors then e = e + u.errors end
  200. if 0 <= u.warnings then w = w + u.warnings end
  201. if 0 <= u.timeouts then t = t + u.timeouts end
  202. end
  203. end
  204. end
  205. end
  206. end
  207. end
  208. end
  209. perfData['errors'] = e
  210. perfData['warnings'] = w
  211. perfData['timeouts'] = t
  212. perfCount = perfCount + 3
  213. if 0 < e then returnCode = CRITICAL; status = 'CRITICAL'
  214. elseif 0 < w then returnCode = WARNING; status = 'WARNING'
  215. -- elseif 0 < t then returnCode = UNKNOWN; status = 'UNKNOWN'
  216. else returnCode = OK; status = 'OK'
  217. end
  218. if arguments["verbose"].value == 1 then
  219. status = status .. ': ' .. APT.plurals(e, w, t)
  220. else
  221. extra = '\n' .. APT.plurals(e, w, t)
  222. end
  223. else
  224. status = "UNKNOWN: no records for host " .. host
  225. returnCode = UNKNOWN
  226. end
  227. end
  228. -- Send the results to Nagios.
  229. -------------------------------------------------------------------------------
  230. --[[ Verbosity levels mean -
  231. 0 Single line, minimal output. Summary
  232. 1 Single line, additional information (eg list processes that fail)
  233. 2 Multi line, configuration debug output (eg ps command used)
  234. 3 Lots of detail for plugin problem diagnosis
  235. ]]
  236. printf(status)
  237. if arguments["verbose"].value > 0 then
  238. printf(extra)
  239. end
  240. -- Performance data can be a complex format, or a simple format.
  241. if perfCount > 0 then
  242. printf(" | ")
  243. for k, v in pairs(perfData) do
  244. printf("'%s'=%s\n", k, v)
  245. end
  246. else
  247. print()
  248. end
  249. if arguments["verbose"].value > 1 then
  250. print("\nCheckGeneric.lua arguments -")
  251. for k, v in pairs(arguments) do
  252. if type(v.value) == "table" then
  253. APT.dumpTable(v.value, " --" .. k)
  254. elseif type(v.value) == "boolean" then
  255. if (v.value) then
  256. printf(" --%s: true\n", k)
  257. else
  258. printf(" --%s: false\n", k)
  259. end
  260. else
  261. printf(" --%s: %s\n", k, v.value)
  262. end
  263. end
  264. end
  265. if arguments["verbose"].value == 3 then
  266. print(debugText)
  267. end
  268. os.exit(returnCode)