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