I am currently writing my first special agent (to monitor a Things Network Gateway). I am using check_mk 1.6.0p19. I have it almost working, but I have problems writing the actual check.
I wrote a parse function and registered it using check_info, but it never gets called, instead the “unparsed” regular info structure is passed into the inventory and check functions…
If I run cmk --debug --checks=ttn_gateway.lastseen --cache -vvII ttig,
the pprint at the top of the inventory function will print parsed as:
[[u’Markus’, u’TTIG’, u’(The’, u’Things’, u’Indoor’, u’Gateway’, u’2.0.0):’, u’52’]]
instead of the dictionary that should have been built in the parse function…
Can someone help me howto fix or further debug this?
The dot indicates that this is a sub-check of the main check ttn_gateway. For subchecks, there can only be one parse function: that of the main check. You cannot have separate parse functions for the main check and subchecks.
Btw, the check function can be simplified to
def check_ttn_gateway_lastseen(item, params, parsed):
if not parsed or item not in parsed:
return
if isinstance(params, tuple):
warn, crit = params
else:
warn, crit = params['levels']
value = saveint(parsed[item])
perfdata = [('last seen', value, warn, crit)]
if value < warn:
yield 0, "%d s since last seen" % value, perfdata
elif value < crit:
yield 1, "%d s since last seen" % value, perfdata
else:
yield 2, "%d s since last seen" % value, perfdata
No need for the for loop because item is already the key to the parsed structure.
I did not have a main check at all, so renaming the key in check_info to ‘ttn_gateway’ solved it => the parse function gets called correctly.
Thank you also for the suggestion about removing the for loop in the check function. Now a feel a bit stupid
One last thing: the info that is passed into the parse function is still preprocessed. So I had to change the code of the parse function a bit, because I actually do not get the raw output of the agent (other than I expected), see Reworked version of check_mk check ttn_gateway · GitHub for the current version.
I used the join method to restore the lines – see line 26.
Is there a better way of doing that?
The join is ok. Another option is to use the separator inside the agent output header.
Example <<<plugin_name:sep(0)>>> this will tell CMK that is should split the output at ASCII 0 and not at ASCII 32 what is the default. As you will not have ASCII 0 inside your string you will get every line already as one string and don’t need the join.
You can also remove the if not line.startswith("<<<"): line as the header from you plugin output is already removed from CheckMK.
<<<ttn_gateway>>>
Markus TTIG (The Things Indoor Gateway 2.0.0): 64
Then I’d suggest the colon : as separator, i.e.
<<<ttn_gateway:sep(58)>>>
Markus TTIG (The Things Indoor Gateway 2.0.0): 64
The parse function can then be written as
def parse_ttn_gateway_info(info):
parsed = {}
for gateway, value in info:
parsed[gateway.strip()] = saveint(value.strip())
return parsed
This will result in
parsed = {
'Markus TTIG (The Things Indoor Gateway 2.0.0)': 64
}
Note: the parse function will crash if the agent output does not contain exactly two fields separated by a : (as does your version, btw.). To circumvent this, use the following instead:
def parse_ttn_gateway_info(info):
parsed = {}
for line in info:
if len(line)==2:
(gateway, value) = line
parsed[gateway.strip()] = saveint(value.strip())
return parsed
Update: I’ve put the call to saveint into the parse function here, so you can drop that step from the check function. Or drop it here and keep it in the check function. Actually, it doesn’t matter and I have to confess I don’t do it consistently myself.
If you have a parse function you should cast the numbers there and also do the error handling. Or no error handling at all, then also no checks would be discovered.
And there is a helper function check_levels() that can replace all the if value > warn ... elif value > crit ... code. This method is also available in the new checkmk 2.0 plugin API.