Merge of og-work branch, many new features.
Squashed commit of the following: commita43c98962aAuthor: Oliver Gorwits <oliver@cpan.org> Date: Mon Jun 3 20:37:39 2013 +0100 Missing mibdirs causes all MIBs to be loaded (with a warning) commit09829a25b8Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Jun 3 20:07:31 2013 +0100 local plugins site_plugins dir commitb0e804e558Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Jun 3 19:59:04 2013 +0100 use send_error and redirect from Dancer commit3d1185261aAuthor: Oliver Gorwits <oliver@cpan.org> Date: Mon Jun 3 19:13:40 2013 +0100 support path config option commit31ca119f84Merge:9a798554d2b3a5Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Jun 3 00:06:17 2013 +0100 Merge remote-tracking branch 'origin/og-work' into og-work g-work" This reverts commit9a79855361, reversing changes made to6fd6118354. Conflicts: Netdisco/share/views/plugin/device_port_column/c_observiumsparklines.tt commit9a79855361Merge:6fd6118c8c3b82Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Jun 3 00:03:32 2013 +0100 Merge remote-tracking branch 'origin/master' into og-work commit6fd6118354Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Jun 2 15:47:45 2013 +0100 extra note about behind proxy commit798086ca29Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Jun 2 15:30:26 2013 +0100 complete the observium plugin commit66b3ced179Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Jun 2 12:48:06 2013 +0100 Plugins can have CSS and Javascript loaded within <head> commit4d2b3a5307Author: Oliver Gorwits <oliver@cpan.org> Date: Thu May 30 08:50:16 2013 +0100 get device dns to port template commited1bfa1ae7Author: Oliver Gorwits <oliver@cpan.org> Date: Thu May 30 08:17:02 2013 +0100 observium sparklines plugin; support X:: namespace commit76b7636c74Author: Oliver Gorwits <oliver@cpan.org> Date: Thu May 30 06:30:06 2013 +0100 rename private settings keys commitfdac8f6c33Author: Oliver Gorwits <oliver@cpan.org> Date: Thu May 30 05:59:53 2013 +0100 add macwalk and arpnip buttons to device details commit3d688c7d83Author: Oliver Gorwits <oliver@cpan.org> Date: Thu May 30 05:57:20 2013 +0100 Revert "reduce refresh to 5sec" This reverts commit8ea9ec7dd9. commitdc62382112Author: Oliver Gorwits <oliver@cpan.org> Date: Thu May 30 05:50:34 2013 +0100 support for arpwalk and macwalk and all jobs via web commit8bc7d83c98Author: Oliver Gorwits <oliver@cpan.org> Date: Thu May 30 05:35:41 2013 +0100 simplify discover options to only discoverall and discover commit8ea9ec7dd9Author: Oliver Gorwits <oliver@cpan.org> Date: Wed May 29 20:23:08 2013 +0100 reduce refresh to 5sec commit8c54e6c58bAuthor: Oliver Gorwits <oliver@cpan.org> Date: Wed May 29 20:11:06 2013 +0100 show undiscovered neighbor properly commite0ee25628fAuthor: Oliver Gorwits <oliver@cpan.org> Date: Wed May 29 19:54:09 2013 +0100 avoid unecessary log for queueing commitd5565423f2Author: Oliver Gorwits <oliver@cpan.org> Date: Wed May 29 19:51:37 2013 +0100 avoid warning on undefined remote type commit5d9b58a6b2Author: Oliver Gorwits <oliver@cpan.org> Date: Wed May 29 19:48:22 2013 +0100 avoid explosion when not admin commit377bb942e0Author: Oliver Gorwits <oliver@cpan.org> Date: Wed May 29 19:46:52 2013 +0100 avoid undefined warning commit08806dcfa2Author: Oliver Gorwits <oliver@cpan.org> Date: Wed May 29 19:46:42 2013 +0100 get_db_version will be 0 at first deploy commit9511c17056Author: Oliver Gorwits <oliver@cpan.org> Date: Wed May 29 19:15:55 2013 +0100 fix name of Template module commiteb0288de35Author: Oliver Gorwits <oliver@cpan.org> Date: Tue May 28 07:17:07 2013 +0100 initial config settings documentation commit7f2ea7f8dcAuthor: Oliver Gorwits <oliver@cpan.org> Date: Mon May 27 15:18:15 2013 +0100 remove check_mac to own module, use in macsuck too commitb995cf6398Author: Oliver Gorwits <oliver@cpan.org> Date: Mon May 27 15:01:29 2013 +0100 show probable but undiscovered neighbor is ports display commitdd8d461188Author: Oliver Gorwits <oliver@cpan.org> Date: Mon May 27 14:52:41 2013 +0100 new schema version for is_uplink and is_uplink_admin commit3f6a7b5aa2Author: Oliver Gorwits <oliver@cpan.org> Date: Mon May 27 14:47:59 2013 +0100 make sure device_port is updated when manual_topo is set commit33bf9a6599Author: Oliver Gorwits <oliver@cpan.org> Date: Sun May 26 19:51:49 2013 +0100 export store_arp and store_node commit0ed356d560Author: Oliver Gorwits <oliver@cpan.org> Date: Sat May 25 17:12:31 2013 +0100 use row lock not table lock commitf830bc3a3bAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sat May 25 16:38:33 2013 +0100 move macsuck/arpnip/discover to ::Core namespace commitbe40788987Author: Oliver Gorwits <oliver@cpan.org> Date: Fri May 24 21:10:34 2013 +0100 add maybe_uplink to device_port; more macsuck implementation commit88371026d5Author: Oliver Gorwits <oliver@cpan.org> Date: Fri May 24 14:34:58 2013 +0100 start on macsuck; tweak update locking commit6f7c87ac07Author: Oliver Gorwits <oliver@cpan.org> Date: Fri May 24 13:10:58 2013 +0100 ORDER BY ... FOR UPDATE will allow us to avoid table lock commit7c438e01fcAuthor: Oliver Gorwits <oliver@cpan.org> Date: Fri May 24 12:12:46 2013 +0100 yet more efficient arpnip commitc74c56dc02Author: Oliver Gorwits <oliver@cpan.org> Date: Fri May 24 11:34:23 2013 +0100 guard against race with *_or_* DBIC methods commitd50c54972eAuthor: Oliver Gorwits <oliver@cpan.org> Date: Mon May 20 23:42:41 2013 +0100 more efficient arpnip commit73c8979130Author: Oliver Gorwits <oliver@cpan.org> Date: Sun May 19 22:52:15 2013 +0100 fix confusing name commitbf78e82411Author: Oliver Gorwits <oliver@cpan.org> Date: Sun May 19 22:37:22 2013 +0100 fix mistake in DBIx::Class schema commit6a5af95836Author: Oliver Gorwits <oliver@cpan.org> Date: Sun May 19 22:06:27 2013 +0100 arpnip implementation commit594abd3f82Author: Oliver Gorwits <oliver@cpan.org> Date: Thu May 16 00:00:50 2013 +0100 PostgreSQL explicit locking support. Squashed commit of the following: commit76e1539102Author: Oliver Gorwits <oliver@cpan.org> Date: Wed May 15 23:54:25 2013 +0100 finished explicit locking module commit369387258bAuthor: Oliver Gorwits <oliver@cpan.org> Date: Tue May 14 23:50:42 2013 +0100 initial implementation of locking from schema object commit55c6d4fe63Author: Oliver Gorwits <oliver@cpan.org> Date: Tue May 14 21:05:01 2013 +0100 add discover button to device details page commit11fd8bf964Author: Oliver Gorwits <oliver@cpan.org> Date: Tue May 14 20:43:43 2013 +0100 fix typo and clear port box on autocomplete dropdown commita00f9b5c2eAuthor: Oliver Gorwits <oliver@cpan.org> Date: Tue May 14 20:38:54 2013 +0100 move admin tasks and remove JobControl package commit74bc0023dfAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sat May 11 18:25:04 2013 +0100 complete job queue delete and kill running timers properly when reloading page commitdd6947f38dAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sat May 11 16:51:28 2013 +0100 fix improper use of bootstrap table class commitcd5b83f71eAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sat May 11 15:55:45 2013 +0100 fix update view icon in sidebar commite9349f325dAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sat May 11 11:57:19 2013 +0100 css audit commit201470275dAuthor: Oliver Gorwits <oliver@cpan.org> Date: Thu May 9 23:48:05 2013 +0100 add job queue to standard plugins list commita18a3c72a3Author: Oliver Gorwits <oliver@cpan.org> Date: Thu May 9 23:37:43 2013 +0100 fix table headings and improve Action display in Job Queue commit70f5da8bb6Author: Oliver Gorwits <oliver@cpan.org> Date: Thu May 9 23:30:32 2013 +0100 implement "no devices" prompt for admin users to do first discover commit2e8ac83173Author: Oliver Gorwits <oliver@cpan.org> Date: Thu May 9 21:53:39 2013 +0100 more js refactoring for report and search commit479ac0e55dAuthor: Oliver Gorwits <oliver@cpan.org> Date: Thu May 9 21:50:29 2013 +0100 refactor js for device tabs commit6a17fe5d6cAuthor: Oliver Gorwits <oliver@cpan.org> Date: Thu May 9 21:05:42 2013 +0100 fix crazy races with javasacript by using global delegations commite94e3cef3bAuthor: Oliver Gorwits <oliver@cpan.org> Date: Wed May 8 23:06:41 2013 +0100 remove Try::Tiny from web runtime commitc746e68b9bAuthor: Oliver Gorwits <oliver@cpan.org> Date: Tue May 7 21:54:11 2013 +0100 make topo autocomplete more responsive commit24c511786fAuthor: Oliver Gorwits <oliver@cpan.org> Date: Tue May 7 21:52:17 2013 +0100 display name and IP for device typeahead commit52ab7d1266Author: Oliver Gorwits <oliver@cpan.org> Date: Tue May 7 21:47:05 2013 +0100 add drop-down control for the topo form fields commit5744b6845fAuthor: Oliver Gorwits <oliver@cpan.org> Date: Tue May 7 21:25:30 2013 +0100 complete the topology editor (add/delete) commitb510fbe8c5Author: Oliver Gorwits <oliver@cpan.org> Date: Tue May 7 00:59:11 2013 +0100 add new admin tasks to default plugins list commit11d55e0129Author: Oliver Gorwits <oliver@cpan.org> Date: Tue May 7 00:56:19 2013 +0100 Manual Device Topology Needed to add the 'autocomplete' jQuery UI component because it can do minLength=0 properly. Used the smoothness UI theme. Added typeahead AJAX calls to support the topology searching. Added new plugin and template for the topology editing page. commitbf7a419d08Author: Oliver Gorwits <oliver@cpan.org> Date: Mon May 6 22:16:24 2013 +0100 add a little colour to lone tab titles commit9690a31f19Author: Oliver Gorwits <oliver@cpan.org> Date: Mon May 6 22:01:13 2013 +0100 complete Manage Pseudo Devices commit024f4d9a83Author: Oliver Gorwits <oliver@cpan.org> Date: Mon May 6 00:49:47 2013 +0100 use bootstrap font colour instead of css commitf75f1e5cbfAuthor: Oliver Gorwits <oliver@cpan.org> Date: Mon May 6 00:45:18 2013 +0100 add frontend update/del forms, and display port count commitf0899e16b3Author: Oliver Gorwits <oliver@cpan.org> Date: Sun May 5 23:53:20 2013 +0100 add frontend pseudo device add form commit3271c01931Author: Oliver Gorwits <oliver@cpan.org> Date: Sun May 5 21:45:17 2013 +0100 complete the code for admin tasks page loading commit38f70624f3Author: Oliver Gorwits <oliver@cpan.org> Date: Sun May 5 17:04:30 2013 +0100 set up file paths consistently in all scripts commitc761ca839bAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sun May 5 17:00:30 2013 +0100 Helper script to import the Netdisco 1.x Topology file to the database commitf468b48049Author: Oliver Gorwits <oliver@cpan.org> Date: Sun May 5 16:20:39 2013 +0100 Handle whitespace ahead of OUI data commit5c8a5754f6Author: Oliver Gorwits <oliver@cpan.org> Date: Sun May 5 16:16:20 2013 +0100 also set neighbor info when discovering device interfaces commitacb988b6afAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sun May 5 15:34:20 2013 +0100 try to avoid duplicate execution of scheduled jobs commitc6bcaf66c5Author: Oliver Gorwits <oliver@cpan.org> Date: Sun May 5 14:16:25 2013 +0100 do not clobber manual topo when discovering neighbors commitd9a6a1882aAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sun May 5 13:02:45 2013 +0100 User icon color indicates port_control/admin ability commit2cdcb9db7eAuthor: Oliver Gorwits <oliver@cpan.org> Date: Mon Apr 29 23:34:27 2013 +0100 add support for admin tasks as plugins commit075a770c9aAuthor: Oliver Gorwits <oliver@cpan.org> Date: Mon Apr 29 22:23:20 2013 +0100 skip pseudo devices (vendor netdisco) commit045c022d42Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Apr 29 21:58:33 2013 +0100 incorporate manual topo info from the topology db table commit09285d42b4Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Apr 27 18:39:12 2013 +0100 add unique constraints to topology table commit2780b72e49Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Apr 27 15:38:05 2013 +0100 muted help text in sidebar commit733d4f83fbAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sat Apr 27 14:39:54 2013 +0100 sorry, testing hook changes commit71e366e352Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Apr 27 14:34:36 2013 +0100 sorry, testing hook changes commit7f9eaa99f5Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Apr 27 14:33:44 2013 +0100 sorry, testing hook changes commit5215fd632dAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sat Apr 27 14:30:07 2013 +0100 sorry, testing hook changes commitbe817d60c2Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Apr 27 14:21:45 2013 +0100 sorry, testing hook changes commit1fd3695358Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Apr 27 14:18:57 2013 +0100 sorry, testing hook changes commitac448c4a91Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Apr 27 14:13:03 2013 +0100 sorry, testing hook changes commitc563b8d9afAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sat Apr 27 14:08:54 2013 +0100 sorry, testing hook changes commit3abcfb01d5Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Apr 27 14:06:25 2013 +0100 sorry, testing hook changes commit877a81facfAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sat Apr 27 14:05:25 2013 +0100 sorry, testing hook changes
This commit is contained in:
		| @@ -1,730 +0,0 @@ | ||||
| package App::Netdisco::Util::DiscoverAndStore; | ||||
|  | ||||
| use Dancer qw/:syntax :script/; | ||||
| use Dancer::Plugin::DBIC 'schema'; | ||||
|  | ||||
| use App::Netdisco::Util::Device 'get_device'; | ||||
| use App::Netdisco::Util::DNS ':all'; | ||||
| use NetAddr::IP::Lite ':lower'; | ||||
| use Try::Tiny; | ||||
|  | ||||
| use base 'Exporter'; | ||||
| our @EXPORT = (); | ||||
| our @EXPORT_OK = qw/ | ||||
|   store_device store_interfaces store_wireless | ||||
|   store_vlans store_power store_modules | ||||
|   find_neighbors | ||||
| /; | ||||
| our %EXPORT_TAGS = (all => \@EXPORT_OK); | ||||
|  | ||||
| =head1 NAME | ||||
|  | ||||
| App::Netdisco::Util::DiscoverAndStore | ||||
|  | ||||
| =head1 DESCRIPTION | ||||
|  | ||||
| A set of helper subroutines to support parts of the Netdisco application. | ||||
|  | ||||
| There are no default exports, however the C<:all> tag will export all | ||||
| subroutines. | ||||
|  | ||||
| =head1 EXPORT_OK | ||||
|  | ||||
| =head2 store_device( $device, $snmp ) | ||||
|  | ||||
| Given a Device database object, and a working SNMP connection, discover and | ||||
| store basic device 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_device { | ||||
|   my ($device, $snmp) = @_; | ||||
|  | ||||
|   my $ip_index   = $snmp->ip_index; | ||||
|   my $interfaces = $snmp->interfaces; | ||||
|   my $ip_netmask = $snmp->ip_netmask; | ||||
|  | ||||
|   # find root IP | ||||
|   _set_canonical_ip($device, $snmp); | ||||
|  | ||||
|   my $hostname = hostname_from_ip($device->ip); | ||||
|   $device->dns($hostname) if length $hostname; | ||||
|  | ||||
|   # build device aliases suitable for DBIC | ||||
|   my @aliases; | ||||
|   foreach my $entry (keys %$ip_index) { | ||||
|       my $ip = NetAddr::IP::Lite->new($entry); | ||||
|       my $addr = $ip->addr; | ||||
|  | ||||
|       next if $addr eq '0.0.0.0'; | ||||
|       next if $ip->within(NetAddr::IP::Lite->new('127.0.0.0/8')); | ||||
|       next if setting('ignore_private_nets') and $ip->is_rfc1918; | ||||
|  | ||||
|       my $iid = $ip_index->{$addr}; | ||||
|       my $port = $interfaces->{$iid}; | ||||
|       my $subnet = $ip_netmask->{$addr} | ||||
|         ? NetAddr::IP::Lite->new($addr, $ip_netmask->{$addr})->network->cidr | ||||
|         : undef; | ||||
|  | ||||
|       debug sprintf ' [%s] device - aliased as %s', $device->ip, $addr; | ||||
|       push @aliases, { | ||||
|           alias => $addr, | ||||
|           port => $port, | ||||
|           subnet => $subnet, | ||||
|           dns => hostname_from_ip($addr), | ||||
|       }; | ||||
|   } | ||||
|  | ||||
|   # VTP Management Domain -- assume only one. | ||||
|   my $vtpdomains = $snmp->vtp_d_name; | ||||
|   my $vtpdomain; | ||||
|   if (defined $vtpdomains and scalar values %$vtpdomains) { | ||||
|       $device->vtp_domain( (values %$vtpdomains)[-1] ); | ||||
|   } | ||||
|  | ||||
|   my @properties = qw/ | ||||
|     snmp_ver snmp_comm | ||||
|     description uptime contact name location | ||||
|     layers ports mac serial model | ||||
|     ps1_type ps2_type ps1_status ps2_status | ||||
|     fan slots | ||||
|     vendor os os_ver | ||||
|   /; | ||||
|  | ||||
|   foreach my $property (@properties) { | ||||
|       $device->$property( $snmp->$property ); | ||||
|   } | ||||
|  | ||||
|   $device->snmp_class( $snmp->device_type ); | ||||
|   $device->last_discover(\'now()'); | ||||
|  | ||||
|   schema('netdisco')->txn_do(sub { | ||||
|     my $gone = $device->device_ips->delete; | ||||
|     debug sprintf ' [%s] device - removed %s aliases', | ||||
|       $device->ip, $gone; | ||||
|     $device->update_or_insert; | ||||
|     $device->device_ips->populate(\@aliases); | ||||
|     debug sprintf ' [%s] device - added %d new aliases', | ||||
|       $device->ip, scalar @aliases; | ||||
|   }); | ||||
| } | ||||
|  | ||||
| sub _set_canonical_ip { | ||||
|   my ($device, $snmp) = @_; | ||||
|  | ||||
|   my $oldip = $device->ip; | ||||
|   my $newip = $snmp->root_ip; | ||||
|  | ||||
|   if (length $newip) { | ||||
|       if ($oldip ne $newip) { | ||||
|           debug sprintf ' [%s] device - changing root IP to alt IP %s', | ||||
|             $oldip, $newip; | ||||
|  | ||||
|           # remove old device and aliases | ||||
|           schema('netdisco')->txn_do(sub { | ||||
|             my $gone = $device->device_ips->delete; | ||||
|             debug sprintf ' [%s] device - removed %s aliases', | ||||
|               $oldip, $gone; | ||||
|             $device->delete; | ||||
|             debug sprintf ' [%s] device - deleted self', $oldip; | ||||
|           }); | ||||
|  | ||||
|           $device->ip($newip); | ||||
|       } | ||||
|  | ||||
|       # either root_ip is changed or unchanged, but it exists | ||||
|       return; | ||||
|   } | ||||
|  | ||||
|   my $revname = ipv4_from_hostname($snmp->name); | ||||
|   if (setting('reverse_sysname') and $revname) { | ||||
|       debug sprintf ' [%s] device - changing root IP to revname %s', | ||||
|         $oldip, $revname; | ||||
|       $device->ip($revname); | ||||
|   } | ||||
| } | ||||
|  | ||||
| =head2 store_interfaces( $device, $snmp ) | ||||
|  | ||||
| Given a Device database object, and a working SNMP connection, discover and | ||||
| store the device's interface/port 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_interfaces { | ||||
|   my ($device, $snmp) = @_; | ||||
|  | ||||
|   my $interfaces     = $snmp->interfaces; | ||||
|   my $i_type         = $snmp->i_type; | ||||
|   my $i_ignore       = $snmp->i_ignore; | ||||
|   my $i_descr        = $snmp->i_description; | ||||
|   my $i_mtu          = $snmp->i_mtu; | ||||
|   my $i_speed        = $snmp->i_speed; | ||||
|   my $i_mac          = $snmp->i_mac; | ||||
|   my $i_up           = $snmp->i_up; | ||||
|   my $i_up_admin     = $snmp->i_up_admin; | ||||
|   my $i_name         = $snmp->i_name; | ||||
|   my $i_duplex       = $snmp->i_duplex; | ||||
|   my $i_duplex_admin = $snmp->i_duplex_admin; | ||||
|   my $i_stp_state    = $snmp->i_stp_state; | ||||
|   my $i_vlan         = $snmp->i_vlan; | ||||
|   my $i_pvid         = $snmp->i_pvid; | ||||
|   my $i_lastchange   = $snmp->i_lastchange; | ||||
|  | ||||
|   # clear the cached uptime and get a new one | ||||
|   my $dev_uptime = $snmp->load_uptime; | ||||
|  | ||||
|   if (scalar grep {$_ > $dev_uptime} values %$i_lastchange) { | ||||
|       info sprintf ' [%s] interfaces - device uptime has wrapped - correcting', | ||||
|         $device->ip; | ||||
|       $device->uptime( $dev_uptime + 2**32 ); | ||||
|   } | ||||
|  | ||||
|   # build device interfaces suitable for DBIC | ||||
|   my @interfaces; | ||||
|   foreach my $entry (keys %$interfaces) { | ||||
|       my $port = $interfaces->{$entry}; | ||||
|  | ||||
|       if (not length $port) { | ||||
|           debug sprintf ' [%s] interfaces - ignoring %s (no port mapping)', | ||||
|             $device->ip, $port; | ||||
|           next; | ||||
|       } | ||||
|  | ||||
|       if (scalar grep {$port =~ m/$_/} @{setting('ignore_interfaces') || []}) { | ||||
|           debug sprintf | ||||
|             ' [%s] interfaces - ignoring %s (%s) (config:ignore_interfaces)', | ||||
|             $device->ip, $entry, $port; | ||||
|           next; | ||||
|       } | ||||
|  | ||||
|       if (exists $i_ignore->{$entry}) { | ||||
|           debug sprintf ' [%s] interfaces - ignoring %s (%s) (%s)', | ||||
|             $device->ip, $entry, $port, $i_type->{$entry}; | ||||
|           next; | ||||
|       } | ||||
|  | ||||
|       my $lc = $i_lastchange->{$entry}; | ||||
|       if ($device->is_column_changed('uptime') and $lc) { | ||||
|           if ($lc < $dev_uptime) { | ||||
|               # ambiguous: lastchange could be sysUptime before or after wrap | ||||
|               if ($dev_uptime > 30000 and $lc < 30000) { | ||||
|                   # uptime wrap more than 5min ago but lastchange within 5min | ||||
|                   # assume lastchange was directly after boot -> no action | ||||
|               } | ||||
|               else { | ||||
|                   # uptime wrap less than 5min ago or lastchange > 5min ago | ||||
|                   # to be on safe side, assume lastchange after counter wrap | ||||
|                   debug sprintf | ||||
|                     ' [%s] interfaces - correcting LastChange for %s, assuming sysUptime wrap', | ||||
|                     $device->ip, $port; | ||||
|                   $lc += 2**32; | ||||
|               } | ||||
|           } | ||||
|       } | ||||
|  | ||||
|       push @interfaces, { | ||||
|           port         => $port, | ||||
|           descr        => $i_descr->{$entry}, | ||||
|           up           => $i_up->{$entry}, | ||||
|           up_admin     => $i_up_admin->{$entry}, | ||||
|           mac          => $i_mac->{$entry}, | ||||
|           speed        => $i_speed->{$entry}, | ||||
|           mtu          => $i_mtu->{$entry}, | ||||
|           name         => $i_name->{$entry}, | ||||
|           duplex       => $i_duplex->{$entry}, | ||||
|           duplex_admin => $i_duplex_admin->{$entry}, | ||||
|           stp          => $i_stp_state->{$entry}, | ||||
|           type         => $i_type->{$entry}, | ||||
|           vlan         => $i_vlan->{$entry}, | ||||
|           pvid         => $i_pvid->{$entry}, | ||||
|           lastchange   => $lc, | ||||
|       }; | ||||
|   } | ||||
|  | ||||
|   schema('netdisco')->txn_do(sub { | ||||
|     my $gone = $device->ports->delete; | ||||
|     debug sprintf ' [%s] interfaces - removed %s interfaces', | ||||
|       $device->ip, $gone; | ||||
|     $device->update_or_insert; | ||||
|     $device->ports->populate(\@interfaces); | ||||
|     debug sprintf ' [%s] interfaces - added %d new interfaces', | ||||
|       $device->ip, scalar @interfaces; | ||||
|   }); | ||||
| } | ||||
|  | ||||
| =head2 store_wireless( $device, $snmp ) | ||||
|  | ||||
| Given a Device database object, and a working SNMP connection, discover and | ||||
| store the device's wireless interface 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_wireless { | ||||
|   my ($device, $snmp) = @_; | ||||
|  | ||||
|   my $ssidlist = $snmp->i_ssidlist; | ||||
|   return unless scalar keys %$ssidlist; | ||||
|  | ||||
|   my $interfaces = $snmp->interfaces; | ||||
|   my $ssidbcast  = $snmp->i_ssidbcast; | ||||
|   my $ssidmac    = $snmp->i_ssidmac; | ||||
|   my $channel    = $snmp->i_80211channel; | ||||
|   my $power      = $snmp->dot11_cur_tx_pwr_mw; | ||||
|  | ||||
|   # build device ssid list suitable for DBIC | ||||
|   my @ssids; | ||||
|   foreach my $entry (keys %$ssidlist) { | ||||
|       (my $iid = $entry) =~ s/\.\d+$//; | ||||
|       my $port = $interfaces->{$iid}; | ||||
|  | ||||
|       if (not length $port) { | ||||
|           debug sprintf ' [%s] wireless - ignoring %s (no port mapping)', | ||||
|             $device->ip, $port; | ||||
|           next; | ||||
|       } | ||||
|  | ||||
|       push @ssids, { | ||||
|           port      => $port, | ||||
|           ssid      => $ssidlist->{$entry}, | ||||
|           broadcast => $ssidbcast->{$entry}, | ||||
|           bssid     => $ssidmac->{$entry}, | ||||
|       }; | ||||
|   } | ||||
|  | ||||
|   schema('netdisco')->txn_do(sub { | ||||
|     my $gone = $device->ssids->delete; | ||||
|     debug sprintf ' [%s] wireless - removed %s SSIDs', | ||||
|       $device->ip, $gone; | ||||
|     $device->ssids->populate(\@ssids); | ||||
|     debug sprintf ' [%s] wireless - added %d new SSIDs', | ||||
|       $device->ip, scalar @ssids; | ||||
|   }); | ||||
|  | ||||
|   # build device channel list suitable for DBIC | ||||
|   my @channels; | ||||
|   foreach my $entry (keys %$channel) { | ||||
|       my $port = $interfaces->{$entry}; | ||||
|  | ||||
|       if (not length $port) { | ||||
|           debug sprintf ' [%s] wireless - ignoring %s (no port mapping)', | ||||
|             $device->ip, $port; | ||||
|           next; | ||||
|       } | ||||
|  | ||||
|       push @channels, { | ||||
|           port    => $port, | ||||
|           channel => $channel->{$entry}, | ||||
|           power   => $power->{$entry}, | ||||
|       }; | ||||
|   } | ||||
|  | ||||
|   schema('netdisco')->txn_do(sub { | ||||
|     my $gone = $device->wireless_ports->delete; | ||||
|     debug sprintf ' [%s] wireless - removed %s wireless channels', | ||||
|       $device->ip, $gone; | ||||
|     $device->wireless_ports->populate(\@channels); | ||||
|     debug sprintf ' [%s] wireless - added %d new wireless channels', | ||||
|       $device->ip, scalar @channels; | ||||
|   }); | ||||
| } | ||||
|  | ||||
| =head2 store_vlans( $device, $snmp ) | ||||
|  | ||||
| Given a Device database object, and a working SNMP connection, discover and | ||||
| store the device's vlan 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_vlans { | ||||
|   my ($device, $snmp) = @_; | ||||
|  | ||||
|   my $v_name  = $snmp->v_name; | ||||
|   my $v_index = $snmp->v_index; | ||||
|  | ||||
|   # build device vlans suitable for DBIC | ||||
|   my %v_seen = (); | ||||
|   my @devicevlans; | ||||
|   foreach my $entry (keys %$v_name) { | ||||
|       my $vlan = $v_index->{$entry}; | ||||
|       ++$v_seen{$vlan}; | ||||
|  | ||||
|       push @devicevlans, { | ||||
|           vlan => $vlan, | ||||
|           description => $v_name->{$entry}, | ||||
|           last_discover => \'now()', | ||||
|       }; | ||||
|   } | ||||
|  | ||||
|   my $i_vlan            = $snmp->i_vlan; | ||||
|   my $i_vlan_membership = $snmp->i_vlan_membership; | ||||
|   my $i_vlan_type       = $snmp->i_vlan_type; | ||||
|   my $interfaces        = $snmp->interfaces; | ||||
|  | ||||
|   # build device port vlans suitable for DBIC | ||||
|   my @portvlans; | ||||
|   foreach my $entry (keys %$i_vlan_membership) { | ||||
|       my $port = $interfaces->{$entry}; | ||||
|       next unless defined $port; | ||||
|  | ||||
|       my $type = $i_vlan_type->{$entry}; | ||||
|  | ||||
|       foreach my $vlan (@{ $i_vlan_membership->{$entry} }) { | ||||
|           my $native = ((defined $i_vlan->{$entry}) and ($vlan eq $i_vlan->{$entry})) ? "t" : "f"; | ||||
|           push @portvlans, { | ||||
|               port => $port, | ||||
|               vlan => $vlan, | ||||
|               native => $native, | ||||
|               vlantype => $type, | ||||
|               last_discover => \'now()', | ||||
|           }; | ||||
|  | ||||
|           next if $v_seen{$vlan}; | ||||
|  | ||||
|           # also add an unnamed vlan to the device | ||||
|           push @devicevlans, { | ||||
|               vlan => $vlan, | ||||
|               description => (sprintf "VLAN %d", $vlan), | ||||
|               last_discover => \'now()', | ||||
|           }; | ||||
|           ++$v_seen{$vlan}; | ||||
|       } | ||||
|   } | ||||
|  | ||||
|   schema('netdisco')->txn_do(sub { | ||||
|     my $gone = $device->vlans->delete; | ||||
|     debug sprintf ' [%s] vlans - removed %s device VLANs', | ||||
|       $device->ip, $gone; | ||||
|     $device->vlans->populate(\@devicevlans); | ||||
|     debug sprintf ' [%s] vlans - added %d new device VLANs', | ||||
|       $device->ip, scalar @devicevlans; | ||||
|   }); | ||||
|  | ||||
|   schema('netdisco')->txn_do(sub { | ||||
|     my $gone = $device->port_vlans->delete; | ||||
|     debug sprintf ' [%s] vlans - removed %s port VLANs', | ||||
|       $device->ip, $gone; | ||||
|     $device->port_vlans->populate(\@portvlans); | ||||
|     debug sprintf ' [%s] vlans - added %d new port VLANs', | ||||
|       $device->ip, scalar @portvlans; | ||||
|   }); | ||||
| } | ||||
|  | ||||
| =head2 store_power( $device, $snmp ) | ||||
|  | ||||
| Given a Device database object, and a working SNMP connection, discover and | ||||
| store the device's PoE 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_power { | ||||
|   my ($device, $snmp) = @_; | ||||
|  | ||||
|   my $p_watts  = $snmp->peth_power_watts; | ||||
|   my $p_status = $snmp->peth_power_status; | ||||
|  | ||||
|   if (!defined $p_watts) { | ||||
|       debug sprintf ' [%s] power - 0 power modules', $device->ip; | ||||
|       return; | ||||
|   } | ||||
|  | ||||
|   # build device module power info suitable for DBIC | ||||
|   my @devicepower; | ||||
|   foreach my $entry (keys %$p_watts) { | ||||
|       push @devicepower, { | ||||
|           module => $entry, | ||||
|           power  => $p_watts->{$entry}, | ||||
|           status => $p_status->{$entry}, | ||||
|       }; | ||||
|   } | ||||
|  | ||||
|   my $interfaces = $snmp->interfaces; | ||||
|   my $p_ifindex  = $snmp->peth_port_ifindex; | ||||
|   my $p_admin    = $snmp->peth_port_admin; | ||||
|   my $p_pstatus  = $snmp->peth_port_status; | ||||
|   my $p_class    = $snmp->peth_port_class; | ||||
|   my $p_power    = $snmp->peth_port_power; | ||||
|  | ||||
|   # 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; | ||||
|  | ||||
|       my ($module) = split m/\./, $entry; | ||||
|  | ||||
|       push @portpower, { | ||||
|           port   => $port, | ||||
|           module => $module, | ||||
|           admin  => $p_admin->{$entry}, | ||||
|           status => $p_pstatus->{$entry}, | ||||
|           class  => $p_class->{$entry}, | ||||
|           power  => $p_power->{$entry}, | ||||
|  | ||||
|       }; | ||||
|   } | ||||
|  | ||||
|   schema('netdisco')->txn_do(sub { | ||||
|     my $gone = $device->power_modules->delete; | ||||
|     debug sprintf ' [%s] power - removed %s power modules', | ||||
|       $device->ip, $gone; | ||||
|     $device->power_modules->populate(\@devicepower); | ||||
|     debug sprintf ' [%s] power - added %d new power modules', | ||||
|       $device->ip, scalar @devicepower; | ||||
|   }); | ||||
|  | ||||
|   schema('netdisco')->txn_do(sub { | ||||
|     my $gone = $device->powered_ports->delete; | ||||
|     debug sprintf ' [%s] power - removed %s PoE capable ports', | ||||
|       $device->ip, $gone; | ||||
|     $device->powered_ports->populate(\@portpower); | ||||
|     debug sprintf ' [%s] power - added %d new PoE capable ports', | ||||
|       $device->ip, scalar @portpower; | ||||
|   }); | ||||
| } | ||||
|  | ||||
| =head2 store_modules( $device, $snmp ) | ||||
|  | ||||
| Given a Device database object, and a working SNMP connection, discover and | ||||
| store the device's module 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_modules { | ||||
|   my ($device, $snmp) = @_; | ||||
|  | ||||
|   my $e_index  = $snmp->e_index; | ||||
|  | ||||
|   if (!defined $e_index) { | ||||
|       debug sprintf ' [%s] modules - 0 chassis components', $device->ip; | ||||
|       return; | ||||
|   } | ||||
|  | ||||
|   my $e_descr   = $snmp->e_descr; | ||||
|   my $e_type    = $snmp->e_type; | ||||
|   my $e_parent  = $snmp->e_parent; | ||||
|   my $e_name    = $snmp->e_name; | ||||
|   my $e_class   = $snmp->e_class; | ||||
|   my $e_pos     = $snmp->e_pos; | ||||
|   my $e_hwver   = $snmp->e_hwver; | ||||
|   my $e_fwver   = $snmp->e_fwver; | ||||
|   my $e_swver   = $snmp->e_swver; | ||||
|   my $e_model   = $snmp->e_model; | ||||
|   my $e_serial  = $snmp->e_serial; | ||||
|   my $e_fru     = $snmp->e_fru; | ||||
|  | ||||
|   # build device modules list for DBIC | ||||
|   my @modules; | ||||
|   foreach my $entry (keys %$e_class) { | ||||
|       push @modules, { | ||||
|           index  => $e_index->{$entry}, | ||||
|           type   => $e_type->{$entry}, | ||||
|           parent => $e_parent->{$entry}, | ||||
|           name   => $e_name->{$entry}, | ||||
|           class  => $e_class->{$entry}, | ||||
|           pos    => $e_pos->{$entry}, | ||||
|           hw_ver => $e_hwver->{$entry}, | ||||
|           fw_ver => $e_fwver->{$entry}, | ||||
|           sw_ver => $e_swver->{$entry}, | ||||
|           model  => $e_model->{$entry}, | ||||
|           serial => $e_serial->{$entry}, | ||||
|           fru    => $e_fru->{$entry}, | ||||
|           description => $e_descr->{$entry}, | ||||
|           last_discover => \'now()', | ||||
|       }; | ||||
|   } | ||||
|  | ||||
|   schema('netdisco')->txn_do(sub { | ||||
|     my $gone = $device->modules->delete; | ||||
|     debug sprintf ' [%s] modules - removed %s chassis modules', | ||||
|       $device->ip, $gone; | ||||
|     $device->modules->populate(\@modules); | ||||
|     debug sprintf ' [%s] modules - added %d new chassis modules', | ||||
|       $device->ip, scalar @modules; | ||||
|   }); | ||||
| } | ||||
|  | ||||
| =head2 find_neighbors( $device, $snmp ) | ||||
|  | ||||
| Given a Device database object, and a working SNMP connection, discover and | ||||
| store the device's port neighbors information. | ||||
|  | ||||
| If any neighbor is unknown to Netdisco, a discover job for it will immediately | ||||
| be queued (modulo configuration file C<discover_no_type> setting). | ||||
|  | ||||
| The Device database object can be a fresh L<DBIx::Class::Row> object which is | ||||
| not yet stored to the database. | ||||
|  | ||||
| =cut | ||||
|  | ||||
| sub find_neighbors { | ||||
|   my ($device, $snmp) = @_; | ||||
|  | ||||
|   my $c_ip = $snmp->c_ip; | ||||
|   unless ($snmp->hasCDP or scalar keys %$c_ip) { | ||||
|       debug sprintf ' [%s] neigh - CDP/LLDP not enabled!', $device->ip; | ||||
|       return; | ||||
|   } | ||||
|  | ||||
|   my $interfaces = $snmp->interfaces; | ||||
|   my $c_if       = $snmp->c_if; | ||||
|   my $c_port     = $snmp->c_port; | ||||
|   my $c_id       = $snmp->c_id; | ||||
|   my $c_platform = $snmp->c_platform; | ||||
|  | ||||
|   foreach my $entry (keys %$c_ip) { | ||||
|       my $port = $interfaces->{ $c_if->{$entry} }; | ||||
|       if (!defined $port) { | ||||
|           debug sprintf ' [%s] neigh - port for IID:%s not resolved, skipping', | ||||
|             $device->ip, $entry; | ||||
|           next; | ||||
|       } | ||||
|  | ||||
|       my $remote_ip   = $c_ip->{$entry}; | ||||
|       my $remote_ipad = NetAddr::IP::Lite->new($remote_ip); | ||||
|       my $remote_port = undef; | ||||
|       my $remote_type = $c_platform->{$entry}; | ||||
|       my $remote_id   = $c_id->{$entry}; | ||||
|  | ||||
|       next unless length $remote_ip; | ||||
|  | ||||
|       # a bunch of heuristics to search known devices if we don't have a | ||||
|       # useable remote IP... | ||||
|  | ||||
|       if ($remote_ip eq '0.0.0.0' or | ||||
|           $remote_ipad->within(NetAddr::IP::Lite->new('127.0.0.0/8'))) { | ||||
|  | ||||
|           if ($remote_id) { | ||||
|               my $devices = schema('netdisco')->resultset('Device'); | ||||
|               my $neigh = $devices->single({name => $remote_id}); | ||||
|               info sprintf | ||||
|                 ' [%s] neigh - bad address %s on port %s, searching for %s instead', | ||||
|                 $device->ip, $remote_ip, $port, $remote_id; | ||||
|  | ||||
|               if (!defined $neigh) { | ||||
|                   (my $shortid = $remote_id) =~ s/\..*//; | ||||
|                   $neigh = $devices->single({name => { -ilike => "${shortid}%" }}); | ||||
|               } | ||||
|  | ||||
|               if ($neigh) { | ||||
|                   $remote_ip = $neigh->ip; | ||||
|                   info sprintf ' [%s] neigh - found %s with IP %s', | ||||
|                     $device->ip, $remote_id, $remote_ip; | ||||
|               } | ||||
|               else { | ||||
|                   info 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', | ||||
|                 $device->ip, $remote_ip, $port; | ||||
|               next; | ||||
|           } | ||||
|       } | ||||
|  | ||||
|       # hack for devices seeing multiple neighbors on the port | ||||
|       if (ref [] eq ref $remote_ip) { | ||||
|           debug sprintf | ||||
|             ' [%s] neigh - port %s has multiple neighbors, setting remote as self', | ||||
|             $device->ip, $port; | ||||
|  | ||||
|           foreach my $n (@$remote_ip) { | ||||
|               debug sprintf | ||||
|                 ' [%s] neigh - adding neighbor %s, type [%s], on %s to discovery queue', | ||||
|                 $device->ip, $n, $remote_type, $port; | ||||
|               _enqueue_discover($n, $remote_type); | ||||
|           } | ||||
|  | ||||
|           # set self as remote IP to suppress any further work | ||||
|           $remote_ip = $device->ip; | ||||
|           $remote_port = $port; | ||||
|       } | ||||
|       else { | ||||
|           $remote_port = $c_port->{$entry}; | ||||
|  | ||||
|           if (defined $remote_port) { | ||||
|               # clean weird characters | ||||
|               $remote_port =~ s/[^\d\/\.,()\w:-]+//gi; | ||||
|           } | ||||
|           else { | ||||
|               info sprintf ' [%s] neigh - no remote port found for port %s at %s', | ||||
|                 $device->ip, $port, $remote_ip; | ||||
|           } | ||||
|       } | ||||
|  | ||||
|       # XXX too custom? IP Phone detection | ||||
|       if (defined $remote_type and $remote_type =~ m/(mitel.5\d{3})/i) { | ||||
|           $remote_type = 'IP Phone - '. $remote_type | ||||
|             if $remote_type !~ /ip phone/i; | ||||
|       } | ||||
|  | ||||
|       my $portrow = schema('netdisco')->resultset('DevicePort') | ||||
|           ->single({ip => $device->ip, port => $port}); | ||||
|  | ||||
|       if (!defined $portrow) { | ||||
|           info sprintf ' [%s] neigh - local port %s not in database!', | ||||
|             $device->ip, $port; | ||||
|           next; | ||||
|       } | ||||
|  | ||||
|       $portrow->update({ | ||||
|           remote_ip   => $remote_ip, | ||||
|           remote_port => $remote_port, | ||||
|           remote_type => $remote_type, | ||||
|           remote_id   => $remote_id, | ||||
|       }); | ||||
|  | ||||
|       debug sprintf | ||||
|         ' [%s] neigh - adding neighbor %s, type [%s], on %s to discovery queue', | ||||
|         $device->ip, $remote_ip, $remote_type, $port; | ||||
|       _enqueue_discover($remote_ip, $remote_type); | ||||
|   } | ||||
| } | ||||
|  | ||||
| # only enqueue if device is not already discovered, and | ||||
| # discover_no_type config permits the discovery | ||||
| sub _enqueue_discover { | ||||
|   my ($ip, $remote_type) = @_; | ||||
|  | ||||
|   my $device = get_device($ip); | ||||
|   return if $device->in_storage; | ||||
|  | ||||
|   my $remote_type_match = setting('discover_no_type'); | ||||
|   if ($remote_type and $remote_type_match | ||||
|       and $remote_type =~ m/$remote_type_match/) { | ||||
|     debug sprintf '      queue - %s, type [%s] excluded by discover_no_type', | ||||
|       $ip, $remote_type; | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   try { | ||||
|       # could fail if queued job already exists | ||||
|       schema('netdisco')->resultset('Admin')->create({ | ||||
|           device => $ip, | ||||
|           action => 'discover', | ||||
|           status => 'queued', | ||||
|       }); | ||||
|   }; | ||||
| } | ||||
|  | ||||
| 1; | ||||
							
								
								
									
										56
									
								
								Netdisco/lib/App/Netdisco/Util/PortMAC.pm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								Netdisco/lib/App/Netdisco/Util/PortMAC.pm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| package App::Netdisco::Util::PortMAC; | ||||
|  | ||||
| use Dancer qw/:syntax :script/; | ||||
| use Dancer::Plugin::DBIC 'schema'; | ||||
|  | ||||
| use base 'Exporter'; | ||||
| our @EXPORT = (); | ||||
| our @EXPORT_OK = qw/ get_port_macs /; | ||||
| our %EXPORT_TAGS = (all => \@EXPORT_OK); | ||||
|  | ||||
| =head1 NAME | ||||
|  | ||||
| App::Netdisco::Util::PortMAC | ||||
|  | ||||
| =head1 DESCRIPTION | ||||
|  | ||||
| Helper subroutine to support parts of the Netdisco application. | ||||
|  | ||||
| There are no default exports, however the C<:all> tag will export all | ||||
| subroutines. | ||||
|  | ||||
| =head1 EXPORT_OK | ||||
|  | ||||
| =head2 get_port_macs( $device ) | ||||
|  | ||||
| Returns a Hash reference of C<< { MAC => IP } >> for all interface MAC | ||||
| addresses on a device. | ||||
|  | ||||
| =cut | ||||
|  | ||||
| sub get_port_macs { | ||||
|   my $device = shift; | ||||
|   my $port_macs = {}; | ||||
|  | ||||
|   unless ($device->in_storage) { | ||||
|       debug sprintf ' [%s] get_port_macs - skipping device not yet discovered', | ||||
|         $device->ip; | ||||
|       return $port_macs; | ||||
|   } | ||||
|  | ||||
|   my $dp_macs = schema('netdisco')->resultset('DevicePort') | ||||
|     ->search({ mac => { '!=' => undef} }); | ||||
|   while (my $r = $dp_macs->next) { | ||||
|       $port_macs->{ $r->mac } = $r->ip; | ||||
|   } | ||||
|  | ||||
|   my $d_macs = schema('netdisco')->resultset('Device') | ||||
|     ->search({ mac => { '!=' => undef} }); | ||||
|   while (my $r = $d_macs->next) { | ||||
|       $port_macs->{ $r->mac } = $r->ip; | ||||
|   } | ||||
|  | ||||
|   return $port_macs; | ||||
| } | ||||
|  | ||||
| 1; | ||||
| @@ -10,7 +10,7 @@ use Path::Class 'dir'; | ||||
| use base 'Exporter'; | ||||
| our @EXPORT = (); | ||||
| our @EXPORT_OK = qw/ | ||||
|   snmp_connect snmp_connect_rw | ||||
|   snmp_connect snmp_connect_rw snmp_comm_reindex | ||||
| /; | ||||
| our %EXPORT_TAGS = (all => \@EXPORT_OK); | ||||
|  | ||||
| @@ -88,8 +88,8 @@ sub _snmp_connect_generic { | ||||
|   my $comm_type = pop; | ||||
|   my @communities = @{ setting($comm_type) || []}; | ||||
|   unshift @communities, $device->snmp_comm | ||||
|     if length $device->snmp_comm | ||||
|        and length $comm_type and $comm_type eq 'community'; | ||||
|     if defined $device->snmp_comm | ||||
|        and defined $comm_type and $comm_type eq 'community'; | ||||
|  | ||||
|   my $info = undef; | ||||
|   VERSION: foreach my $ver (@versions) { | ||||
| @@ -123,7 +123,7 @@ sub _try_connect { | ||||
|       $info = $class->new(%$snmp_args, Version => $ver, Community => $comm); | ||||
|       undef $info unless ( | ||||
|         (not defined $info->error) | ||||
|         and length $info->uptime | ||||
|         and defined $info->uptime | ||||
|         and ($info->layers or $info->description) | ||||
|         and $info->class | ||||
|       ); | ||||
| @@ -149,7 +149,35 @@ sub _try_connect { | ||||
| sub _build_mibdirs { | ||||
|   my $home = (setting('mibhome') || $ENV{NETDISCO_HOME} || $ENV{HOME}); | ||||
|   return map { dir($home, $_) } | ||||
|              @{ setting('mibdirs') || [] }; | ||||
|              @{ setting('mibdirs') || _get_mibdirs_content($home) }; | ||||
| } | ||||
|  | ||||
| sub _get_mibdirs_content { | ||||
|   my $home = shift; | ||||
|   warning 'Netdisco SNMP work will be really slow - loading ALL MIBs. Please set mibdirs.'; | ||||
|   my @list = map {s|$home/||; $_} grep {-d} glob("$home/*"); | ||||
|   return \@list; | ||||
| } | ||||
|  | ||||
| =head2 snmp_comm_reindex( $snmp, $vlan ) | ||||
|  | ||||
| Takes an established L<SNMP::Info> instance and makes a fresh connection using | ||||
| community indexing, with the given C<$vlan> ID. Works for all SNMP versions. | ||||
|  | ||||
| =cut | ||||
|  | ||||
| sub snmp_comm_reindex { | ||||
|   my ($snmp, $vlan) = @_; | ||||
|  | ||||
|   my $ver  = $snmp->snmp_ver; | ||||
|   my $comm = $snmp->snmp_comm; | ||||
|  | ||||
|   if ($ver == 3) { | ||||
|       $snmp->update(Context => "vlan-$vlan"); | ||||
|   } | ||||
|   else { | ||||
|       $snmp->update(Community => $comm . '@' . $vlan); | ||||
|   } | ||||
| } | ||||
|  | ||||
| 1; | ||||
|   | ||||
							
								
								
									
										115
									
								
								Netdisco/lib/App/Netdisco/Util/SanityCheck.pm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								Netdisco/lib/App/Netdisco/Util/SanityCheck.pm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| package App::Netdisco::Util::SanityCheck; | ||||
|  | ||||
| use Dancer qw/:syntax :script/; | ||||
| use Dancer::Plugin::DBIC 'schema'; | ||||
|  | ||||
| use App::Netdisco::Util::PortMAC ':all'; | ||||
| use Net::MAC; | ||||
|  | ||||
| use base 'Exporter'; | ||||
| our @EXPORT = (); | ||||
| our @EXPORT_OK = qw/ check_mac /; | ||||
| our %EXPORT_TAGS = (all => \@EXPORT_OK); | ||||
|  | ||||
| =head1 NAME | ||||
|  | ||||
| App::Netdisco::Util::SanityCheck | ||||
|  | ||||
| =head1 DESCRIPTION | ||||
|  | ||||
| Helper subroutines to support parts of the Netdisco application. | ||||
|  | ||||
| There are no default exports, however the C<:all> tag will export all | ||||
| subroutines. | ||||
|  | ||||
| =head1 EXPORT_OK | ||||
|  | ||||
| =head2 check_mac( $device, $node, $port_macs? ) | ||||
|  | ||||
| Given a Device database object and a MAC address, perform various sanity | ||||
| checks which need to be done before writing an ARP/Neighbor entry to the | ||||
| database storage. | ||||
|  | ||||
| Returns false, and might log a debug level message, if the checks fail. | ||||
|  | ||||
| Returns a true value if these checks pass: | ||||
|  | ||||
| =over 4 | ||||
|  | ||||
| =item * | ||||
|  | ||||
| MAC address is well-formed (according to common formats) | ||||
|  | ||||
| =item * | ||||
|  | ||||
| MAC address is not all-zero, broadcast, CLIP, VRRP or HSRP | ||||
|  | ||||
| =item * | ||||
|  | ||||
| MAC address does not belong to an interface on any known Device | ||||
|  | ||||
| =back | ||||
|  | ||||
| Optionally pass a cached set of Device port MAC addresses as the third | ||||
| argument, or else C<check_mac> will retrieve this for itself from the | ||||
| database. | ||||
|  | ||||
| =cut | ||||
|  | ||||
| sub check_mac { | ||||
|   my ($device, $node, $port_macs) = @_; | ||||
|   $port_macs ||= get_port_macs($device); | ||||
|   my $mac = Net::MAC->new(mac => $node, 'die' => 0, verbose => 0); | ||||
|  | ||||
|   # incomplete MAC addresses (BayRS frame relay DLCI, etc) | ||||
|   if ($mac->get_error) { | ||||
|       debug sprintf ' [%s] check_mac - mac [%s] malformed - skipping', | ||||
|         $device->ip, $node; | ||||
|       return 0; | ||||
|   } | ||||
|   else { | ||||
|       # lower case, hex, colon delimited, 8-bit groups | ||||
|       $node = lc $mac->as_IEEE; | ||||
|   } | ||||
|  | ||||
|   # broadcast MAC addresses | ||||
|   return 0 if $node eq 'ff:ff:ff:ff:ff:ff'; | ||||
|  | ||||
|   # all-zero MAC addresses | ||||
|   return 0 if $node eq '00:00:00:00:00:00'; | ||||
|  | ||||
|   # CLIP | ||||
|   return 0 if $node eq '00:00:00:00:00:01'; | ||||
|  | ||||
|   # multicast | ||||
|   if ($node =~ m/^[0-9a-f](?:1|3|5|7|9|b|d|f):/) { | ||||
|       debug sprintf ' [%s] check_mac - multicast mac [%s] - skipping', | ||||
|         $device->ip, $node; | ||||
|       return 0; | ||||
|   } | ||||
|  | ||||
|   # VRRP | ||||
|   if (index($node, '00:00:5e:00:01:') == 0) { | ||||
|       debug sprintf ' [%s] check_mac - VRRP mac [%s] - skipping', | ||||
|         $device->ip, $node; | ||||
|       return 0; | ||||
|   } | ||||
|  | ||||
|   # HSRP | ||||
|   if (index($node, '00:00:0c:07:ac:') == 0) { | ||||
|       debug sprintf ' [%s] check_mac - HSRP mac [%s] - skipping', | ||||
|         $device->ip, $node; | ||||
|       return 0; | ||||
|   } | ||||
|  | ||||
|   # device's own MACs | ||||
|   if (exists $port_macs->{$node}) { | ||||
|       debug sprintf ' [%s] check_mac - mac [%s] is device port - skipping', | ||||
|         $device->ip, $node; | ||||
|       return 0; | ||||
|   } | ||||
|  | ||||
|   return 1; | ||||
| } | ||||
|  | ||||
| 1; | ||||
		Reference in New Issue
	
	Block a user