Problem with parsing by spaces

Hi

I have this “interesting” problem while developing an inventory plugin “AIX_LVM_ALL”
where I want to enrich the inventory of AIX hosts with infos about their logical volumes and volume groups.

There’s a nice AIX command giving me the following output (with spaces in-between):

lv_problem

My problem now is that in some cases the “type” of a volume is empty as can be seen in line 4 above (“xxxlv_private3…”).

As a consequence the CheckMK gives me 6 entries instead of the expected 7 (because the delimiters are spaces).

I was wondering if somebody already had a similar problem and what their approach was?

My first rough idea would be to

  • wrap above command in a python script on the host writing the raw result to some tmp first
  • parse the sections,
  • figure the distances and possible overflows,
  • replacing with appropriate number of tabs
  • and write out the enhanced

Any better ideas? Is there already some solution?

Cheers
Markus

I assume you have something like this in your parse (or check) function:

def parse_my_aix_check(info):
    parsed = {}
    for lv_name, lv_type, lps, pps, pvs, lv_state, mount_point in info[1:]:
        parsed[lv_name] = {
            'lv_type'     : lv_type,
            'lps'         : int(lps),
            'pps'         : int(pps),
            'pvs'         : int(pvs),
            'lv_state'    : lv_state,
            'mount_point' : mount_point
        }
    return parsed

If you know for sure that it is always the 2nd column that could be missing, then change the parsing like so:

def parse_my_aix_check(info):
    parsed = {}
    for line in info[1:]:
        if len(line) == 7:
            lv_name, lv_type, lps, pps, pvs, lv_state, mount_point = line
        elif len(line) == 6:
            lv_name, lps, pps, pvs, lv_state, mount_point = line
            lv_type = None
        parsed[lv_name] = {
            'lv_type'     : lv_type,
            'lps'         : int(lps),
            'pps'         : int(pps),
            'pvs'         : int(pvs),
            'lv_state'    : lv_state,
            'mount_point' : mount_point
        }
    return parsed

For your sample input the parse function will return:

{u'xxxlv_private1': {'lps': 1,
                     'lv_state': u'closed/syncd',
                     'lv_type': u'boot',
                     'mount_point': u'N/A',
                     'pps': 1,
                     'pvs': 1},
 u'xxxlv_private2': {'lps': 1,
                     'lv_state': u'closed/syncd',
                     'lv_type': u'boot',
                     'mount_point': u'N/A',
                     'pps': 1,
                     'pvs': 1},
 u'xxxlv_private3': {'lps': 4,
                     'lv_state': u'open/syncd',
                     'lv_type': None,
                     'mount_point': u'N/A',
                     'pps': 4,
                     'pvs': 1}}
4 Likes

Hi Dirk

Many thanks for your prompt and effective answer !!

Maybe as a suggestion for some later time, a “fixed field width” pre-parser would be nice, where one could instead of specifying the delimiter specify some array of field widths or starting positions.
This would allow to parse output with several empty fields as well.

A colleague is suggesting just now that one could also specify a field delimiter which would never exist in the lines to parse. This would then always yield a single column to be parsed as one likes…

Cheers
Markus

1 Like

Hi Markus,

you are welcome. As for the delimiter that never occurs: use

<<<my_check:sep(0)>>>

Some agent plugins already do it that way if the data cannot be split into fields properly. A first approach for the parse function with fixed field lengths would then be something like this (I made up the column widths, they are not real):

def parse_my_aix_check(info):
    parsed = {}
    for line in info[1:]:
        real_line = line[0]

        lv_name = real_line[0:5]
        lv_type = real_line[5:10]
        lps     = int(real_line[12:16])
        ...
    return parsed

Anecdote: I once used 7 as the delimiter and had the output delimited by \a, which is the BEL character. It was fun and annoying because everytime you watched the agent output you could also hear it. Just save this as /usr/lib/check_mk_agent/plugins/beepy and have fun:

#!/usr/bin/env bash

echo "<<<beepy:sep(7)>>>"
echo -e "One\aTwo"

Dirk

1 Like