Add store_stp method to gather and store spanning tree instance(s) and topology information
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user