Parse_function is never called

Hi,

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…

The code is there: https://gist.github.com/mabauer/b4db1bf821d5e25aedbba6cf06a8519c

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?

Thanks,
Markus

Please test with the parse function changed from

def parse_ttn_gateway_info(lines)

to

def parse_ttn_gateway_info(info)

Thanks Andreas.
I tried that – see updated gist – but it did not work.
I reloaded check_mk with cmk -R and then tried again. Same behaviour as before :frowning:

You defined the check as

check_info["ttn_gateway.lastseen"]

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.

3 Likes

Hi Dirk,

thanks! That helped a lot.

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 :slightly_smiling_face:

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?

Markus

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.

2 Likes

If the agent output looks like this:

<<<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. :wink:

1 Like

Thanks for your help, Dirk. I’ve learned quite a bit from your answers! :+1:

2 Likes

BTW: saveint() and savefloat() are deprecated.

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.

2 Likes