Merge branch 'master' into og-api-tokens-simple
This commit is contained in:
@@ -45,7 +45,7 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
# only enqueue if device is not already discovered,
|
||||
# discover_* config permits the discovery
|
||||
foreach my $neighbor (@to_discover) {
|
||||
my ($ip, $remote_type, $remote_id) = @$neighbor;
|
||||
my ($ip, $remote_id) = @$neighbor;
|
||||
if ($seen_ip{ $ip }++) {
|
||||
debug sprintf
|
||||
' queue - skip: IP %s is already queued from %s',
|
||||
@@ -63,13 +63,6 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
my $newdev = get_device($ip);
|
||||
next if $newdev->in_storage;
|
||||
|
||||
if (not is_discoverable($newdev, $remote_type)) {
|
||||
debug sprintf
|
||||
' queue - skip: %s of type [%s] excluded by discover_* config',
|
||||
$ip, ($remote_type || '');
|
||||
next;
|
||||
}
|
||||
|
||||
# risk of things going wrong...?
|
||||
# https://quickview.cloudapps.cisco.com/quickview/bug/CSCur12254
|
||||
|
||||
@@ -114,6 +107,7 @@ sub store_neighbors {
|
||||
or return (); # already checked!
|
||||
|
||||
# first allow any manually configured topology to be set
|
||||
# and do this before we cache the rows in vars->{'device_ports'}
|
||||
set_manual_topology($device);
|
||||
|
||||
if (!defined $snmp->has_topo) {
|
||||
@@ -126,6 +120,12 @@ sub store_neighbors {
|
||||
my $c_port = $snmp->c_port;
|
||||
my $c_id = $snmp->c_id;
|
||||
my $c_platform = $snmp->c_platform;
|
||||
my $c_cap = $snmp->c_cap;
|
||||
|
||||
# cache the device ports to save hitting the database for many single rows
|
||||
vars->{'device_ports'} =
|
||||
{ map {($_->port => $_)} $device->ports->reset->all };
|
||||
my $device_ports = vars->{'device_ports'};
|
||||
|
||||
# v4 and v6 neighbor tables
|
||||
my $c_ip = ($snmp->c_ip || {});
|
||||
@@ -144,12 +144,12 @@ sub store_neighbors {
|
||||
next;
|
||||
}
|
||||
|
||||
my $port = $interfaces->{ $c_if->{$entry} };
|
||||
my $portrow = schema('netdisco')->resultset('DevicePort')
|
||||
->single({ip => $device->ip, port => $port});
|
||||
# WRT #475 this is SAFE because we check against known ports below
|
||||
my $port = $interfaces->{ $c_if->{$entry} } or next;
|
||||
my $portrow = $device_ports->{$port};
|
||||
|
||||
if (!defined $portrow) {
|
||||
info sprintf ' [%s] neigh - local port %s not in database!',
|
||||
debug sprintf ' [%s] neigh - local port %s already skipped, ignoring',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
@@ -161,7 +161,7 @@ sub store_neighbors {
|
||||
}
|
||||
|
||||
if ($portrow->manual_topo) {
|
||||
info sprintf ' [%s] neigh - %s has manually defined topology',
|
||||
debug sprintf ' [%s] neigh - %s has manually defined topology',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
@@ -170,12 +170,13 @@ sub store_neighbors {
|
||||
my $remote_port = undef;
|
||||
my $remote_type = Encode::decode('UTF-8', $c_platform->{$entry} || '');
|
||||
my $remote_id = Encode::decode('UTF-8', $c_id->{$entry});
|
||||
my $remote_cap = $c_cap->{$entry} || [];
|
||||
|
||||
next unless $remote_ip;
|
||||
my $r_netaddr = NetAddr::IP::Lite->new($remote_ip);
|
||||
|
||||
if ($r_netaddr and ($r_netaddr->addr ne $remote_ip)) {
|
||||
info sprintf ' [%s] neigh - IP on %s: using %s as canonical form of %s',
|
||||
debug sprintf ' [%s] neigh - IP on %s: using %s as canonical form of %s',
|
||||
$device->ip, $port, $r_netaddr->addr, $remote_ip;
|
||||
$remote_ip = $r_netaddr->addr;
|
||||
}
|
||||
@@ -189,7 +190,7 @@ sub store_neighbors {
|
||||
if ($remote_id) {
|
||||
my $devices = schema('netdisco')->resultset('Device');
|
||||
my $neigh = $devices->single({name => $remote_id});
|
||||
info sprintf
|
||||
debug sprintf
|
||||
' [%s] neigh - bad address %s on port %s, searching for %s instead',
|
||||
$device->ip, $remote_ip, $port, $remote_id;
|
||||
|
||||
@@ -207,7 +208,7 @@ sub store_neighbors {
|
||||
(my $tmpid = $remote_id) =~ s/.*\(([0-9a-f]{6})-([0-9a-f]{6})\).*/$1$2/;
|
||||
my $mac = NetAddr::MAC->new(mac => $tmpid);
|
||||
if ($mac and not $mac->errstr) {
|
||||
info sprintf
|
||||
debug sprintf
|
||||
' [%s] neigh - trying to find neighbor %s by MAC %s',
|
||||
$device->ip, $remote_id, $mac->as_ieee;
|
||||
$neigh = $devices->single({mac => $mac->as_ieee});
|
||||
@@ -221,17 +222,17 @@ sub store_neighbors {
|
||||
|
||||
if ($neigh) {
|
||||
$remote_ip = $neigh->ip;
|
||||
info sprintf ' [%s] neigh - found %s with IP %s',
|
||||
debug sprintf ' [%s] neigh - found %s with IP %s',
|
||||
$device->ip, $remote_id, $remote_ip;
|
||||
}
|
||||
else {
|
||||
info sprintf ' [%s] neigh - could not find %s, skipping',
|
||||
debug sprintf ' [%s] neigh - could not find %s, skipping',
|
||||
$device->ip, $remote_id;
|
||||
next;
|
||||
}
|
||||
}
|
||||
else {
|
||||
info sprintf ' [%s] neigh - skipping unuseable address %s on port %s',
|
||||
debug sprintf ' [%s] neigh - skipping unuseable address %s on port %s',
|
||||
$device->ip, $remote_ip, $port;
|
||||
next;
|
||||
}
|
||||
@@ -240,7 +241,15 @@ sub store_neighbors {
|
||||
# what we came here to do.... discover the neighbor
|
||||
debug sprintf ' [%s] neigh - %s with ID [%s] on %s',
|
||||
$device->ip, $remote_ip, ($remote_id || ''), $port;
|
||||
push @to_discover, [$remote_ip, $remote_type, $remote_id];
|
||||
|
||||
if (is_discoverable($remote_ip, $remote_type, $remote_cap)) {
|
||||
push @to_discover, [$remote_ip, $remote_id];
|
||||
}
|
||||
else {
|
||||
debug sprintf
|
||||
' [%s] neigh - skip: %s of type [%s] excluded by discover_* config',
|
||||
$device->ip, $remote_ip, ($remote_type || '');
|
||||
}
|
||||
|
||||
$remote_port = $c_port->{$entry};
|
||||
if (defined $remote_port) {
|
||||
@@ -248,18 +257,18 @@ sub store_neighbors {
|
||||
$remote_port =~ s/[^\d\s\/\.,()\w:-]+//gi;
|
||||
}
|
||||
else {
|
||||
info sprintf ' [%s] neigh - no remote port found for port %s at %s',
|
||||
debug sprintf ' [%s] neigh - no remote port found for port %s at %s',
|
||||
$device->ip, $port, $remote_ip;
|
||||
}
|
||||
|
||||
$portrow->update({
|
||||
$portrow = $portrow->update({
|
||||
remote_ip => $remote_ip,
|
||||
remote_port => $remote_port,
|
||||
remote_type => $remote_type,
|
||||
remote_id => $remote_id,
|
||||
is_uplink => \"true",
|
||||
manual_topo => \"false",
|
||||
});
|
||||
})->discard_changes();
|
||||
|
||||
# update master of our aggregate to be a neighbor of
|
||||
# the master on our peer device (a lot of iffs to get there...).
|
||||
|
||||
@@ -22,8 +22,8 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
my $eigrp_peers = $snmp->eigrp_peers || {};
|
||||
|
||||
return Status->info(" [$device] neigh - no BGP, OSPF, or EIGRP peers")
|
||||
unless ((scalar values %$ospf_peers) or (scalar values %$bgp_peers)
|
||||
or (scalar values %$eigrp_peers));
|
||||
unless ((scalar values %$ospf_peers) or (scalar values %$ospf_routers)
|
||||
or (scalar values %$bgp_peers) or (scalar values %$eigrp_peers));
|
||||
|
||||
my $count = 0;
|
||||
foreach my $ip ((values %$ospf_peers), (values %$ospf_routers),
|
||||
|
||||
@@ -32,6 +32,10 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
};
|
||||
}
|
||||
|
||||
# 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 $interfaces = $snmp->interfaces;
|
||||
my $p_ifindex = $snmp->peth_port_ifindex;
|
||||
my $p_admin = $snmp->peth_port_admin;
|
||||
@@ -42,11 +46,16 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
# build device port power info suitable for DBIC
|
||||
my @portpower;
|
||||
foreach my $entry (keys %$p_ifindex) {
|
||||
my $port = $interfaces->{ $p_ifindex->{$entry} };
|
||||
next unless $port;
|
||||
# WRT #475 this is SAFE because we check against known ports below
|
||||
my $port = $interfaces->{ $p_ifindex->{$entry} } or next;
|
||||
|
||||
if (!defined $device_ports->{$port}) {
|
||||
debug sprintf ' [%s] power - local port %s already skipped, ignoring',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
my ($module) = split m/\./, $entry;
|
||||
|
||||
push @portpower, {
|
||||
port => $port,
|
||||
module => $module,
|
||||
|
||||
@@ -8,7 +8,7 @@ use App::Netdisco::Transport::SNMP ();
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
|
||||
use Encode;
|
||||
use App::Netdisco::Util::Device 'match_devicetype';
|
||||
use App::Netdisco::Util::Device 'match_to_setting';
|
||||
|
||||
register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
my ($job, $workerconf) = @_;
|
||||
@@ -21,10 +21,20 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
my $interfaces = $snmp->interfaces || {};
|
||||
my %properties = ();
|
||||
|
||||
# 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 $raw_speed = $snmp->i_speed_raw || {};
|
||||
|
||||
foreach my $idx (keys %$raw_speed) {
|
||||
my $port = $interfaces->{$idx} or next;
|
||||
if (!defined $device_ports->{$port}) {
|
||||
debug sprintf ' [%s] properties/speed - local port %s already skipped, ignoring',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
$properties{ $port }->{raw_speed} = $raw_speed->{$idx};
|
||||
}
|
||||
|
||||
@@ -32,6 +42,12 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
|
||||
foreach my $idx (keys %$err_cause) {
|
||||
my $port = $interfaces->{$idx} or next;
|
||||
if (!defined $device_ports->{$port}) {
|
||||
debug sprintf ' [%s] properties/errdis - local port %s already skipped, ignoring',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
$properties{ $port }->{error_disable_cause} = $err_cause->{$idx};
|
||||
}
|
||||
|
||||
@@ -39,6 +55,12 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
|
||||
foreach my $idx (keys %$faststart) {
|
||||
my $port = $interfaces->{$idx} or next;
|
||||
if (!defined $device_ports->{$port}) {
|
||||
debug sprintf ' [%s] properties/faststart - local port %s already skipped, ignoring',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
$properties{ $port }->{faststart} = $faststart->{$idx};
|
||||
}
|
||||
|
||||
@@ -54,17 +76,22 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
|
||||
foreach my $idx (keys %$c_if) {
|
||||
my $port = $interfaces->{ $c_if->{$idx} } or next;
|
||||
if (!defined $device_ports->{$port}) {
|
||||
debug sprintf ' [%s] properties/lldpcap - local port %s already skipped, ignoring',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
my $remote_cap = $c_cap->{$idx} || [];
|
||||
my $remote_type = Encode::decode('UTF-8', $c_platform->{$idx} || '');
|
||||
|
||||
$properties{ $port }->{remote_is_wap} = 'true'
|
||||
if scalar grep {match_devicetype($_, 'wap_capabilities')} @$remote_cap
|
||||
or match_devicetype($remote_type, 'wap_platforms');
|
||||
if scalar grep {match_to_setting($_, 'wap_capabilities')} @$remote_cap
|
||||
or match_to_setting($remote_type, 'wap_platforms');
|
||||
|
||||
$properties{ $port }->{remote_is_phone} = 'true'
|
||||
if scalar grep {match_devicetype($_, 'phone_capabilities')} @$remote_cap
|
||||
or match_devicetype($remote_type, 'phone_platforms');
|
||||
if scalar grep {match_to_setting($_, 'phone_capabilities')} @$remote_cap
|
||||
or match_to_setting($remote_type, 'phone_platforms');
|
||||
|
||||
next unless scalar grep {defined && m/^inventory$/} @{ $rem_media_cap->{$idx} };
|
||||
|
||||
@@ -74,17 +101,28 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
$properties{ $port }->{remote_serial} = $rem_serial->{ $idx };
|
||||
}
|
||||
|
||||
foreach my $idx (keys %$interfaces) {
|
||||
my $port = $interfaces->{$idx} or next;
|
||||
if (!defined $device_ports->{$port}) {
|
||||
debug sprintf ' [%s] properties/ifindex - local port %s already skipped, ignoring',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
$properties{ $port }->{ifindex} = $idx;
|
||||
}
|
||||
|
||||
return Status->info(" [$device] no port properties to record")
|
||||
unless scalar keys %properties;
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->properties_ports->delete;
|
||||
debug sprintf ' [%s] props - removed %d ports with properties',
|
||||
debug sprintf ' [%s] properties - removed %d ports with properties',
|
||||
$device->ip, $gone;
|
||||
$device->properties_ports->populate(
|
||||
[map {{ port => $_, %{ $properties{$_} } }} keys %properties] );
|
||||
|
||||
return Status->info(sprintf ' [%s] props - added %d new port properties',
|
||||
return Status->info(sprintf ' [%s] properties - added %d new port properties',
|
||||
$device->ip, scalar keys %properties);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -151,7 +151,7 @@ register_worker({ phase => 'early', driver => 'snmp' }, sub {
|
||||
if (defined $snmp->snmpEngineTime) {
|
||||
$dev_uptime_wrapped = int( $snmp->snmpEngineTime * 100 / 2**32 );
|
||||
if ($dev_uptime_wrapped > 0) {
|
||||
info sprintf ' [%s] interface - device uptime wrapped %d times - correcting',
|
||||
debug sprintf ' [%s] interfaces - device uptime wrapped %d times - correcting',
|
||||
$device->ip, $dev_uptime_wrapped;
|
||||
$device->uptime( $dev_uptime + $dev_uptime_wrapped * 2**32 );
|
||||
}
|
||||
@@ -190,7 +190,7 @@ register_worker({ phase => 'early', driver => 'snmp' }, sub {
|
||||
|
||||
my $lc = $i_lastchange->{$entry} || 0;
|
||||
if (not $dev_uptime_wrapped and $lc > $dev_uptime) {
|
||||
info sprintf ' [%s] interfaces - device uptime wrapped (%s) - correcting',
|
||||
debug sprintf ' [%s] interfaces - device uptime wrapped (%s) - correcting',
|
||||
$device->ip, $port;
|
||||
$device->uptime( $dev_uptime + 2**32 );
|
||||
$dev_uptime_wrapped = 1;
|
||||
@@ -238,6 +238,7 @@ register_worker({ phase => 'early', driver => 'snmp' }, sub {
|
||||
# must do this after building %interfaces so that we can set is_master
|
||||
foreach my $sidx (keys %$agg_ports) {
|
||||
my $slave = $interfaces->{$sidx} or next;
|
||||
next unless defined $agg_ports->{$sidx}; # slave without a master?!
|
||||
my $master = $interfaces->{ $agg_ports->{$sidx} } or next;
|
||||
next unless exists $interfaces{$slave} and exists $interfaces{$master};
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
};
|
||||
}
|
||||
|
||||
# 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_membership = $snmp->i_vlan_membership;
|
||||
my $i_vlan_type = $snmp->i_vlan_type;
|
||||
@@ -42,8 +46,13 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
my @portvlans = ();
|
||||
foreach my $entry (keys %$i_vlan_membership) {
|
||||
my %port_vseen = ();
|
||||
my $port = $interfaces->{$entry};
|
||||
next unless defined $port;
|
||||
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 $type = $i_vlan_type->{$entry};
|
||||
|
||||
|
||||
@@ -18,6 +18,10 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
my $ssidlist = $snmp->i_ssidlist;
|
||||
return unless scalar keys %$ssidlist;
|
||||
|
||||
# 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 $interfaces = $snmp->interfaces;
|
||||
my $ssidbcast = $snmp->i_ssidbcast;
|
||||
my $ssidmac = $snmp->i_ssidmac;
|
||||
@@ -36,6 +40,12 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
next;
|
||||
}
|
||||
|
||||
if (!defined $device_ports->{$port}) {
|
||||
debug sprintf ' [%s] wireless - local port %s already skipped, ignoring',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
push @ssids, {
|
||||
port => $port,
|
||||
ssid => $ssidlist->{$entry},
|
||||
@@ -64,6 +74,12 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
next;
|
||||
}
|
||||
|
||||
if (!defined $device_ports->{$port}) {
|
||||
debug sprintf ' [%s] wireless - local port %s already skipped, ignoring',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
push @channels, {
|
||||
port => $port,
|
||||
channel => $channel->{$entry},
|
||||
|
||||
@@ -7,7 +7,7 @@ use aliased 'App::Netdisco::Worker::Status';
|
||||
use App::Netdisco::Transport::SNMP ();
|
||||
use App::Netdisco::Util::Permission 'check_acl_no';
|
||||
use App::Netdisco::Util::PortMAC 'get_port_macs';
|
||||
use App::Netdisco::Util::Device 'match_devicetype';
|
||||
use App::Netdisco::Util::Device 'match_to_setting';
|
||||
use App::Netdisco::Util::Node 'check_mac';
|
||||
use App::Netdisco::Util::SNMP 'snmp_comm_reindex';
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
@@ -248,7 +248,6 @@ sub get_vlan_list {
|
||||
|
||||
# check in use by a port on this device
|
||||
if (!$vlans{$vlan} && !setting('macsuck_all_vlans')) {
|
||||
|
||||
debug sprintf
|
||||
' [%s] macsuck VLAN %s/%s - not in use by any port - skipping.',
|
||||
$device->ip, $vlan, $name;
|
||||
@@ -299,6 +298,8 @@ sub walk_fwtable {
|
||||
next;
|
||||
}
|
||||
|
||||
# WRT #475 this is SAFE because we check against known ports below
|
||||
# but we do need the SNMP interface IDs to get the job done
|
||||
my $port = $interfaces->{$iid};
|
||||
|
||||
unless (defined $port) {
|
||||
@@ -318,6 +319,7 @@ sub walk_fwtable {
|
||||
# this uses the cached $ports resultset to limit hits on the db
|
||||
my $device_port = $device_ports->{$port};
|
||||
|
||||
# WRT #475 ... see? :-)
|
||||
unless (defined $device_port) {
|
||||
debug sprintf
|
||||
' [%s] macsuck %s - port %s is not in database - skipping.',
|
||||
@@ -340,7 +342,7 @@ sub walk_fwtable {
|
||||
# neighbors otherwise it would kill the DB with device lookups.
|
||||
my $neigh_cannot_macsuck = eval { # can fail
|
||||
check_acl_no(($device_port->neighbor || "0 but true"), 'macsuck_unsupported') ||
|
||||
match_devicetype($device_port->remote_type, 'macsuck_unsupported_type') };
|
||||
match_to_setting($device_port->remote_type, 'macsuck_unsupported_type') };
|
||||
|
||||
if ($device_port->is_uplink) {
|
||||
if ($neigh_cannot_macsuck) {
|
||||
|
||||
@@ -41,11 +41,11 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
next unless defined $mac; # avoid null entries
|
||||
# there can be more rows in txrate than other tables
|
||||
|
||||
my $txrate = defined $txrates->[$#$txrates]
|
||||
my $txrate = (ref $txrates and defined $txrates->[$#$txrates])
|
||||
? int($txrates->[$#$txrates])
|
||||
: undef;
|
||||
|
||||
my $maxrate = defined $rates->[$#$rates]
|
||||
my $maxrate = (ref $rates and defined $rates->[$#$rates])
|
||||
? int($rates->[$#$rates])
|
||||
: undef;
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package App::Netdisco::Worker::Plugin::MakeRancidConf;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Dancer ':syntax';
|
||||
use Dancer::Plugin::DBIC;
|
||||
|
||||
@@ -8,7 +11,7 @@ use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use Path::Class;
|
||||
use List::Util qw/pairkeys pairfirst/;
|
||||
use File::Slurper 'write_text';
|
||||
use File::Slurper qw/read_lines write_text/;
|
||||
use App::Netdisco::Util::Permission 'check_acl_no';
|
||||
|
||||
register_worker({ phase => 'main' }, sub {
|
||||
@@ -16,14 +19,31 @@ register_worker({ phase => 'main' }, sub {
|
||||
my $config = setting('rancid') || {};
|
||||
|
||||
my $domain_suffix = setting('domain_suffix') || '';
|
||||
my $delimiter = $config->{delimiter} || ':';
|
||||
my $delimiter = $config->{delimiter} || ';';
|
||||
my $down_age = $config->{down_age} || '1 day';
|
||||
my $default_group = $config->{default_group} || 'default';
|
||||
|
||||
my $rancidhome = $config->{rancid_home}
|
||||
my $rancidconf = $config->{rancid_conf} || '/etc/rancid';
|
||||
my $rancidcvsroot = $config->{rancid_cvsroot}
|
||||
|| dir($ENV{NETDISCO_HOME}, 'rancid')->stringify;
|
||||
mkdir $rancidhome if ! -d $rancidhome;
|
||||
return Status->error("cannot create or see rancid home: $rancidhome")
|
||||
if ! -d $rancidhome;
|
||||
mkdir $rancidcvsroot if ! -d $rancidcvsroot;
|
||||
return Status->error("cannot create or access rancid cvsroot: $rancidcvsroot")
|
||||
if ! -d $rancidcvsroot;
|
||||
|
||||
my $allowed_types = {};
|
||||
foreach my $type (qw/base conf/) {
|
||||
my $type_file = file($rancidconf, "rancid.types.$type")->stringify;
|
||||
debug sprintf("trying rancid configuration file %s\n", $type_file);
|
||||
next unless -f $type_file;
|
||||
my @lines = read_lines($type_file);
|
||||
foreach my $line (@lines) {
|
||||
next if $line =~ m/^(?:\#|\$)/;
|
||||
$allowed_types->{$1} += 1 if $line =~ m/^([a-z0-9_\-]+);login;.*$/;
|
||||
}
|
||||
}
|
||||
|
||||
return Status->error("You didn't have any device types configured in your rancid installation.")
|
||||
if ! scalar keys %$allowed_types;
|
||||
|
||||
my $devices = schema('netdisco')->resultset('Device')->search(undef, {
|
||||
'+columns' => { old =>
|
||||
@@ -32,24 +52,37 @@ register_worker({ phase => 'main' }, sub {
|
||||
|
||||
$config->{groups} ||= { default => 'any' };
|
||||
$config->{vendormap} ||= {};
|
||||
$config->{excluded} ||= {};
|
||||
$config->{by_ip} ||= {};
|
||||
$config->{by_hostname} ||= {};
|
||||
|
||||
my $routerdb = {};
|
||||
while (my $d = $devices->next) {
|
||||
my $name =
|
||||
check_acl_no($d, $config->{by_ip}) ? $d->ip : ($d->dns || $d->name);
|
||||
$name =~ s/$domain_suffix$//
|
||||
if check_acl_no($d, $config->{by_hostname});
|
||||
|
||||
if (check_acl_no($d, $config->{excluded})) {
|
||||
debug " skipping $d: device excluded of export";
|
||||
next
|
||||
}
|
||||
|
||||
my $name = check_acl_no($d, $config->{by_ip}) ? $d->ip : ($d->dns || $d->name);
|
||||
$name =~ s/$domain_suffix$// if check_acl_no($d, $config->{by_hostname});
|
||||
|
||||
my ($group) =
|
||||
pairkeys pairfirst { check_acl_no($d, $b) } %{ $config->{groups} };
|
||||
(pairkeys pairfirst { check_acl_no($d, $b) } %{ $config->{groups} }) || $default_group;
|
||||
|
||||
my ($vendor) =
|
||||
(pairkeys pairfirst { check_acl_no($d, $b) } %{ $config->{vendormap} })
|
||||
|| $d->vendor;
|
||||
|
||||
if ($vendor =~ m/(?:enterprises\.|netdisco)/) {
|
||||
if (not ($name and $vendor)) {
|
||||
debug " skipping $d: the name or vendor is not defined";
|
||||
next
|
||||
} elsif ($vendor =~ m/(?:enterprises\.|netdisco)/) {
|
||||
debug " skipping $d with unresolved vendor: $vendor";
|
||||
next;
|
||||
} elsif (scalar keys %$allowed_types and !exists($allowed_types->{$vendor})) {
|
||||
debug " skipping $d: $vendor doesn't exist in rancid's vendor list";
|
||||
next;
|
||||
}
|
||||
|
||||
push @{$routerdb->{$group}},
|
||||
@@ -58,12 +91,14 @@ register_worker({ phase => 'main' }, sub {
|
||||
}
|
||||
|
||||
foreach my $group (keys %$routerdb) {
|
||||
mkdir dir($rancidhome, $group)->stringify;
|
||||
my $content = join "\n", @{$routerdb->{$group}};
|
||||
write_text(file($rancidhome, $group, 'router.db')->stringify, "${content}\n");
|
||||
mkdir dir($rancidcvsroot, $group)->stringify;
|
||||
my $content = "#\n# Router list file for rancid group $group.\n";
|
||||
$content .= "# Generate automatically by App::Netdisco::Worker::Plugin::MakeRancidConf\n#\n";
|
||||
$content .= join "\n", sort @{$routerdb->{$group}};
|
||||
write_text(file($rancidcvsroot, $group, 'router.db')->stringify, "${content}\n");
|
||||
}
|
||||
|
||||
return Status->done('Wrote RANCID configuration.');
|
||||
return Status->done('Wrote rancid configuration.');
|
||||
});
|
||||
|
||||
true;
|
||||
@@ -72,17 +107,20 @@ true;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
MakeRancidConf - Generate RANCID Configuration
|
||||
MakeRancidConf - Generate rancid Configuration
|
||||
|
||||
=head1 INTRODUCTION
|
||||
|
||||
This worker will generate a RANCID configuration for all devices in Netdisco.
|
||||
This worker will generate a rancid configuration for all devices in Netdisco.
|
||||
|
||||
Optionally you can provide configuration to control the output, however the
|
||||
defaults are sane, and will create one RANCID group called "C<default>" which
|
||||
contains all devices. Those devices not discovered successfully within the
|
||||
past day will be marked as "down" for RANCID to skip. Configuration is saved
|
||||
to the "rancid" subdirectory of Netdisco's home folder.
|
||||
defaults are sane for rancid versions 3.x and will create one rancid group
|
||||
called C<default> which contains all devices. Those devices not discovered
|
||||
successfully within the past day will be marked as C<down> for rancid to skip.
|
||||
Configuration is saved to the F<~/rancid> subdirectory of Netdisco's home folder.
|
||||
|
||||
Note that this only generates the router.db files, you will still need to
|
||||
configure rancid's F<.cloginrc> and schedule C<rancid-run> to run.
|
||||
|
||||
You could run this worker at 09:05 each day using the following configuration:
|
||||
|
||||
@@ -90,95 +128,142 @@ You could run this worker at 09:05 each day using the following configuration:
|
||||
makerancidconf:
|
||||
when: '5 9 * * *'
|
||||
|
||||
Since MakeRancidConf is a worker module it can also be run via C<netdisco-do>:
|
||||
|
||||
netdisco-do makerancidconf
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
Here is a complete example of the configuration, which must be called
|
||||
"C<rancid>". All keys are optional:
|
||||
C<rancid>. All keys are optional:
|
||||
|
||||
rancid:
|
||||
rancid_home: "$ENV{NETDISCO_HOME}/rancid" # default
|
||||
down_age: '1 day' # default
|
||||
delimiter: ':' # default
|
||||
rancid_cvsroot: '$ENV{NETDISCO_HOME}/rancid' # default
|
||||
rancid_conf: '/etc/rancid' # default
|
||||
down_age: '1 day' # default
|
||||
delimiter: ';' # default
|
||||
default_group: 'default' # default
|
||||
excluded:
|
||||
excludegroup1: 'host_group1_acl'
|
||||
excludegroup2: 'host_group2_acl'
|
||||
groups:
|
||||
groupname1: 'host_group1_acl'
|
||||
groupname2: 'host_group2_acl'
|
||||
groupname1: 'host_group3_acl'
|
||||
groupname2: 'host_group4_acl'
|
||||
vendormap:
|
||||
vname1: 'host_group3_acl'
|
||||
vname2: 'host_group4_acl'
|
||||
by_ip: 'host_group5_acl'
|
||||
by_hostname: 'host_group6_acl'
|
||||
vname1: 'host_group5_acl'
|
||||
vname2: 'host_group6_acl'
|
||||
by_ip: 'host_group7_acl'
|
||||
by_hostname: 'host_group8_acl'
|
||||
|
||||
Note that the default home for writing files is not "C</var/lib/rancid>" so
|
||||
you may wish to set this (especially if migrating from the old
|
||||
Note that the default directory for writing files is not F</var/lib/rancid> so
|
||||
you may wish to set this in C<rancid_cvsroot>, (especially if migrating from the old
|
||||
C<netdisco-rancid-export> script).
|
||||
|
||||
Any values above that are a Host Group ACL will take either a single item or
|
||||
list of Network Identifiers or Device Properties. See the L<ACL
|
||||
Any values above that are a host group ACL will take either a single item or
|
||||
a list of network identifiers or device properties. See the L<ACL
|
||||
documentation|https://github.com/netdisco/netdisco/wiki/Configuration#access-control-lists>
|
||||
wiki page for full details. We advise you to use the "C<host_groups>" setting
|
||||
wiki page for full details. We advise you to use the C<host_groups> setting
|
||||
and then refer to named entries in that, for example:
|
||||
|
||||
host_groups:
|
||||
coredevices: '192.0.2.0/24'
|
||||
edgedevices: '172.16.0.0/16'
|
||||
|
||||
grp-nxos: 'os:nx-os'
|
||||
|
||||
rancid:
|
||||
groups:
|
||||
core_devices: 'group:coredevices'
|
||||
edge_devices: 'group:edgedevices'
|
||||
vendormap:
|
||||
cisco-nx: 'group:grp-nxos'
|
||||
by_ip: 'any'
|
||||
|
||||
=head2 C<rancid_home>
|
||||
Do not forget that rancid also needs configuring when adding a new group,
|
||||
such as scheduling the group to run, adding it to F<rancid.conf>, setting up the
|
||||
email config and creating the repository with C<rancid-cvs>.
|
||||
|
||||
The location to write RANCID Group configuration files into. A subdirectory
|
||||
for each Group will be created.
|
||||
=head2 C<rancid_conf>
|
||||
|
||||
The location where the rancid configuration (F<rancid.types.base> and
|
||||
F<rancid.types.conf>) is installed. It will be used to check the existance
|
||||
of device types before exporting the devices to the rancid configuration. if no match
|
||||
is found the device will not be added to rancid.
|
||||
|
||||
=head2 C<rancid_cvsroot>
|
||||
|
||||
The location to write rancid group configuration files (F<router.db>) into. A
|
||||
subdirectory for each group will be created.
|
||||
|
||||
=head2 C<down_age>
|
||||
|
||||
This should be the same or greater than the interval between regular discover
|
||||
jobs on your network. Devices which have not been discovered within this time
|
||||
will be marked as "C<down>" to RANCID.
|
||||
will be marked as C<down> to rancid.
|
||||
|
||||
The format is any time interval known and understood by PostgreSQL, such as at
|
||||
L<https://www.postgresql.org/docs/8.4/static/functions-datetime.html>.
|
||||
L<https://www.postgresql.org/docs/10/static/functions-datetime.html>.
|
||||
|
||||
=head2 C<delimiter>
|
||||
|
||||
Set this to the delimiter character if needed to be different from the
|
||||
default.
|
||||
Set this to the delimiter character for your F<router.db> entries if needed to
|
||||
be different from the default, the default is C<;>.
|
||||
|
||||
=head2 C<default_group>
|
||||
|
||||
Put devices into this group if they do not match any other groups defined.
|
||||
|
||||
=head2 C<excluded>
|
||||
|
||||
This dictionary defines a list of devices that you do not wish to export to
|
||||
rancid configuration.
|
||||
|
||||
The value should be a L<Netdisco ACL|https://github.com/netdisco/netdisco/wiki/Configuration#access-control-lists>
|
||||
to select devices in the Netdisco database.
|
||||
|
||||
=head2 C<groups>
|
||||
|
||||
This dictionary maps RANCID Group names with configuration which will match
|
||||
This dictionary maps rancid group names with configuration which will match
|
||||
devices in the Netdisco database.
|
||||
|
||||
The left hand side (key) should be the RANCID group name, the right hand side
|
||||
The left hand side (key) should be the rancid group name, the right hand side
|
||||
(value) should be a L<Netdisco
|
||||
ACL|https://github.com/netdisco/netdisco/wiki/Configuration#access-control-lists>
|
||||
to select devices in the Netdisco database.
|
||||
|
||||
=head2 C<vendormap>
|
||||
|
||||
If the device Vendor in Netdisco is not the same as the RANCID vendor script,
|
||||
configure a mapping here.
|
||||
If the device vendor in Netdisco is not the same as the rancid vendor script or
|
||||
device type, configure a mapping here.
|
||||
|
||||
The left hand side (key) should be the RANCID vendor, the right hand side
|
||||
The left hand side (key) should be the rancid device type, the right hand side
|
||||
(value) should be a L<Netdisco
|
||||
ACL|https://github.com/netdisco/netdisco/wiki/Configuration#access-control-lists>
|
||||
to select devices in the Netdisco database.
|
||||
|
||||
Note that vendors might have a large array of operating systems which require
|
||||
different rancid modules. Mapping operating systems to rancid device types is
|
||||
a good solution to use the correct device type. Example:
|
||||
|
||||
host_groups:
|
||||
grp-ciscosb: 'os:ros'
|
||||
|
||||
rancid:
|
||||
vendormap:
|
||||
cisco-sb: 'group:grp-ciscosb'
|
||||
|
||||
=head2 C<by_ip>
|
||||
|
||||
L<Netdisco
|
||||
ACL|https://github.com/netdisco/netdisco/wiki/Configuration#access-control-lists>
|
||||
to select devices which will be written to the RANCID config as an IP address,
|
||||
instead of the DNS FQDN or SNMP host name.
|
||||
to select devices which will be written to the rancid config as an IP address,
|
||||
instead of the DNS FQDN or SNMP hostname.
|
||||
|
||||
=head2 C<by_hostname>
|
||||
|
||||
L<Netdisco
|
||||
ACL|https://github.com/netdisco/netdisco/wiki/Configuration#access-control-lists>
|
||||
to select devices which will have the unqualified host name written to the
|
||||
RANCID config. This is done simply by stripping the C<domain_suffix>
|
||||
to select devices which will have the unqualified hostname written to the
|
||||
rancid config. This is done simply by stripping the C<domain_suffix>
|
||||
configuration setting from the device FQDN.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
@@ -47,4 +47,3 @@ register_worker({ phase => 'main' }, sub {
|
||||
});
|
||||
|
||||
true;
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@ register_worker({ phase => 'check' }, sub {
|
||||
or return Status->error(sprintf "Unknown port name [%s] on device %s",
|
||||
$job->port, $job->device);
|
||||
|
||||
my $vlan_reconfig_check = vlan_reconfig_check(vars->{'port'});
|
||||
return Status->error("Cannot alter vlan: $vlan_reconfig_check")
|
||||
if $vlan_reconfig_check;
|
||||
my $port_reconfig_check = port_reconfig_check(vars->{'port'});
|
||||
return Status->error("Cannot alter port: $port_reconfig_check")
|
||||
if $port_reconfig_check;
|
||||
|
||||
return Status->done('PortControl is able to run');
|
||||
});
|
||||
@@ -56,7 +56,7 @@ sub _action {
|
||||
my $rv = $snmp->set_i_up_admin($data, $iid);
|
||||
|
||||
if (!defined $rv) {
|
||||
return Status->error(sprintf 'Failed to set [%s] up_admin to [%s] on $device: %s',
|
||||
return Status->error(sprintf "Failed to set [%s] up_admin to [%s] on $device: %s",
|
||||
$pn, $data, ($snmp->error || ''));
|
||||
}
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ register_worker({ phase => 'check' }, sub {
|
||||
or return Status->error(sprintf "Unknown port name [%s] on device %s",
|
||||
$job->port, $job->device);
|
||||
|
||||
my $vlan_reconfig_check = vlan_reconfig_check(vars->{'port'});
|
||||
return Status->error("Cannot alter vlan: $vlan_reconfig_check")
|
||||
if $vlan_reconfig_check;
|
||||
my $port_reconfig_check = port_reconfig_check(vars->{'port'});
|
||||
return Status->error("Cannot alter port: $port_reconfig_check")
|
||||
if $port_reconfig_check;
|
||||
|
||||
return Status->error("No PoE service on port [$pn] on device $device")
|
||||
unless vars->{'port'}->power;
|
||||
@@ -40,7 +40,7 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
|
||||
# snmp connect using rw community
|
||||
my $snmp = App::Netdisco::Transport::SNMP->writer_for($device)
|
||||
or return Status->defer("failed to connect to $device to update vlan");
|
||||
or return Status->defer("failed to connect to $device to set power");
|
||||
|
||||
my $powerid = get_powerid($snmp, vars->{'port'})
|
||||
or return Status->error("failed to get power ID for [$pn] from $device");
|
||||
|
||||
@@ -18,13 +18,14 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
||||
my ($device, $port, $extra) = map {$job->$_} qw/device port extra/;
|
||||
|
||||
$extra ||= 'interfaces'; my $class = undef;
|
||||
($class, $extra) = split(/::([^:]+)$/, $extra);
|
||||
if ($class and $extra) {
|
||||
$class = 'SNMP::Info::'.$class;
|
||||
}
|
||||
else {
|
||||
$extra = $class;
|
||||
undef $class;
|
||||
my @values = split /::/, $extra;
|
||||
$extra = pop @values;
|
||||
if (scalar(@values)) {
|
||||
$class = "SNMP::Info";
|
||||
foreach my $v (@values) {
|
||||
last if ($v eq '');
|
||||
$class = $class.'::'.$v;
|
||||
}
|
||||
}
|
||||
|
||||
my $i = App::Netdisco::Transport::SNMP->reader_for($device, $class);
|
||||
|
||||
Reference in New Issue
Block a user