use Dancer ':syntax';
use Dancer::Plugin::DBIC 'schema';
use List::MoreUtils 'uniq';
register_worker({ phase => 'main', driver => 'snmp' }, sub {
my ($job, $workerconf) = @_;
my $device = $job->device;
return unless $device->in_storage;
my $snmp = App::Netdisco::Transport::SNMP->reader_for($device)
or return Status->defer("discover failed: could not SNMP connect to $device");
my $v_name = $snmp->v_name;
my $v_index = $snmp->v_index;
# cache the device ports to save hitting the database for many single rows
my $device_ports = vars->{'device_ports'}
|| { map {($_->port => $_)} $device->ports->all };
my $i_vlan = $snmp->i_vlan;
my $i_vlan_type = $snmp->i_vlan_type;
my $interfaces = $snmp->interfaces;
my $i_vlan_membership = $snmp->i_vlan_membership;
my $i_vlan_membership_untagged = $snmp->i_vlan_membership_untagged;
my %p_seen = ();
my @portvlans = ();
my @active_ports = uniq (keys %$i_vlan_membership_untagged, keys %$i_vlan_membership);
# build port vlans suitable for DBIC
foreach my $entry (@active_ports) {
my $port = $interfaces->{$entry} or next;
if (!defined $device_ports->{$port}) {
debug sprintf ' [%s] vlans - local port %s already skipped, ignoring',
$device->ip, $port;
next;
}
my %this_port_vlans = ();
my $type = $i_vlan_type->{$entry};
foreach my $vlan (@{ $i_vlan_membership_untagged->{$entry} || [] }) {
next unless $vlan;
next if $this_port_vlans{$vlan};
my $native = ((defined $i_vlan->{$entry})
and ($vlan eq $i_vlan->{$entry})) ? 't' : 'f';
push @portvlans, {
port => $port,
vlan => $vlan,
native => $native,
egress_tag => 'f',
vlantype => $type,
last_discover => \'LOCALTIMESTAMP',
};
++$this_port_vlans{$vlan};
++$p_seen{$vlan};
}
foreach my $vlan (@{ $i_vlan_membership->{$entry} || [] }) {
next unless $vlan;
next if $this_port_vlans{$vlan};
my $native = ((defined $i_vlan->{$entry})
and ($vlan eq $i_vlan->{$entry})) ? 't' : 'f';
push @portvlans, {
port => $port,
vlan => $vlan,
native => $native,
egress_tag => ($native eq 't' ? 'f' : 't'),
vlantype => $type,
last_discover => \'LOCALTIMESTAMP',
};
++$this_port_vlans{$vlan};
++$p_seen{$vlan};
}
}
# # set is_uplink on the ports with non-native vlans
# foreach my $pv (@portvlans) {
#  next unless $pv->{native} and $pv->{native} eq 'f';
#  $device_ports->{$pv->{port}}->update({is_uplink => \'true'});
# }
schema('netdisco')->txn_do(sub {
my $gone = $device->port_vlans->delete;
debug sprintf ' [%s] vlans - removed %d port VLANs',
$device->ip, $gone;
$device->port_vlans->populate(\@portvlans);
debug sprintf ' [%s] vlans - added %d new port VLANs',
$device->ip, scalar @portvlans;
});
my %d_seen = ();
my @devicevlans = ();
# add named vlans to the device
foreach my $entry (keys %$v_name) {
my $vlan = $v_index->{$entry};
next unless $vlan;
next unless defined $vlan and $vlan;
++$d_seen{$vlan};
push @devicevlans, {
vlan => $vlan,
description => $v_name->{$entry},
last_discover => \'LOCALTIMESTAMP',
};
}
# also add unnamed vlans to the device
foreach my $vlan (keys %p_seen) {
next unless $vlan;
next if $d_seen{$vlan};
push @devicevlans, {
vlan => $vlan,
description => (sprintf "VLAN %d", $vlan),
last_discover => \'LOCALTIMESTAMP',
};
}
# support for Hooks
vars->{'hook_data'}->{'vlans'} = \@devicevlans;
schema('netdisco')->txn_do(sub {
my $gone = $device->vlans->delete;
debug sprintf ' [%s] vlans - removed %d device VLANs',
$device->ip, $gone;
$device->vlans->populate(\@devicevlans);
debug sprintf ' [%s] vlans - added %d new device VLANs',
$device->ip, scalar @devicevlans;
});
return Status->info(sprintf ' [%s] vlans - discovered for ports and device',
$device->ip);
});
true;