Mikrotik QoS Queue Tree plugin

Hi, I want to share my working check_MK v2.0 plugin to discover and check Mikrotik QueueTree Queues via SNMP.
It creates one Service for every Queue in the Queue Tree and shows

  • Bits per second
  • Packets per second
  • Dropped Packets per second

Via a service monitoring rule “Mikrotik QueueTree Parameters” the warn- and critical-Limits for packet drops can be adjusted.

I hope that helps anyone - have fun,
Bernhard

The check-plugin comes to folder local/lib/check_mk/base/plugins/agent_based/:frowning:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2022 Bernhard Voit - License: GNU General Public License v2
# check-plugin for mikrotik_queuetree

# OIDs overview
# .1.3.6.1.4.1.14988.1.1.2 mtxrQueues
# .1.3.6.1.4.1.14988.1.1.2.1 mtxrQueueSimpleTable
# .1.3.6.1.4.1.14988.1.1.2.1 mtxrQueueTreeTable
# .1.3.6.1.4.1.14988.1.1.2.2.1 mtxrQueueTreeEntry
# .1.3.6.1.4.1.14988.1.1.2.2.1.1 mtxrQueueTreeIndex
# .1.3.6.1.4.1.14988.1.1.2.2.1.2 mtxrQueueTreeName (used here)
# .1.3.6.1.4.1.14988.1.1.2.2.1.3 mtxrQueueTreeFlow
# .1.3.6.1.4.1.14988.1.1.2.2.1.4 mtxrQueueTreeParentIndex
# .1.3.6.1.4.1.14988.1.1.2.2.1.5 mtxrQueueTreeBytes (used here)
# .1.3.6.1.4.1.14988.1.1.2.2.1.6 mtxrQueueTreePackets (used here)
# .1.3.6.1.4.1.14988.1.1.2.2.1.7 mtxrQueueTreeHCBytes
# .1.3.6.1.4.1.14988.1.1.2.2.1.8 mtxrQueueTreePCQQueues
# .1.3.6.1.4.1.14988.1.1.2.2.1.9 mtxrQueueTreeDropped (used here)
# Example:
# .1.3.6.1.4.1.14988.1.1.2.2.1.2.16777216 Total upstream
# .1.3.6.1.4.1.14988.1.1.2.2.1.2.16777218 up_Citrix
# .1.3.6.1.4.1.14988.1.1.2.2.1.2.16777219 up_default
# .1.3.6.1.4.1.14988.1.1.2.2.1.2.16777220 up_Teams_Audio
# .1.3.6.1.4.1.14988.1.1.2.2.1.2.16777221 Teams_Video
# .1.3.6.1.4.1.14988.1.1.2.2.1.2.16777222 Teams_TRAP
# .1.3.6.1.4.1.14988.1.1.2.2.1.2.16777223 Total downstream
# .1.3.6.1.4.1.14988.1.1.2.2.1.2.16777225 down_default
#
# .1.3.6.1.4.1.14988.1.1.2.2.1.5.16777216 4151882005
# .1.3.6.1.4.1.14988.1.1.2.2.1.5.16777218 241862080
# .1.3.6.1.4.1.14988.1.1.2.2.1.5.16777219 3862058184
# .1.3.6.1.4.1.14988.1.1.2.2.1.5.16777220 34687687
# .1.3.6.1.4.1.14988.1.1.2.2.1.5.16777221 12577891
# .1.3.6.1.4.1.14988.1.1.2.2.1.5.16777222 696163
# .1.3.6.1.4.1.14988.1.1.2.2.1.5.16777223 917641369
# .1.3.6.1.4.1.14988.1.1.2.2.1.5.16777225 917641369
#
# .1.3.6.1.4.1.14988.1.1.2.2.1.6.16777216 7061846
# .1.3.6.1.4.1.14988.1.1.2.2.1.6.16777218 3098779
# .1.3.6.1.4.1.14988.1.1.2.2.1.6.16777219 3643690
# .1.3.6.1.4.1.14988.1.1.2.2.1.6.16777220 184218
# .1.3.6.1.4.1.14988.1.1.2.2.1.6.16777221 129855
# .1.3.6.1.4.1.14988.1.1.2.2.1.6.16777222 5304
# .1.3.6.1.4.1.14988.1.1.2.2.1.6.16777223 3325213
# .1.3.6.1.4.1.14988.1.1.2.2.1.6.16777225 3325213
#
# .1.3.6.1.4.1.14988.1.1.2.2.1.9.16777216 0
# .1.3.6.1.4.1.14988.1.1.2.2.1.9.16777218 0
# .1.3.6.1.4.1.14988.1.1.2.2.1.9.16777219 3087
# .1.3.6.1.4.1.14988.1.1.2.2.1.9.16777220 0
# .1.3.6.1.4.1.14988.1.1.2.2.1.9.16777221 0
# .1.3.6.1.4.1.14988.1.1.2.2.1.9.16777222 0
# .1.3.6.1.4.1.14988.1.1.2.2.1.9.16777223 0
# .1.3.6.1.4.1.14988.1.1.2.2.1.9.16777225 0

from .agent_based_api.v1 import *
from .agent_based_api.v1.render import bytes as render_bytes
from .agent_based_api.v1.type_defs import CheckResult, DiscoveryResult
from time import time
from cmk.base.plugins.agent_based.agent_based_api.v1 import (
    get_rate,
    get_value_store,
)
import pprint

def discover_mikrotik_queuetree(section):
    # pprint.pprint(section)
    for id, QueueTreeName, QueueTreeBytes, QueueTreePackets, QueueTreeDropped in section:
        yield Service(item=QueueTreeName)

# def check_mikrotik_queuetree(item, section) -> CheckResult:
# def check_mikrotik_queuetree(item, section):
def check_mikrotik_queuetree(item, params, section):
    for id, QueueTreeName, QueueTreeBytes, QueueTreePackets, QueueTreeDropped in section:
        if QueueTreeName == item:
            QueueTreeBits = int(QueueTreeBytes)*8      # convert string to int (and Bytes to Bits)
            QueueTreePackets = int(QueueTreePackets)  # convert string to int
            QueueTreeDropped = int(QueueTreeDropped)  # convert string to int
            # print(item, ": ", QueueTreeBytes, "Bytes, ", QueueTreePackets, "Packets, ", QueueTreeDropped, "Drops")
            # pprint.pprint(params)

            now_time = time()
            value_store = get_value_store()
            rate_Bits = get_rate(value_store, f'QueueTreeBits', now_time, QueueTreeBits, raise_overflow=True)
            rate_Packets = get_rate(value_store, f'QueueTreePackets', now_time, QueueTreePackets, raise_overflow=True)
            rate_Drops = get_rate(value_store, f'QueueTreeDropped', now_time, QueueTreeDropped, raise_overflow=True)
            warn_level = params["warn_level"]
            crit_level = params["crit_level"]
            # print(item, ": ", rate_Bits, "Bits/s, ", rate_Packets, "Packets/s, ", rate_Drops, "Drops/s")
            yield Metric(
                "queue_bps",
                rate_Bits,
                boundaries=(0, None))
            yield Metric(
                "queue_pkts",
                rate_Packets,
                boundaries=(0, None))
            yield Metric(
                "queue_drops",
                rate_Drops,
                levels=(warn_level, crit_level),
                boundaries=(0, None))
            if rate_Drops >= crit_level:
                queue_state = State.CRIT
            elif rate_Drops >= warn_level:
                queue_state = State.WARN
            else:
                queue_state = State.OK
            yield Result(state = queue_state, notice = f"{rate_Drops} dropped Packets per second (average last minute)")
            return

register.snmp_section(
    name = "mikrotik_queuetree",
    detect = matches(".1.3.6.1.2.1.1.1.0","RouterOS RB4011iGS\+"),
    fetch = SNMPTree(
        base = '.1.3.6.1.4.1.14988.1.1.2.2.1', # mtxrQueueTreeEntry
        oids = [
            OIDEnd(),
            '2', # mtxrQueueTreeName
            '5', # mtxrQueueTreeBytes
            '6', # mtxrQueueTreePackets
            '9', # mtxrQueueTreeDropped
        ],
    ),
)

register.check_plugin(
    name="mikrotik_queuetree",
    service_name="Queue %s",
    discovery_function=discover_mikrotik_queuetree,
    check_function=check_mikrotik_queuetree,
    check_default_parameters={"warn_level": 0.01, 'crit_level': 0.01},
    check_ruleset_name="mikrotik_queuetree",
)

The metric-definition comes to folder local/share/check_mk/web/plugins/metrics:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from cmk.gui.i18n import _

from cmk.gui.plugins.metrics import metric_info

#.
#   .--Metrics-------------------------------------------------------------.
#   |                   __  __      _        _                             |
#   |                  |  \/  | ___| |_ _ __(_) ___ ___                    |
#   |                  | |\/| |/ _ \ __| '__| |/ __/ __|                   |
#   |                  | |  | |  __/ |_| |  | | (__\__ \                   |
#   |                  |_|  |_|\___|\__|_|  |_|\___|___/                   |
#   |                                                                      |
#   +----------------------------------------------------------------------+
#   |  Definitions of metrics                                              |
#   '----------------------------------------------------------------------'

metric_info["queue_bps"] = {
    "title": _("Queue bandwidth"),
    "unit": "bits/s",
    "color": "#00e060",
}

metric_info["queue_pkts"] = {
    "title": _("Queue Packets"),
    "unit": "1/s",
    "color": "#00e060",
}

metric_info["queue_drops"] = {
    "title": _("Queue Drops"),
    "unit": "1/s",
    "color": "#ff0080",
}

and the parameter-rule comes to local/share/check_mk/web/plugins/wato:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2022 Bernhard Voit - License: GNU General Public License v2
# check_parameter definition for the mikrotik_queuetree

from cmk.gui.i18n import _

from cmk.gui.valuespec import (
    Dictionary,
    Float,
    TextAscii,
)
from cmk.gui.plugins.wato import (
    CheckParameterRulespecWithItem,
    rulespec_registry,
    RulespecGroupCheckParametersNetworking,
)

def _item_valuespec_mikrotik_queuetree():
    return TextAscii(title=_("Queue Name"))

def _parameter_valuespec_mikrotik_queuetree():
    return Dictionary(
        elements=[
            ("warn_level", Float(title=_("Warning above Drops per second"))),
            ("crit_level", Float(title=_("Critical above Drops per second"))),
        ],
    )

rulespec_registry.register(
    CheckParameterRulespecWithItem(
        check_group_name="mikrotik_queuetree",
        group=RulespecGroupCheckParametersNetworking,
        match_type="dict",
        item_spec=_item_valuespec_mikrotik_queuetree,
        parameter_valuespec=_parameter_valuespec_mikrotik_queuetree,
        title=lambda: _("Mikrotik QueueTree Parameters"),
    ))
1 Like

Great. Now create an MKP and upload it to the https://exchange.checkmk.com/.

1 Like