Help needed to create plugin for pdu

Hi I’m new to this! I am a ICT engineer and have been put on a project to read information from our pdus and display it on check_mk and set warnings. So far iv found the mib numbers that tell us the information we need. I`m new to code and with help iv made a script that pulls all the relevant information we need iv added it to this folder (/opt/omd/versions/1.5.0p9.cee/share/check_mk/checks) and check_mk automatically picks this up when we run a test it gives us the information but it says there’s an error. What im asking is how do I create a plugin, where do I put it and am I going the right way about it! il added the code to this
Thanks for reading

#!/usr/bin/python
import subprocess
#~#
output_list = list()
test_list = [
β€˜.1.1’,
β€˜.1.3’,
β€˜.1.2’,
β€˜.1.4’,
β€˜.1.5’,
β€˜.1.7’,
β€˜.25.1.5’,
β€˜.25.1.6’,
β€˜.25.1.7’,
β€˜.25.1.8’,
β€˜.25.1.9’,
β€˜.25.1.10’,
β€˜.25.1.11’,
β€˜.25.1.12’,
]
#~#
def run_command(end_point):
result = subprocess.Popen([β€˜snmpwalk’, β€˜-v2c’, β€˜-c’, β€˜public’, β€˜159.107.170.11’, β€˜1.3.6.1.4.1.21239.2{}’.format(end_point)], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout,stderr = result.communicate()
output_list.append(stdout)
#~#
for end_point in test_list:
run_command(end_point)
#~#
print(β€˜Print output_list’)
print(output_list)

Hi @Dwaynef71,

At first glance I would say that is an execution error or bad formatted output (in cmk way).
What I suggest is reading the docs (https://checkmk.com/cms_legacy_devel_snmpbased.html) and come back with any doubts that may arise.

1 Like

perhaps take a look at the PDU Check for GUDE:

GUDE-PDU
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2016             mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software;  you can redistribute it and/or modify it
# under the  terms of the  GNU General Public License  as published by
# the Free Software Foundation in version 2.  check_mk is  distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
# out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
# PARTICULAR PURPOSE. See the  GNU General Public License for more de-
# tails. You should have  received  a copy of the  GNU  General Public
# License along with GNU Make; see the file  COPYING.  If  not,  write
# to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
# Boston, MA 02110-1301 USA.

# Knowledge from customer:
# Devices with OID_END=38 are 12 port power switches with two powerbanks.
# Means each powerbank has 6 outlets. Here we can use ChanStatus in order
# to find out if one powerbank is enabled/used.
#
# Device with OID_END=19 is a simple switch outlet: 1 Port and 1 powerbank
# Once it's plugged in, the state is "on". Thus we use PortState in
# discovering function.

factory_settings["gude_powerbank_default_levels"] = {
    "voltage": (220, 210),
    "current": (15, 16),
}


def parse_gude_powerbanks(info):
    map_port_states = {
        "0": (2, "off"),
        "1": (0, "on"),
    }
    map_channel_states = {
        "0": (2, "data not active"),
        "1": (0, "data valid"),
    }

    ports = dict(info[0])
    parsed = {}
    for oid_idx, dev_state, energy_str, active_power_str, \
        current_str, volt_str, freq_str, appower_str in info[1]:

        oid, idx = oid_idx.split(".")
        device_state = None
        if oid in ["19"]:
            device_state = map_port_states[ports[oid_idx]]
        if oid in ["38"]:
            device_state = map_channel_states[dev_state]

        if device_state is None:
            continue

        parsed.setdefault(idx, {"device_state": device_state})

        for what, key, factor in [
            (energy_str, "energy", 1.0),
            (active_power_str, "power", 1.0),
            (current_str, "current", 0.001),
            (volt_str, "voltage", 1.0),
            (freq_str, "frequency", 0.01),
            (appower_str, "appower", 1.0),
        ]:
            parsed[idx][key] = float(what) * factor

    return parsed


def inventory_gude_powerbanks(parsed):
    return [(powerbank, {})
            for powerbank, attrs in parsed.items()
            if attrs["device_state"][1] not in ["off", "data not active"]]


check_info['gude_powerbanks'] = {
    'parse_function'          : parse_gude_powerbanks,
    'inventory_function'      : inventory_gude_powerbanks,
    'check_function'          : check_elphase,
    'service_description'     : 'Powerbank %s',
    'has_perfdata'            : True,
    'snmp_info'               : [('.1.3.6.1.4.1.28507', ['19', '38'], [
                                      OID_END,
                                      "1.3.1.2.1.3",    # GUDEADS=EPC****-MIB::epc****PortState
                                 ]),
                                 ('.1.3.6.1.4.1.28507', ['19', '38'], [
                                      OID_END,
                                      "1.5.1.2.1.2",    # GUDEADS-EPC****-MIB::epc****ChanStatus
                                      "1.5.1.2.1.3",    # GUDEADS-EPC****-MIB::epc****AbsEnergyActive
                                      "1.5.1.2.1.4",    # GUDEADS-EPC****-MIB::epc****PowerActive
                                      "1.5.1.2.1.5",    # GUDEADS-EPC****-MIB::epc****Current
                                      "1.5.1.2.1.6",    # GUDEADS-EPC****-MIB::epc****Voltage
                                      "1.5.1.2.1.7",    # GUDEADS-EPC****-MIB::epc****Frequency
                                      "1.5.1.2.1.10",   # GUDEADS-EPC****-MIB::epc****PowerApparent
                                ])],
    'snmp_scan_function'      : lambda oid: oid(".1.3.6.1.2.1.1.2.0").startswith(".1.3.6.1.4.1.28507.19") or \
                                            oid(".1.3.6.1.2.1.1.2.0").startswith(".1.3.6.1.4.1.28507.38"),
    'default_levels_variable' : 'gude_powerbank_default_levels',
    'group'                   : 'el_inphase',
    'includes'                : [ 'elphase.include' ],
}

Greets Bernd

1 Like

@Dwaynef71 any News ???

Hi Bernd

I am new to coding so im trying to understand what the code is doing!
my code is throwing an error and im trying to see do I need more than one script

Regards,
Dwayne

1 Like

for dev. a script/plugin/check it`s a good way to analyze the existing checks etc

for snmp the dell or apc scripts are easy to understand how it works

just keep trying !!! :+1:

1 Like

Does there need to be more than one file? Like one with parameters and one with the main code

that is different how You want to dev. the check

the actuell way is to define the functions in one file and build the check in a seperate file

it`s also the question what kind of language you use bash/perl/python/…

the new design is for example this check:

https://checkmk.de/check_mk-exchange-file.php?&file=NTI-Enviromux-0.1.mkp

perhaps @kbu has also some time over for explanation

@Dwaynef71 post what you got. It is way easier to help, if we can see what you’re talking about.
I started last week with custom plugins, and Dokumentation is a mess. But the link tavanez posted, is a good start.
Limit your data to a single snmp statement like in the tutorial and ignore wato rules and metrics for now.
Just get the plugin to run with only returning state 0 and a message.

This: https://checkmk.com/cms_legacy_devel_agentbased.html#H1:Check%20parameters should help too.
And this is the most basic I came up with:

def inventory_bitbox_cpu(info): # all info from the snmp query is inside the array 'info' and you need to yield an identifier for each service that sould be created. My data looks like [[u'BITBOX01', u'1882340287'], [u'BITBOX02', u'54526005']] meaning there are 2 instances of bitbox running and each has a cpu time.
    for line in info:
        yield line[0], None

def check_bitbox_cpu(item, params, info): # for each identifier you yielded in the inventory function this will be called. The identifier is inside 'item' the 'info' is the full data like in the inventory function. Params should be ignored for now. 
    for line in info: # since info is the full data I need to find the line which belongs to the identifier.
        if item == line[0]:
            return 0, "CPU Time: %s." % line[1]

check_info["bitbox_cpu"] = {
    "inventory_function"    : inventory_bitbox_cpu,
    "check_function"        : check_bitbox_cpu,
    "service_description"   : "CPU Time %s",
    "snmp_info"             : ( ".1.3.6.1.4.1.30205.18.1.1", ["1", "5"])
}

This results in 2 services (one for each instance of bitbox) both displaying OK and the current CPU time.
hope this does help with getting started :slight_smile:

Hi Sebi
My code is in the first comments
Its getting very frustrating now haha

Thanks for the support lads

Yeah starting from scratch is always hard, but those 2 tutorials explain a lot and once u get a basic plugin going it gets easier to add complexity (warn/crit, more data /services). just start as minimal as possible.
Once you get you plugin in the right structure (inventory_, check_, check_info[""]) just post it here again :slight_smile: