Created
July 21, 2017 07:05
-
-
Save ollyg/2c93e9e0780ec800561b92e2e3111596 to your computer and use it in GitHub Desktop.
Collection script for UniFi AP controller
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env perl | |
use strict; | |
use warnings; | |
our $home; | |
BEGIN { | |
use FindBin; | |
FindBin::again(); | |
$home = ($ENV{NETDISCO_HOME} || $ENV{HOME}); | |
# try to find a localenv if one isn't already in place. | |
if (!exists $ENV{PERL_LOCAL_LIB_ROOT}) { | |
use File::Spec; | |
my $localenv = File::Spec->catfile($FindBin::RealBin, 'localenv'); | |
exec($localenv, $0, @ARGV) if -f $localenv; | |
$localenv = File::Spec->catfile($home, 'perl5', 'bin', 'localenv'); | |
exec($localenv, $0, @ARGV) if -f $localenv; | |
die "Sorry, can't find libs required for App::Netdisco.\n" | |
if !exists $ENV{PERLBREW_PERL}; | |
} | |
} | |
BEGIN { | |
use Path::Class; | |
# stuff useful locations into @INC and $PATH | |
unshift @INC, | |
dir($FindBin::RealBin)->parent->subdir('lib')->stringify, | |
dir($FindBin::RealBin, 'lib')->stringify; | |
use Config; | |
$ENV{PATH} = $FindBin::RealBin . $Config{path_sep} . $ENV{PATH}; | |
} | |
use App::Netdisco; | |
use Dancer ':script'; | |
use Dancer::Plugin::DBIC 'schema'; | |
use App::Netdisco::Util::Permission ':all'; | |
use Time::HiRes 'gettimeofday'; | |
use WWW::Mechanize; | |
use JSON qw//; | |
use App::Netdisco::Core::Macsuck 'store_node'; | |
# config example for deployment.yml | |
#unifiapi: | |
# controller: 192.168.1.1 | |
# username: 'ubnt' | |
# password: 'ubnt' | |
# site: 'default' | |
my $settings = setting( 'unifiapi' ); | |
my $controller = $settings->{ 'controller' } || '127.0.0.1'; | |
my $username = $settings->{ 'username' } || 'ubnt'; | |
my $password = $settings->{ 'password' } || 'ubnt'; | |
my $site = $settings->{ 'site' } || 'default'; | |
my $browser = WWW::Mechanize->new(); | |
$browser->cookie_jar(HTTP::Cookies->new); | |
# TODO: INSECURE settings! | |
# You should not use it in production environment! | |
$browser->ssl_opts( | |
SSL_verify_mode => 0, | |
verify_hostname => 0 | |
); | |
unifi_login($browser, $controller, $username, $password); | |
unifi_get_ap($browser, $controller, $site); | |
unifi_get_clients($browser, $controller, $site); | |
unifi_logout($browser, $controller); | |
sub unifi_login | |
{ | |
my ( $browser, $controller, $username, $password) = @_; | |
my $login_url = "https://$controller:8443/login"; | |
$browser->get( $login_url ); | |
$browser-> submit_form( | |
form_number => 1, | |
fields => { | |
username => $username, | |
password => $password, | |
login => 'login' | |
}, | |
button => 'login' | |
); | |
} | |
sub unifi_get_ap { | |
my ( $browser, $controller, $site) =@_; | |
my $cmd_url = "https://$controller:8443/api/s/$site/stat/device"; | |
$browser->get( $cmd_url ); | |
my $content = $browser->content(); | |
my $json = new JSON; | |
my $json_text = $json->allow_nonref->utf8->relaxed->decode($content); | |
foreach my $ap(@{$json_text->{data}}){ | |
my $deviceip = $ap->{config_network}->{ip}; | |
my @channels; | |
foreach my $vap(@{$ap->{vap_table}}){ | |
push @channels, { | |
port => $vap->{name}, | |
channel => $vap->{channel}, | |
power => $vap->{tx_power}, | |
}; | |
} | |
my $devices = schema('netdisco')->resultset('Device'); | |
my $device = $devices->single({ip => $deviceip}); | |
schema('netdisco')->txn_do(sub { | |
my $gone = $device->wireless_ports->delete; | |
debug sprintf ' [%s] wireless - removed %d wireless channels', | |
$device->ip, $gone; | |
$device->wireless_ports->populate(\@channels); | |
debug sprintf ' [%s] wireless - added %d new wireless channels', | |
$device->ip, scalar @channels; | |
}); | |
} | |
} | |
sub unifi_get_clients { | |
my $now = 'to_timestamp('. (join '.', gettimeofday) .')'; | |
my ( $browser, $controller, $site) =@_; | |
my $cmd_url = "https://$controller:8443/api/s/$site/stat/sta"; | |
$browser->get( $cmd_url ); | |
my $content = $browser->content(); | |
my $json = new JSON; | |
my $json_text = $json->allow_nonref->utf8->relaxed->decode($content); | |
foreach my $sta(@{$json_text->{data}}){ | |
my $mac = $sta->{mac}; | |
my $ssid = $sta->{essid}; | |
my $bssid = $sta->{bssid}; | |
my $portrow = schema('netdisco')->resultset('DevicePort') | |
->columns( [qw/ ip port /] )->single({mac => $bssid}); | |
store_node($portrow->ip, "", $portrow->port, $mac, $now); | |
schema('netdisco')->txn_do(sub { | |
schema('netdisco')->resultset('NodeWireless') | |
->search({ 'me.mac' => $mac, 'me.ssid' => $ssid }) | |
->update_or_create({ | |
txrate => int($sta->{tx_rate}/1000 + 0.5), | |
maxrate => undef, | |
uptime => $sta->{_uptime}, | |
rxpkt => $sta->{rx_packets}, | |
txpkt => $sta->{tx_packets}, | |
rxbyte => $sta->{rx_bytes}, | |
txbyte => $sta->{tx_bytes}, | |
sigqual => $sta->{rssi}, | |
sigstrength => $sta->{signal}, | |
time_last => \$now, | |
}, { | |
order_by => [qw/mac ssid/], | |
for => 'update', | |
}); | |
}); | |
} | |
} | |
sub unifi_logout | |
{ | |
my ( $broswer, $controller) = @_; | |
my $logout_url = "https://$controller:8443/logout"; | |
$browser->get( $logout_url ); | |
} | |
=head1 NAME | |
netdisco-import-unifi - Collect information about wireless UniFi AP and | |
wireless client to Netdisco 2 DB | |
=head1 DESCRIPTION | |
Collects UniFi wireless AP data for Netdisco via Unifi web API. | |
=head1 CONFIGURATION | |
The following should go into your Netdisco 2 configuration file, C<< | |
~/environments/deployment.yml >> | |
=over 4 | |
=item C<unifiapi> | |
Data is collected from UniFi AP controller specified in this setting. The format is a | |
list of dictionaries. The keys C<controller>, C<username>, C<password>, and C<site> | |
are required. For example: | |
unifiapi: | |
controller: 192.168.1.1 | |
username: 'ubnt' | |
password: 'ubnt' | |
site: 'default' | |
=back | |
=head1 LICENSE | |
This software is released to the public domain. It is available as-is | |
and comes with no guarantee whatsoever. | |
=head1 AUTHOR | |
Max Kosmach <[email protected]> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment