Skip to content

Instantly share code, notes, and snippets.

@ollyg
Created July 21, 2017 07:05
Show Gist options
  • Save ollyg/2c93e9e0780ec800561b92e2e3111596 to your computer and use it in GitHub Desktop.
Save ollyg/2c93e9e0780ec800561b92e2e3111596 to your computer and use it in GitHub Desktop.
Collection script for UniFi AP controller
#!/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