mirror of
https://github.com/munin-monitoring/contrib.git
synced 2025-07-22 02:51:03 +00:00
Also make the daily graph more flexible, by always reporting the second last value (presumably the previous, but sometimes the API lags a bit, and actively requesting by date result in never reporting some late data). Signed-off-by: Olivier Mehani <shtrom@ssji.net>
257 lines
6.7 KiB
Bash
Executable file
257 lines
6.7 KiB
Bash
Executable file
#!/bin/sh -eu
|
|
# -*- 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"
|
|
|
|
CURL_ARGS='-s'
|
|
if [ "${MUNIN_DEBUG:-0}" = 1 ]; then
|
|
CURL_ARGS='-v'
|
|
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() {
|
|
# shellcheck disable=SC2039
|
|
local xpath="$1"
|
|
# shellcheck disable=SC2039
|
|
local node="$(xpath -q -n -e "${xpath}")" \
|
|
|| { echo "error extracting ${xpath}" >&2; false; }
|
|
echo "${node}" | sed 's/<\([^>]*\)>\([^<]*\)<[^>]*>/\2/;s^N/A^U^'
|
|
}
|
|
|
|
xpath_extract_attribute() {
|
|
# shellcheck disable=SC2039
|
|
local xpath="$1"
|
|
# shellcheck disable=SC2039
|
|
local node="$(xpath -q -n -e "${xpath}")" \
|
|
|| { echo "error extracting attribute at ${xpath}" >&2; false; }
|
|
echo "${node}" | sed 's/.*="\([^"]\+\)".*/\1/'
|
|
}
|
|
|
|
fetch() {
|
|
# shellcheck disable=SC2154
|
|
curl -u "${internode_api_login}:${internode_api_password}" -f ${CURL_ARGS} "$@" \
|
|
|| { echo "error fetching ${*} for user ${internode_api_login}" >&2; false; }
|
|
}
|
|
|
|
get_data() {
|
|
SERVICE_USERNAME='n/a'
|
|
SERVICE_QUOTA='n/a'
|
|
SERVICE_PLAN='n/a'
|
|
SERVICE_ROLLOVER='n/a'
|
|
IDEAL_USAGE=''
|
|
USAGE_CRITICAL=''
|
|
SERVICE_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/service" \
|
|
|| true)"
|
|
if [ -n "${SERVICE_XML}" ]; then
|
|
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$//')"
|
|
|
|
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)"
|
|
|
|
USAGE_CRITICAL="${SERVICE_QUOTA}"
|
|
fi
|
|
|
|
DAILY_TIMESTAMP=N
|
|
DAILY_USAGE=U
|
|
HISTORY_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/history" \
|
|
|| true)"
|
|
if [ -n "${HISTORY_XML}" ]; then
|
|
DAILY_USAGE="$(echo "${HISTORY_XML}" | xpath_extract "internode/api/usagelist/usage[last()-1]/traffic")"
|
|
DAILY_DATE="$(echo "${HISTORY_XML}" | xpath_extract_attribute "internode/api/usagelist/usage[last()-1]/@day")"
|
|
DAILY_TIMESTAMP="$(date -d "${DAILY_DATE} $(date +%H:%M:%S)" +%s \
|
|
|| echo N)"
|
|
fi
|
|
|
|
SERVICE_USAGE='U'
|
|
USAGE_XML="$(fetch "${internode_api_url}/${SERVICE_ID}/usage" \
|
|
|| true)"
|
|
if [ -n "${USAGE_XML}" ]; then
|
|
SERVICE_USAGE="$(echo "${USAGE_XML}" | xpath_extract "internode/api/traffic")"
|
|
|
|
|
|
fi
|
|
}
|
|
|
|
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 ${SERVICE_USAGE:-U}"
|
|
;;
|
|
.daily)
|
|
echo "daily_rate.value ${DAILY_TIMESTAMP}:${DAILY_USAGE:-U}"
|
|
;;
|
|
*)
|
|
echo "usage.value ${SERVICE_USAGE:-U}"
|
|
echo "ideal.value ${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
|
|
# Otherwise, get the first service in the list
|
|
API_XML="$(fetch "${internode_api_url}" \
|
|
|| true)"
|
|
if [ -z "${API_XML}" ]; then
|
|
echo "unable to determine service ID for user ${internode_api_login}" >&2
|
|
exit 1
|
|
fi
|
|
SERVICE_ID="$(echo "${API_XML}" | xpath_extract "internode/api/services/service")"
|
|
fi
|
|
|
|
get_data
|
|
|
|
main "${1:-}"
|