Advice for Creating SNMP Based Discovery and Check with Multiple Metrics for Broadworks

Checkmk Managed Services Edition 2.1.0p28

Greetings plugin gurus. Here to ask for advice again.
I am currently working on a custom plugin for Broadworks related SNMP checks to replace an aging Cacti server.

My thought process is to check for a specific SNMP response for a vendor OID (e.g. ‘Broadworks Application Server’) and create a single service check called “Application Server” in CheckMK for the service to be running. I plan to expand the check to include other services and their associated metrics in the future but for now I am focusing on this.

There are multiple metrics associated with an “Application Server” such as Answer Delay, Auth Challenge, Network attempts. As of this time there are no defined requirements or thresholds to alarm on values for these additional metrics but I anticipate that may be requested in the future.

I think for my initial deployment I would like to include all of these metrics as graphs under the “Application Server” service rather than as individual services and graphs as I have now.

Could someone provide a link to a document, example(s) or describe the methodology to accomplish this? I understand my discovery will need to change to a single result but can’t find anything to start me working with multiple graphs under a single service.

As of this time I have the following file to detect all of the metrics but each as a service:
~/local/lib/check_mk/base/plugins/agent_based/broadworks.py

#!/usr/bin/env python3
# -*- encoding: utf-8; py-indent-offset: 4 -*-

from typing import Dict, List

from .agent_based_api.v1 import (
    all_of,
    equals,
    contains,
    register,
    Metric,
    Result,
    SNMPTree,
    Service,
    State,
)

from .agent_based_api.v1.type_defs import (
    StringTable,
    StringByteTable,
    CheckResult,
    DiscoveryResult,
)

# Adding debug support
from cmk.utils import debug
from pprint import pprint


def parse_broadworks(string_table):
    section = {}
    if debug.enabled():
        print("parser string_table contents:\n",type(string_table))
        pprint(string_table)
    fulllist = []
    for itemlist in string_table:
        for item in itemlist:
            fulllist.extend(item)

    if debug.enabled():
        print("fulllist contents:\n",type(fulllist))
        pprint(fulllist)

    section['NetworkOrigAttempt'] = int(fulllist[0])
    section['NetworkTermAttempt'] = int(fulllist[1])
    section['NetworkTermAnswered'] = int(fulllist[2])
    section['NetworkTotalCalls'] = int(fulllist[0]) + int(fulllist[2])
    section['UserOrigAttempt'] = int(fulllist[3])
    section['UserTermAttempt'] = int(fulllist[4])
    section['UserTermAnswered'] = int(fulllist[5])
    section['UserTotalCalls'] = int(fulllist[3]) + int(fulllist[5])
    section['bwCallpActiveCalls'] = int(fulllist[6])
    section['psOciStatsNbUpdateRequests'] = int(fulllist[7])
    section['psOciStatsNbQueryRequests'] = int(fulllist[8])
    section['bwSipStatsRegisterIns'] = int(fulllist[9])
    section['bwSipStatsRegisterResponseOuts.200'] = int(fulllist[10])
    section['bwSipStatsRegisterResponseOuts.401'] = int(fulllist[11])
    section['bwSipStatsRegisterResponseOuts.403'] = int(fulllist[12])
    section['bwSipStatsRegisterResponseOuts.404'] = int(fulllist[13])
    section['bwSipStatsSetupSignalDelay'] = int(fulllist[14])
    section['bwSipStatsMinSetupSignalDelay'] = int(fulllist[15])
    section['bwSipStatsMaxSetupSignalDelay'] = int(fulllist[16])
    section['bwSipStatsAnswerSignalDelay'] = int(fulllist[17])
    section['bwSipStatsMinAnswerSignalDelay'] = int(fulllist[18])
    section['bwSipStatsMaxAnswerSignalDelay'] = int(fulllist[19])
    section['bwMeetMeNumPortsInUse'] = int(fulllist[20])
    section['bwMeetMeNumJoinFailureLicensing'] = int(fulllist[21])
    section['bwMeetMeNumActiveConferences'] = int(fulllist[22])
    section['bwMeetMeNumJoinFailureMediaServerBusy'] = int(fulllist[23])
    section['bwAuthenticationNumChallenges'] = int(fulllist[24])
    section['bwAuthenticationNumValidResponses'] = int(fulllist[25])
    section['nbOfMigratedUsers'] = int(fulllist[26])
    return section

register.snmp_section(
    name = "broadworks_as",
    detect = all_of(
        equals(".1.3.6.1.2.1.1.1.0", "Broadworks Application Server"),
        contains(".1.3.6.1.4.1.6431.1.1.2.1.1.0", "Application Server")
    ),
    parse_function = parse_broadworks,
    fetch = [
        SNMPTree(
            base=".1.3.6.1.4.1.6431.1.2.7.1",
            oids=[
                "1.0", # BW-Execution::bwCallpNetworkOriginationAttempts.0 Counter32: <INT>
                "2.0", # BW-Execution::bwCallpNetworkTerminationAttempts.0 Counter32: <INT>
                "3.0", # BW-Execution::bwCallpNetworkTerminationsAnswered.0 Counter32: <INT>
                "4.0", # BW-Execution::bwCallpUserOriginationAttempts.0 Counter32: <INT>
                "5.0", # BW-Execution::bwCallpUserTerminationAttempts.0 Counter32: <INT>
                "6.0", # BW-Execution::bwCallpUserTerminationsAnswered.0 Counter32: <INT>
                "10.0", #BW-Execution::bwCallpActiveCalls.0 Gauge32: <INT>
            ],
        ),
        SNMPTree(
            base = ".1.3.6.1.4.1.6431.1.6.8.1",
            oids = [
                "1.0", # BW-Provisioning::psOciStatsNbUpdateRequests.0 Counter32: <INT>
                "3.0", # BW-Provisioning::psOciStatsNbQueryRequests.0 Counter32: <INT>
            ],
        ),
        SNMPTree(
            base = ".1.3.6.1.4.1.6431.1.2.9.1",
            oids = [
                "12.0", # BW-Execution::bwSipStatsRegisterIns.0 Counter32: <INT>
                "26.1.3.200", # BW-Execution::bwSipStatsRegisterResponseOuts.200 Counter32: <INT>
                "26.1.3.401", # BW-Execution::bwSipStatsRegisterResponseOuts.401 Counter32: <INT>
                "26.1.3.403", # BW-Execution::bwSipStatsRegisterResponseOuts.403 Counter32: <INT>
                "26.1.3.404", # BW-Execution::bwSipStatsRegisterResponseOuts.404 Counter32: <INT>
                "44.0", # BW-Execution::bwSipStatsSetupSignalDelay.0 Gauge32: <INT>
                "45.0", # BW-Execution::bwSipStatsMinSetupSignalDelay.0 Gauge32: <INT>
                "46.0", # BW-Execution::bwSipStatsMaxSetupSignalDelay.0 Gauge32: <INT>
                "47.0", # BW-Execution::bwSipStatsAnswerSignalDelay.0 Gauge32: <INT>
                "49.0", # BW-Execution::bwSipStatsMinAnswerSignalDelay.0 Gauge32: <INT>
                "50.0", # BW-Execution::bwSipStatsMaxAnswerSignalDelay.0 Gauge32: <INT>
            ],
        ),
        SNMPTree(
            base = ".1.3.6.1.4.1.6431.1.2.18.1",
            oids = [
                "1.0", # BW-Execution::nbOfMigratedUsers.0 Gauge32: <INT>
            ],
        ),
        SNMPTree(
            base = ".1.3.6.1.4.1.6431.1.2.11",
            oids = [
                "89.3.0", # BW-Execution::bwMeetMeNumPortsInUse.0 Gauge32: <INT>
                "89.6.0", # BW-Execution::bwMeetMeNumJoinFailureLicensing.0 Counter32: <INT>
                "89.1.0", # BW-Execution::bwMeetMeNumActiveConferences.0 Gauge32: <INT>
                "89.8.0", # BW-Execution::bwMeetMeNumJoinFailureMediaServerBusy.0 Counter32: <INT>
                "3.1.0", # BW-Execution::bwAuthenticationNumChallenges.0 Counter32: <INT>
                "3.2.0", # BW-Execution::bwAuthenticationNumValidResponses.0 Counter32: <INT>
            ],
        )
    ],
)

def discover_broadworks(section) -> DiscoveryResult:
    # section comes in as
    # {'UserOrigAttempt': 0, 'UserTermAnswered': 0, 'UserTermAttempt': 0}
    if debug.enabled():
        pprint(section)
    for item in section.keys():
        yield Service(item=item)

def check_broadworks(item, params, section) -> CheckResult:
    if debug.enabled():
        pprint(item)
        pprint(params)
        pprint(section)
    yield Result(state=State.OK, summary='Always OK, no thresholds')
    yield Metric(item, section[item])

register.check_plugin(
    name='broadworks',
    sections=['broadworks_as'],
    service_name='BW %s',
    check_default_parameters={},
    discovery_function=discover_broadworks,
    check_function=check_broadworks,
)

This file for some logical grouping for some of the graphs

/local/share/check_mk/web/plugins/metrics/broadworks_metric.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from cmk.gui.i18n import _l
from cmk.gui.plugins.metrics.utils import graph_info, metric_info

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

# Title are always lower case - except the first character!
# Colors: See indexed_color() in cmk/gui/plugins/metrics/utils.py

metric_info["NetworkOrigAttempt"] = {
    "title": _l("Net origination attempts per minute"),
    "unit": "count",
    "color": "11/a",
}

metric_info["NetworkTermAttempt"] = {
    "title": _l("Net Termination attempts per minute"),
    "unit": "count",
    "color": "14/a",
}

metric_info["NetworkTermAnswered"] = {
    "title": _l("Net termination answered per minute"),
    "unit": "count",
    "color": "21/a",
}

metric_info["NetworkTotalCalls"] = {
    "title": _l("Net termination answered per minute"),
    "unit": "count",
    "color": "21/a",
}

metric_info["UserOrigAttempt"] = {
    "title": _l("User origination attempts per minute"),
    "unit": "count",
    "color": "11/a",
}

metric_info["UserTermAttempt"] = {
    "title": _l("User Termination attempts per minute"),
    "unit": "count",
    "color": "14/a",
}

metric_info["UserTermAnswered"] = {
    "title": _l("User termination answered per minute"),
    "unit": "count",
    "color": "21/a",
}

metric_info["UserTotalCalls"] = {
    "title": _l("User termination answered per minute"),
    "unit": "count",
    "color": "21/a",
}


# .
#   .--Graphs--------------------------------------------------------------.
#   |                    ____                 _                            |
#   |                   / ___|_ __ __ _ _ __ | |__  ___                    |
#   |                  | |  _| '__/ _` | '_ \| '_ \/ __|                   |
#   |                  | |_| | | | (_| | |_) | | | \__ \                   |
#   |                   \____|_|  \__,_| .__/|_| |_|___/                   |
#   |                                  |_|                                 |
#   +----------------------------------------------------------------------+
#   |  Definitions of time series graphs                                   |
#   '----------------------------------------------------------------------'


graph_info["bw_as_network_attempts"] = {
    "title": _l("Bw as network attempts"),
    "metrics": [
        ("NetworkOrigAttempt", "area"),
        ("NetworkTermAttempt", "stack"),
        ("NetworkTermAnswered", "line"),
        ("NetworkTotalCalls", "line"),
    ],
}

graph_info["bw_as_user_attempts"] = {
    "title": _l("Bw as user attempts"),
    "metrics": [
        ("UserOrigAttempt", "area"),
        ("UserTermAttempt", "stack"),
        ("UserTermAnswered", "line"),
        ("UserTotalCalls", "line"),
    ],
}

Critiques, guidance and advice is greatly appreciated. Mistakes are all mine and how I learn.

Sincerely,
Scotsie

1 Like

If I understand your plugin code correctly, you are creating a service for each OID in your OID list. In your metrics, you want to have some charts created from a set of those OIDs. This is not going to work that way. The metrics in a chart need to come from the same service. So you need to change the way you are creating the services. In my opinion you have 3 possibilities:

  1. one check/service (without item) with all metrics
  2. one check with several services (items) and a group of metrics
  3. multiple checks (without item) with a group of metrics

For option 1:

  • remove the item from your service definition
  • in the discover function create a service without an item
  • in the check function, iterate over the items in the section and output them as metrics

for option 2:

  • modify your parse function so that you have a two-level dictionary
    i.e.:
   section = {
    'networkorigattempt': {
        "networkorigattempt": fulllist[0],
        "networktermattempt": fulllist[1],
        "networktermanswered": fulllist[2],
        "networktotalcalls": fulllist[3],
    },
    'usertempts': {
        "UserOrigAttempt": fulllist[4],
        "UserTermAttempt": fulllist[5],
        "UserTermAnswered": FullList[6],
        "UserTotalCalls": fulllist[7],
      }
   }
  • in the dicover function, the services are created from the top level of the dictionary
  • in the check function provide the metrics under the item (top level) of the section

for option 3:

  • split your check according to the related OIDs
  • use a different name for each check
  • rest like option 1

One word about the item. Take a look at the documentation 3. Checks with more than one service (items) per host (here you will find also some basic information how to write a plugin :slight_smile: ). Basically, the item is for when your check has to deal with multiple instances of the same type (i.e. interfaces → one check → one service per interface). Keeping this in mind, Option 1 and 2 are “by the book”, while option 2 works but is a bit outside of the design guide.

Cheers
Thomas

1 Like

@thl-cmk thank you for taking the time to review and provide some direction.

Your option 1 is the direction I was thinking I would have to go but didn’t have an idea on how to execute that plan. My original attempt was just to start recording something for visualization to prove that it could be done.

I will work on modify the check more towards option 1 and post some follow up.

I appreciate your time!

Sincerely,
Scotsie

1 Like

After a lot of trial and error among several releases of Broadworks, I found inconsistencies with getting responses via SNMP across various servers (likely application rules, acls, firewalls). Instead I went with a perl based script (due to lesser added dependencies) to poll via localhost and write output for use by the Check_MK agent.

Monitored host local path /lib/check_mk_agent/plugins/broadworks.pl
CheckMK host for future autodeployment $OMD_ROOT/local/share/check_mk/agents/plugins/broadworks.pl

#!/usr/bin/perl

use strict;
use warnings;
use Net::SNMP;
use JSON;

my %oid_map = (
    "AS" => {
        "UserOrigAttempt"           => ".1.3.6.1.4.1.6431.1.2.7.1.4.0",
        "UserTermAttempt"           => ".1.3.6.1.4.1.6431.1.2.7.1.5.0",
        "UserTermAnswered"          => ".1.3.6.1.4.1.6431.1.2.7.1.6.0",
        "NetOrigAttempt"            => ".1.3.6.1.4.1.6431.1.2.7.1.1.0",
        "NetTermAttempt"            => ".1.3.6.1.4.1.6431.1.2.7.1.2.0",
        "NetTermAnswered"           => ".1.3.6.1.4.1.6431.1.2.7.1.3.0",
        "Calls"                     => ".1.3.6.1.4.1.6431.1.2.7.1.10.0",
        "QueryRequests"             => ".1.3.6.1.4.1.6431.1.6.8.1.3.0",
        "UpdateRequests"            => ".1.3.6.1.4.1.6431.1.6.8.1.1.0",
        "RegisterIn"                => ".1.3.6.1.4.1.6431.1.2.9.1.12.0",
        "RegisterResponseOuts200"   => ".1.3.6.1.4.1.6431.1.2.9.1.26.1.3.200",
        "RegisterResponseOuts401"   => ".1.3.6.1.4.1.6431.1.2.9.1.26.1.3.401",
        "RegisterResponseOuts403"   => ".1.3.6.1.4.1.6431.1.2.9.1.26.1.3.403",
        "RegisterResponseOuts404"   => ".1.3.6.1.4.1.6431.1.2.9.1.26.1.3.404",
        "MinSetupSignalDelay"       => ".1.3.6.1.4.1.6431.1.2.9.1.45.0",
        "MaxSetupSignalDelay"       => ".1.3.6.1.4.1.6431.1.2.9.1.46.0",
        "MinAnswerSignalDelay"      => ".1.3.6.1.4.1.6431.1.2.9.1.49.0",
        "MaxAnswerSignalDelay"      => ".1.3.6.1.4.1.6431.1.2.9.1.50.0",
        "SetupSignalDelay"          => ".1.3.6.1.4.1.6431.1.2.9.1.44.0",
        "AnswerSignalDelay"         => ".1.3.6.1.4.1.6431.1.2.9.1.47.0",
        "NumMigratedUsers"          => ".1.3.6.1.4.1.6431.1.2.18.1.1.0",
        "MeetMeInUse"               => ".1.3.6.1.4.1.6431.1.2.11.89.3.0",
        "MeetMeFailureLicense"      => ".1.3.6.1.4.1.6431.1.2.11.89.6.0",
        "NumActiveConferences"      => ".1.3.6.1.4.1.6431.1.2.11.89.1.0",
        "MeetMeFailureMSBusy"       => ".1.3.6.1.4.1.6431.1.2.11.89.8.0",
        "AuthChallenges"            => ".1.3.6.1.4.1.6431.1.2.11.3.1.0",
        "ValidResponses"            => ".1.3.6.1.4.1.6431.1.2.11.3.2.0",
    },
    "MS" => {
        "IvrSessions"           => ".1.3.6.1.4.1.6431.1.3.1.4.16.0",
        "PortsInUse"            => ".1.3.6.1.4.1.6431.1.3.1.5.0",
        "MaxPorts"              => ".1.3.6.1.4.1.6431.1.3.4.4.0",
        "NumLicensedPorts"      => ".1.3.6.1.4.1.6431.1.3.4.5.0",
        "NoPortError"           => ".1.3.6.1.4.1.6431.1.3.1.6.0",
        "G729Ports"             => ".1.3.6.1.4.1.6431.1.3.1.10.0",
        "G722Ports"             => ".1.3.6.1.4.1.6431.1.3.1.12.0",
        "MP3EncoderPorts"       => ".1.3.6.1.4.1.6431.1.3.1.13.0",
        "MP3DecoderPorts"       => ".1.3.6.1.4.1.6431.1.3.1.14.0",
        "RTPSessionCount"       => ".1.3.6.1.4.1.6431.1.3.2.1.0",
        "RTPPacketsExpected"    => ".1.3.6.1.4.1.6431.1.3.2.2.2.0",
        "RTPPacketsReceived"    => ".1.3.6.1.4.1.6431.1.3.2.2.3.0",
        "RTPPacketsSent"        => ".1.3.6.1.4.1.6431.1.3.2.3.4.0",
        "PrimaryEmailSent"      => ".1.3.6.1.4.1.6431.1.3.3.1.1.0",
        "FilesDownloaded"       => ".1.3.6.1.4.1.6431.1.3.5.1.0",
        "FilesRetreivedCache"   => ".1.3.6.1.4.1.6431.1.3.5.3.0",
        "ConfChansInUse"        => ".1.3.6.1.4.1.6431.1.3.14.3.1.0",
    },
    "NS" => {
        "StatsInviteIns.0"              => ".1.3.6.1.4.1.6431.1.5.3.1.1.0",
        "StatsInviteResponseOuts.302"   => ".1.3.6.1.4.1.6431.1.5.3.1.3.1.3.302",
        "StatsInviteResponseOuts.404"   => ".1.3.6.1.4.1.6431.1.5.3.1.3.1.3.404",
        "StatsInviteResponseOuts.406"   => ".1.3.6.1.4.1.6431.1.5.3.1.3.1.3.406",
        "StatsInviteResponseOuts.484"   => ".1.3.6.1.4.1.6431.1.5.3.1.3.1.3.484",
        "StatsInviteResponseOuts.500"   => ".1.3.6.1.4.1.6431.1.5.3.1.3.1.3.500",
        "StatsRegisterIns.0"            => ".1.3.6.1.4.1.6431.1.5.3.1.5.0",
        "StatsUdpIns.0"                 => ".1.3.6.1.4.1.6431.1.5.3.1.22.0",
        "StatsUdpOuts.0"                => ".1.3.6.1.4.1.6431.1.5.3.1.23.0",
        "StatsTcpIns.0"                 => ".1.3.6.1.4.1.6431.1.5.3.1.19.0",
        "StatsTcpOuts.0"                => ".1.3.6.1.4.1.6431.1.5.3.1.20.0",
        "CallsPerSecond.0"              => ".1.3.6.1.4.1.6431.1.5.2.6.0",
        "UserLocationRequests.0"        => ".1.3.6.1.4.1.6431.1.19.1.1.0",
        "UserLocationRequestSuccess.0"  => ".1.3.6.1.4.1.6431.1.19.1.2.0",
        "UserLocationRequestUnknownUser.0" => ".1.3.6.1.4.1.6431.1.19.1.3.0",
    },
);

sub broadworksType {
    my ($ip, $comm, $port) = @_;
    my ($session, $error) = Net::SNMP->session(
        -hostname  => $ip,
        -community => $comm,
        -port      => $port,
    );

    if (!$session) {
        print("Error: $error");
        exit(1);
    }

    my $result = $session->get_request(-varbindlist => [".1.3.6.1.4.1.6431.1.1.2.1.1.0"]);

    if (!$result) {
        print("Error: " . $session->error());
        $session->close();
        exit(1);
    }

    my $value = $result->{".1.3.6.1.4.1.6431.1.1.2.1.1.0"};

    if ($value eq "Application Server") {
        return 'AS';
    } elsif ($value eq "Media Server") {
        return "MS";
    } elsif ($value eq "Network Server") {
        return "NS";
    } else {
        return "UNKNOWN";
    }
}

my ($ip, $comm, $port);

if (@ARGV == 3) {
    ($ip, $port, $comm) = @ARGV;
    #print("Parameters passed for check IP: $ip, Port: $port, Community: $comm.\n");
} else {
    $ip = "127.0.0.1";
    $port = 8001;
    $comm = "public";
    #print("Default parameters used for check IP: $ip, Port: $port, Community: $comm.\n");
}

my $broadworks_type = broadworksType($ip, $comm, $port);

if ($broadworks_type eq "UNKNOWN") {
    exit(0);  # Exit with code 0 when broadworksType is UNKNOWN
}

#print("Broadworks Type: $broadworks_type\n");

my %result_in;

foreach my $item (keys %{$oid_map{$broadworks_type}}) {
    my $oid = $oid_map{$broadworks_type}{$item};
    my ($session, $error) = Net::SNMP->session(
        -hostname  => $ip,
        -community => $comm,
        -port      => $port,
    );

    if (!$session) {
        print("Error: $error\n");
        exit(1);
    }

    my $result = $session->get_request(-varbindlist => [$oid]);

    if (!$result) {
        print("Error: " . $session->error() . "\n");
        $session->close();
        exit(1);
    }
    $result_in{$item} = $result->{$oid};
    $session->close();
}

my $result_json = encode_json(\%result_in);
my $broadworks_type_lc = lc $broadworks_type;

print("<<<broadworks_$broadworks_type_lc:sep(0)>>>\n");
print("$result_json\n");
print("<<<>>>\n");

I have 2 working “AS” type hosts and am working through tweaking the graphing metrics for those.
CheckMK host path $OMD_ROOT/local/share/check_mk/web/plugins/metrics/broadworks_metric.py

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

from cmk.gui.i18n import _l
from cmk.gui.plugins.metrics.utils import graph_info, metric_info

### Broadworks AS Example Feed ###
"""
<<<broadworks_as:sep(0)>>>
{"MaxSetupSignalDelay":363,"ValidResponses":26787,"Calls":0,"MeetMeInUse":0,"RegisterResponseOuts404":4703,"AuthChallenges":26961,"NetOrigAttempt":67,"RegisterResponseOuts401":4353,"NumMigratedUsers":0,"UserTermAnswered":50,"AnswerSignalDelay":3,"MeetMeFailureMSBusy":0,"UserTermAttempt":107,"UserTerminationsAnswered":50,"MeetMeFailureLicense":0,"SetupSignalDelay":63,"MinSetupSignalDelay":8,"RegisterResponseOuts200":10094,"RegisterIn":19150,"QueryRequests":13744936,"UserOrigAttempt":8,"MinAnswerSignalDelay":1,"MaxAnswerSignalDelay":13,"UpdateRequests":45,"RegisterResponseOuts403":0,"NumActiveConferences":0,"NetTermAttempt":8,"NetTermAnswered":5}
<<<>>>
"""


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

# Title are always lower case - except the first character!
# Colors: See indexed_color() in cmk/gui/plugins/metrics/utils.py

metric_info["Calls"] = {
    "title": _l("BW AS Calls in Progress"),
    "unit": "count",
    "color": "11/a",
}

metric_info["NumMigratedUsers"] = {
    "title": _l("# of Migrated Users"),
    "unit": "count",
    "color": "11/a",
}

# .
#   .--Graphs--------------------------------------------------------------.
#   |                    ____                 _                            |
#   |                   / ___|_ __ __ _ _ __ | |__  ___                    |
#   |                  | |  _| '__/ _` | '_ \| '_ \/ __|                   |
#   |                  | |_| | | | (_| | |_) | | | \__ \                   |
#   |                   \____|_|  \__,_| .__/|_| |_|___/                   |
#   |                                  |_|                                 |
#   +----------------------------------------------------------------------+
#   |  Definitions of time series graphs                                   |
#   '----------------------------------------------------------------------'

graph_info["bw_as_answer_delay"] = {
    "title": _l("BW AS Answer Delay"),
    "metrics": [
        ("AnswerSignalDelay", "area", _l("Signal Delay")),
        ("MinAnswerSignalDelay", "line", _l("Minimum Answer Signal Delay")),
        ("MaxAnswerSignalDelay", "line", _l("Max Answer Signal Delay Since Reset")),
    ],
}

graph_info["bw_as_auth_challenges"] = {
    "title": _l("BW AS Auth Challenges"),
    "metrics": [
        ("AuthChallenges,60,*", "line", _l("Auth Challenges per Minute")),
        ("ValidResponses,60,*", "area", _l("Valid Responses per Minute")),
    ],
    "scalars": [
        ("ValidResponses,AuthChallenges,/", "%", _l("Percentage")),
    ],
}

graph_info["bw_as_collaborate"] = {
    "title": _l("BW AS Collaborate"),
    "metrics": [
        ("NumActAudioPorts", "line", _l("Active Audio Ports")),
        ("NumActVideoPorts", "line", _l("Active Video Ports")),
        ("NumNoRsrcJoinFail", "line", _l("No Resource Failures")),
    ],
}

graph_info["bw_as_meet_me"] = {
    "title": _l("BW AS Meet Me"),
    "metrics": [
        ("MeetMeFailureLicense", "line", _l("Meet Me Failure - License")),
        ("MeetMeFailureMSBusy", "line", _l("Meet Me Failure - MS Busy")),
        ("MeetMeInUse", "area", _l("Meet Me In Use")),
    ],
}

graph_info["bw_as_network_attempts"] = {
    "title": _l("BW AS Network Attempts"),
    "metrics": [
        ("NetOrigAttempt,60,*", "area", _l("Net Origination Attempts per minute")),
        ("NetTermAttempt,60,*", "stack", _l("Net Termination Attempts per minute")),
        ("NetTermAnswered,60,*", "line", _l("Net Termination Answered per minute")),
        ("NetOrigAttempt,NetTermAttempt,+,60,*", "line", _l("Total Calls per minute")),
    ],
}

graph_info["bw_as_requests"] = {
    "title": _l("BW AS Requests"),
    "metrics": [
        ("QueryRequests,60,*", "line", _l("Query Requests")),
        ("UpdateRequests,60,*", "area", _l("Update Requests")),
    ],
}

graph_info["bw_as_setup_delay"] = {
    "title": _l("BW AS Setup Delay"),
    "metrics": [
        ("SetupSignalDelay", "area", _l("Current Setup Signal Delay")),
        ("MaxSetupSignalDelay", "line", _l("Maximum Setup Signal Delay Since Reset")),
        ("MinSetupSignalDelay", "line", _l("Minimum Setup Signal Delay")),
    ],
}

graph_info["bw_as_sip_registers"] = {
    "title": _l("BW AS SIP Registers"),
    "metrics": [
        ("RegisterIn", "line", _l("Registers In")),
        ("RegisterResponseOuts200", "area", _l("200 Responses Out")),
        ("RegisterResponseOuts401", "stack", _l("401 Responses Out")),
        ("RegisterResponseOuts403", "stack", _l("403 Responses Out")),
        ("RegisterResponseOuts404", "stack", _l("404 Responses Out")),
    ],
}

graph_info["bw_as_user_attempts"] = {
    "title": _l("BW AS User Attempts"),
    "metrics": [
        ("UserOrigAttempt,60,*", "area", _l("User Origination Attempts per minute")),
        ("UserTermAttempt,60,*", "stack", _l("User Termination Attempts per minute")),
        ("UserTermAnswered,60,*", "line", _l("User Termination Answered per minute")),
        ("UserOrigAttempt,UserTermAttempt,+,60,*", "line", _l("Total Calls per minute")),
    ],
}

Struggling with mimicking some cacti graphs at the moment and digging through existing settings to piece it together since documentation shows ‘coming soon’. I will post another question specific to graphing though to keep on topic.

With this as a base though I will also try to work on building this as an official package to put out there for community use.

As always, if you have any advice or feedback, it’s always welcome. I’ve learned a lot just seeing how other stuff works and from pointers provided in these forums.

Sincerely,
Scotsie