1213 lines
38 KiB
Text
1213 lines
38 KiB
Text
|
#!/usr/bin/perl
|
||
|
# -*- perl -*-
|
||
|
|
||
|
=encoding utf8
|
||
|
|
||
|
=head1 NAME
|
||
|
|
||
|
unifi_api - Munin plugin to display device and network information from the
|
||
|
Ubiquiti unifi API
|
||
|
|
||
|
=head1 APPLICABLE SYSTEMS
|
||
|
|
||
|
Unifi controllors with direct API access
|
||
|
|
||
|
Controller version 5+ required (tested with 5.8.x)
|
||
|
|
||
|
WebRTC is not supported at this time
|
||
|
|
||
|
=head1 CONFIGURATION
|
||
|
|
||
|
This script uses the multigraph functionality to generate many graphs. As such, there
|
||
|
are a significant amount of available configuration options
|
||
|
|
||
|
=head2 API Details
|
||
|
|
||
|
You will need to supply your API login details:
|
||
|
|
||
|
[unifi_api]
|
||
|
# User name to login to unifi controller API. Default is "ubnt". Ideally, this should
|
||
|
# point to a read-only account.
|
||
|
env.user Controller_Username
|
||
|
|
||
|
# Password to login to unifi controller API. Default is "ubnt"
|
||
|
env.pass Controller_Password
|
||
|
|
||
|
# URL of the API, with port if needed. No trailing slash.
|
||
|
# Default is https://localhost:8443
|
||
|
env.api_url https://unifi.fqdn.com:8443
|
||
|
|
||
|
# Verify SSL certificate name against host.
|
||
|
# Note: if using a default cloudkey certificate, this will fail unless you manually add it
|
||
|
# to the local keystore.
|
||
|
# Default is "yes"
|
||
|
env.ssl_verify_host no
|
||
|
|
||
|
# Verify Peer's SSL vertiicate.
|
||
|
# Note: if using a default cloudkey certificate, this will fail
|
||
|
# Default is "yes"
|
||
|
env.ssl_verify_peer no
|
||
|
|
||
|
# The human readable name of the unifi site - used for graph titles
|
||
|
env.name Site Name
|
||
|
|
||
|
# "Site" string - the internal unifi API site identifier.
|
||
|
# default is "default" - found when you connect to the web interface
|
||
|
# it's the term in the URL - /manage/site/site_string/dashboard
|
||
|
env.site site_string
|
||
|
|
||
|
|
||
|
=head2 Graph Categories / Host Management
|
||
|
|
||
|
Sometimes, you need more control over where the unifi graphs appear.
|
||
|
|
||
|
env.force_category 0
|
||
|
# By default, Use standard munin well know categories -
|
||
|
# system: cpu, mem, load, & uptime
|
||
|
# network: clients, transfer statistics.
|
||
|
#
|
||
|
|
||
|
To use this feature, set "force_category" to a text string (i.e. "unifi").
|
||
|
|
||
|
This is very helpful if your graphs are going to appear inside another host - for instance
|
||
|
if your munin graphs for that host are monitoring the host the controller is running on, and
|
||
|
the unifi API instance.
|
||
|
|
||
|
Sometimes however, you want to monitor either an offsite API, or a cloudkey which, at least by
|
||
|
default, does not run munin-node. In that case, you can actually create a "virtual" munin host to
|
||
|
display only these graphs (or any combination you like). This is documented in the main munin docs,
|
||
|
but in a nutshell:
|
||
|
|
||
|
In your munin-node plugin configuration: (Something like: /etc/munin/plugin-conf.d/munin-node)
|
||
|
|
||
|
[unifi_api]
|
||
|
host_name hostname.whatever.youlike
|
||
|
env.force_category unifi
|
||
|
|
||
|
And, in your munin master configuration: (Something like: /etc/munin/munin.conf)
|
||
|
|
||
|
[hostname.whatever.youlike]
|
||
|
address ip.of.munin.node
|
||
|
|
||
|
Make sure you do *not* set "use_node_name" on this new host. It may be necessary to define "host_name"
|
||
|
in your munin-node configuration as well, if you have not already (Likely, on a multi-homed host, this
|
||
|
has been done to keep munin-node from advertising itself as localhost)
|
||
|
|
||
|
More information:
|
||
|
|
||
|
* L<host_name|http://guide.munin-monitoring.org/en/latest/plugin/use.html>
|
||
|
|
||
|
|
||
|
=head2 Toggling of graphs / Individual options
|
||
|
|
||
|
You can turn off individual graphs. A few graphs have extra configuration
|
||
|
options.
|
||
|
|
||
|
By default, everything is enabled. Set to "no" to disable
|
||
|
|
||
|
[unifi_api]
|
||
|
# Show device CPU utilization
|
||
|
env.enable_device_cpu yes
|
||
|
|
||
|
# Show device memory usage
|
||
|
env.enable_device_mem yes
|
||
|
|
||
|
# Show device load average (switches and APs only)
|
||
|
env.enable_device_load yes
|
||
|
|
||
|
# Show device uptime
|
||
|
env.enable_device_uptime yes
|
||
|
|
||
|
# Show number of clients connected to each device
|
||
|
env.enable_clients_device yes
|
||
|
# Show detailed graphs for each device (per device graphs)
|
||
|
env.enable_detail_clients_device yes
|
||
|
|
||
|
# Show number of clients connected to each network type
|
||
|
env.enable_clients_type yes
|
||
|
# Show detailed graphs for each client type (per type graphs)
|
||
|
env.enable_detail_clients_type yes
|
||
|
# Show unauthorized / authorized client list
|
||
|
# if you are not using the guest portal, this is useless
|
||
|
env.show_authorized_clients_type yes
|
||
|
|
||
|
# Show transfer statistics on switch ports
|
||
|
env.enable_xfer_port yes
|
||
|
# Show detailed graphs per switch port
|
||
|
env.enable_detail_xfer_port yes
|
||
|
# Hide ports that have no link (When set to no, unplugged ports will transfer 0, not be undefined)
|
||
|
env.hide_empty_xfer_port yes
|
||
|
|
||
|
# Show transfer statistics per device
|
||
|
env.enable_xfer_device yes
|
||
|
# Show detailed graphs for each device
|
||
|
env.enable_detail_xfer_device yes
|
||
|
|
||
|
# Show transfer statistics per named network
|
||
|
env.enable_xfer_network yes
|
||
|
# Show detailed graphs for each named network
|
||
|
env.enable_detail_xfer_network yes
|
||
|
|
||
|
# Show transfer statistics per radio
|
||
|
env.enable_xfer_radio yes
|
||
|
# Show detailed graphs for each radio
|
||
|
env.enable_detail_xfer_radio yes
|
||
|
|
||
|
|
||
|
=head1 CAPABILITIES
|
||
|
|
||
|
This plugin supports L<DIRTYCONFIG|http://guide.munin-monitoring.org/en/latest/plugin/protocol-dirtyconfig.html>
|
||
|
|
||
|
=head1 DEPENDENCIES
|
||
|
|
||
|
This plugin requires munin-multiugraph.
|
||
|
|
||
|
=over
|
||
|
|
||
|
=item WWW::Curl::Easy
|
||
|
|
||
|
Perl extension interface for libcurl
|
||
|
|
||
|
=item JSON
|
||
|
|
||
|
JSON (JavaScript Object Notation) encoder/decoder
|
||
|
|
||
|
=back
|
||
|
|
||
|
=head1 PERFORMANCE CONCERNS
|
||
|
|
||
|
The main performance concern on this is the huge number of graphs that may be
|
||
|
generated. Using the cron version of munin-graph may hurt a lot.
|
||
|
|
||
|
A bit of a case study:
|
||
|
|
||
|
| My Site | UBNT Demo
|
||
|
---------------------------------------
|
||
|
Devices | 8 | 126
|
||
|
AP's | 4 | 118
|
||
|
24xSwitch | 1 | 5
|
||
|
8xSwitch | 2 | 2
|
||
|
Output Bytes | 64,262 | 431,434
|
||
|
Output Lines | 1,761 | 14,586
|
||
|
Output Graphs | 77 | 530
|
||
|
|
||
|
So, just note that the growth in the amount of graphed date can be extreme.
|
||
|
|
||
|
|
||
|
=head1 LICENSE
|
||
|
|
||
|
Copyright (C) 2018 J.T.Sage (jtsage@gmail.com)
|
||
|
|
||
|
This program 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, either version 3 of the License, or
|
||
|
any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program. If not, see L<http://www.gnu.org/licenses/>.
|
||
|
|
||
|
=head1 MAGIC MARKERS
|
||
|
|
||
|
#%# family=manual
|
||
|
#%# capabilities=
|
||
|
|
||
|
=cut
|
||
|
|
||
|
use warnings;
|
||
|
use strict;
|
||
|
use utf8;
|
||
|
use Munin::Plugin;
|
||
|
|
||
|
# Check dependencies
|
||
|
my @errorCode;
|
||
|
my $me = (split '/', $0)[-1];
|
||
|
|
||
|
if (! eval {require JSON; JSON->import(); 1; } ) {
|
||
|
push @errorCode, "JSON module not found";
|
||
|
}
|
||
|
if (! eval {require WWW::Curl::Easy; 1;} ) {
|
||
|
push @errorCode, "WWW::Curl::Easy module not found";
|
||
|
}
|
||
|
|
||
|
# Fail on not found dependencies
|
||
|
if ( @errorCode != 0 ) {
|
||
|
die "FATAL:$me: Perl dependencies not installed (", join(", " => @errorCode), ")\n";
|
||
|
}
|
||
|
|
||
|
# Multigraph cabability is required for this plugin
|
||
|
need_multigraph();
|
||
|
|
||
|
# Somewhat (in)sane defaults for host, pass, etc
|
||
|
my %APIconfig = (
|
||
|
'user' => env_default_text('user' , 'ubnt'),
|
||
|
'pass' => env_default_text('pass' , 'ubnt'),
|
||
|
'api_url' => env_default_text('api_url' , 'https://localhost:8443'),
|
||
|
'site' => env_default_text('site' , 'default'),
|
||
|
'ssl_verify_host' => env_default_text('ssl_verify_host', 'yes'),
|
||
|
'ssl_verify_peer' => env_default_text('ssl_verify_peer', 'yes'),
|
||
|
'name' => env_default_text('name' , 'Unnamed Site'),
|
||
|
);
|
||
|
|
||
|
# The big table of plugin options - see POD documentation for what these do.
|
||
|
my %PluginConfig = (
|
||
|
'enable_device_cpu' => env_default_bool_true('enable_device_cpu'),
|
||
|
'enable_device_mem' => env_default_bool_true('enable_device_mem'),
|
||
|
'enable_device_load' => env_default_bool_true('enable_device_load'),
|
||
|
'enable_device_uptime' => env_default_bool_true('enable_device_uptime'),
|
||
|
'enable_clients_device' => env_default_bool_true('enable_clients_device'),
|
||
|
'enable_clients_type' => env_default_bool_true('enable_clients_network'),
|
||
|
'enable_xfer_port' => env_default_bool_true('enable_xfer_port'),
|
||
|
'enable_xfer_device' => env_default_bool_true('enable_xfer_device'),
|
||
|
'enable_xfer_network' => env_default_bool_true('enable_xfer_network'),
|
||
|
'enable_xfer_radio' => env_default_bool_true('enable_xfer_radio'),
|
||
|
'enable_detail_xfer_port' => env_default_bool_true('enable_detail_xfer_port'),
|
||
|
'enable_detail_xfer_device' => env_default_bool_true('enable_detail_xfer_device'),
|
||
|
'enable_detail_xfer_network' => env_default_bool_true('enable_detail_xfer_network'),
|
||
|
'enable_detail_xfer_radio' => env_default_bool_true('enable_detail_xfer_radio'),
|
||
|
'enable_detail_clients_device' => env_default_bool_true('enable_detail_clients_device'),
|
||
|
'enable_detail_clients_type' => env_default_bool_true('enable_detail_clients_network'),
|
||
|
'hide_empty_xfer_port' => env_default_bool_true('hide_empty_xfer_port'),
|
||
|
'show_authorized_clients_type' => env_default_bool_true('show_authorized_clients_type'),
|
||
|
'force_category' => env_default_text('force_category', 0),
|
||
|
);
|
||
|
|
||
|
# Set up needed API endpoints
|
||
|
my %APIPoint = (
|
||
|
'login' => $APIconfig{"api_url"} . "/api/login",
|
||
|
'device' => $APIconfig{"api_url"} . "/api/s/" . $APIconfig{"site"} . "/stat/device",
|
||
|
'wlan' => $APIconfig{"api_url"} . "/api/s/" . $APIconfig{"site"} . "/rest/wlanconf",
|
||
|
'sta' => $APIconfig{"api_url"} . "/api/s/" . $APIconfig{"site"} . "/stat/sta",
|
||
|
);
|
||
|
|
||
|
my %APIResponse;
|
||
|
my %APIJsonResponse;
|
||
|
my %Data;
|
||
|
my $retcode;
|
||
|
|
||
|
# Init curl and JSON
|
||
|
my $curl = WWW::Curl::Easy->new() or die "FATAL:$me: WWW::Curl::Easy init failed!\n";
|
||
|
my $jsonOBJ = JSON->new() or die "FATAL:$me: JSON init failed!\n";
|
||
|
|
||
|
|
||
|
## Fetch the data from the API
|
||
|
|
||
|
# The rest is a way to use local files from the local disk. Undocumented and not really supported.
|
||
|
|
||
|
if ( !env_default_bool_true('USE_API') ) {
|
||
|
if (! eval {require File::Slurp; File::Slurp->import(); 1; } ) {
|
||
|
die "Local debug unavailable, File::Slurp CPAN module required\n";
|
||
|
}
|
||
|
foreach ( "./demo-test-files/device", "./demo-test-files/sta", "./demo-test-files/wlanconf" ) {
|
||
|
if ( ! -f $_ ) { die "File not found: $_\n"; }
|
||
|
}
|
||
|
$APIJsonResponse{'device'} = $jsonOBJ->allow_nonref->utf8->relaxed->decode(read_file('./demo-test-files/device'));
|
||
|
$APIJsonResponse{'sta'} = $jsonOBJ->allow_nonref->utf8->relaxed->decode(read_file('./demo-test-files/sta'));
|
||
|
$APIJsonResponse{'wlan'} = $jsonOBJ->allow_nonref->utf8->relaxed->decode(read_file('./demo-test-files/wlanconf'));
|
||
|
} else {
|
||
|
fetch_data();
|
||
|
}
|
||
|
|
||
|
|
||
|
## Process the data
|
||
|
|
||
|
make_data();
|
||
|
|
||
|
if ( defined($ARGV[0]) && $ARGV[0] eq "config" ) {
|
||
|
# Do the config step for each set of graphs
|
||
|
do_config_mem();
|
||
|
do_config_cpu();
|
||
|
do_config_load();
|
||
|
do_config_uptime();
|
||
|
do_config_xfer_by_device();
|
||
|
do_config_xfer_by_uplink();
|
||
|
do_config_xfer_by_port();
|
||
|
do_config_xfer_by_network();
|
||
|
do_config_xfer_by_radio();
|
||
|
do_config_clients_by_device();
|
||
|
do_config_clients_by_type();
|
||
|
|
||
|
# If dirtyconfig is not supported, or turned off, exit here. Otherwise, continue to fetch section
|
||
|
if ( !defined($ENV{'MUNIN_CAP_DIRTYCONFIG'}) || !$ENV{'MUNIN_CAP_DIRTYCONFIG'} ) { exit 0; }
|
||
|
}
|
||
|
|
||
|
# Do the fetch step for each set of graphs
|
||
|
do_values_cpu();
|
||
|
do_values_mem();
|
||
|
do_values_load();
|
||
|
do_values_uptime();
|
||
|
do_values_xfer_by_device();
|
||
|
do_values_xfer_by_uplink();
|
||
|
do_values_xfer_by_port();
|
||
|
do_values_xfer_by_network();
|
||
|
do_values_xfer_by_radio();
|
||
|
do_values_clients_by_device();
|
||
|
do_values_clients_by_type();
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#######################
|
||
|
# SUBROUTINES CONFIG #
|
||
|
#######################
|
||
|
|
||
|
sub do_config_clients_by_type {
|
||
|
# Provide client count by type - CONFIG
|
||
|
if ( !$PluginConfig{'enable_clients_type'} ) { return 0; }
|
||
|
|
||
|
graph_prologue(
|
||
|
'unifi_clients_per_network',
|
||
|
'Clients Connected / Network',
|
||
|
'-l 0 --base 1000',
|
||
|
'clients',
|
||
|
'network',
|
||
|
'Clients connected per type - manually summing these numbers may be meaningful, as clients are often of multiple types'
|
||
|
);
|
||
|
|
||
|
foreach ( @{$Data{'typesOrder'}} ) {
|
||
|
print $_ , ".label " , $Data{'types'}{$_}[0] , "\n";
|
||
|
}
|
||
|
|
||
|
if ( ! $PluginConfig{'enable_detail_clients_type'} ) { return 1; }
|
||
|
|
||
|
foreach ( @{$Data{'typesOrder'}} ) {
|
||
|
if ( $Data{'types'}{$_}[1] == 1 ) {
|
||
|
graph_prologue(
|
||
|
'unifi_clients_per_network.' . $_,
|
||
|
'Clients Connected : ' . $Data{'types'}{$_}[0],
|
||
|
'-l 0 --base 1000',
|
||
|
'clients',
|
||
|
'network',
|
||
|
'Clients connected via that are of type: ' . $Data{'types'}{$_}[0]
|
||
|
);
|
||
|
print "users.label Users\n";
|
||
|
print "guests.label Guests\n";
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_config_clients_by_device {
|
||
|
# Provide client count by device - CONFIG
|
||
|
if ( !$PluginConfig{'enable_clients_device'} ) { return 0; }
|
||
|
|
||
|
graph_prologue(
|
||
|
'unifi_clients_per_device',
|
||
|
'Clients Connected / Device',
|
||
|
'-l 0 --base 1000',
|
||
|
'clients',
|
||
|
'network',
|
||
|
'Clients connected to each unifi device'
|
||
|
);
|
||
|
|
||
|
foreach ( sort keys %{$Data{'device'}} ) {
|
||
|
print $_ , ".label " , $Data{'device'}{$_}->{'label'} , "\n";
|
||
|
}
|
||
|
|
||
|
if ( ! $PluginConfig{'enable_detail_clients_device'} ) { return 1; }
|
||
|
|
||
|
foreach ( sort keys %{$Data{'device'}} ) {
|
||
|
graph_prologue(
|
||
|
'unifi_clients_per_device.' . $_,
|
||
|
'Clients / Device : ' . $Data{'device'}{$_}->{'label'},
|
||
|
'-l 0 --base 1000',
|
||
|
'clients',
|
||
|
'network',
|
||
|
'Clients connected to the ' . $Data{'device'}{$_}->{'label'} . " unifi device"
|
||
|
);
|
||
|
print "users.label Users\n";
|
||
|
print "guests.label Guests\n";
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_config_xfer_by_radio {
|
||
|
# Provide transfer for radios - CONFIG
|
||
|
if ( !$PluginConfig{'enable_xfer_radio'} ) { return 0; }
|
||
|
|
||
|
graph_prologue(
|
||
|
'unifi_xfer_per_radio',
|
||
|
'Transfer / radio',
|
||
|
'--base 1000',
|
||
|
'Packets/${graph_period}',
|
||
|
'network',
|
||
|
'Number of packets transferred per individual radio band'
|
||
|
);
|
||
|
|
||
|
foreach my $thisDevice ( sort keys %{$Data{'device'}} ) {
|
||
|
if ( $Data{'device'}{$thisDevice}->{'type'} ne "uap" ) { next; }
|
||
|
|
||
|
foreach ( @{$Data{'device'}{$thisDevice}{'radio'}} ) {
|
||
|
print $thisDevice , "_" , $_->{"name"} , "_pack.label " , $_->{"label"} , "\n";
|
||
|
print $thisDevice , "_" , $_->{"name"} , "_pack.type DERIVE\n";
|
||
|
print $thisDevice , "_" , $_->{"name"} , "_pack.min 0\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ! $PluginConfig{'enable_detail_xfer_radio'} ) { return 1; }
|
||
|
|
||
|
foreach my $thisDevice ( sort keys %{$Data{'device'}} ) {
|
||
|
if ( $Data{'device'}{$thisDevice}->{'type'} ne "uap" ) { next; }
|
||
|
|
||
|
graph_prologue(
|
||
|
'unifi_xfer_per_radio.' . $thisDevice,
|
||
|
'Transfer / radio : ' . $Data{'device'}{$thisDevice}->{'name'},
|
||
|
'--base 1000',
|
||
|
'Packets/${graph_period}',
|
||
|
'network',
|
||
|
'Transfered Packets, Dropped / Retried Packets, and Error Packets for the WLAN device: ' . $Data{'device'}{$thisDevice}->{'name'}
|
||
|
);
|
||
|
|
||
|
foreach ( @{$Data{'device'}{$thisDevice}{'radio'}} ) {
|
||
|
print $_->{"name"} , "_pkt.label " , $_->{"type"} , " Packets\n";
|
||
|
print $_->{"name"} , "_pkt.type DERIVE\n";
|
||
|
print $_->{"name"} , "_pkt.min 0\n";
|
||
|
print $_->{"name"} , "_dret.label " , $_->{"type"} , " Dropped / Retries\n";
|
||
|
print $_->{"name"} , "_dret.type DERIVE\n";
|
||
|
print $_->{"name"} , "_dret.min 0\n";
|
||
|
print $_->{"name"} , "_err.label " , $_->{"type"} , " Errors\n";
|
||
|
print $_->{"name"} , "_err.type DERIVE\n";
|
||
|
print $_->{"name"} , "_err.min 0\n";
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_config_xfer_by_network {
|
||
|
# Provide transfer for named networks - CONFIG
|
||
|
if ( !$PluginConfig{'enable_xfer_network'} ) { return 0; }
|
||
|
|
||
|
graph_prologue(
|
||
|
'unifi_xfer_per_network',
|
||
|
'Transfer / named network',
|
||
|
'--base 1000',
|
||
|
'Bytes/${graph_period} rcvd (-) / trans (+)',
|
||
|
'network',
|
||
|
'Bytes sent and received per each named network'
|
||
|
);
|
||
|
|
||
|
foreach my $thisNet ( sort keys %{$Data{'networks'}} ) {
|
||
|
foreach ( "_rxbytes", "_txbytes" ) {
|
||
|
print $thisNet , $_ , ".label " , $Data{'networks'}{$thisNet}->{"label"} . "\n";
|
||
|
print $thisNet , $_ , ".type DERIVE\n";
|
||
|
print $thisNet , $_ , ".min 0\n";
|
||
|
}
|
||
|
print $thisNet , "_rxbytes.graph no\n";
|
||
|
print $thisNet , "_txbytes.negative " , $thisNet , "_rxbytes\n";
|
||
|
}
|
||
|
|
||
|
if ( ! $PluginConfig{'enable_detail_xfer_network'} ) { return 1; }
|
||
|
|
||
|
foreach my $thisNet ( sort keys %{$Data{'networks'}} ) {
|
||
|
graph_prologue(
|
||
|
'unifi_xfer_per_network.' . $thisNet,
|
||
|
'Transfer / named network : ' . $Data{'networks'}{$thisNet}->{'label'},
|
||
|
'--base 1000',
|
||
|
'Bytes/${graph_period} rcvd (-) / trans (+)',
|
||
|
'network',
|
||
|
'Bytes sent and received for the network named: ' . $Data{'networks'}{$thisNet}->{'label'}
|
||
|
);
|
||
|
foreach ( "rxbyte", "txbyte" ) {
|
||
|
print $_ , ".label Bytes\n";
|
||
|
print $_ , ".type DERIVE\n";
|
||
|
print $_ , ".min 0\n";
|
||
|
}
|
||
|
print "rxbyte.graph no\n";
|
||
|
print "txbyte.negative rxbyte\n";
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_config_xfer_by_port {
|
||
|
# Provide transfer for switch ports - CONFIG
|
||
|
if ( !$PluginConfig{'enable_xfer_port'} ) { return 0; }
|
||
|
|
||
|
foreach my $thisDevice ( sort keys %{$Data{'device'}} ) {
|
||
|
if ( $Data{'device'}{$thisDevice}->{'type'} ne "usw" ) { next; }
|
||
|
graph_prologue(
|
||
|
'unifi_xfer_per_port_' . $thisDevice,
|
||
|
'Transfer / port : ' . $Data{'device'}{$thisDevice}->{'label'},
|
||
|
'--base 1000',
|
||
|
'Bytes/${graph_period} rcvd (-) / trans (+)',
|
||
|
'network',
|
||
|
'Bytes sent and received per port on the switch named: ' . $Data{'device'}{$thisDevice}->{'label'}
|
||
|
);
|
||
|
foreach my $thisPort ( @{$Data{'device'}{$thisDevice}{'ports'}} ) {
|
||
|
foreach ( "_rxbytes", "_txbytes" ) {
|
||
|
print $thisDevice , "_" , $thisPort->{"name"} , $_ , ".label " , $thisPort->{"label"} . "\n";
|
||
|
print $thisDevice , "_" , $thisPort->{"name"} , $_ , ".type DERIVE\n";
|
||
|
print $thisDevice , "_" , $thisPort->{"name"} , $_ , ".min 0\n";
|
||
|
}
|
||
|
print $thisDevice , "_" , $thisPort->{"name"} , "_rxbytes.graph no\n";
|
||
|
print $thisDevice , "_" , $thisPort->{"name"} , "_txbytes.negative " , $thisDevice , "_" , $thisPort->{"name"} , "_rxbytes\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ! $PluginConfig{'enable_detail_xfer_port'} ) { return 1; }
|
||
|
|
||
|
# Extended graphs
|
||
|
foreach my $thisDevice ( sort keys %{$Data{'device'}} ) {
|
||
|
if ( $Data{'device'}{$thisDevice}->{'type'} ne "usw" ) { next; }
|
||
|
foreach my $thisPort ( @{$Data{'device'}{$thisDevice}{'ports'}} ) {
|
||
|
graph_prologue(
|
||
|
'unifi_xfer_per_port_' . $thisDevice . "." . $thisPort->{'name'},
|
||
|
'Transfer / port : ' . $Data{'device'}{$thisDevice}->{'label'} . " : " . $thisPort->{'label'},
|
||
|
'--base 1000',
|
||
|
'Bytes/${graph_period} rcvd (-) / trans (+)',
|
||
|
'network',
|
||
|
'Bytes sent and received on port "' . $thisPort->{'label'} . '" of the switch "' . $Data{'device'}{$thisDevice}->{'label'} . '"'
|
||
|
);
|
||
|
foreach ( "rxbyte", "txbyte" ) {
|
||
|
print $_ . ".label Bytes\n";
|
||
|
print $_ . ".type DERIVE\n";
|
||
|
print $_ . ".min 0\n";
|
||
|
}
|
||
|
print "rxbyte.graph no\n";
|
||
|
print "txbyte.negative rxbyte\n";
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_config_xfer_by_uplink {
|
||
|
# Provide transfer for unifi uplink - CONFIG
|
||
|
if ( !$PluginConfig{'enable_xfer_device'} ) { return 0; }
|
||
|
|
||
|
graph_prologue(
|
||
|
'unifi_xfer_by_uplink',
|
||
|
'Transfer on uplink : ' . $Data{'uplink'}{'devName'},
|
||
|
'--base 1000',
|
||
|
'Bytes/${graph_period} rcvd (-) / trans (+)',
|
||
|
'network',
|
||
|
'Bytes sent and received on the WAN port of the USG, and the speedtest result of the same port'
|
||
|
);
|
||
|
|
||
|
foreach ( "rx", "tx" ) {
|
||
|
print $_ , "_speed.label Speedtest\n";
|
||
|
print $_ , "_bytes.label Transferred\n";
|
||
|
print $_ , "_speed.type GAUGE\n";
|
||
|
print $_ , "_bytes.type DERIVE\n";
|
||
|
print $_ , "_speed.min 0\n";
|
||
|
print $_ , "_bytes.min 0\n";
|
||
|
}
|
||
|
|
||
|
print "rx_speed.graph no\n";
|
||
|
print "rx_bytes.graph no\n";
|
||
|
print "tx_speed.negative rx_speed\n";
|
||
|
print "tx_bytes.negative rx_bytes\n";
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_config_xfer_by_device {
|
||
|
# Provide transfer for each unifi device - CONFIG
|
||
|
if ( !$PluginConfig{'enable_xfer_device'} ) { return 0; }
|
||
|
|
||
|
graph_prologue(
|
||
|
'unifi_xfer_per_device',
|
||
|
'Transfer / device',
|
||
|
'--base 1000',
|
||
|
'Bytes/${graph_period} rcvd (-) / trans (+)',
|
||
|
'network',
|
||
|
'Bytes sent and received per unifi device'
|
||
|
);
|
||
|
|
||
|
foreach my $thisDevice ( sort keys %{$Data{'device'}} ) {
|
||
|
foreach ( "_rxbytes", "_txbytes" ) {
|
||
|
print $thisDevice , $_ , ".label " , $Data{'device'}{$thisDevice}->{'label'} , "\n";
|
||
|
print $thisDevice , $_ , ".type DERIVE\n";
|
||
|
print $thisDevice , $_ , ".min 0\n";
|
||
|
}
|
||
|
print $thisDevice , "_rxbytes.graph no\n";
|
||
|
print $thisDevice , "_txbytes.negative " , $thisDevice , "_rxbytes\n";
|
||
|
}
|
||
|
|
||
|
if ( $PluginConfig{'enable_detail_xfer_device'} ) {
|
||
|
foreach my $thisDevice ( sort keys %{$Data{'device'}} ) {
|
||
|
graph_prologue(
|
||
|
'unifi_xfer_per_device.' . $thisDevice,
|
||
|
'Transfer / device : ' . $Data{'device'}{$thisDevice}->{'label'},
|
||
|
'--base 1000',
|
||
|
'Bytes/${graph_period} rcvd (-) / trans (+)',
|
||
|
'network',
|
||
|
'Bytes sent and received on the unifi device named: ' . $Data{'device'}{$thisDevice}->{'label'}
|
||
|
);
|
||
|
foreach ( "rxbyte", "txbyte" ) {
|
||
|
print $_ , ".label Bytes\n";
|
||
|
print $_ , ".type DERIVE\n";
|
||
|
print $_ , ".min 0\n";
|
||
|
}
|
||
|
print "rxbyte.graph no\n";
|
||
|
print "txbyte.negative rxbyte\n";
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_config_uptime {
|
||
|
# Provide device uptime for each unifi device - CONFIG
|
||
|
if ( !$PluginConfig{'enable_device_uptime'} ) { return 0; }
|
||
|
graph_prologue(
|
||
|
'unifi_device_uptime',
|
||
|
'Uptime',
|
||
|
'--base 1000 -r --lower-limit 0',
|
||
|
'days',
|
||
|
'system',
|
||
|
'Uptime in days for each unifi device'
|
||
|
);
|
||
|
|
||
|
foreach ( sort keys %{$Data{'device'}} ) {
|
||
|
print $_ , ".label " , $Data{'device'}{$_}->{"name"} , "\n";
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_config_cpu {
|
||
|
# Provide device CPU usage for each unifi device - CONFIG
|
||
|
if ( !$PluginConfig{'enable_device_cpu'} ) { return 0; }
|
||
|
graph_prologue(
|
||
|
'unifi_device_cpu',
|
||
|
'CPU Usage',
|
||
|
'--base 1000 -r --lower-limit 0 --upper-limit 100',
|
||
|
'%',
|
||
|
'system',
|
||
|
'CPU usage as a percentage for each unifi device'
|
||
|
);
|
||
|
|
||
|
foreach ( sort keys %{$Data{'device'}} ) {
|
||
|
print $_ , ".label " , $Data{'device'}{$_}->{"name"} , "\n";
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_config_load {
|
||
|
# Provide device load average for each unifi device - CONFIG
|
||
|
if ( !$PluginConfig{'enable_device_load'} ) { return 0; }
|
||
|
graph_prologue(
|
||
|
'unifi_device_load',
|
||
|
'Load Average',
|
||
|
'-l 0 --base 1000',
|
||
|
'load',
|
||
|
'system',
|
||
|
'Load average for each unifi Access Point or Switch'
|
||
|
);
|
||
|
|
||
|
foreach ( sort keys %{$Data{'device'}} ) {
|
||
|
if ( $Data{'device'}{$_}->{'type'} eq 'ugw' ) { next; }
|
||
|
print $_ , ".label " , $Data{'device'}{$_}->{"name"} , "\n";
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_config_mem {
|
||
|
# Provide device memory usage for each unifi device - CONFIG
|
||
|
if ( !$PluginConfig{'enable_device_mem'} ) { return 0; }
|
||
|
graph_prologue(
|
||
|
'unifi_device_mem',
|
||
|
'Memory Usage',
|
||
|
'--base 1000 -r --lower-limit 0 --upper-limit 100',
|
||
|
'%',
|
||
|
'system',
|
||
|
'Memory usage as a percentage for each unifi device'
|
||
|
);
|
||
|
|
||
|
foreach ( sort keys %{$Data{'device'}} ) {
|
||
|
print $_ , ".label " , $Data{'device'}{$_}->{"name"} , "\n";
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#########################
|
||
|
# SUBROUTINES VALUES #
|
||
|
#########################
|
||
|
|
||
|
sub do_values_clients_by_type {
|
||
|
# Provide client count by type - VALUES
|
||
|
if ( !$PluginConfig{'enable_clients_type'} ) { return 0; }
|
||
|
|
||
|
print "multigraph unifi_clients_per_network\n";
|
||
|
|
||
|
foreach ( @{$Data{'typesOrder'}} ) {
|
||
|
print $_ , ".value " , ( $Data{'types'}{$_}[2] + $Data{'types'}{$_}[3] ) , "\n";
|
||
|
}
|
||
|
|
||
|
if ( ! $PluginConfig{'enable_detail_clients_type'} ) { return 1; }
|
||
|
|
||
|
foreach ( @{$Data{'typesOrder'}} ) {
|
||
|
if ( $Data{'types'}{$_}[1] == 1 ) {
|
||
|
print "multigraph unifi_clients_per_network.$_\n";
|
||
|
print "users.value " , $Data{'types'}{$_}[2] , "\n";
|
||
|
print "guests.value " , $Data{'types'}{$_}[3] , "\n";
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_values_clients_by_device {
|
||
|
# Provide client count by device - VALUES
|
||
|
if ( !$PluginConfig{'enable_clients_device'} ) { return 0; }
|
||
|
|
||
|
print "multigraph unifi_clients_per_device\n";
|
||
|
|
||
|
foreach ( sort keys %{$Data{'device'}} ) {
|
||
|
print $_ , ".value " , $Data{'device'}{$_}->{'clients'} , "\n";
|
||
|
}
|
||
|
|
||
|
if ( ! $PluginConfig{'enable_detail_clients_device'} ) { return 1; }
|
||
|
|
||
|
foreach ( sort keys %{$Data{'device'}} ) {
|
||
|
print "multigraph unifi_clients_per_device.$_\n";
|
||
|
print "users.value " , $Data{'device'}{$_}->{'users'} , "\n";
|
||
|
print "guests.value " , $Data{'device'}{$_}->{'guests'} , "\n";
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_values_xfer_by_radio {
|
||
|
# Provide transfer for radios - VALUES
|
||
|
if ( !$PluginConfig{'enable_xfer_radio'} ) { return 0; }
|
||
|
|
||
|
print "multigraph unifi_xfer_per_radio\n";
|
||
|
|
||
|
foreach my $thisDevice ( sort keys %{$Data{'device'}} ) {
|
||
|
if ( $Data{'device'}{$thisDevice}->{'type'} ne "uap" ) { next; }
|
||
|
|
||
|
foreach ( @{$Data{'device'}{$thisDevice}{'radio'}} ) {
|
||
|
print $thisDevice , "_" , $_->{"name"} , "_pack.value " , ($_->{"pckt"} // 0), "\n";;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ! $PluginConfig{'enable_detail_xfer_radio'} ) { return 1; }
|
||
|
|
||
|
foreach my $thisDevice ( sort keys %{$Data{'device'}} ) {
|
||
|
if ( $Data{'device'}{$thisDevice}->{'type'} ne "uap" ) { next; }
|
||
|
|
||
|
print "multigraph unifi_xfer_per_radio.$thisDevice\n";
|
||
|
|
||
|
foreach ( @{$Data{'device'}{$thisDevice}{'radio'}} ) {
|
||
|
print $_->{"name"} , "_pkt.value " , ($_->{"pckt"} // 0) , "\n";
|
||
|
print $_->{"name"} , "_dret.value " , ($_->{"dret"} // 0) , "\n";
|
||
|
print $_->{"name"} , "_err.value " , ($_->{"err"} // 0) , "\n";
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_values_xfer_by_network {
|
||
|
# Provide transfer for named networks - CONFIG
|
||
|
if ( !$PluginConfig{'enable_xfer_network'} ) { return 0; }
|
||
|
|
||
|
print "multigraph unifi_xfer_per_network\n";
|
||
|
|
||
|
foreach my $thisNet ( sort keys %{$Data{'networks'}} ) {
|
||
|
print $thisNet , "_rxbytes.value " , ($Data{'networks'}{$thisNet}->{"rx"} // 0) , "\n";
|
||
|
print $thisNet , "_txbytes.value " , ($Data{'networks'}{$thisNet}->{"tx"} // 0) , "\n";
|
||
|
}
|
||
|
|
||
|
if ( ! $PluginConfig{'enable_detail_xfer_network'} ) { return 1; }
|
||
|
|
||
|
foreach my $thisNet ( sort keys %{$Data{'networks'}} ) {
|
||
|
print "multigraph unifi_xfer_per_network.$thisNet\n";
|
||
|
print "rxbyte.value " , ($Data{'networks'}{$thisNet}->{"rx"} // 0) , "\n";
|
||
|
print "txbyte.value " , ($Data{'networks'}{$thisNet}->{"tx"} // 0) , "\n";
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_values_xfer_by_port {
|
||
|
# Provide transfer for switch ports - VALUES
|
||
|
if ( !$PluginConfig{'enable_xfer_port'} ) { return 0; }
|
||
|
|
||
|
foreach my $thisDevice ( sort keys %{$Data{'device'}} ) {
|
||
|
if ( $Data{'device'}{$thisDevice}->{'type'} ne "usw" ) { next; }
|
||
|
print "multigraph unifi_xfer_per_port_$thisDevice\n";
|
||
|
|
||
|
foreach ( @{$Data{'device'}{$thisDevice}{'ports'}} ) {
|
||
|
print $thisDevice , "_" , $_->{"name"} , "_rxbytes.value " , $_->{"rx"} , "\n";
|
||
|
print $thisDevice , "_" , $_->{"name"} , "_txbytes.value " , $_->{"tx"} , "\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ! $PluginConfig{'enable_detail_xfer_port'} ) { return 1; }
|
||
|
|
||
|
# Extended graphs
|
||
|
foreach my $thisDevice ( sort keys %{$Data{'device'}} ) {
|
||
|
if ( $Data{'device'}{$thisDevice}->{'type'} ne "usw" ) { next; }
|
||
|
foreach ( @{$Data{'device'}{$thisDevice}{'ports'}} ) {
|
||
|
print 'multigraph unifi_xfer_per_port_' . $thisDevice . "." . $_->{'name'} . "\n";
|
||
|
print "rxbyte.value " , $_->{"rx"} , "\n";
|
||
|
print "txbyte.value " , $_->{"tx"} , "\n";
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_values_xfer_by_uplink {
|
||
|
# Provide transfer for unifi uplink - CONFIG
|
||
|
if ( !$PluginConfig{'enable_xfer_device'} ) { return 0; }
|
||
|
|
||
|
print "multigraph unifi_xfer_by_uplink\n";
|
||
|
print "rx_speed.value " . $Data{'uplink'}{"rx_speed"} . "\n";
|
||
|
print "tx_speed.value " . $Data{'uplink'}{"tx_speed"} . "\n";
|
||
|
print "rx_bytes.value " . $Data{'uplink'}{"rx_bytes"} . "\n";
|
||
|
print "tx_bytes.value " . $Data{'uplink'}{"tx_bytes"} . "\n";
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_values_xfer_by_device {
|
||
|
# Provide transfer for each unifi device - CONFIG
|
||
|
if ( !$PluginConfig{'enable_xfer_device'} ) { return 0; }
|
||
|
|
||
|
print "multigraph unifi_xfer_per_device\n";
|
||
|
foreach ( sort keys %{$Data{'device'}} ) {
|
||
|
print $_ . "_rxbytes.value " . $Data{'device'}{$_}->{"rx"} , "\n";
|
||
|
print $_ . "_txbytes.value " . $Data{'device'}{$_}->{"tx"} , "\n";
|
||
|
}
|
||
|
if ( $PluginConfig{'enable_detail_xfer_device'} ) {
|
||
|
foreach ( sort keys %{$Data{'device'}} ) {
|
||
|
print "multigraph unifi_xfer_per_device." , $_ , "\n";
|
||
|
print "rxbyte.value " , $Data{'device'}{$_}->{"rx"} , "\n";
|
||
|
print "txbyte.value " , $Data{'device'}{$_}->{"tx"} , "\n";
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_values_cpu {
|
||
|
# Provide device CPU usage for each unifi device - VALUES
|
||
|
if ( !$PluginConfig{'enable_device_cpu'} ) { return 0; }
|
||
|
|
||
|
print "multigraph unifi_device_cpu\n";
|
||
|
foreach ( sort keys %{$Data{'device'}} ) {
|
||
|
print $_ , ".value " , ( $Data{'device'}{$_}->{"cpu"} ) , "\n";
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_values_mem {
|
||
|
# Provide device memory usage for each unifi device - VALUES
|
||
|
if ( !$PluginConfig{'enable_device_mem'} ) { return 0; }
|
||
|
|
||
|
print "multigraph unifi_device_mem\n";
|
||
|
foreach ( sort keys %{$Data{'device'}} ) {
|
||
|
print $_ , ".value " , ( $Data{'device'}{$_}->{"mem"} ) , "\n";
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_values_load {
|
||
|
# Provide device load average for each unifi device - VALUES
|
||
|
if ( !$PluginConfig{'enable_device_load'} ) { return 0; }
|
||
|
|
||
|
print "multigraph unifi_device_load\n";
|
||
|
foreach ( sort keys %{$Data{'device'}} ) {
|
||
|
if ( $Data{'device'}{$_}->{'type'} eq 'ugw' ) { next; }
|
||
|
print $_ , ".value " , ( $Data{'device'}{$_}->{"load"} ) , "\n";
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
sub do_values_uptime {
|
||
|
# Provide device uptime for each unifi device - VALUES
|
||
|
if ( !$PluginConfig{'enable_device_uptime'} ) { return 0; }
|
||
|
|
||
|
print "multigraph unifi_device_uptime\n";
|
||
|
foreach ( sort keys %{$Data{'device'}} ) {
|
||
|
print $_ , ".value " , ( $Data{'device'}{$_}->{"uptime"} / 86400 ) , "\n";
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#########################
|
||
|
# SUBROUTINES GENERAL #
|
||
|
#########################
|
||
|
|
||
|
sub graph_prologue {
|
||
|
# Generate graph prologues - slightly less copy-pasta, and less chance for things to go wrong
|
||
|
my ( $id, $title, $args, $vlabel, $category, $info ) = (@_);
|
||
|
|
||
|
print "multigraph $id\n";
|
||
|
print 'graph_title ' , $title , ' : ' , $APIconfig{"name"} , "\n";
|
||
|
print "graph_args $args\n";
|
||
|
print "graph_vlabel $vlabel\n";
|
||
|
if ( $PluginConfig{'force_category'} ) {
|
||
|
print "graph_category ", $PluginConfig{'force_category'}, "\n";
|
||
|
} else {
|
||
|
print "graph_category $category\n";
|
||
|
}
|
||
|
if ( $info ) {
|
||
|
print 'graph_info For the unifi site named "' , $APIconfig{"name"} , "\", $info\n";
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
# Collate all collected data into something we can use.
|
||
|
sub make_data {
|
||
|
foreach my $thisDevice ( @{$APIJsonResponse{'device'}->{'data'}} ) {
|
||
|
# Grab everything we care to know about each device.
|
||
|
$Data{'device'}{ make_safe($thisDevice->{'name'}, $thisDevice->{'serial'}) } = {
|
||
|
'label' => $thisDevice->{'name'},
|
||
|
'users' => ($thisDevice->{'user-num_sta'} || 0),
|
||
|
'guests' => ($thisDevice->{'guest-num_sta'} || 0),
|
||
|
'clients' => ($thisDevice->{'user-num_sta'} + $thisDevice->{'guest-num_sta'} || 0),
|
||
|
'tx' => $thisDevice->{'rx_bytes'},
|
||
|
'rx' => $thisDevice->{'tx_bytes'},
|
||
|
'name' => $thisDevice->{'name'},
|
||
|
'uptime' => $thisDevice->{'uptime'},
|
||
|
'cpu' => $thisDevice->{'system-stats'}->{'cpu'},
|
||
|
'mem' => $thisDevice->{'system-stats'}->{'mem'},
|
||
|
'load' => ( $thisDevice->{'type'} eq 'ugw' ? 'U' : $thisDevice->{'sys_stats'}->{'loadavg_1'} ),
|
||
|
'type' => $thisDevice->{'type'}
|
||
|
};
|
||
|
|
||
|
if ( $thisDevice->{'type'} eq 'ugw' ) { # Handle firewall specially, record uplink and networks
|
||
|
foreach my $thisNet ( @{$thisDevice->{'network_table'}} ) {
|
||
|
$Data{'networks'}{ make_safe($thisNet->{'name'}, $thisNet->{'_id'} ) } = {
|
||
|
'label' => $thisNet->{'name'},
|
||
|
'tx' => $thisNet->{'tx_bytes'},
|
||
|
'rx' => $thisNet->{'rx_bytes'}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$Data{'uplink'}{'devName'} = $thisDevice->{'name'};
|
||
|
$Data{'uplink'}{'rx_speed'} = $thisDevice->{'speedtest-status'}->{'xput_download'} * 1000000;
|
||
|
$Data{'uplink'}{'tx_speed'} = $thisDevice->{'speedtest-status'}->{'xput_upload'} * 1000000;
|
||
|
|
||
|
foreach ( @{$thisDevice->{"port_table"}} ) {
|
||
|
if ( $_->{name} eq "wan" ) {
|
||
|
$Data{'uplink'}{'rx_bytes'} = $_->{'rx_bytes'};
|
||
|
$Data{'uplink'}{'tx_bytes'} = $_->{'tx_bytes'};
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( $thisDevice->{'type'} eq 'usw' ) { # Handle swiches specially - record port stats
|
||
|
my @port_list;
|
||
|
|
||
|
foreach my $port ( @{$thisDevice->{'port_table'}} ) {
|
||
|
if ( !$PluginConfig{'hide_empty_xfer_port'} || $port->{'up'} ) {
|
||
|
push @port_list , {
|
||
|
'name' => 'port_' . zPad($port->{'port_idx'}),
|
||
|
'label' => zPad($port->{'port_idx'}) . '-' . $port->{'name'},
|
||
|
'rx' => $port->{'rx_bytes'},
|
||
|
'tx' => $port->{'tx_bytes'}
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
$Data{'device'}{ make_safe($thisDevice->{'name'}, $thisDevice->{'serial'}) }{'ports'} = \@port_list;
|
||
|
}
|
||
|
|
||
|
if ( $thisDevice->{'type'} eq 'uap' ) { # Handle APS specially - record radio stats
|
||
|
my @theseRadios;
|
||
|
|
||
|
foreach my $thisRadio ( @{$thisDevice->{'radio_table_stats'}} ) {
|
||
|
my $name = make_safe( $thisRadio->{'name'}, "" );
|
||
|
my $label = ( $thisRadio->{'channel'} < 12 ) ? '2.4Ghz' : '5Ghz';
|
||
|
|
||
|
$_ = $thisDevice->{'stat'}->{'ap'};
|
||
|
|
||
|
push @theseRadios, {
|
||
|
'name' => $name,
|
||
|
'label' => $label . '-' . $thisDevice->{'name'},
|
||
|
'pckt' => ($_->{$name . '-rx_packets'} // 0) + ($_->{$name . '-tx_packets'} // 0),
|
||
|
'dret' => ($_->{$name . '-rx_dropped'} // 0) + ($_->{$name . '-tx_retries'} // 0) + ($_->{$name . '-tx_dropped'} // 0),
|
||
|
'err' => ($_->{$name . '-rx_errors'} // 0) + ($_->{$name . '-tx_errors'} // 0),
|
||
|
'type' => $label
|
||
|
};
|
||
|
}
|
||
|
$Data{'device'}{ make_safe($thisDevice->{'name'}, $thisDevice->{'serial'}) }{'radio'} = \@theseRadios;
|
||
|
}
|
||
|
} # END PROCESSING OF DEVICE DATA
|
||
|
|
||
|
|
||
|
# PROCESS NETWORK TYPE DATA
|
||
|
|
||
|
# -> UNLESS, type graph is disabled.
|
||
|
#
|
||
|
# WHY: if the client list is large (huge. 10,000+), this is CPU intensive
|
||
|
if ( !$PluginConfig{'enable_clients_type'} ) { return 1; }
|
||
|
|
||
|
$Data{'types'} = {
|
||
|
"wired" => ["Wired Connection", 1, 0, 0],
|
||
|
"wifi" => ["Wireless Connection", 1, 0, 0],
|
||
|
"tuser" => ["Total Users", 0, 0, 0],
|
||
|
"tguest" => ["Total Guests", 0, 0, 0],
|
||
|
"authed" => ["Authorized Guests", 0, 0, 0],
|
||
|
"unauth" => ["Unauthorized Guests", 0, 0, 0],
|
||
|
};
|
||
|
|
||
|
$Data{'typesOrder'} = ( $PluginConfig{'show_authorized_clients_type'} ) ?
|
||
|
[ "wired", "wifi", "tuser", "tguest", "authed", "unauth"] :
|
||
|
[ "wired", "wifi", "tuser", "tguest" ];
|
||
|
|
||
|
|
||
|
my @wlans;
|
||
|
|
||
|
foreach my $thisNet ( @{$APIJsonResponse{'wlan'}->{'data'}} ) {
|
||
|
$Data{'types'}{ make_safe($thisNet->{'name'}, "") } = [ $thisNet->{'name'}, 1, 0, 0 ];
|
||
|
push @wlans, make_safe($thisNet->{'name'}, "");
|
||
|
}
|
||
|
|
||
|
foreach ( sort @wlans ) {
|
||
|
push @{$Data{'typesOrder'}}, $_;
|
||
|
}
|
||
|
|
||
|
foreach my $client ( @{$APIJsonResponse{'sta'}->{'data'}} ) {
|
||
|
if ( $client->{"is_wired"} ) {
|
||
|
if ( $client->{"is_guest"} ) {
|
||
|
$Data{'types'}->{'wired'}[3]++;
|
||
|
$Data{'types'}->{'guest'}[3]++;
|
||
|
} else {
|
||
|
$Data{'types'}->{'wired'}[2]++;
|
||
|
$Data{'types'}->{'user'}[2]++;
|
||
|
}
|
||
|
} else {
|
||
|
if ( $client->{"is_guest"} ) {
|
||
|
$Data{'types'}->{make_safe($client->{"essid"}, "")}[3]++;
|
||
|
$Data{'types'}->{'wifi'}[3]++;
|
||
|
$Data{'types'}->{'guest'}[3]++;
|
||
|
if ( $client->{"authorized"} ) {
|
||
|
$Data{'types'}->{'authed'}[3]++;
|
||
|
} else {
|
||
|
$Data{'types'}->{'unauth'}[3]++;
|
||
|
}
|
||
|
} else {
|
||
|
$Data{'types'}->{make_safe($client->{"essid"}, "")}[2]++;
|
||
|
$Data{'types'}->{'wifi'}[2]++;
|
||
|
$Data{'types'}->{'user'}[2]++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
sub fetch_data {
|
||
|
# Set up curl, and login to API
|
||
|
$curl->setopt($curl->CURLOPT_POST,1);
|
||
|
$curl->setopt($curl->CURLOPT_COOKIEFILE,""); # Session only cookie
|
||
|
$curl->setopt($curl->CURLOPT_SSL_VERIFYPEER, (( $APIconfig{"ssl_verify_peer"} =~ m/no/i ) ? 0 : 1) );
|
||
|
$curl->setopt($curl->CURLOPT_SSL_VERIFYHOST, (( $APIconfig{"ssl_verify_host"} =~ m/no/i ) ? 0 : 2) );
|
||
|
$curl->setopt($curl->CURL_SSLVERSION_TLSv1, 1);
|
||
|
$curl->setopt($curl->CURLOPT_URL, $APIPoint{'login'});
|
||
|
$curl->setopt($curl->CURLOPT_POSTFIELDS, q[{"username":"] . $APIconfig{"user"} . q[", "password":"] . $APIconfig{"pass"} . q["}] );
|
||
|
$curl->setopt($curl->CURLOPT_WRITEDATA, \$APIResponse{'login'});
|
||
|
$retcode = $curl->perform;
|
||
|
|
||
|
if ( $retcode != 0 ) {
|
||
|
die "FATAL:$me: Unable to connect to API: " . $curl->strerror($retcode) . " " . $curl->errbuf . "\n";
|
||
|
}
|
||
|
|
||
|
$APIJsonResponse{'login'} = $jsonOBJ->allow_nonref->utf8->relaxed->decode($APIResponse{'login'});
|
||
|
|
||
|
if ( $APIJsonResponse{'login'}->{'meta'}->{'rc'} ne 'ok' ) {
|
||
|
die "FATAL:$me: Unable to login to API - it said: " , $APIJsonResponse{'login'}->{'meta'}->{'msg'} , "\n";
|
||
|
}
|
||
|
|
||
|
# Change method to GET
|
||
|
$curl->setopt($curl->CURLOPT_HTTPGET,1);
|
||
|
|
||
|
|
||
|
# Get some API data.
|
||
|
|
||
|
# Device data
|
||
|
$curl->setopt($curl->CURLOPT_WRITEDATA, \$APIResponse{'device'});
|
||
|
$curl->setopt($curl->CURLOPT_URL, $APIPoint{'device'});
|
||
|
$retcode = $curl->perform;
|
||
|
|
||
|
if ( $retcode != 0 ) {
|
||
|
die "FATAL:$me: Unable to connect to API: " . $curl->strerror($retcode) . " " . $curl->errbuf . "\n";
|
||
|
}
|
||
|
|
||
|
$APIJsonResponse{'device'} = $jsonOBJ->allow_nonref->utf8->relaxed->decode($APIResponse{'device'});
|
||
|
|
||
|
if ( $APIJsonResponse{'device'}->{'meta'}->{'rc'} ne 'ok' ) {
|
||
|
die "FATAL:$me: Unable get device data from API - it said: " , $APIJsonResponse{'device'}->{'meta'}->{'msg'} , "\n";
|
||
|
}
|
||
|
|
||
|
# STA (client) data
|
||
|
$curl->setopt($curl->CURLOPT_WRITEDATA, \$APIResponse{'sta'});
|
||
|
$curl->setopt($curl->CURLOPT_URL, $APIPoint{'sta'});
|
||
|
$retcode = $curl->perform;
|
||
|
|
||
|
if ( $retcode != 0 ) {
|
||
|
die "FATAL:$me: Unable to connect to API: " . $curl->strerror($retcode) . " " . $curl->errbuf . "\n";
|
||
|
}
|
||
|
|
||
|
$APIJsonResponse{'sta'} = $jsonOBJ->allow_nonref->utf8->relaxed->decode($APIResponse{'sta'});
|
||
|
|
||
|
if ( $APIJsonResponse{'sta'}->{'meta'}->{'rc'} ne 'ok' ) {
|
||
|
die "FATAL:$me: Unable get sta data from API - it said: " , $APIJsonResponse{'sta'}->{'meta'}->{'msg'} , "\n";
|
||
|
}
|
||
|
|
||
|
# WLAN data
|
||
|
$curl->setopt($curl->CURLOPT_WRITEDATA, \$APIResponse{'wlan'});
|
||
|
$curl->setopt($curl->CURLOPT_URL, $APIPoint{'wlan'});
|
||
|
$retcode = $curl->perform;
|
||
|
|
||
|
if ( $retcode != 0 ) {
|
||
|
die "FATAL:$me: Unable to connect to API: " . $curl->strerror($retcode) . " " . $curl->errbuf . "\n";
|
||
|
}
|
||
|
|
||
|
$APIJsonResponse{'wlan'} = $jsonOBJ->allow_nonref->utf8->relaxed->decode($APIResponse{'wlan'});
|
||
|
|
||
|
if ( $APIJsonResponse{'wlan'}->{'meta'}->{'rc'} ne 'ok' ) {
|
||
|
die "FATAL:$me: Unable get wlan data from API - it said: " , $APIJsonResponse{'wlan'}->{'meta'}->{'msg'} , "\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Make field names safe, and lowercase.
|
||
|
#
|
||
|
# Typically, $extraName should be the MAC address of the unique ID identifier as the unifi
|
||
|
# controller software does not enforce that device names or network names are unique.
|
||
|
sub make_safe {
|
||
|
my ( $name, $extraName ) = ( @_ );
|
||
|
if ( $extraName ne "" ) {
|
||
|
return clean_fieldname(lc($name) . "_" . $extraName);
|
||
|
} else {
|
||
|
return lc(clean_fieldname($name));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Get a default from an environmental variable - return text
|
||
|
#
|
||
|
# env_default(<variable name>, <default>)
|
||
|
sub env_default_text {
|
||
|
my ( $env_var, $default ) = (@_);
|
||
|
return ( ( defined $ENV{$env_var} ) ? $ENV{$env_var} : $default ),
|
||
|
}
|
||
|
|
||
|
# Get a default from an environmental variable - boolean true
|
||
|
#
|
||
|
# env_default_bool_true (<variable name>, <default>)
|
||
|
sub env_default_bool_true {
|
||
|
my $env_var = $_[0];
|
||
|
return ( ( defined $ENV{$env_var} && $ENV{$env_var} =~ m/no/i ) ? 0 : 1 );
|
||
|
}
|
||
|
|
||
|
# Quick 2 digit zero pad
|
||
|
sub zPad { return sprintf("%02d", $_[0]); }
|