Plugin with check parse function Check_MK 1.6

Hi.

For the v1.6 the documentation about plugins was/is almost nonexisting, so I wrote my custom check plugin for checking disk status on DELL BOSS card by reading other checks and with try/error method.

Now, I would like to write plugin for Adaptec RAID controllers which we use in our servers and would like to use parse function. So I tried to write it according official ‘megaraid_pdisks’ plugin. I would like to send multi line output from agent and to the parse of data by plugin. But I don’t understand what is going in background with parse function and my code doesn’t work and can’t figure it out why.

Is there any documentation or someones personal notes available about parse function?

Docu for v2 is better, but API is completely different, so not usable for me.

The parse_function gets the “raw” agent data before the inventory_function or check_function are called. They become the output of the parse_function instead.

Yeah, I know that statement. But I woud like to know more about technical stuff. Maybe my example will bring light to my problem.

Agent sends this data:

<<<adaptec_raid>>>
Controller Status: Optimal
Controller Model: Adaptec ASR8885
Controller Serial Number: 6A506399A1D
PCI Device ID: 653

Here is my plugin adaptec_raid:

def adaptec_raid_parse(info):
  return_var = []
  for line in info:
      if line[0] == 'Controller' and line[1] == 'Status:':
          state = line[2:]
      elif line[0] == 'Controller' and line[1] == 'Model:':
          model = line[2:]
      elif line[0] == 'Controller' and line[1] == 'Serial' and line[2] == 'Number:':
          serial = line[3:]
      elif line[0] == 'PCI' and line[1] == 'Device':
          devid = int(line[-1])
          return_var.append((devid, state, model))

  return return_var

# inventory
def inventory_adaptec_raid(info):
  info = adaptec_raid_parse(info)
  inventory = []
  for devid, state, model in info:
    inventory.append(("%s" % devid, None))
  return inventory
  #print info

# check
def check_adaptec_raid(item, params, info):
    info = adaptec_raid_parse(info)
    for devid, state, model in info:
        if "%s" % (devid) == item:
            infotext = "%s" % (state)

            return 0, infotext
    return 3, "No controller found. - %s" % item

# declare
check_info["adaptec_raid"] = {
  'check_function':     check_adaptec_raid,
  'inventory_function': inventory_adaptec_raid,
  'service_description': 'RAID Controller %s',
}

I would like to show Device ID in service_description and Status/Model in service output. This is how it looks in standard service view of host:
image

And this how it looks in WATO:

You need to declare the parse_function in check_info.

1 Like

I’m writing plugin for version 1.6. Not 2.0. In 1.6 I haven’t seen any parse_function declaration. Example official megaraid_pdisks plugin

I made some changes and here is the output I get in services:

Unfortunately I don’t understand how it works, and don’t understand why it doesn’t work fully properly, means why check_mk didn’t parsed the output properly.

def adaptec_raid_parse(info):
  return_var = []
  for line in info:
      if line[0] == 'Controller' and line[1] == 'Status:':
          state = line[2:]
      elif line[0] == 'Controller' and line[1] == 'Model:':
          model = line[2:]
      elif line[0] == 'Controller' and line[1] == 'Serial' and line[2] == 'Number:':
          serial = line[3:]
      elif line[0] == 'PCI' and line[1] == 'Device' and line[2] == 'ID:':
          devid = line[3]
          return_var.append((devid, state, model))

  return return_var

# inventory
def inventory_adaptec_raid(info):
  info = adaptec_raid_parse(info)
  inventory = []
  for devid, state, model in info:
    inventory.append(("%s" % devid, None))
  return inventory
  #print info

# check
def check_adaptec_raid(item, _no_params, info):
    info = adaptec_raid_parse(info)
    for devid, state, model in info:
        if "%s" % devid == item:
            infotext = "%s %s" % (state, model)

            return 0, infotext
    #return 3, "No controller found. - %d" % item

# declare
check_info["adaptec_raid"] = {
  'check_function':     check_adaptec_raid,
  'inventory_function': inventory_adaptec_raid,
  'service_description': 'RAID Controller %s',
}

You need to define the parse function whithin the check_info structure. Then checkmk will call it automatically for you. The data flow then is:

  • The parse function gets called with the raw agent output as a list of lists (line by line and separated at whitespace). The parse function must return “something”, usually either a dictionary with the items as keys or a list.
  • The inventory function gets called with the return value of the parse_function. It must return every item found (in case of a service with items). So the easiest is to just iterate the dictionary which the parse function returned and return its keys.
  • The check function gets called with an item (one that was determined by the inventory function) and the return value of the parse function. It can thus just check the values of parsed[item].

I’d try this approach:

# <<<adaptec_raid>>>
# Controller Status: Optimal
# Controller Model: Adaptec ASR8885
# Controller Serial Number: 6A506399A1D
# PCI Device ID: 653


def adaptec_raid_parse(info):
    parsed = {}

    state, model, serial, devid = None, None, None, None
    for line in info:
        if line[0] == 'Controller' and line[1] == 'Status:':
            state = ' '.join(line[2:])
        elif line[0] == 'Controller' and line[1] == 'Model:':
            model = ' '.join(line[2:])
        elif line[0] == 'Controller' and line[1] == 'Serial' and line[2] == 'Number:':
            serial = ' '.join(line[3:])
        elif line[0] == 'PCI' and line[1] == 'Device':
            devid = int(line[-1])
            if state and model and serial:
                parsed[devid] = (state, model, serial)
                state, model, serial = None, None, None

    # now parsed is a dictionary.
    # { 653: ('Optimal', 'Adaptec ASR8885', '6A506399A1D') }
    # if you have multiple PCI devices, it would look like this:
    # { 653: ('Optimal', 'Adaptec ASR8885', '6A506399A1D'),
    #   123: ('Even Better', 'Adaptec xxxx', 'yyyyy') }

    return parsed


# inventory. gets called with the return value of the parse function.
# it returns one item (=device-id) at a time.
def inventory_adaptec_raid(parsed):
    for item in parsed.keys():
        yield item, None


# check. gets called with the return value of the parse function.
def check_adaptec_raid(item, params, parsed):
    if not parsed or item not in parsed:
        return  # this indicates "UNKN - Item not found"

    # now parsed[item] contains the data for this particular item (aka device-id). so:
    yield 0, parsed[item][0]  # state
    yield 0, "model=%s" % parsed[item][1]  # model
    yield 0, "serial=%s" % parsed[item][2]  # serial


# declare
check_info["adaptec_raid"] = {
    'parse_function': adaptec_raid_parse,
    'check_function': check_adaptec_raid,
    'inventory_function': inventory_adaptec_raid,
    'service_description': 'RAID Controller %s',
}
2 Likes

This is not a good example as it calls a parse function within the inventory resp check functions.

1 Like

@Dirk This is awesome. Thank you very much for example and especially comments. It makes more clear now, how it works.

I made small change and put Adapter model into Service description instead of Device ID and change yield’s to one return statement (also modified the parse function accordingly).

# check. gets called with the return value of the parse function.
def check_adaptec_raid(item, params, parsed):
    if not parsed or item not in parsed:
        return  # this indicates "UNKN - Item not found"

    # now parsed[item] contains the data for this particular item (aka device-id). so:
    #yield 0, parsed[item][0]  # state
    #yield 0, "model=%s" % parsed[item][1]  # model
    #yield 0, "serial=%s" % parsed[item][2]  # serial
    state = parsed[item][0]
    if state == 'Optimal':
        retval = 0
    else:
        retval = 2

    infotext = "Status=%s, PCI Device ID=%s, Serial=%s" % (parsed[item][0], parsed[item][1], parsed[item][2])
    return retval, infotext

@r.sander Yes, I realized that later :frowning:

1 Like

You’re welcome. Yes, of course there are many ways to write such a plugin. I think one of the things that confused you was a snippet like this:

serial = line[3:]

Here serial is a list of things, i.e. if line is e.g. ['a', 'b', 'c', 'd'], then serial now is ['d']. That’s why it showed up as [u'ASR8885'] (or similar) in the GUI. I re-joined the elements in that list with space characters (serial=' '.join(line[3:]) to get a simple string.

1 Like

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed. Contact an admin if you think this should be re-opened.