[internode_usage] New plugin for Australian ISP Internode

* Add BUGS and SPDX license
* Add dirtyconfig support
* Use ideal usage as warning
This commit is contained in:
Olivier Mehani 2019-04-11 22:56:28 +10:00
parent 92db831bc3
commit 691e52b868
No known key found for this signature in database
GPG Key ID: 6F82C3C767D655AA
4 changed files with 224 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

224
plugins/isp/internode_usage Executable file
View File

@ -0,0 +1,224 @@
#!/bin/sh -u
# -*- sh -*-
: << =cut
=head1 NAME
internode_usage - Plugin to monitor quota usage of an Internode service
The ideal usage is also used as an updated warning limit.
=head1 CONFIGURATION
[internode_usage]
env.internode_api_login LOGIN
env.internode_api_password PASSWORD
An optional 'env.internode_api_url' can be used, but should not be needed. It
will default to https://customer-webtools-api.internode.on.net/api/v1.5.
If multiple services are available, the plugin will automatically pick the first
service from the list. To monitor other services, the plugin can be used
multiple times, by symlinking it as 'internode_usage_SERVICEID'.
=head1 CAVEATS
* The hourly rate are a bit spikey in the -day view, as the API seems to update
every 20 to 30 minutes; it is fine in the -month and more aggregated views
* The daily rate is the _previous_ day, and does always lag by 24h
* Due to the way the API seems to update the data, values for the daily rate
are missing for a short period every day
=head1 AUTHOR
Olivier Mehani
Copyright (C) 2019 Olivier Mehani <shtrom+munin@ssji.net>
=head1 LICENSE
SPDX-License-Identifier: GPL-3.0-or-later
=cut
# shellcheck disable=SC1090
. "${MUNIN_LIBDIR:-.}/plugins/plugin.sh"
if [ "${MUNIN_DEBUG:-0}" = 1 ]; then
set -x
fi
if ! command -v curl >/dev/null; then
echo "curl not found" >&2
exit 1
fi
if ! command -v xpath >/dev/null; then
echo "xpath (Perl XML::LibXML) not found" >&2
exit 1
fi
if ! command -v bc >/dev/null; then
echo "bc not found" >&2
exit 1
fi
if [ -z "${internode_api_url:-}" ]; then
internode_api_url="https://customer-webtools-api.internode.on.net/api/v1.5"
fi
xpath_extract() {
xpath="$1"
xpath -q -n -e "${xpath}" | \
sed 's/<\([^>]*\)>\([^<]*\)<[^>]*>/\2/'
}
xpath_extract_attribute() {
xpath="$1"
xpath -q -n -e "${xpath}" | \
sed 's/.*="\([^"]\+\)".*/\1/'
}
fetch() {
# shellcheck disable=SC2154
curl -u "${internode_api_login}:${internode_api_password}" -s "$@"
}
get_data() {
SERVICE_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/service")"
SERVICE_USERNAME="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/username")"
SERVICE_QUOTA="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/quota")"
SERVICE_PLAN="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/plan")"
SERVICE_ROLLOVER="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/rollover")"
SERVICE_INTERVAL="$(echo "${SERVICE_XML}" | xpath_extract "internode/api/service/plan-interval" | sed 's/ly$//')"
HISTORY_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/history")"
TODAY_TIMESTAMP="$(date +%s)"
DAILY_DATE="$(date +%Y-%m-%d -d yesterday)"
DAILY_TIMESTAMP="$(date -d "${DAILY_DATE} $(date +%H:%M:%S)" +%s)"
DAILY_USAGE="$(echo "${HISTORY_XML}" | xpath_extract "internode/api/usagelist/usage[@day=\"${DAILY_DATE}\"]/traffic")"
USAGE_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/usage")"
SERVICE_USAGE="$(echo "${USAGE_XML}" | xpath_extract "internode/api/traffic")"
USAGE_CRITICAL="${SERVICE_QUOTA}"
USAGE_WARNING="$(echo "${SERVICE_QUOTA}*.75" | bc -ql)"
TODAY="$(date +%s)"
FIRST_DAY="$(date +%s --date "${SERVICE_ROLLOVER} -1 ${SERVICE_INTERVAL}")"
LAST_DAY="$(date +%s --date "${SERVICE_ROLLOVER}")"
BILLING_PERIOD="(${LAST_DAY}-${FIRST_DAY})"
IDEAL_USAGE="$(echo "${SERVICE_QUOTA}-(${SERVICE_QUOTA}*(${LAST_DAY}-${TODAY})/${BILLING_PERIOD})" | bc -ql)"
}
graph_config() {
graph=""
if [ -n "${1:-}" ]; then
graph=".$1"
fi
echo "multigraph internode_usage_${SERVICE_ID}${graph}"
case "$graph" in
.current)
echo "graph_title Hourly rate for ${SERVICE_USERNAME}"
echo 'graph_category network'
# ${graph_period} is not a shell variable
# shellcheck disable=SC2016
echo 'graph_vlabel bytes per ${graph_period}'
# XXX: this seems to be updated twice per hour;
# the data from this graph may be nonsense
echo 'graph_period hour'
echo "hourly_rate.label Hourly usage"
echo "hourly_rate.type DERIVE"
echo "hourly_rate.min 0"
;;
.daily)
echo "graph_title Previous-day usage for ${SERVICE_USERNAME}"
echo 'graph_category network'
# ${graph_period} is not a shell variable
# shellcheck disable=SC2016
echo 'graph_vlabel bytes per ${graph_period}'
echo 'graph_period day'
echo "daily_rate.label Previous-day usage"
echo "daily_rate.type GAUGE"
;;
*)
#.usage)
echo "graph_title Uplink usage for ${SERVICE_USERNAME}"
echo 'graph_category network'
echo 'graph_vlabel bytes'
echo 'graph_period hour'
echo "usage.label Total usage"
echo "usage.draw AREA"
echo "usage.extinfo ${SERVICE_PLAN}; rollover: ${SERVICE_ROLLOVER}"
echo "ideal.label Ideal usage"
echo "ideal.draw LINE"
echo "ideal.colour FFA500"
echo "usage.critical ${USAGE_CRITICAL}"
echo "usage.warning ${IDEAL_USAGE}"
echo "ideal.critical 0:"
echo "ideal.warning 0:"
;;
esac
echo
}
graph_data() {
graph=""
if [ -n "${1:-}" ]; then
graph=".${1}"
fi
echo "multigraph internode_usage_${SERVICE_ID}${graph}"
case "${graph}" in
.current)
echo "hourly_rate.value ${TODAY_TIMESTAMP}:${SERVICE_USAGE:-U}"
;;
.daily)
echo "daily_rate.value ${DAILY_TIMESTAMP}:${DAILY_USAGE:-U}"
;;
*)
echo "usage.value ${TODAY_TIMESTAMP}:${SERVICE_USAGE:-U}"
echo "ideal.value ${TODAY_TIMESTAMP}:${IDEAL_USAGE:-U}"
;;
esac
echo
}
main() {
case ${1:-} in
config)
graph_config
graph_config usage
graph_config current
graph_config daily
if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then
main
fi
;;
*)
graph_data
graph_data usage
graph_data current
graph_data daily
;;
esac
}
# Determine the service ID from the name of the symlink
SERVICE_ID="$(echo "${0}" | sed -n 's/^.*internode_usage_//p')"
if [ -z "${SERVICE_ID}" ]; then
API_XML="$(fetch "${internode_api_url}")"
# Otherwise, get the first service in the list
SERVICE_ID="$(echo "${API_XML}" | xpath_extract "internode/api/services/service")"
fi
get_data
main ${1:-}