Add store_stp method to gather and store spanning tree instance(s) and topology information

This commit is contained in:
Eric A. Miller
2014-12-27 14:31:56 -05:00
parent be01c3937a
commit 5e28c6995c
2 changed files with 164 additions and 2 deletions

View File

@@ -5,6 +5,7 @@ use Dancer::Plugin::DBIC 'schema';
use App::Netdisco::Util::Device qw/get_device is_discoverable/; use App::Netdisco::Util::Device qw/get_device is_discoverable/;
use App::Netdisco::Util::DNS ':all'; use App::Netdisco::Util::DNS ':all';
use App::Netdisco::Util::SNMP qw/snmp_connect get_comm_reindex_vlan_list snmp_comm_reindex/;
use App::Netdisco::JobQueue qw/jq_queued jq_insert/; use App::Netdisco::JobQueue qw/jq_queued jq_insert/;
use NetAddr::IP::Lite ':lower'; use NetAddr::IP::Lite ':lower';
use List::MoreUtils (); use List::MoreUtils ();
@@ -17,7 +18,7 @@ our @EXPORT = ();
our @EXPORT_OK = qw/ our @EXPORT_OK = qw/
set_canonical_ip set_canonical_ip
store_device store_interfaces store_wireless store_device store_interfaces store_wireless
store_vlans store_power store_modules store_vlans store_power store_modules store_stp
store_neighbors discover_new_neighbors store_neighbors discover_new_neighbors
/; /;
our %EXPORT_TAGS = (all => \@EXPORT_OK); our %EXPORT_TAGS = (all => \@EXPORT_OK);
@@ -165,6 +166,7 @@ sub store_device {
ps1_type ps2_type ps1_status ps2_status ps1_type ps2_type ps1_status ps2_status
fan slots fan slots
vendor os os_ver vendor os os_ver
stp_ver b_mac
/; /;
foreach my $property (@properties) { foreach my $property (@properties) {
@@ -871,6 +873,165 @@ sub store_neighbors {
return @to_discover; return @to_discover;
} }
=head2 store_stp( $device, $snmp )
Given a Device database object, and a working SNMP connection, discover and
store the device's Spanning Tree Protocol (STP) information.
The Device database object can be a fresh L<DBIx::Class::Row> object which is
not yet stored to the database.
=cut
sub store_stp {
my ($device, $snmp) = @_;
# cache the device ports to save hitting the database for many single rows
my $device_ports = {map {($_->port => $_)}
$device->ports(undef, {prefetch => 'neighbor_alias'})->all};
my $interfaces = $snmp->interfaces;
my $stptable = [];
# ...then per-vlan if supported
my @vlan_list = get_comm_reindex_vlan_list($device, $snmp);
if (@vlan_list) {
foreach my $vlan (@vlan_list) {
snmp_comm_reindex($snmp, $device, $vlan);
my $pv_stptable = _walk_stp($device, $snmp, $interfaces, $device_ports, $vlan);
push @$stptable, @$pv_stptable;
}
}
else {
# get STP table data via basic snmp connection
$stptable = _walk_stp($device, $snmp, $interfaces, $device_ports);
}
schema('netdisco')->txn_do(sub {
my $gone = $device->stp_instances->delete;
debug sprintf ' [%s] stp - removed %d stp instances',
$device->ip, $gone;
my $stp_p_gone = $device->stp_ports->delete;
debug sprintf ' [%s] stp - removed %d stp port instances',
$device->ip, $stp_p_gone;
$device->stp_instances->populate($stptable);
debug sprintf ' [%s] stp - added %d stp instances',
$device->ip, scalar @$stptable;
});
}
# walks device STP instances and per port STP instances
sub _walk_stp {
my ($device, $snmp, $interfaces, $device_ports, $comm_vlan) = @_;
my $device_stp = [];
# Device STP instances
my $stp_i_id = $snmp->stp_i_id;
my $stp_i_mac = $snmp->stp_i_mac;
my $stp_i_time = $snmp->stp_i_time;
my $stp_i_ntop = $snmp->stp_i_ntop;
my $stp_i_root = $snmp->stp_i_root;
my $stp_i_root_port = $snmp->stp_i_root_port;
# Device per Port STP instances
my $stp_p_id = $snmp->stp_p_id;
my $stp_p_stg_id = $snmp->stp_p_stg_id;
my $stp_p_bridge = $snmp->stp_p_bridge;
my $stp_p_port = $snmp->stp_p_port;
my $stp_p_state = $snmp->stp_p_state;
# Needed for mapping to interface
my $bp_index = $snmp->bp_index;
while (my ($idx, $mac) = each %$stp_i_mac) {
my $instance = $stp_i_id->{$idx} || $comm_vlan || '0';
my $des_root_mac =
$stp_i_root->{$idx}
? substr( $stp_i_root->{$idx}, 6, 17 )
: $stp_i_root->{$idx};
my $dev_instance = {
instance => $instance,
mac => $mac,
top_change => $stp_i_id->{$idx},
top_lastchange => $stp_i_time->{$idx},
des_root_mac => $des_root_mac,
root_port => $stp_i_root_port->{$idx},
# Some versions of DBIx::Class require the first
# array element to have all columns for populate
ports => [],
};
while (my ($iid, $bridge) = each %$stp_p_bridge) {
my $p_instance = $stp_p_stg_id->{$iid} || $comm_vlan || '0';
next unless ($p_instance = $instance);
my $bp_id = $stp_p_id->{$iid};
unless (defined $bp_id) {
debug sprintf
' [%s] stpsuck %s - instance %s has no port mapping - skipping.',
$device->ip, $iid, $p_instance;
next;
}
my $ifindex = $bp_index->{$bp_id};
unless (defined $ifindex) {
debug sprintf
' [%s] stpsuck instance %s - port %s has no bp_index mapping - skipping.',
$device->ip, $p_instance, $bp_id;
next;
}
my $port = $interfaces->{$ifindex};
unless (defined $port) {
debug sprintf
' [%s] stpsuck instance %s - ifindex %s has no port mapping - skipping.',
$device->ip, $p_instance, $ifindex;
next;
}
# this uses the cached $ports resultset to limit hits on the db
my $device_port = $device_ports->{$port};
unless (defined $device_port) {
debug sprintf
' [%s] stpsuck instance %s - port %s is not in database - skipping.',
$device->ip, $p_instance, $port;
next;
}
my $des_bridge_mac =
$stp_p_bridge->{$iid}
? substr( $stp_p_bridge->{$iid}, 6, 17 )
: $stp_p_bridge->{$iid};
my $des_port_id =
$stp_p_port->{$iid}
? hex (substr( $stp_p_port->{$iid}, 3, 4 ))
: $stp_p_port->{$iid};
my $port_instance = {
port => $port,
instance => $p_instance,
port_id => $bp_id,
des_bridge_mac => $des_bridge_mac,
des_port_id => $des_port_id,
status => $stp_p_state->{$iid},
};
push @{$dev_instance->{ports}}, $port_instance;
}
push @$device_stp, $dev_instance;
}
return $device_stp;
}
# take data from the topology table and update remote_ip and remote_port # take data from the topology table and update remote_ip and remote_port
# in the devices table. only use root_ips and skip any bad topo entries. # in the devices table. only use root_ips and skip any bad topo entries.
sub _set_manual_topology { sub _set_manual_topology {

View File

@@ -2,7 +2,7 @@ package App::Netdisco::Daemon::Worker::Poller::Device;
use Dancer qw/:moose :syntax :script/; use Dancer qw/:moose :syntax :script/;
use App::Netdisco::Util::SNMP 'snmp_connect'; use App::Netdisco::Util::SNMP qw/snmp_connect snmp_comm_reindex/;
use App::Netdisco::Util::Device qw/get_device is_discoverable/; use App::Netdisco::Util::Device qw/get_device is_discoverable/;
use App::Netdisco::Core::Discover ':all'; use App::Netdisco::Core::Discover ':all';
use App::Netdisco::Daemon::Util ':all'; use App::Netdisco::Daemon::Util ':all';
@@ -67,6 +67,7 @@ sub discover {
store_vlans($device, $snmp); store_vlans($device, $snmp);
store_power($device, $snmp); store_power($device, $snmp);
store_modules($device, $snmp) if setting('store_modules'); store_modules($device, $snmp) if setting('store_modules');
store_stp($device, $snmp);
discover_new_neighbors($device, $snmp); discover_new_neighbors($device, $snmp);
# if requested, and the device has not yet been arpniped/macsucked, queue now # if requested, and the device has not yet been arpniped/macsucked, queue now