From bc6f941c0f08cc8cbcf25f3b80782dba8316b3e8 Mon Sep 17 00:00:00 2001 From: "Eric A. Miller" Date: Tue, 8 Jul 2014 21:19:18 -0400 Subject: [PATCH 01/41] Fix table rendering on port search tab when VLAN is null --- Netdisco/Changes | 6 ++++++ Netdisco/share/views/ajax/search/port.tt | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 4580a201..ce798cd6 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,3 +1,9 @@ +2.028001 + + [BUG FIXES] + + * Fix table rendering on port search tab when VLAN is null + 2.028000 - 2014-07-01 [NEW FEATURES] diff --git a/Netdisco/share/views/ajax/search/port.tt b/Netdisco/share/views/ajax/search/port.tt index 16a4f928..a4fbed26 100644 --- a/Netdisco/share/views/ajax/search/port.tt +++ b/Netdisco/share/views/ajax/search/port.tt @@ -42,7 +42,7 @@ $(document).ready(function() { }, { "data": 'port_vlans.vlan', "render": function(data, type, row, meta) { - return data; + return data || ''; } } ] From 3ee27f0439454c7dd451cf9c243931176abe6eae Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 13 Jul 2014 17:25:51 +0100 Subject: [PATCH 02/41] add bug id to last fix --- Netdisco/Changes | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index ce798cd6..1fd39636 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -2,8 +2,8 @@ [BUG FIXES] - * Fix table rendering on port search tab when VLAN is null - + * [#118] Fix table rendering on port search tab when VLAN is null + 2.028000 - 2014-07-01 [NEW FEATURES] From 769aa350441b150f244b3d1465b4640df7119eac Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 13 Jul 2014 17:35:23 +0100 Subject: [PATCH 03/41] [ND1#117] unknown devices missing from inventory --- Netdisco/Changes | 1 + Netdisco/share/views/inventory.tt | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 1fd39636..d0600dd7 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -2,6 +2,7 @@ [BUG FIXES] + * [ND1#117] unknown devices missing from inventory * [#118] Fix table rendering on port search tab when VLAN is null 2.028000 - 2014-07-01 diff --git a/Netdisco/share/views/inventory.tt b/Netdisco/share/views/inventory.tt index 1b1dc13e..323f917e 100644 --- a/Netdisco/share/views/inventory.tt +++ b/Netdisco/share/views/inventory.tt @@ -13,17 +13,21 @@ [% FOREACH platform IN models.all %] - [% NEXT UNLESS platform.vendor AND platform.model %] + [% NEXT UNLESS platform.vendor OR platform.model %] + [% IF platform.vendor %] [% platform.vendor | html_entity %] + [% ELSE %]unknown[% END %] + [% IF platform.model %] [% platform.model | html_entity %] + [% ELSE %]unknown[% END %] [% platform.get_column('count') | html_entity %] @@ -43,13 +47,15 @@ [% FOREACH release IN releases.all %] - [% NEXT UNLESS (release.os AND release.os_ver) %] + [% NEXT UNLESS (release.os OR release.os_ver) %] - [% release.os | html_entity %] + [% release.os || 'unknown' | html_entity %] + [% IF release.os_ver %] [% release.os_ver | html_entity %] + [% ELSE %]unknown[% END %] [% release.get_column('count') | html_entity %] From 3457d083f4dd619698d1bf284714cfd72bad56be Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 13 Jul 2014 17:51:41 +0100 Subject: [PATCH 04/41] portctl_nameonly to limit port control to name only (F. Schiavarelli) --- Netdisco/Changes | 4 ++++ Netdisco/lib/App/Netdisco/Manual/Configuration.pod | 8 ++++++++ Netdisco/lib/App/Netdisco/Util/Port.pm | 8 ++++++++ Netdisco/share/config.yml | 1 + 4 files changed, 21 insertions(+) diff --git a/Netdisco/Changes b/Netdisco/Changes index d0600dd7..558d2445 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,5 +1,9 @@ 2.028001 + [ENHANCEMENTS] + + * portctl_nameonly to limit port control to name only (F. Schiavarelli) + [BUG FIXES] * [ND1#117] unknown devices missing from inventory diff --git a/Netdisco/lib/App/Netdisco/Manual/Configuration.pod b/Netdisco/lib/App/Netdisco/Manual/Configuration.pod index 8407d3ab..7857e593 100644 --- a/Netdisco/lib/App/Netdisco/Manual/Configuration.pod +++ b/Netdisco/lib/App/Netdisco/Manual/Configuration.pod @@ -782,6 +782,14 @@ field to use as the management IP address for a device. Value: Boolean. Default: C. Set to false to prevent users from changing the default VLAN on an interface. +This setting has no effect when C below is set to true. + +=head3 C + +Value: Boolean. Default: C. + +Set to true to limit port control action to only changing the interface name +(description). =head3 C diff --git a/Netdisco/lib/App/Netdisco/Util/Port.pm b/Netdisco/lib/App/Netdisco/Util/Port.pm index eabea8af..a9a990d0 100644 --- a/Netdisco/lib/App/Netdisco/Util/Port.pm +++ b/Netdisco/lib/App/Netdisco/Util/Port.pm @@ -68,6 +68,10 @@ sub vlan_reconfig_check { =item * +Permission check that C is false in Netdisco config. + +=item * + Permission check that C is true in Netdisco config, if C<$port> is an uplink. @@ -95,6 +99,10 @@ sub port_reconfig_check { my $has_phone = port_has_phone($port); my $is_vlan = is_vlan_interface($port); + # only permitted to change interface name + return "forbidden: not permitted to change port configuration" + if setting('portctl_nameonly'); + # uplink check return "forbidden: port [$name] on [$ip] is an uplink" if $port->remote_type and not $has_phone and not setting('portctl_uplinks'); diff --git a/Netdisco/share/config.yml b/Netdisco/share/config.yml index e21eee9d..61609a56 100644 --- a/Netdisco/share/config.yml +++ b/Netdisco/share/config.yml @@ -144,6 +144,7 @@ ignore_interfaces: ignore_private_nets: false reverse_sysname: false vlanctl: true +portctl_nameonly: false portctl_nophones: false portctl_vlans: false portctl_uplinks: false From 13de616e35b30595a2c194fc2f0cf506a91e4010 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 13 Jul 2014 18:08:56 +0100 Subject: [PATCH 05/41] Fix truncated port description change (F. Schiavarelli) --- Netdisco/Changes | 1 + .../App/Netdisco/Daemon/Worker/Interactive/PortActions.pm | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 558d2445..8f199e4d 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -8,6 +8,7 @@ * [ND1#117] unknown devices missing from inventory * [#118] Fix table rendering on port search tab when VLAN is null + * Fix truncated port description change (F. Schiavarelli) 2.028000 - 2014-07-01 diff --git a/Netdisco/lib/App/Netdisco/Daemon/Worker/Interactive/PortActions.pm b/Netdisco/lib/App/Netdisco/Daemon/Worker/Interactive/PortActions.pm index 76761cae..917851a1 100644 --- a/Netdisco/lib/App/Netdisco/Daemon/Worker/Interactive/PortActions.pm +++ b/Netdisco/lib/App/Netdisco/Daemon/Worker/Interactive/PortActions.pm @@ -23,6 +23,10 @@ sub set_portcontrol { return job_error("Cannot alter port: $reconfig_check") if $reconfig_check; + # need to remove "-other" which appears for power/portcontrol + (my $sa = $job->subaction) =~ s/-\w+//; + $job->subaction($sa); + return _set_port_generic($job, 'up_admin'); } @@ -50,7 +54,7 @@ sub _set_port_generic { my $ip = $job->device; my $pn = $job->port; - (my $data = $job->subaction) =~ s/-\w+//; + my $data = $job->subaction; my $port = get_port($ip, $pn) or return job_error("Unknown port name [$pn] on device [$ip]"); From e06f1b34371d92fc4976171a2bf184e7bc73e5ba Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 13 Jul 2014 18:24:56 +0100 Subject: [PATCH 06/41] add by_hostname config option to netdisco-rancid-export --- Netdisco/Changes | 1 + Netdisco/bin/netdisco-rancid-export | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 8f199e4d..b01d5464 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -3,6 +3,7 @@ [ENHANCEMENTS] * portctl_nameonly to limit port control to name only (F. Schiavarelli) + * add by_hostname config option to netdisco-rancid-export [BUG FIXES] diff --git a/Netdisco/bin/netdisco-rancid-export b/Netdisco/bin/netdisco-rancid-export index 578d6c91..cf784fc7 100755 --- a/Netdisco/bin/netdisco-rancid-export +++ b/Netdisco/bin/netdisco-rancid-export @@ -43,15 +43,22 @@ use Dancer::Plugin::DBIC 'schema'; use App::Netdisco::Util::Permission ':all'; my $settings = setting( 'rancid' ); +my $domain_suffix = setting( 'domain_suffix' ); my $delimiter = $settings->{ 'delimiter' } || ':'; my $down_age = $settings->{ 'down_age' } || '1 day'; my $rancidhome = $settings->{ 'rancid_home' } || '/var/lib/rancid'; my $config_vendormap = $settings->{ 'vendormap' } || {}; + my $by_ip = {}; foreach my $g (@{$settings->{ 'by_ip' }}) { $by_ip->{$g} = 1; } +my $by_hostname = {}; +foreach my $g (@{$settings->{ 'by_hostname' }}) { + $by_hostname->{$g} = 1; +} + my @devices = schema('netdisco')->resultset('Device')->search({}, { '+columns' => { @@ -92,13 +99,16 @@ foreach my $group (keys %$list) { $vendor = $VENDORMAP{$vendormodel} || $VENDORMAP{$vendor}; } if ( $config_vendormap->{$vendor} or $config_vendormap->{$vendormodel} ) { - $vendor = $config_vendormap{$vendormodel} || $config_vendormap{$vendor}; + $vendor = $config_vendormap->{$vendormodel} || $config_vendormap->{$vendor}; } if ($by_ip->{$group}) { $name = $dev->ip; } else { $name = ($dev->dns || $dev->name); } + if ($by_hostname->{$group}) { + $name =~ s/$domain_suffix$//; + } printf ROUTER "%s$delimiter%s$delimiter%s\n", $name, $vendor, $dev->get_column( 'old' ) ? "down" : "up"; } @@ -119,6 +129,7 @@ This script requires some configuration to be added to your Netdisco down_age: '1 day' delimiter: ':' by_ip: [ other ] + by_hostname: [ other2 ] groups: switch: [ 'name:.*[Ss][Ww].*' ] rtr: [ 'name:[rR]tr.*' ] @@ -172,6 +183,12 @@ Netdisco's "C<*_only>" settings, and accepts IP, prefix, device property. List of RANCID Groups which will have Device IPs written to the RANCID configuration file, instead of DNS or SNMP host names. +=head2 C + +List of RANCID Groups which will have Device Hostname written to the RANCID +configuration file, instead of FQDN. This is done simply by stripping the +C configuration item from the FQDN. + =head1 SEE ALSO =over 4 From 38cd5d98f9ebf0d7b462e6bb0280e2255d24dc70 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 13 Jul 2014 18:36:30 +0100 Subject: [PATCH 07/41] Added by_hostname config option support --- Netdisco/bin/netdisco-rancid-export | 2 +- Web-Plugin-RANCID/Changes | 4 ++++ .../lib/App/NetdiscoX/Web/Plugin/RANCID.pm | 16 +++++++++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Netdisco/bin/netdisco-rancid-export b/Netdisco/bin/netdisco-rancid-export index cf784fc7..60dfee22 100755 --- a/Netdisco/bin/netdisco-rancid-export +++ b/Netdisco/bin/netdisco-rancid-export @@ -43,7 +43,7 @@ use Dancer::Plugin::DBIC 'schema'; use App::Netdisco::Util::Permission ':all'; my $settings = setting( 'rancid' ); -my $domain_suffix = setting( 'domain_suffix' ); +my $domain_suffix = setting( 'domain_suffix' ) || ''; my $delimiter = $settings->{ 'delimiter' } || ':'; my $down_age = $settings->{ 'down_age' } || '1 day'; my $rancidhome = $settings->{ 'rancid_home' } || '/var/lib/rancid'; diff --git a/Web-Plugin-RANCID/Changes b/Web-Plugin-RANCID/Changes index 35146aae..fc192f7a 100644 --- a/Web-Plugin-RANCID/Changes +++ b/Web-Plugin-RANCID/Changes @@ -1,3 +1,7 @@ +2.004000 - 2014-07-13 + + * Added by_hostname config option support + 2.003002 - 2014-05-03 * POD fixups diff --git a/Web-Plugin-RANCID/lib/App/NetdiscoX/Web/Plugin/RANCID.pm b/Web-Plugin-RANCID/lib/App/NetdiscoX/Web/Plugin/RANCID.pm index 759130aa..93ac09c1 100644 --- a/Web-Plugin-RANCID/lib/App/NetdiscoX/Web/Plugin/RANCID.pm +++ b/Web-Plugin-RANCID/lib/App/NetdiscoX/Web/Plugin/RANCID.pm @@ -1,6 +1,6 @@ package App::NetdiscoX::Web::Plugin::RANCID; -our $VERSION = '2.003002'; +our $VERSION = '2.004000'; use Dancer ':syntax'; @@ -25,6 +25,7 @@ hook 'before_template' => sub { my $config = config; my $tokens = shift; my $device = $tokens->{d}; + my $domain_suffix = setting('domain_suffix') || ''; # defaults $tokens->{rancidgroup} = ''; @@ -33,14 +34,17 @@ hook 'before_template' => sub { return unless exists $config->{rancid}; my $rancid = $config->{rancid}; - $rancid->{groups} ||= {}; - $rancid->{by_ip} ||= []; + $rancid->{groups} ||= {}; + $rancid->{by_ip} ||= []; + $rancid->{by_hostname} ||= []; foreach my $g (keys %{ $rancid->{groups} }) { if (check_acl( get_device($device->{ip}), $rancid->{groups}->{$g} )) { $tokens->{rancidgroup} = $g; $tokens->{ranciddevice} = $device->{ip} if 0 < scalar grep {$_ eq $g} @{ $rancid->{by_ip} }; + $tokens->{ranciddevice} =~ s/$domain_suffix$// + if 0 < scalar grep {$_ eq $g} @{ $rancid->{by_hostname} }; last; } } @@ -89,7 +93,8 @@ known to Netdisco. This uses the same configuration as for L, an example of which is below: rancid: - by_ip: [ other ] + by_ip: [ other ] + by_hostname: [ other2 ] groups: switch: [ 'name:.*[Ss][Ww].*' ] rtr: [ 'name:[rR]tr.*' ] @@ -102,7 +107,8 @@ regular expression as in the above example. The device DNS name is used, or if missing the device SNMP sysName. Adding the group to the list in C will make the link include the device IP -instead of the name. +instead of the name. Adding the group to the list in C will +use the device FQDN minus the C config item (i.e. the hostname). =head2 open_in_same_window From 3a84c28c4566380e4b54cc8822714c40131b45c2 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 13 Jul 2014 18:37:32 +0100 Subject: [PATCH 08/41] release Web-Plugin-RANCID 2.004000 --- Web-Plugin-RANCID/META.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Web-Plugin-RANCID/META.yml b/Web-Plugin-RANCID/META.yml index 53c7c1e6..faa3136f 100644 --- a/Web-Plugin-RANCID/META.yml +++ b/Web-Plugin-RANCID/META.yml @@ -24,4 +24,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: 2.003002 +version: 2.004000 From 6d8ccca21c042c3d04a5b9cab6ee5937e7d26aa4 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 13 Jul 2014 18:53:06 +0100 Subject: [PATCH 09/41] Fix device port ordering in DataTables when Port Control is enabled --- Netdisco/Changes | 1 + Netdisco/share/views/ajax/device/ports.tt | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index b01d5464..83c7cb49 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -10,6 +10,7 @@ * [ND1#117] unknown devices missing from inventory * [#118] Fix table rendering on port search tab when VLAN is null * Fix truncated port description change (F. Schiavarelli) + * Fix device port ordering in DataTables when Port Control is enabled 2.028000 - 2014-07-01 diff --git a/Netdisco/share/views/ajax/device/ports.tt b/Netdisco/share/views/ajax/device/ports.tt index 3bef8142..c9053b28 100644 --- a/Netdisco/share/views/ajax/device/ports.tt +++ b/Netdisco/share/views/ajax/device/ports.tt @@ -60,12 +60,14 @@ [% IF user_can_port_control AND params.c_admin %] [% IF row.up_admin == 'up' %] [% ELSE %] [% ELSE %] - + [% END %] From 183547abef407a06847c579b6b08085875da6e90 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 13 Jul 2014 18:54:52 +0100 Subject: [PATCH 10/41] release 2.028001 --- Netdisco/META.yml | 2 +- Netdisco/lib/App/Netdisco.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Netdisco/META.yml b/Netdisco/META.yml index 2fe0fcfd..f2976065 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -81,4 +81,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: 2.028000 +version: 2.028001 diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index a986f42a..5535e512 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -4,7 +4,7 @@ use strict; use warnings; use 5.010_000; -our $VERSION = '2.028000'; +our $VERSION = '2.028001'; use App::Netdisco::Configuration; =head1 NAME From 3a1093626091e5dabd230420f1615cace2b64eda Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 13 Jul 2014 21:58:44 +0100 Subject: [PATCH 11/41] GraphViz graph export (beta) --- Netdisco/Changes | 10 +- Netdisco/lib/App/Netdisco/Util/Graph.pm | 471 ++++++++++++++++++++++++ Netdisco/share/config.yml | 44 +++ 3 files changed, 523 insertions(+), 2 deletions(-) create mode 100644 Netdisco/lib/App/Netdisco/Util/Graph.pm diff --git a/Netdisco/Changes b/Netdisco/Changes index 83c7cb49..9a850f54 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,6 +1,12 @@ -2.028001 +2.028002 - [ENHANCEMENTS] + [NEW FEATURES] + + * GraphViz graph export (beta) + +2.028001 - 2014-07-13 + + [NEW FEATURES] * portctl_nameonly to limit port control to name only (F. Schiavarelli) * add by_hostname config option to netdisco-rancid-export diff --git a/Netdisco/lib/App/Netdisco/Util/Graph.pm b/Netdisco/lib/App/Netdisco/Util/Graph.pm new file mode 100644 index 00000000..d2b8a26d --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Util/Graph.pm @@ -0,0 +1,471 @@ +package App::Netdisco::Util::Graph; + +use App::Netdisco; + +use Dancer qw/:syntax :script/; +use Dancer::Plugin::DBIC 'schema'; + +use App::Netdisco::Util::DNS qw/hostname_from_ip ipv4_from_hostname/; +use Graph::Undirected (); +use GraphViz (); + +use base 'Exporter'; +our @EXPORT = ('graph'); +our @EXPORT_OK = qw/ + graph_each + graph_addnode + make_graph +/; +our %EXPORT_TAGS = (all => \@EXPORT_OK); + +# nothing to see here, please move along... +our ($ip, $label, $isdev, $devloc, %GRAPH, %GRAPH_SPEED); + +=head1 NAME + +App::Netdisco::Util::Graph + +=head1 SYNOPSIS + + $ brew install graphviz <-- install graphviz on your system + + $ ~/bin/localenv bash + $ cpanm --notest Graph GraphViz + $ mkdir ~/graph + + use App::Netdisco::Util::Graph 'graph'; + graph; + +=head1 DESCRIPTION + +Generate GraphViz output from Netdisco data. Requires that the L and +L distributions be installed. + +Requires the same config as for Netdisco 1, but within a C key. See +C in the source distribution for an example. + +The C subroutine is exported by default. The C<:all> tag will export +all subroutines. + +=head1 EXPORT + +=item graph() + +Creates netmap of network. + +=cut + +sub graph { + my %CONFIG = %{ setting('graph') }; + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); + my $month = sprintf("%d%02d",$year+1900,$mon+1); + + info "graph() - Creating Graphs"; + my $G = make_graph(); + + unless (defined $G){ + print "graph() - make_graph() failed. Try running with debug (-D)."; + return; + } + + my @S = $G->connected_components; + + # Count number of nodes in each subgraph + my %S_count; + for (my $i=0;$i< scalar @S;$i++){ + $S_count{$i} = scalar @{$S[$i]}; + } + + foreach my $subgraph (sort { $S_count{$b} <=> $S_count{$a} } keys %S_count){ + my $SUBG = $G->copy; + print "\$S[$subgraph] has $S_count{$subgraph} nodes.\n"; + + # Remove other subgraphs from this one + my %S_notme = %S_count; + delete $S_notme{$subgraph}; + foreach my $other (keys %S_notme){ + print "Removing Non-connected nodes: ",join(',',@{$S[$other]}),"\n"; + $SUBG->delete_vertices(@{$S[$other]}) + } + + # Create the subgraph + my $timeout = defined $CONFIG{graph_timeout} ? $CONFIG{graph_timeout} : 60; + + eval { + alarm($timeout*60); + graph_each($SUBG,''); + alarm(0); + }; + if ($@) { + if ($@ =~ /timeout/){ + print "! Creating Graph timed out!\n"; + } else { + print "\n$@\n"; + } + } + + # Facility to create subgraph for each non-connected network segment. + # Right now, let's just make the biggest one only. + last; + } +} + +=head1 EXPORT_OK + +=item graph_each($graph_obj, $name) + +Generates subgraph. Does actual GraphViz calls. + +=cut + +sub graph_each { + my ($G, $name) = @_; + my %CONFIG = %{ setting('graph') }; + info "Creating new Graph"; + + my $graph_defs = { + 'bgcolor' => $CONFIG{graph_bg} || 'black', + 'color' => $CONFIG{graph_color} || 'white', + 'overlap' => $CONFIG{graph_overlap} || 'scale', + 'fontpath'=> _homepath('graph_fontpath',''), + 'ranksep' => $CONFIG{graph_ranksep} || 0.3, + 'nodesep' => $CONFIG{graph_nodesep} || 2, + 'ratio' => $CONFIG{graph_ratio} || 'compress', + 'splines' => ($CONFIG{graph_splines} ? 'true' : 'false'), + 'fontcolor' => $CONFIG{node_fontcolor} || 'white', + 'fontname' => $CONFIG{node_font} || 'lucon', + 'fontsize' => $CONFIG{node_fontsize} || 12, + }; + my $edge_defs = { + 'color' => $CONFIG{edge_color} || 'wheat', + }; + my $node_defs = { + 'shape' => $CONFIG{node_shape} || 'box', + 'fillcolor' => $CONFIG{node_fillcolor} || 'dimgrey', + 'fontcolor' => $CONFIG{node_fontcolor} || 'white', + 'style' => $CONFIG{node_style} || 'filled', + 'fontname' => $CONFIG{node_font} || 'lucon', + 'fontsize' => $CONFIG{node_fontsize} || 12, + 'fixedsize' => ($CONFIG{node_fixedsize} ? 'true' : 'false'), + }; + $node_defs->{height} = $CONFIG{node_height} if defined $CONFIG{node_height}; + $node_defs->{width} = $CONFIG{node_width} if defined $CONFIG{node_width}; + + my $epsilon = undef; + if (defined $CONFIG{graph_epsilon}){ + $epsilon = "0." . '0' x $CONFIG{graph_epsilon} . '1'; + } + + my %gv = ( + directed => 0, + layout => $CONFIG{graph_layout} || 'twopi', + graph => $graph_defs, + node => $node_defs, + edge => $edge_defs, + width => $CONFIG{graph_x} || 30, + height => $CONFIG{graph_y} || 30, + epsilon => $epsilon, + ); + + my $gv = GraphViz->new(%gv); + + my %node_map = (); + my @nodes = $G->vertices; + + foreach my $dev (@nodes){ + my $node_name = graph_addnode($gv,$dev); + $node_map{$dev} = $node_name; + } + + my $root_ip = defined $CONFIG{root_device} + ? (ipv4_from_hostname($CONFIG{root_device}) || $CONFIG{root_device}) + : undef; + + if (defined $root_ip and defined $node_map{$root_ip}){ + my $gv_root_name = $gv->_quote_name($root_ip); + if (defined $gv_root_name){ + $gv->{GRAPH_ATTRS}->{root}=$gv_root_name; + } + } + + my @edges = $G->edges; + + while (my $e = shift @edges){ + my $link = $e->[0]; + my $dest = $e->[1]; + my $speed = $GRAPH_SPEED{$link}->{$dest}->{speed}; + + if (!defined($speed)) { + info " ! No link speed for $link -> $dest"; + $speed = 0; + } + + my %edge = (); + my $val = ''; my $suffix = ''; + + if ($speed =~ /^([\d.]+)\s+([a-z])bps$/i) { + $val = $1; $suffix = $2; + } + + if ( ($suffix eq 'k') or ($speed =~ m/(t1|ds3)/i) ){ + $edge{color} = 'green'; + $edge{style} = 'dotted'; + } + + if ($suffix eq 'M'){ + if ($val < 10.0){ + $edge{color} = 'green'; + #$edge{style} = 'dotted'; + $edge{style} = 'dashed'; + } elsif ($val < 100.0){ + $edge{color} = '#8b7e66'; + #$edge{style} = 'normal'; + $edge{style} = 'solid'; + } else { + $edge{color} = '#ffe7ba'; + $edge{style} = 'solid'; + } + } + + if ($suffix eq 'G'){ + #$edge{style} = 'bold'; + $edge{color} = 'cyan1'; + } + + # Add extra styles to edges (mainly for modifying width) + if(defined $CONFIG{edge_style}) { + $edge{style} .= "," . $CONFIG{edge_style}; + } + + $gv->add_edge($link => $dest, %edge ); + } + + info "Ignore all warnings about node size"; + + if (defined $CONFIG{graph_raw} and $CONFIG{graph_raw}){ + my $graph_raw = _homepath('graph_raw'); + info " Creating raw graph: $graph_raw"; + $gv->as_canon($graph_raw); + } + + if (defined $CONFIG{graph} and $CONFIG{graph}){ + my $graph_gif = _homepath('graph'); + info " Creating graph: $graph_gif"; + $gv->as_gif($graph_gif); + } + + if (defined $CONFIG{graph_png} and $CONFIG{graph_png}){ + my $graph_png = _homepath('graph_png'); + info " Creating png graph: $graph_png"; + $gv->as_png($graph_png); + } + + if (defined $CONFIG{graph_map} and $CONFIG{graph_map}){ + my $graph_map = _homepath('graph_map'); + info " Creating CMAP : $graph_map"; + $gv->as_cmap($graph_map); + } + + if (defined $CONFIG{graph_svg} and $CONFIG{graph_svg}){ + my $graph_svg = _homepath('graph_svg'); + info " Creating SVG : $graph_svg"; + $gv->as_svg($graph_svg); + } +} + +=item graph_addnode($graphviz_obj, $node_ip) + +Checks for mapping settings in config file and adds node to the GraphViz +object. + +=cut + +sub graph_addnode { + my $gv = shift; + my %CONFIG = %{ setting('graph') }; + my %node = (); + + $ip = shift; + $label = $GRAPH{$ip}->{dns}; + $isdev = $GRAPH{$ip}->{isdev}; + $devloc = $GRAPH{$ip}->{location}; + + $label = "($ip)" unless defined $label; + my $domain_suffix = setting('domain_suffix') || ''; + $label =~ s/$domain_suffix$//; + $node{label} = $label; + + # Dereferencing the scalar by name below + # requires that the variable be non-lexical (not my) + # we'll create some local non-lexical versions + # that will expire at the end of this block + # Node Mappings + foreach my $map (@{ $CONFIG{'node_map'} || [] }){ + my ($var, $regex, $attr, $val) = split(':', $map); + + { no strict 'refs'; + $var = ${"$var"}; + } + next unless defined $var; + + if ($var =~ /$regex/) { + debug " graph_addnode - Giving node $ip $attr = $val"; + $node{$attr} = $val; + } + } + + # URL for image maps FIXME for non-root hosting + if ($isdev) { + $node{URL} = "/device?&q=$ip"; + } + else { + $node{URL} = "/search?tab=node&q=$ip"; + # Overrides any colors given to nodes above. Bug 1094208 + $node{fillcolor} = $CONFIG{'node_problem'} || 'red'; + } + + if ($CONFIG{'graph_clusters'} && $devloc) { + # This odd construct works around a bug in GraphViz.pm's + # quoting of cluster names. If it has a name with spaces, + # it'll just quote it, resulting in creating a subgraph name + # of cluster_"location with spaces". This is an illegal name + # according to the dot grammar, so if the name matches the + # problematic regexp we make GraphViz.pm generate an internal + # name by using a leading space in the name. + # + # This is bug ID 16912 at rt.cpan.org - + # http://rt.cpan.org/NoAuth/Bug.html?id=16912 + # + # Another bug, ID 11514, prevents us from using a combination + # of name and label attributes to hide the extra space from + # the user. However, since it's just a space, hopefully it + # won't be too noticable. + my($loc) = $devloc; + $loc = " " . $loc if ($loc =~ /^[a-zA-Z](\w| )*$/); + $node{cluster} = { name => $loc }; + } + + my $rv = $gv->add_node($ip, %node); + return $rv; +} + +=head2 make_graph() + +Returns C object that represents the discovered network. + +Graph is made by loading all the C entries that have a neighbor, +using them as edges. Then each device seen in those entries is added as a +vertex. + +Nodes without topology information are not included. + +=cut + +sub make_graph { + my $G = Graph::Undirected->new(); + + my $devices = schema('netdisco')->resultset('Device') + ->search({}, { columns => [qw/ip dns location /] }); + my $links = schema('netdisco')->resultset('DevicePort') + ->search({remote_ip => { -not => undef }}, + { columns => [qw/ip remote_ip speed remote_type/]}); + my %aliases = map {$_->alias => $_->ip} + schema('netdisco')->resultset('DeviceIp') + ->search({}, { columns => [qw/ip alias/] })->all; + + my %devs = ( map {($_->ip => $_->dns)} $devices->all ); + my %locs = ( map {($_->ip => $_->location)} $devices->all ); + + # Check for no topology info + unless ($links->count > 0) { + debug "make_graph() - No topology information. skipping."; + return undef; + } + + my %link_seen = (); + my %linkmap = (); + + while (my $link = $links->next) { + my $source = $link->ip; + my $dest = $link->remote_ip; + my $speed = $link->speed; + my $type = $link->remote_type; + + # Check for Aliases + if (defined $aliases{$dest}) { + # Set to root device + $dest = $aliases{$dest}; + } + + # Remove loopback - After alias check (bbaetz) + if ($source eq $dest) { + debug " make_graph() - Loopback on $source"; + next; + } + + # Skip IP Phones + if (defined $type and $type =~ /ip.phone/i) { + debug " make_graph() - Skipping IP Phone. $source -> $dest ($type)"; + next; + } + next if exists $link_seen{$source}->{$dest}; + + push(@{ $linkmap{$source} }, $dest); + + # take care of reverse too + $link_seen{$source}->{$dest}++; + $link_seen{$dest}->{$source}++; + + $GRAPH_SPEED{$source}->{$dest}->{speed}=$speed; + $GRAPH_SPEED{$dest}->{$source}->{speed}=$speed; + } + + foreach my $link (keys %linkmap) { + foreach my $dest (@{ $linkmap{$link} }) { + + foreach my $side ($link, $dest) { + unless (defined $GRAPH{$side}) { + my $is_dev = exists $devs{$side}; + my $dns = $is_dev ? + $devs{$side} : + hostname_from_ip($side); + + # Default to IP if no dns + $dns = defined $dns ? $dns : "($side)"; + + $G->add_vertex($side); + debug " make_graph() - add_vertex('$side')"; + + $GRAPH{$side}->{dns} = $dns; + $GRAPH{$side}->{isdev} = $is_dev; + $GRAPH{$side}->{seen}++; + $GRAPH{$side}->{location} = $locs{$side}; + } + } + + $G->add_edge($link,$dest); + debug " make_graph - add_edge('$link','$dest')"; + } + } + + return $G; +} + +sub _homepath { + my ($path, $default) = @_; + + my $home = $ENV{NETDISCO_HOME}; + my $item = setting('graph')->{$path} || $default; + return undef unless defined($item); + + if ($item =~ m,^/,) { + return $item; + } + else { + $home =~ s,/*$,,; + return $home . "/" . $item; + } +} + +1; diff --git a/Netdisco/share/config.yml b/Netdisco/share/config.yml index 61609a56..a5cfbf82 100644 --- a/Netdisco/share/config.yml +++ b/Netdisco/share/config.yml @@ -211,6 +211,50 @@ job_type_keys: Poller: pollers Interactive: interactives +# --------------- +# GraphViz Export +# --------------- + +graphviz: + # ---- Graph Settings ---- + edge_color : wheat + + graph : 'graph/netmap.gif' + graph_png : 'graph/netmap.png' + graph_bg : black + graph_clusters : false # try fdp layout + graph_color : white + graph_default : png + #graph_dir : net_dir.gif + graph_epsilon : 6 + graph_layout : twopi # try neato or fdp too + graph_map : 'graph/netmap.map' + graph_overlap : scale + graph_nodesep : 2 + graph_ranksep : .3 + graph_raw : 'graph/graph_raw.dot' + graph_splines : false + graph_svg : 'graph/netmap.svg' + graph_timeout : 90 + graph_x : 30 + graph_y : 30 + + node_fillcolor : dimgrey + node_font : lucon + node_fontsize : 46.0 + node_fontcolor : white + node_problem : red + node_shape : box + node_style : filled + #edge_style : setlinewidth(10) + + # ---- Node Maps ---- + # variable:matching pattern:node attribute:attribute value:key:key name + #node_map: + # - 'label:cat(?!-g):fillcolor:blue:cat:Blue Box - Catalyst Device' + # - 'label:-g:fillcolor:darkgreen:dev-g:Green Box - Gateway / Router' + # - 'ip:^192.168\.:color:yellow:dev:Yellow Border - ResNet' + # --------------- # DANCER INTERNAL # --------------- From 820c0a6b79c497ada545ac120fbc6e9c594f6651 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 13 Jul 2014 22:02:00 +0100 Subject: [PATCH 12/41] release 2.028002_001 --- Netdisco/Changes | 2 +- Netdisco/MANIFEST | 1 + Netdisco/META.yml | 2 +- Netdisco/lib/App/Netdisco.pm | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 9a850f54..fd7bdc00 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,4 +1,4 @@ -2.028002 +2.028002_001 - 2014-07-13 [NEW FEATURES] diff --git a/Netdisco/MANIFEST b/Netdisco/MANIFEST index ee1dab17..910fa460 100644 --- a/Netdisco/MANIFEST +++ b/Netdisco/MANIFEST @@ -151,6 +151,7 @@ lib/App/Netdisco/Manual/WritingPlugins.pod lib/App/Netdisco/Util/Device.pm lib/App/Netdisco/Util/DNS.pm lib/App/Netdisco/Util/ExpandParams.pm +lib/App/Netdisco/Util/Graph.pm lib/App/Netdisco/Util/Node.pm lib/App/Netdisco/Util/Noop.pm lib/App/Netdisco/Util/Permission.pm diff --git a/Netdisco/META.yml b/Netdisco/META.yml index f2976065..26f5b14c 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -81,4 +81,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: 2.028001 +version: 2.028002_001 diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index 5535e512..30816f83 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -4,7 +4,7 @@ use strict; use warnings; use 5.010_000; -our $VERSION = '2.028001'; +our $VERSION = '2.028002_001'; use App::Netdisco::Configuration; =head1 NAME From 36a6d2e33ca9a99249aa7872aceaf2a3dab41213 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Tue, 15 Jul 2014 16:51:59 +0100 Subject: [PATCH 13/41] No default for interactives made daemon die (H. Weber) --- Netdisco/Changes | 6 +++++- Netdisco/lib/App/Netdisco/Configuration.pm | 3 ++- Netdisco/share/config.yml | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index fd7bdc00..1096a6cd 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,9 +1,13 @@ -2.028002_001 - 2014-07-13 +2.028002_002 - 2014-07-13 [NEW FEATURES] * GraphViz graph export (beta) + [BUG FIXES] + + * No default for interactives made daemon die (H. Weber) + 2.028001 - 2014-07-13 [NEW FEATURES] diff --git a/Netdisco/lib/App/Netdisco/Configuration.pm b/Netdisco/lib/App/Netdisco/Configuration.pm index d6db6a89..2dc444bc 100644 --- a/Netdisco/lib/App/Netdisco/Configuration.pm +++ b/Netdisco/lib/App/Netdisco/Configuration.pm @@ -41,8 +41,9 @@ setting('plugins')->{DBIC}->{daemon} = { schema_class => 'App::Netdisco::Daemon::DB', }; -# default queue model is Pg +# defaults for workers setting('workers')->{queue} ||= 'PostgreSQL'; +setting('workers')->{interactives} ||= 1; # force skipped DNS resolution, if unset setting('dns')->{hosts_file} ||= '/etc/hosts'; diff --git a/Netdisco/share/config.yml b/Netdisco/share/config.yml index a5cfbf82..1205a086 100644 --- a/Netdisco/share/config.yml +++ b/Netdisco/share/config.yml @@ -166,7 +166,7 @@ port_control_reasons: # -------------- workers: - interactives: 2 + interactives: 1 pollers: 10 sleep_time: 2 queue: PostgreSQL From 5a84399e2c04eefdfdb01be06cde67b16913dcca Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Tue, 15 Jul 2014 17:08:04 +0100 Subject: [PATCH 14/41] fix config name to be graph --- Netdisco/lib/App/Netdisco/Util/Graph.pm | 4 ++-- Netdisco/share/config.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Netdisco/lib/App/Netdisco/Util/Graph.pm b/Netdisco/lib/App/Netdisco/Util/Graph.pm index d2b8a26d..90d5008c 100644 --- a/Netdisco/lib/App/Netdisco/Util/Graph.pm +++ b/Netdisco/lib/App/Netdisco/Util/Graph.pm @@ -33,7 +33,7 @@ App::Netdisco::Util::Graph $ cpanm --notest Graph GraphViz $ mkdir ~/graph - use App::Netdisco::Util::Graph 'graph'; + use App::Netdisco::Util::Graph; graph; =head1 DESCRIPTION @@ -41,7 +41,7 @@ App::Netdisco::Util::Graph Generate GraphViz output from Netdisco data. Requires that the L and L distributions be installed. -Requires the same config as for Netdisco 1, but within a C key. See +Requires the same config as for Netdisco 1, but within a C key. See C in the source distribution for an example. The C subroutine is exported by default. The C<:all> tag will export diff --git a/Netdisco/share/config.yml b/Netdisco/share/config.yml index 1205a086..43429d3f 100644 --- a/Netdisco/share/config.yml +++ b/Netdisco/share/config.yml @@ -215,7 +215,7 @@ job_type_keys: # GraphViz Export # --------------- -graphviz: +graph: # ---- Graph Settings ---- edge_color : wheat From 29f4231be98219a6c9413bae81c46705ee8ae9fc Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Tue, 15 Jul 2014 17:10:40 +0100 Subject: [PATCH 15/41] release 2.028003 --- Netdisco/Changes | 2 +- Netdisco/META.yml | 2 +- Netdisco/lib/App/Netdisco.pm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 1096a6cd..6404f996 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,4 +1,4 @@ -2.028002_002 - 2014-07-13 +2.028003 - 2014-07-15 [NEW FEATURES] diff --git a/Netdisco/META.yml b/Netdisco/META.yml index 26f5b14c..9868c000 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -81,4 +81,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: 2.028002_001 +version: 2.028003 diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index 30816f83..02b29e97 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -4,7 +4,7 @@ use strict; use warnings; use 5.010_000; -our $VERSION = '2.028002_001'; +our $VERSION = '2.028003'; use App::Netdisco::Configuration; =head1 NAME From c3b34b1a83c003bc878e2dd340f8eda34d8ea3cc Mon Sep 17 00:00:00 2001 From: "Eric A. Miller" Date: Tue, 15 Jul 2014 22:52:25 -0400 Subject: [PATCH 16/41] Close socket upon completion of async DNS resolution --- Netdisco/Changes | 1 + Netdisco/lib/App/Netdisco/Util/DNS.pm | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 6404f996..311c5e75 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -7,6 +7,7 @@ [BUG FIXES] * No default for interactives made daemon die (H. Weber) + * Close socket upon completion of async DNS resolution 2.028001 - 2014-07-13 diff --git a/Netdisco/lib/App/Netdisco/Util/DNS.pm b/Netdisco/lib/App/Netdisco/Util/DNS.pm index eb32361e..04f538c3 100644 --- a/Netdisco/lib/App/Netdisco/Util/DNS.pm +++ b/Netdisco/lib/App/Netdisco/Util/DNS.pm @@ -103,7 +103,6 @@ addresses which resolved. sub hostnames_resolve_async { my $ips = shift; - my $resolver = AnyEvent::DNS->new(); my %HOSTS = (); $HOSTS{$_} = [ map { [ $_ ? (format_address $_->[0]) : '' ] } @@ -136,6 +135,9 @@ sub hostnames_resolve_async { # Wait for the resolver to perform all resolutions $done->recv; + + # Remove reference to resolver so that we close sockets + undef $AnyEvent::DNS::RESOLVER if $AnyEvent::DNS::RESOLVER; return $ips; } From e1e4723c4790a4f077bc8e4dc1da5c8627387daf Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Wed, 16 Jul 2014 08:04:38 +0100 Subject: [PATCH 17/41] release 2.028004 --- Netdisco/Changes | 7 ++++++- Netdisco/META.yml | 2 +- Netdisco/lib/App/Netdisco.pm | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 311c5e75..073cf14d 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,3 +1,9 @@ +2.028004 - 2014-07-16 + + [BUG FIXES] + + * Close socket upon completion of async DNS resolution + 2.028003 - 2014-07-15 [NEW FEATURES] @@ -7,7 +13,6 @@ [BUG FIXES] * No default for interactives made daemon die (H. Weber) - * Close socket upon completion of async DNS resolution 2.028001 - 2014-07-13 diff --git a/Netdisco/META.yml b/Netdisco/META.yml index 9868c000..a7ef9527 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -81,4 +81,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: 2.028003 +version: 2.028004 diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index 02b29e97..11d45996 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -4,7 +4,7 @@ use strict; use warnings; use 5.010_000; -our $VERSION = '2.028003'; +our $VERSION = '2.028004'; use App::Netdisco::Configuration; =head1 NAME From 0ea6f754b49d2d4ee3a3e7cbb7d04f9a3005494b Mon Sep 17 00:00:00 2001 From: "Eric A. Miller" Date: Wed, 16 Jul 2014 20:00:39 -0400 Subject: [PATCH 18/41] Custom path handling for DataTables ajax calls --- Netdisco/Changes | 6 ++++++ Netdisco/share/views/ajax/admintask/userlog.tt | 2 +- Netdisco/share/views/ajax/report/apradiochannelpower.tt | 2 +- Netdisco/share/views/ajax/report/devicepoestatus.tt | 2 +- Netdisco/share/views/ajax/report/moduleinventory.tt | 2 +- Netdisco/share/views/ajax/report/netbios.tt | 2 +- Netdisco/share/views/ajax/report/nodevendor.tt | 2 +- 7 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 073cf14d..fb307c9b 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,3 +1,9 @@ +2.028005 + + [BUG FIXES] + + * Custom path handling for DataTables ajax calls + 2.028004 - 2014-07-16 [BUG FIXES] diff --git a/Netdisco/share/views/ajax/admintask/userlog.tt b/Netdisco/share/views/ajax/admintask/userlog.tt index 1b9efd11..d4f04c65 100644 --- a/Netdisco/share/views/ajax/admintask/userlog.tt +++ b/Netdisco/share/views/ajax/admintask/userlog.tt @@ -25,7 +25,7 @@ $(document).ready(function() { }, "serverSide": true, "order": [[ 0, "desc" ]], - "ajax": "/ajax/control/admin/userlog/data", + "ajax": "[% uri_for('/ajax/control/admin/userlog/data') %]", "columns": [{ "data": 'creation', "className": "nd_center-cell", diff --git a/Netdisco/share/views/ajax/report/apradiochannelpower.tt b/Netdisco/share/views/ajax/report/apradiochannelpower.tt index 94a469b3..5ba05093 100644 --- a/Netdisco/share/views/ajax/report/apradiochannelpower.tt +++ b/Netdisco/share/views/ajax/report/apradiochannelpower.tt @@ -45,7 +45,7 @@ $(document).ready(function() { "search": 'Filter records: ' }, "serverSide": true, - "ajax": '/ajax/content/report/apradiochannelpower/data', + "ajax": "[% uri_for('/ajax/content/report/apradiochannelpower/data') %]", "order": [[ 0, 'asc' ]], "columns": [ { diff --git a/Netdisco/share/views/ajax/report/devicepoestatus.tt b/Netdisco/share/views/ajax/report/devicepoestatus.tt index a162e21e..bdcaecdf 100644 --- a/Netdisco/share/views/ajax/report/devicepoestatus.tt +++ b/Netdisco/share/views/ajax/report/devicepoestatus.tt @@ -71,7 +71,7 @@ $(document).ready(function() { "search": 'Filter records: ' }, "serverSide": true, - "ajax": '/ajax/content/report/devicepoestatus/data', + "ajax": "[% uri_for('/ajax/content/report/devicepoestatus/data') %]", "order": [[ 0, 'asc' ]], "columns": [ { diff --git a/Netdisco/share/views/ajax/report/moduleinventory.tt b/Netdisco/share/views/ajax/report/moduleinventory.tt index b84b96bd..be393121 100644 --- a/Netdisco/share/views/ajax/report/moduleinventory.tt +++ b/Netdisco/share/views/ajax/report/moduleinventory.tt @@ -40,7 +40,7 @@ $(document).ready(function() { "serverSide": true, "searching": false, "order": [[ 0, "desc" ]], - "ajax": '/ajax/content/report/moduleinventory/data?[% url(params('query').hash) %]', + "ajax": "[% uri_for('/ajax/content/report/moduleinventory/data') %]?[% url(params('query').hash) %]", "columns": [ { "data": 'ip', diff --git a/Netdisco/share/views/ajax/report/netbios.tt b/Netdisco/share/views/ajax/report/netbios.tt index 3a30f3c6..964d949b 100644 --- a/Netdisco/share/views/ajax/report/netbios.tt +++ b/Netdisco/share/views/ajax/report/netbios.tt @@ -35,7 +35,7 @@ $(document).ready(function() { [% IF opt %] "serverSide": true, "order": [[ 0, "desc" ]], - "ajax": '/ajax/content/report/netbios/data?[% url(params('query').hash) %]', + "ajax": "[% uri_for('/ajax/content/report/netbios/data') %]?[% url(params('query').hash) %]", "columns": [ { "data": 'domain', diff --git a/Netdisco/share/views/ajax/report/nodevendor.tt b/Netdisco/share/views/ajax/report/nodevendor.tt index 22b01bf5..dade6972 100644 --- a/Netdisco/share/views/ajax/report/nodevendor.tt +++ b/Netdisco/share/views/ajax/report/nodevendor.tt @@ -35,7 +35,7 @@ $(document).ready(function() { [% IF opt %] "serverSide": true, "order": [[ 0, "desc" ]], - "ajax": '/ajax/content/report/nodevendor/data?[% url(params('query').hash) %]', + "ajax": "[% uri_for('/ajax/content/report/nodevendor/data') %]?[% url(params('query').hash) %]", "columns": [ { "data": 'mac', From 91c09305e90d247f8e7bbde577f917bd0b3af28c Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Thu, 17 Jul 2014 13:34:07 +0100 Subject: [PATCH 19/41] [#116] Log true client IP when running through a proxy --- Netdisco/Changes | 1 + Netdisco/Makefile.PL | 1 + Netdisco/bin/netdisco-web-fg | 1 + 3 files changed, 3 insertions(+) diff --git a/Netdisco/Changes b/Netdisco/Changes index fb307c9b..0b9ffd65 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -3,6 +3,7 @@ [BUG FIXES] * Custom path handling for DataTables ajax calls + * [#116] Log true client IP when running through a proxy 2.028004 - 2014-07-16 diff --git a/Netdisco/Makefile.PL b/Netdisco/Makefile.PL index 36b5ca7b..4e99c5fe 100644 --- a/Netdisco/Makefile.PL +++ b/Netdisco/Makefile.PL @@ -41,6 +41,7 @@ requires 'Opcode' => 1.07; requires 'Path::Class' => 0.32; requires 'Plack' => 1.0023; requires 'Plack::Middleware::Expires' => 0.03; +requires 'Plack::Middleware::ReverseProxy' => 0.15; requires 'Role::Tiny' => 1.002005; requires 'Socket6' => 0.23; requires 'Starman' => 0.4008; diff --git a/Netdisco/bin/netdisco-web-fg b/Netdisco/bin/netdisco-web-fg index 7b31bf26..c84129f7 100755 --- a/Netdisco/bin/netdisco-web-fg +++ b/Netdisco/bin/netdisco-web-fg @@ -28,6 +28,7 @@ my $home = ($ENV{NETDISCO_HOME} || $ENV{HOME}); set(session_dir => dir($home, 'netdisco-web-sessions')->stringify); set plack_middlewares => [ + ['Plack::Middleware::ReverseProxy'], [ Expires => ( content_type => [qr{^application/javascript}, qr{^text/css}, qr{image}, qr{font}], expires => 'access plus 1 day', From 2a77e13e100a4d8681409cb9ad3f3350703af7a0 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Thu, 17 Jul 2014 13:43:04 +0100 Subject: [PATCH 20/41] Use /etc/hosts for all forward/reverse DNS --- Netdisco/Changes | 1 + Netdisco/lib/App/Netdisco/Util/DNS.pm | 35 ++++++++++++++++++--------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 0b9ffd65..87208d09 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -4,6 +4,7 @@ * Custom path handling for DataTables ajax calls * [#116] Log true client IP when running through a proxy + * Use /etc/hosts for all forward/reverse DNS 2.028004 - 2014-07-16 diff --git a/Netdisco/lib/App/Netdisco/Util/DNS.pm b/Netdisco/lib/App/Netdisco/Util/DNS.pm index 04f538c3..77b64ba9 100644 --- a/Netdisco/lib/App/Netdisco/Util/DNS.pm +++ b/Netdisco/lib/App/Netdisco/Util/DNS.pm @@ -8,6 +8,13 @@ use Net::DNS; use AnyEvent::DNS; use NetAddr::IP::Lite ':lower'; +use base 'Exporter'; +our @EXPORT = (); +our @EXPORT_OK = qw/ + hostname_from_ip hostnames_resolve_async ipv4_from_hostname +/; +our %EXPORT_TAGS = (all => \@EXPORT_OK); + # AE::DNS::EtcHosts only works for A/AAAA/SRV, but we want PTR. # this loads+parses /etc/hosts file using AE. dirty hack. use AnyEvent::Socket 'format_address'; @@ -15,12 +22,10 @@ use AnyEvent::DNS::EtcHosts; AnyEvent::DNS::EtcHosts::_load_hosts_unless(sub{},AE::cv); no AnyEvent::DNS::EtcHosts; # unimport -use base 'Exporter'; -our @EXPORT = (); -our @EXPORT_OK = qw/ - hostname_from_ip hostnames_resolve_async ipv4_from_hostname -/; -our %EXPORT_TAGS = (all => \@EXPORT_OK); +our %HOSTS = (); +$HOSTS{$_} = [ map { [ $_ ? (format_address $_->[0]) : '' ] } + @{$AnyEvent::DNS::EtcHosts::HOSTS{$_}} ] + for keys %AnyEvent::DNS::EtcHosts::HOSTS; =head1 NAME @@ -47,6 +52,13 @@ sub hostname_from_ip { my $ip = shift; return unless $ip; + # check /etc/hosts file and short-circuit if found + foreach my $name (reverse sort keys %HOSTS) { + if ($HOSTS{$name}->[0]->[0] eq $ip) { + return $name; + } + } + my $res = Net::DNS::Resolver->new; my $query = $res->search($ip); @@ -72,6 +84,12 @@ sub ipv4_from_hostname { my $name = shift; return unless $name; + # check /etc/hosts file and short-circuit if found + if (exists $HOSTS{$name}) { + my $ip = NetAddr::IP::Lite->new($HOSTS{$name}->[0]->[0]); + return $ip->addr if $ip and $ip->bits == 32; + } + my $res = Net::DNS::Resolver->new; my $query = $res->search($name); @@ -104,11 +122,6 @@ addresses which resolved. sub hostnames_resolve_async { my $ips = shift; - my %HOSTS = (); - $HOSTS{$_} = [ map { [ $_ ? (format_address $_->[0]) : '' ] } - @{$AnyEvent::DNS::EtcHosts::HOSTS{$_}} ] - for keys %AnyEvent::DNS::EtcHosts::HOSTS; - # Set up the condvar my $done = AE::cv; $done->begin( sub { shift->send } ); From 470c1061317a5931337ac3ba8681c7ed8c8b99a4 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Thu, 17 Jul 2014 14:24:32 +0100 Subject: [PATCH 21/41] release 2.028005 --- Netdisco/Changes | 2 +- Netdisco/META.yml | 3 ++- Netdisco/lib/App/Netdisco.pm | 2 +- Netdisco/lib/App/Netdisco/Util/DNS.pm | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 87208d09..d083b052 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,4 +1,4 @@ -2.028005 +2.028005 - 2014-07-17 [BUG FIXES] diff --git a/Netdisco/META.yml b/Netdisco/META.yml index a7ef9527..815ee1d3 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -57,6 +57,7 @@ requires: Path::Class: 0.32 Plack: 1.0023 Plack::Middleware::Expires: 0.03 + Plack::Middleware::ReverseProxy: 0.15 Role::Tiny: 1.002005 SNMP::Info: 3.18 SQL::Translator: 0.11016 @@ -81,4 +82,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: 2.028004 +version: 2.028005 diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index 11d45996..bc23871a 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -4,7 +4,7 @@ use strict; use warnings; use 5.010_000; -our $VERSION = '2.028004'; +our $VERSION = '2.028005'; use App::Netdisco::Configuration; =head1 NAME diff --git a/Netdisco/lib/App/Netdisco/Util/DNS.pm b/Netdisco/lib/App/Netdisco/Util/DNS.pm index 77b64ba9..a8712f30 100644 --- a/Netdisco/lib/App/Netdisco/Util/DNS.pm +++ b/Netdisco/lib/App/Netdisco/Util/DNS.pm @@ -85,7 +85,7 @@ sub ipv4_from_hostname { return unless $name; # check /etc/hosts file and short-circuit if found - if (exists $HOSTS{$name}) { + if (exists $HOSTS{$name} and $HOSTS{$name}->[0]->[0]) { my $ip = NetAddr::IP::Lite->new($HOSTS{$name}->[0]->[0]); return $ip->addr if $ip and $ip->bits == 32; } From b86ff6580e64d01e939679127481df162072afc7 Mon Sep 17 00:00:00 2001 From: Bill Fenner Date: Mon, 21 Jul 2014 00:10:10 +0000 Subject: [PATCH 22/41] Use name in the name column, and fix link. The template was a little mangled (missing href=), and the ip address was the preferred value for the name column - switch it to name --- Netdisco/share/views/ajax/report/devicednsmismatch.tt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Netdisco/share/views/ajax/report/devicednsmismatch.tt b/Netdisco/share/views/ajax/report/devicednsmismatch.tt index fba189cb..70045d73 100644 --- a/Netdisco/share/views/ajax/report/devicednsmismatch.tt +++ b/Netdisco/share/views/ajax/report/devicednsmismatch.tt @@ -25,7 +25,7 @@ $(document).ready(function() { { "data": 'ip', "render": function(data, type, row, meta) { - return '' + he.encode(row.ip || row.name) + ''; + return '' + he.encode(row.name || row.ip) + ''; } }, { "data": 'dns', From e7803caa591c7099ef5037df96100b5613ccd372 Mon Sep 17 00:00:00 2001 From: "Eric A. Miller" Date: Sun, 20 Jul 2014 21:51:20 -0400 Subject: [PATCH 23/41] Asynchronous NBTstat --- Netdisco/Changes | 6 + Netdisco/lib/App/Netdisco/AnyEvent/Nbtstat.pm | 277 ++++++++++++++++++ Netdisco/lib/App/Netdisco/Core/Nbtstat.pm | 98 ++++--- .../Netdisco/Daemon/Worker/Poller/Nbtstat.pm | 25 +- .../lib/App/Netdisco/Manual/Configuration.pod | 14 + Netdisco/lib/App/Netdisco/Util/Node.pm | 6 +- Netdisco/share/config.yml | 2 + 7 files changed, 381 insertions(+), 47 deletions(-) create mode 100644 Netdisco/lib/App/Netdisco/AnyEvent/Nbtstat.pm diff --git a/Netdisco/Changes b/Netdisco/Changes index d083b052..23cfa3a3 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,3 +1,9 @@ +2.028006 + + [ENHANCEMENTS] + + * Asynchronous NBTstat + 2.028005 - 2014-07-17 [BUG FIXES] diff --git a/Netdisco/lib/App/Netdisco/AnyEvent/Nbtstat.pm b/Netdisco/lib/App/Netdisco/AnyEvent/Nbtstat.pm new file mode 100644 index 00000000..b8075481 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/AnyEvent/Nbtstat.pm @@ -0,0 +1,277 @@ +package App::Netdisco::AnyEvent::Nbtstat; + +use Socket qw(AF_INET SOCK_DGRAM inet_aton sockaddr_in); +use List::Util (); +use Carp (); + +use AnyEvent (); BEGIN { AnyEvent::common_sense } +use AnyEvent::Util (); + +sub new { + my ( $class, %args ) = @_; + + my $interval = $args{interval}; + # This default should generate ~ 50 requests per second + $interval = 0.2 unless defined $interval; + + my $timeout = $args{timeout}; + + # Timeout should be 250ms according to RFC1002, but we're going to double + $timeout = 0.5 unless defined $timeout; + + my $self = bless { interval => $interval, timeout => $timeout, %args }, + $class; + + Scalar::Util::weaken( my $wself = $self ); + + socket my $fh4, AF_INET, Socket::SOCK_DGRAM(), 0 + or Carp::croak "Unable to create socket : $!"; + + AnyEvent::Util::fh_nonblocking $fh4, 1; + $self->{fh4} = $fh4; + $self->{rw4} = AE::io $fh4, 0, sub { + if ( my $peer = recv $fh4, my $resp, 2048, 0 ) { + $wself->_on_read( $resp, $peer ); + } + }; + + # Nbtstat tasks + $self->{_tasks} = {}; + + return $self; +} + +sub interval { @_ > 1 ? $_[0]->{interval} = $_[1] : $_[0]->{interval} } + +sub timeout { @_ > 1 ? $_[0]->{timeout} = $_[1] : $_[0]->{timeout} } + +sub nbtstat { + my ( $self, $host, $cb ) = @_; + + my $ip = inet_aton($host); + my $port = 137; + + my $request = { + host => $host, + results => {}, + cb => $cb, + destination => scalar sockaddr_in( $port, $ip ), + }; + + $self->{_tasks}{ $request->{destination} } = $request; + + my $delay = $self->interval * scalar keys $self->{_tasks}; + + # There's probably a better way to throttle the sends + # but this will work for now since we currently don't support retries + my $w; $w = AE::timer $delay, 0, sub { + undef $w; + $self->_send_request($request); + }; + + return $self; +} + +sub _on_read { + my ( $self, $resp, $peer ) = @_; + + ($resp) = $resp =~ /^(.*)$/s + if AnyEvent::TAINT && $self->{untaint}; + + # Find our task + my $request = $self->{_tasks}{$peer}; + + return unless $request; + + $self->_store_result( $request, 'OK', $resp ); + + return; +} + +sub _store_result { + my ( $self, $request, $status, $resp ) = @_; + + my $results = $request->{results}; + + my @rr = (); + my $mac_address = ""; + + if ( $status eq 'OK' && length($resp) > 56 ) { + my $num_names = unpack( "C", substr( $resp, 56 ) ); + my $name_data = substr( $resp, 57 ); + + for ( my $i = 0; $i < $num_names; $i++ ) { + my $rr_data = substr( $name_data, 18 * $i, 18 ); + push @rr, _decode_rr($rr_data); + } + + $mac_address = join "-", + map { sprintf "%02X", $_ } + unpack( "C*", substr( $name_data, 18 * $num_names, 6 ) ); + $results = { + 'status' => 'OK', + 'names' => \@rr, + 'mac_address' => $mac_address + }; + } + elsif ( $status eq 'OK' ) { + $results = { 'status' => 'SHORT' }; + } + else { + $results = { 'status' => $status }; + } + + # Clear request specific data + delete $request->{timer}; + + # Cleanup + delete $self->{_tasks}{ $request->{destination} }; + + # Done + $request->{cb}->($results); + + undef $request; + + return; +} + +sub _send_request { + my ( $self, $request ) = @_; + + my $msg = ""; + # We use process id as identifier field, since don't have a need to + # unique responses beyond host / port queried + $msg .= pack( "n*", $$, 0, 1, 0, 0, 0 ); + $msg .= _encode_name( "*", "\x00", 0 ); + $msg .= pack( "n*", 0x21, 0x0001 ); + + $request->{start} = time; + + $request->{timer} = AE::timer $self->timeout, 0, sub { + $self->_store_result( $request, 'TIMEOUT' ); + }; + + my $fh = $self->{fh4}; + + send $fh, $msg, 0, $request->{destination} + or $self->_store_result( $request, 'ERROR' ); + + return; +} + +sub _encode_name { + my $name = uc(shift); + my $pad = shift || "\x20"; + my $suffix = shift || 0x00; + + $name .= $pad x ( 16 - length($name) ); + substr( $name, 15, 1, chr( $suffix & 0xFF ) ); + + my $encoded_name = ""; + for my $c ( unpack( "C16", $name ) ) { + $encoded_name .= chr( ord('A') + ( ( $c & 0xF0 ) >> 4 ) ); + $encoded_name .= chr( ord('A') + ( $c & 0xF ) ); + } + + # Note that the _encode_name function doesn't add any scope, + # nor does it calculate the length (32), it just prefixes it + return "\x20" . $encoded_name . "\x00"; +} + +sub _decode_rr { + my $rr_data = shift; + + my @nodetypes = qw/B-node P-node M-node H-node/; + my ( $name, $suffix, $flags ) = unpack( "a15Cn", $rr_data ); + $name =~ tr/\x00-\x19/\./; # replace ctrl chars with "." + $name =~ s/\s+//g; + + my $rr = {}; + $rr->{'name'} = $name; + $rr->{'suffix'} = $suffix; + $rr->{'G'} = ( $flags & 2**15 ) ? "GROUP" : "UNIQUE"; + $rr->{'ONT'} = $nodetypes[ ( $flags >> 13 ) & 3 ]; + $rr->{'DRG'} = ( $flags & 2**12 ) ? "Deregistering" : "Registered"; + $rr->{'CNF'} = ( $flags & 2**11 ) ? "Conflict" : ""; + $rr->{'ACT'} = ( $flags & 2**10 ) ? "Active" : "Inactive"; + $rr->{'PRM'} = ( $flags & 2**9 ) ? "Permanent" : ""; + + return $rr; +} + +1; +__END__ + +=head1 NAME + +App::Netdisco::AnyEvent::Nbtstat - Request NetBIOS node status with AnyEvent + +=head1 SYNOPSIS + + use App::Netdisco::AnyEvent::Nbtstat;; + + my $request = App::Netdisco::AnyEvent::Nbtstat->new(); + + my $cv = AE::cv; + + $request->nbtstat( + '127.0.0.1', + sub { + my $result = shift; + print "MAC: ", $result->{'mac_address'} || '', " "; + print "Status: ", $result->{'status'}, "\n"; + printf '%3s %-18s %4s %-18s', '', 'Name', '', 'Type' + if ( $result->{'status'} eq 'OK' ); + print "\n"; + for my $rr ( @{ $result->{'names'} } ) { + printf '%3s %-18s <%02s> %-18s', '', $rr->{'name'}, + $rr->{'suffix'}, + $rr->{'G'}; + print "\n"; + } + $cv->send; + } + ); + + $cv->recv; + +=head1 DESCRIPTION + +L is an asynchronous AnyEvent NetBIOS node +status requester. + +=head1 ATTRIBUTES + +L implements the following attributes. + +=head2 C + + my $interval = $request->interval; + $request->interval(1); + +Interval between requests, defaults to 0.02 seconds. + +=head2 C + + my $timeout = $request->timeout; + $request->timeout(2); + +Maximum request response time, defaults to 0.5 seconds. + +=head1 METHODS + +L implements the following methods. + +=head2 C + + $request->nbtstat($ip, sub { + my $result = shift; + }); + +Perform a NetBIOS node status request of $ip. + +=head1 SEE ALSO + +L + +=cut diff --git a/Netdisco/lib/App/Netdisco/Core/Nbtstat.pm b/Netdisco/lib/App/Netdisco/Core/Nbtstat.pm index 8d91171a..568a4189 100644 --- a/Netdisco/lib/App/Netdisco/Core/Nbtstat.pm +++ b/Netdisco/lib/App/Netdisco/Core/Nbtstat.pm @@ -5,11 +5,11 @@ use Dancer::Plugin::DBIC 'schema'; use App::Netdisco::Util::Node 'check_mac'; use NetAddr::IP::Lite ':lower'; -use Net::NBName; +use App::Netdisco::AnyEvent::Nbtstat; use base 'Exporter'; our @EXPORT = (); -our @EXPORT_OK = qw/ do_nbtstat store_nbt /; +our @EXPORT_OK = qw/ nbtstat_resolve_async store_nbt /; our %EXPORT_TAGS = (all => \@EXPORT_OK); =head1 NAME @@ -25,42 +25,64 @@ subroutines. =head1 EXPORT_OK -=head2 do_nbtstat( $node ) +=head2 nbtstat_resolve_async( $ips ) -Connects to node and gets NetBIOS information. Then adds entries to -node_nbt table. +This method uses an asynchronous AnyEvent NetBIOS node status requester +C. -Returns whether a node is answering netbios calls or not. +Given a reference to an array of hashes will connects to the C of a +node and gets NetBIOS node status information. + +Returns the supplied reference to an array of hashes with MAC address, +NetBIOS name, NetBIOS domain/workgroup, NetBIOS user, and NetBIOS server +service status for addresses which responded. =cut -sub do_nbtstat { - my ($host, $now) = @_; - my $ip = NetAddr::IP::Lite->new($host) or return; +sub nbtstat_resolve_async { + my $ips = shift; - unless ( $ip->version() == 4 ) { - debug ' nbtstat only supports IPv4, invalid ip %s', $ip->addr; - return; + my $timeout = setting('nbtstat_timeout') || 1; + my $interval = setting('nbtstat_interval') || 0.02; + + my $stater = App::Netdisco::AnyEvent::Nbtstat->new( + timeout => $timeout, + interval => $interval + ); + + # Set up the condvar + my $cv = AE::cv; + $cv->begin( sub { shift->send } ); + + foreach my $hash_ref (@$ips) { + my $ip = $hash_ref->{'ip'}; + $cv->begin; + $stater->nbtstat( + $ip, + sub { + my $res = shift; + _filter_nbname( $ip, $hash_ref, $res ); + $cv->end; + } + ); } - my $nb = Net::NBName->new; - my $ns = $nb->node_status( $ip->addr ); + # Decrement the cv counter to cancel out the send declaration + $cv->end; - # Check for NetBIOS Info - return unless $ns; + # Wait for the resolver to perform all resolutions + $cv->recv; - my $nbname = _filter_nbname( $ip->addr, $ns ); + # Close sockets + undef $stater; - if ($nbname) { - store_nbt($nbname, $now); - } - - return 1; + return $ips; } # filter nbt names / information sub _filter_nbname { my $ip = shift; + my $hash_ref = shift; my $node_status = shift; my $server = 0; @@ -68,10 +90,10 @@ sub _filter_nbname { my $domain = ''; my $nbuser = ''; - for my $rr ( $node_status->names ) { - my $suffix = defined $rr->suffix ? $rr->suffix : -1; - my $G = defined $rr->G ? $rr->G : ''; - my $name = defined $rr->name ? $rr->name : ''; + for my $rr ( @{$node_status->{'names'}} ) { + my $suffix = defined $rr->{'suffix'} ? $rr->{'suffix'} : -1; + my $G = defined $rr->{'G'} ? $rr->{'G'} : ''; + my $name = defined $rr->{'name'} ? $rr->{'name'} : ''; if ( $suffix == 0 and $G eq "GROUP" ) { $domain = $name; @@ -88,11 +110,11 @@ sub _filter_nbname { } unless ($nbname) { - debug ' nbtstat no computer name found for %s', $ip; + debug sprintf ' nbtstat no computer name found for %s', $ip; return; } - my $mac = $node_status->mac_address || ''; + my $mac = $node_status->{'mac_address'} || ''; unless ( check_mac( $ip, $mac ) ) { @@ -101,23 +123,23 @@ sub _filter_nbname { ->single( { ip => $ip, -bool => 'active' } ); if ( !defined $node_ip ) { - debug ' no MAC for %s returned by nbtstat or in DB', $ip; + debug sprintf ' no MAC for %s returned by nbtstat or in DB', $ip; return; } $mac = $node_ip->mac; } - return { - ip => $ip, - mac => $mac, - nbname => $nbname, - domain => $domain, - server => $server, - nbuser => $nbuser - }; + $hash_ref->{'ip'} = $ip; + $hash_ref->{'mac'} = $mac; + $hash_ref->{'nbname'} = $nbname; + $hash_ref->{'domain'} = $domain; + $hash_ref->{'server'} = $server; + $hash_ref->{'nbuser'} = $nbuser; + + return; } -=head2 store_nbt($nb_hash_ref, $now?) +=item store_nbt($nb_hash_ref, $now?) Stores entries in C table from the provided hash reference; MAC C, IP C, Unique NetBIOS Node Name C, NetBIOS Domain or diff --git a/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Nbtstat.pm b/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Nbtstat.pm index f787d3fd..97c6e882 100644 --- a/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Nbtstat.pm +++ b/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Nbtstat.pm @@ -3,7 +3,7 @@ package App::Netdisco::Daemon::Worker::Poller::Nbtstat; use Dancer qw/:moose :syntax :script/; use Dancer::Plugin::DBIC 'schema'; -use App::Netdisco::Core::Nbtstat 'do_nbtstat'; +use App::Netdisco::Core::Nbtstat qw/nbtstat_resolve_async store_nbt/; use App::Netdisco::Util::Node 'is_nbtstatable'; use App::Netdisco::Util::Device qw/get_device is_discoverable/; use App::Netdisco::Daemon::Util ':all'; @@ -33,7 +33,7 @@ sub nbtstat { } # get list of nodes on device - my $interval = (setting('nbt_max_age') || 7) . ' day'; + my $interval = (setting('nbtstat_max_age') || 7) . ' day'; my $rs = schema('netdisco')->resultset('NodeIp')->search({ -bool => 'me.active', -bool => 'nodes.active', @@ -46,10 +46,25 @@ sub nbtstat { })->ip_version(4); my @nodes = $rs->get_column('ip')->all; - my $now = 'to_timestamp('. (join '.', gettimeofday) .')'; - $self->_single_node_body('nbtstat', $_, $now) - for @nodes; + # Unless we have IP's don't bother + if (scalar @nodes) { + # filter exclusions from config + @nodes = grep { is_nbtstatable( $_ ) } @nodes; + + # setup the hash nbtstat_resolve_async expects + my @ips = map {+{'ip' => $_}} @nodes; + my $now = 'to_timestamp('. (join '.', gettimeofday) .')'; + + my $resolved_nodes = nbtstat_resolve_async(\@ips); + + # update node_nbt with status entries + foreach my $result (@$resolved_nodes) { + if (defined $result->{'nbname'}) { + store_nbt($result, $now); + } + } + } return job_done("Ended nbtstat for ". $host->addr); } diff --git a/Netdisco/lib/App/Netdisco/Manual/Configuration.pod b/Netdisco/lib/App/Netdisco/Manual/Configuration.pod index 7857e593..e89a1f5a 100644 --- a/Netdisco/lib/App/Netdisco/Manual/Configuration.pod +++ b/Netdisco/lib/App/Netdisco/Manual/Configuration.pod @@ -681,6 +681,20 @@ Value: Number. Default: 7. The maximum age of a node in days for it to be checked for NetBIOS information. +=head3 C + +Value: Number. Default: 0.02. + +Interval between nbtstat requests in each poller. Defaults to 0.02 seconds, +equating to 50 requests per second per poller. + +=head3 C + +Value: Number. Default: 1. + +Seconds nbtstat will wait for a response before time out. Accepts fractional +seconds as well as integers. + =head3 C Value: Number of Days. diff --git a/Netdisco/lib/App/Netdisco/Util/Node.pm b/Netdisco/lib/App/Netdisco/Util/Node.pm index 9555a57d..6157f472 100644 --- a/Netdisco/lib/App/Netdisco/Util/Node.pm +++ b/Netdisco/lib/App/Netdisco/Util/Node.pm @@ -221,11 +221,9 @@ Returns false if the host is not permitted to nbtstat the target node. sub is_nbtstatable { my $ip = shift; - return _bail_msg("is_nbtstatable: node matched nbtstat_no") - if check_node_no($ip, 'nbtstat_no'); + return if check_node_no($ip, 'nbtstat_no'); - return _bail_msg("is_nbtstatable: node failed to match nbtstat_only") - unless check_node_only($ip, 'nbtstat_only'); + return unless check_node_only($ip, 'nbtstat_only'); return 1; } diff --git a/Netdisco/share/config.yml b/Netdisco/share/config.yml index 43429d3f..aba89351 100644 --- a/Netdisco/share/config.yml +++ b/Netdisco/share/config.yml @@ -119,6 +119,8 @@ arpnip_min_age: 0 nbtstat_no: [] nbtstat_only: [] nbtstat_max_age: 7 +nbtstat_interval: 0.02 +nbtstat_timeout: 1 expire_devices: 0 expire_nodes: 0 expire_nodes_archive: 0 From f3cd0580e4ae3e4eb7207c9a2e164bf5a0017efa Mon Sep 17 00:00:00 2001 From: "Eric A. Miller" Date: Sun, 20 Jul 2014 21:51:55 -0400 Subject: [PATCH 24/41] Net::NBName is no longer needed --- Netdisco/Makefile.PL | 1 - 1 file changed, 1 deletion(-) diff --git a/Netdisco/Makefile.PL b/Netdisco/Makefile.PL index 4e99c5fe..b3ef5d02 100644 --- a/Netdisco/Makefile.PL +++ b/Netdisco/Makefile.PL @@ -35,7 +35,6 @@ requires 'Net::Domain' => 1.23; requires 'Net::DNS' => 0.72; requires 'Net::LDAP' => 0; requires 'Net::MAC' => 2.103622; -requires 'Net::NBName' => 0.26; requires 'NetAddr::IP' => 4.068; requires 'Opcode' => 1.07; requires 'Path::Class' => 0.32; From f297098a3a2a99296667e6c62d7e600b5a4b65e7 Mon Sep 17 00:00:00 2001 From: "Eric A. Miller" Date: Sun, 20 Jul 2014 21:52:46 -0400 Subject: [PATCH 25/41] POD update --- Netdisco/lib/App/Netdisco/Util/DNS.pm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Netdisco/lib/App/Netdisco/Util/DNS.pm b/Netdisco/lib/App/Netdisco/Util/DNS.pm index a8712f30..d55b2e42 100644 --- a/Netdisco/lib/App/Netdisco/Util/DNS.pm +++ b/Netdisco/lib/App/Netdisco/Util/DNS.pm @@ -110,9 +110,7 @@ resolver C. Given a reference to an array of hashes will resolve the C or C address in the C or C key of each hash into its hostname which -will be inserted in the C key of the hash. The resolver does also -forward-lookups to verify that the resolved hostnames point to the -address. +will be inserted in the C key of the hash. Returns the supplied reference to an array of hashes with dns values for addresses which resolved. From bc2ef8fc5a9fd8b8cf5940f1ff40607498719381 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Mon, 21 Jul 2014 09:08:16 +0100 Subject: [PATCH 26/41] release 2.028006 --- Netdisco/Changes | 4 ++++ Netdisco/MANIFEST | 1 + Netdisco/META.yml | 3 +-- Netdisco/lib/App/Netdisco.pm | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 23cfa3a3..dd8d6a8c 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -4,6 +4,10 @@ * Asynchronous NBTstat + [BUG FIXES] + + * Device DNS Mismatch report: use name in the name column, and fix link + 2.028005 - 2014-07-17 [BUG FIXES] diff --git a/Netdisco/MANIFEST b/Netdisco/MANIFEST index 910fa460..96aea60d 100644 --- a/Netdisco/MANIFEST +++ b/Netdisco/MANIFEST @@ -20,6 +20,7 @@ inc/Module/Install/Share.pm inc/Module/Install/Win32.pm inc/Module/Install/WriteAll.pm lib/App/Netdisco.pm +lib/App/Netdisco/AnyEvent/Nbtstat.pm lib/App/Netdisco/Configuration.pm lib/App/Netdisco/Core/Arpnip.pm lib/App/Netdisco/Core/Discover.pm diff --git a/Netdisco/META.yml b/Netdisco/META.yml index 815ee1d3..c0ec94cf 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -51,7 +51,6 @@ requires: Net::Domain: 1.23 Net::LDAP: 0 Net::MAC: 2.103622 - Net::NBName: 0.26 NetAddr::IP: 4.068 Opcode: 1.07 Path::Class: 0.32 @@ -82,4 +81,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: 2.028005 +version: 2.028006 diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index bc23871a..6b2de515 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -4,7 +4,7 @@ use strict; use warnings; use 5.010_000; -our $VERSION = '2.028005'; +our $VERSION = '2.028006'; use App::Netdisco::Configuration; =head1 NAME From 24a19f5cf8e40c2718fb3cc5beda79b48d032305 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Mon, 21 Jul 2014 21:07:47 +0000 Subject: [PATCH 27/41] DNS Mismatch report incorrectly trimmed domain_suffix --- Netdisco/Changes | 8 +++++++- .../App/Netdisco/DB/Result/Virtual/DeviceDnsMismatch.pm | 5 ++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index dd8d6a8c..15fcb892 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,4 +1,10 @@ -2.028006 +2.028006 - 2014-07-21 + + [BUG FIXES] + + * DNS Mismatch report incorrectly trimmed domain_suffix + +2.028006 - 2014-07-21 [ENHANCEMENTS] diff --git a/Netdisco/lib/App/Netdisco/DB/Result/Virtual/DeviceDnsMismatch.pm b/Netdisco/lib/App/Netdisco/DB/Result/Virtual/DeviceDnsMismatch.pm index 71ac29a1..bde7b768 100644 --- a/Netdisco/lib/App/Netdisco/DB/Result/Virtual/DeviceDnsMismatch.pm +++ b/Netdisco/lib/App/Netdisco/DB/Result/Virtual/DeviceDnsMismatch.pm @@ -17,9 +17,8 @@ SELECT * FROM device WHERE dns IS NULL OR name IS NULL - OR lower(trim(TRAILING ? - FROM dns)::text) != lower(trim(TRAILING ? - FROM name)::text) + OR regexp_replace(lower(dns), ? || '$', '') + != regexp_replace(lower(name), ? || '$', '') ENDSQL 1; From 7241db7a23b5c2017dcdce745f58897aa4723743 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Mon, 21 Jul 2014 21:24:12 +0000 Subject: [PATCH 28/41] Fake one device aliases entry for devices not providing ip_index --- Netdisco/Changes | 1 + Netdisco/lib/App/Netdisco/Core/Discover.pm | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Netdisco/Changes b/Netdisco/Changes index 15fcb892..7e8211fb 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -3,6 +3,7 @@ [BUG FIXES] * DNS Mismatch report incorrectly trimmed domain_suffix + * Fake one device aliases entry for devices not providing ip_index 2.028006 - 2014-07-21 diff --git a/Netdisco/lib/App/Netdisco/Core/Discover.pm b/Netdisco/lib/App/Netdisco/Core/Discover.pm index 0ca20078..64d505ef 100644 --- a/Netdisco/lib/App/Netdisco/Core/Discover.pm +++ b/Netdisco/lib/App/Netdisco/Core/Discover.pm @@ -146,6 +146,10 @@ sub store_device { scalar @aliases, $ENV{'PERL_ANYEVENT_MAX_OUTSTANDING_DNS'}; my $resolved_aliases = hostnames_resolve_async(\@aliases); + # fake one aliases entry for devices not providing ip_index + push @$resolved_aliases, { alias => $device->ip, dns => $hostname } + if 0 == scalar @aliases; + # VTP Management Domain -- assume only one. my $vtpdomains = $snmp->vtp_d_name; my $vtpdomain; From c1846103fe763b845d9e8f97438399f1992f7931 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Mon, 21 Jul 2014 21:48:52 +0000 Subject: [PATCH 29/41] DataTables render function should check for type --- Netdisco/Changes | 1 + Netdisco/share/views/ajax/report/ipinventory.tt | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 7e8211fb..c1f71100 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -4,6 +4,7 @@ * DNS Mismatch report incorrectly trimmed domain_suffix * Fake one device aliases entry for devices not providing ip_index + * DataTables render function should check for type 2.028006 - 2014-07-21 diff --git a/Netdisco/share/views/ajax/report/ipinventory.tt b/Netdisco/share/views/ajax/report/ipinventory.tt index 12e011a0..5eeadd61 100644 --- a/Netdisco/share/views/ajax/report/ipinventory.tt +++ b/Netdisco/share/views/ajax/report/ipinventory.tt @@ -25,11 +25,15 @@ $(document).ready(function() { "data": 'ip', "render": function(data, type, row, meta) { var cell_str = he.encode(data); - if (row.time_last && row.node) { - cell_str = '' + he.encode(data) + (row.active ? '' : '  ') + ''; - } - else if (row.time_last) { - cell_str = '' + he.encode(data) + ''; + if (type == 'display') { + if (row.time_last && row.node) { + cell_str = '' + he.encode(data) + + (row.active ? '' : '  ') + ''; + } + else if (row.time_last) { + cell_str = '' + he.encode(data) + ''; + } } return cell_str; } From 1d0ec0bed04ca853c78acb4dc125c452b7ff3ac2 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Mon, 21 Jul 2014 21:57:59 +0000 Subject: [PATCH 30/41] Add 'graph' option to netdisco-do --- Netdisco/Changes | 4 ++++ Netdisco/bin/netdisco-do | 10 ++++++++++ Netdisco/lib/App/Netdisco/Util/Graph.pm | 10 +++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index c1f71100..b84785d4 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,5 +1,9 @@ 2.028006 - 2014-07-21 + [ENHANCEMENTS] + + * Add 'graph' option to netdisco-do + [BUG FIXES] * DNS Mismatch report incorrectly trimmed domain_suffix diff --git a/Netdisco/bin/netdisco-do b/Netdisco/bin/netdisco-do index 85feea73..34a1a52d 100755 --- a/Netdisco/bin/netdisco-do +++ b/Netdisco/bin/netdisco-do @@ -81,6 +81,12 @@ if (!length $action) { with 'App::Netdisco::Daemon::Worker::Poller::Expiry'; with 'App::Netdisco::Daemon::Worker::Interactive::DeviceActions'; with 'App::Netdisco::Daemon::Worker::Interactive::PortActions'; + + use App::Netdisco::Util::Graph (); + sub graph { + App::Netdisco::Util::Graph::graph(); + return ('done', 'Generated graph data.'); + } } my $worker = MyWorker->new(); @@ -152,6 +158,10 @@ Run an arpnip on the device (specified with C<-d>). Run an nbtstat on the node (specified with C<-d>). +=head2 graph + +Generate GrapgViz graphs for the largest cluster of devices. + =head2 set_location Set the SNMP location field on the device (specified with C<-d>). Pass the diff --git a/Netdisco/lib/App/Netdisco/Util/Graph.pm b/Netdisco/lib/App/Netdisco/Util/Graph.pm index 90d5008c..ba5c2747 100644 --- a/Netdisco/lib/App/Netdisco/Util/Graph.pm +++ b/Netdisco/lib/App/Netdisco/Util/Graph.pm @@ -49,10 +49,14 @@ all subroutines. =head1 EXPORT +=over 4 + =item graph() Creates netmap of network. +=back + =cut sub graph { @@ -113,6 +117,8 @@ sub graph { =head1 EXPORT_OK +=over 4 + =item graph_each($graph_obj, $name) Generates subgraph. Does actual GraphViz calls. @@ -350,7 +356,7 @@ sub graph_addnode { return $rv; } -=head2 make_graph() +=item make_graph() Returns C object that represents the discovered network. @@ -360,6 +366,8 @@ vertex. Nodes without topology information are not included. +=back + =cut sub make_graph { From e991a642342c812f84e1bea13a8dc200434b90e2 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Mon, 21 Jul 2014 22:02:46 +0000 Subject: [PATCH 31/41] release 2.028007 --- Netdisco/Changes | 2 +- Netdisco/META.yml | 2 +- Netdisco/lib/App/Netdisco.pm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index b84785d4..b9213990 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,4 +1,4 @@ -2.028006 - 2014-07-21 +2.028007 - 2014-07-21 [ENHANCEMENTS] diff --git a/Netdisco/META.yml b/Netdisco/META.yml index c0ec94cf..a3800e44 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -81,4 +81,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: 2.028006 +version: '2.028007' diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index 6b2de515..7ae4799a 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -4,7 +4,7 @@ use strict; use warnings; use 5.010_000; -our $VERSION = '2.028006'; +our $VERSION = '2.028007'; use App::Netdisco::Configuration; =head1 NAME From 657eb7fe7b8484f5ceebc4115fe5d4e27d6fe55f Mon Sep 17 00:00:00 2001 From: "Eric A. Miller" Date: Mon, 21 Jul 2014 20:57:40 -0400 Subject: [PATCH 32/41] Encode NetBIOS name, domain, user as UTF-8 --- Netdisco/Changes | 6 ++++++ Netdisco/lib/App/Netdisco/Core/Nbtstat.pm | 7 ++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index b9213990..8e5d83d9 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,3 +1,9 @@ +2.028008 + + [BUG FIXES] + + * Encode NetBIOS name, domain, user as UTF-8 + 2.028007 - 2014-07-21 [ENHANCEMENTS] diff --git a/Netdisco/lib/App/Netdisco/Core/Nbtstat.pm b/Netdisco/lib/App/Netdisco/Core/Nbtstat.pm index 568a4189..2a70a9fc 100644 --- a/Netdisco/lib/App/Netdisco/Core/Nbtstat.pm +++ b/Netdisco/lib/App/Netdisco/Core/Nbtstat.pm @@ -6,6 +6,7 @@ use Dancer::Plugin::DBIC 'schema'; use App::Netdisco::Util::Node 'check_mac'; use NetAddr::IP::Lite ':lower'; use App::Netdisco::AnyEvent::Nbtstat; +use Encode; use base 'Exporter'; our @EXPORT = (); @@ -131,10 +132,10 @@ sub _filter_nbname { $hash_ref->{'ip'} = $ip; $hash_ref->{'mac'} = $mac; - $hash_ref->{'nbname'} = $nbname; - $hash_ref->{'domain'} = $domain; + $hash_ref->{'nbname'} = Encode::decode('UTF-8', $nbname); + $hash_ref->{'domain'} = Encode::decode('UTF-8', $domain); $hash_ref->{'server'} = $server; - $hash_ref->{'nbuser'} = $nbuser; + $hash_ref->{'nbuser'} = Encode::decode('UTF-8', $nbuser); return; } From 6a810d6450fe7861d4b3fa524b16a425c933a25a Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Tue, 22 Jul 2014 06:59:26 +0000 Subject: [PATCH 33/41] release 2.028008 --- Netdisco/Changes | 2 +- Netdisco/META.yml | 2 +- Netdisco/lib/App/Netdisco.pm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 8e5d83d9..1860eb0a 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,4 +1,4 @@ -2.028008 +2.028008 - 2014-07-22 [BUG FIXES] diff --git a/Netdisco/META.yml b/Netdisco/META.yml index a3800e44..63fdac6f 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -81,4 +81,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: '2.028007' +version: '2.028008' diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index 7ae4799a..19d9e6de 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -4,7 +4,7 @@ use strict; use warnings; use 5.010_000; -our $VERSION = '2.028007'; +our $VERSION = '2.028008'; use App::Netdisco::Configuration; =head1 NAME From 888b522c7fe0059ce66598d6917e312e08d54849 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Tue, 22 Jul 2014 20:26:15 +0100 Subject: [PATCH 34/41] node monitor admin panel --- Netdisco/Changes | 6 ++ Netdisco/lib/App/Netdisco.pm | 2 +- Netdisco/lib/App/Netdisco/Util/Node.pm | 17 ++-- .../Web/Plugin/AdminTask/NodeMonitor.pm | 75 +++++++++++++++ .../share/views/ajax/admintask/nodemonitor.tt | 92 +++++++++++++++++++ 5 files changed, 183 insertions(+), 9 deletions(-) create mode 100644 Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/NodeMonitor.pm create mode 100644 Netdisco/share/views/ajax/admintask/nodemonitor.tt diff --git a/Netdisco/Changes b/Netdisco/Changes index 1860eb0a..de8f5f24 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,3 +1,9 @@ +2.028009_001 + + [NEW FEATURES] + + * Node Monitor admin panel and netdisco-do action + 2.028008 - 2014-07-22 [BUG FIXES] diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index 19d9e6de..c1c51fc1 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -4,7 +4,7 @@ use strict; use warnings; use 5.010_000; -our $VERSION = '2.028008'; +our $VERSION = '2.028009_001'; use App::Netdisco::Configuration; =head1 NAME diff --git a/Netdisco/lib/App/Netdisco/Util/Node.pm b/Netdisco/lib/App/Netdisco/Util/Node.pm index 6157f472..ac87ebe1 100644 --- a/Netdisco/lib/App/Netdisco/Util/Node.pm +++ b/Netdisco/lib/App/Netdisco/Util/Node.pm @@ -37,7 +37,7 @@ database storage. Returns false, and might log a debug level message, if the checks fail. -Returns a true value if these checks pass: +Returns a true value (the MAC address in IEEE format) if these checks pass: =over 4 @@ -67,12 +67,13 @@ MAC address does not belong to an interface on any known Device sub check_mac { my ($device, $node, $port_macs) = @_; my $mac = Net::MAC->new(mac => $node, 'die' => 0, verbose => 0); + my $devip = (ref $device ? $device->ip : ''); $port_macs ||= {}; # incomplete MAC addresses (BayRS frame relay DLCI, etc) if ($mac->get_error) { debug sprintf ' [%s] check_mac - mac [%s] malformed - skipping', - $device->ip, $node; + $devip, $node; return 0; } else { @@ -92,32 +93,32 @@ sub check_mac { # 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; + $devip, $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; + $devip, $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; + $devip, $node; return 0; } # device's own MACs - if (exists $port_macs->{$node}) { + if ($port_macs and exists $port_macs->{$node}) { debug sprintf ' [%s] check_mac - mac [%s] is device port - skipping', - $device->ip, $node; + $devip, $node; return 0; } - return 1; + return $node; } =head2 check_node_no( $ip, $setting_name ) diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/NodeMonitor.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/NodeMonitor.pm new file mode 100644 index 00000000..f8483b95 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/NodeMonitor.pm @@ -0,0 +1,75 @@ +package App::Netdisco::Web::Plugin::AdminTask::NodeMonitor; + +use Dancer ':syntax'; +use Dancer::Plugin::Ajax; +use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; + +use App::Netdisco::Web::Plugin; +use App::Netdisco::Util::Node 'check_mac'; + +register_admin_task({ + tag => 'nodemonitor', + label => 'Node Monitor', +}); + +sub _sanity_ok { + return 0 unless param('mac') + and check_mac(undef, param('mac')); + + params->{mac} = check_mac(undef, param('mac')); + return 1; +} + +ajax '/ajax/control/admin/nodemonitor/add' => require_role admin => sub { + send_error('Bad Request', 400) unless _sanity_ok(); + + schema('netdisco')->txn_do(sub { + my $monitor = schema('netdisco')->resultset('NodeMonitor') + ->create({ + mac => param('mac'), + active => (param('active') ? \'true' : \'false'), + why => param('why'), + cc => param('cc'), + }); + }); +}; + +ajax '/ajax/control/admin/nodemonitor/del' => require_role admin => sub { + send_error('Bad Request', 400) unless _sanity_ok(); + + schema('netdisco')->txn_do(sub { + schema('netdisco')->resultset('NodeMonitor') + ->find({mac => param('mac')})->delete; + }); +}; + +ajax '/ajax/control/admin/nodemonitor/update' => require_role admin => sub { + send_error('Bad Request', 400) unless _sanity_ok(); + + schema('netdisco')->txn_do(sub { + my $monitor = schema('netdisco')->resultset('NodeMonitor') + ->find({mac => param('mac')}); + return unless $monitor; + + $monitor->update({ + mac => param('mac'), + active => (param('active') ? \'true' : \'false'), + why => param('why'), + cc => param('cc'), + date => \'now()', + }); + }); +}; + +ajax '/ajax/content/admin/nodemonitor' => require_role admin => sub { + my $set = schema('netdisco')->resultset('NodeMonitor') + ->search(undef, { order_by => [qw/active date mac/] }); + + content_type('text/html'); + template 'ajax/admintask/nodemonitor.tt', { + results => $set, + }, { layout => undef }; +}; + +true; diff --git a/Netdisco/share/views/ajax/admintask/nodemonitor.tt b/Netdisco/share/views/ajax/admintask/nodemonitor.tt new file mode 100644 index 00000000..c80419f5 --- /dev/null +++ b/Netdisco/share/views/ajax/admintask/nodemonitor.tt @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + [% SET count = 0 %] + [% WHILE (row = results.next) %] + [% SET count = count + 1 %] + + + + + + + + + + [% END %] + +
Date AddedMAC AddressEnabledReasonEmailAction
+ +
[% row.date | html_entity %] + + + + + + + + + + + + + +
+ + + From 2178860394c5cc04592c25803422cb72dc95132c Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Tue, 22 Jul 2014 21:06:44 +0100 Subject: [PATCH 35/41] add node monitor email in netdisco-do --- Netdisco/bin/netdisco-do | 6 +++ .../Netdisco/DB/Result/Virtual/NodeMonitor.pm | 49 +++++++++++++++++ Netdisco/lib/App/Netdisco/Util/NodeMonitor.pm | 52 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 Netdisco/lib/App/Netdisco/DB/Result/Virtual/NodeMonitor.pm create mode 100644 Netdisco/lib/App/Netdisco/Util/NodeMonitor.pm diff --git a/Netdisco/bin/netdisco-do b/Netdisco/bin/netdisco-do index 34a1a52d..89adafea 100755 --- a/Netdisco/bin/netdisco-do +++ b/Netdisco/bin/netdisco-do @@ -87,6 +87,12 @@ if (!length $action) { App::Netdisco::Util::Graph::graph(); return ('done', 'Generated graph data.'); } + + use App::Netdisco::Util::NodeMonitor (); + sub monitor { + App::Netdisco::Util::NodeMonitor::monitor(); + return ('done', 'Generated monitor data.'); + } } my $worker = MyWorker->new(); diff --git a/Netdisco/lib/App/Netdisco/DB/Result/Virtual/NodeMonitor.pm b/Netdisco/lib/App/Netdisco/DB/Result/Virtual/NodeMonitor.pm new file mode 100644 index 00000000..40813bff --- /dev/null +++ b/Netdisco/lib/App/Netdisco/DB/Result/Virtual/NodeMonitor.pm @@ -0,0 +1,49 @@ +package App::Netdisco::DB::Result::Virtual::NodeMonitor; + +use strict; +use warnings; + +use utf8; +use base 'DBIx::Class::Core'; + +__PACKAGE__->table_class('DBIx::Class::ResultSource::View'); + +__PACKAGE__->table('node_monitor_virtual'); +__PACKAGE__->result_source_instance->is_virtual(1); +__PACKAGE__->result_source_instance->view_definition(<<'ENDSQL'); +SELECT nm.why, nm.cc, trim(trailing '.' from trim(trailing '0123456789' from date::text)) as date, + n.mac, n.switch, n.port, + d.name, d.location, + dp.name AS portname +FROM node_monitor nm, node n, device d, device_port dp +WHERE nm.mac = n.mac + AND nm.active + AND nm.cc IS NOT NULL + AND d.ip = n.switch + AND dp.ip = n.switch + AND dp.port = n.port + AND d.last_macsuck = n.time_last +ENDSQL + +__PACKAGE__->add_columns( + "why", + { data_type => "text", is_nullable => 1 }, + "cc", + { data_type => "text", is_nullable => 0 }, + "date", + { data_type => "timestamp", is_nullable => 0 }, + "mac", + { data_type => "macaddr", is_nullable => 0 }, + "switch", + { data_type => "inet", is_nullable => 0 }, + "port", + { data_type => "text", is_nullable => 0 }, + "name", + { data_type => "text", is_nullable => 0 }, + "location", + { data_type => "text", is_nullable => 1 }, + "portname", + { data_type => "text", is_nullable => 0 }, +); + +1; diff --git a/Netdisco/lib/App/Netdisco/Util/NodeMonitor.pm b/Netdisco/lib/App/Netdisco/Util/NodeMonitor.pm new file mode 100644 index 00000000..6c47e67c --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Util/NodeMonitor.pm @@ -0,0 +1,52 @@ +package App::Netdisco::Util::NodeMonitor; + +use App::Netdisco; + +use Dancer qw/:syntax :script/; +use Dancer::Plugin::DBIC 'schema'; + +use App::Netdisco::Util::DNS qw/hostname_from_ip ipv4_from_hostname/; + +use base 'Exporter'; +our @EXPORT_OK = qw/ + monitor +/; +our %EXPORT_TAGS = (all => \@EXPORT_OK); + +sub _email { + my ($to, $subject, $body) = @_; + my $domain = setting('domain_suffix') || 'localhost'; + $domain =~ s/^\.//; + + my $SENDMAIL = '/usr/sbin/sendmail'; + open (SENDMAIL, "| $SENDMAIL -t") or die "Can't open sendmail at $SENDMAIL.\n"; + print SENDMAIL "To: $to\n"; + print SENDMAIL "From: Netdisco \n"; + print SENDMAIL "Subject: $subject\n\n"; + print SENDMAIL $body; + close (SENDMAIL) or die "Can't send letter. $!\n"; +} + +sub monitor { + my $monitor = schema('netdisco')->resultset('Virtual::NodeMonitor'); + + while (my $entry = $monitor->next) { + my $body = <<"end_body"; +........ n e t d i s c o ......... + Node : @{[$entry->mac]} (@{[$entry->why]}) + When : @{[$entry->date]} + Switch : @{[$entry->name]} (@{[$entry->switch]}) + Port : @{[$entry->port]} (@{[$entry->portname]}) + Location: @{[$entry->location]} + +end_body + + _email( + $entry->cc, + "Saw mac @{[$entry->mac]} (@{[$entry->why]}) on @{[$entry->name]} @{[$entry->port]}", + $body + ); + } +} + +1; From 723d8b7d30e4b07051b803c2921e3a32b7df4fce Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Tue, 22 Jul 2014 20:18:43 +0000 Subject: [PATCH 36/41] update MANIFEST --- Netdisco/MANIFEST | 4 ++++ Netdisco/META.yml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Netdisco/MANIFEST b/Netdisco/MANIFEST index 96aea60d..e44e74a2 100644 --- a/Netdisco/MANIFEST +++ b/Netdisco/MANIFEST @@ -81,6 +81,7 @@ lib/App/Netdisco/DB/Result/Virtual/DeviceLinks.pm lib/App/Netdisco/DB/Result/Virtual/DevicePoeStatus.pm lib/App/Netdisco/DB/Result/Virtual/DuplexMismatch.pm lib/App/Netdisco/DB/Result/Virtual/GenericReport.pm +lib/App/Netdisco/DB/Result/Virtual/NodeMonitor.pm lib/App/Netdisco/DB/Result/Virtual/NodeWithAge.pm lib/App/Netdisco/DB/Result/Virtual/OrphanedDevices.pm lib/App/Netdisco/DB/Result/Virtual/PhonesDiscovered.pm @@ -154,6 +155,7 @@ lib/App/Netdisco/Util/DNS.pm lib/App/Netdisco/Util/ExpandParams.pm lib/App/Netdisco/Util/Graph.pm lib/App/Netdisco/Util/Node.pm +lib/App/Netdisco/Util/NodeMonitor.pm lib/App/Netdisco/Util/Noop.pm lib/App/Netdisco/Util/Permission.pm lib/App/Netdisco/Util/Port.pm @@ -169,6 +171,7 @@ lib/App/Netdisco/Web/GenericReport.pm lib/App/Netdisco/Web/Password.pm lib/App/Netdisco/Web/Plugin.pm lib/App/Netdisco/Web/Plugin/AdminTask/JobQueue.pm +lib/App/Netdisco/Web/Plugin/AdminTask/NodeMonitor.pm lib/App/Netdisco/Web/Plugin/AdminTask/OrphanedDevices.pm lib/App/Netdisco/Web/Plugin/AdminTask/PollerPerformance.pm lib/App/Netdisco/Web/Plugin/AdminTask/PseudoDevice.pm @@ -294,6 +297,7 @@ share/public/javascripts/toastr.js share/public/javascripts/underscore.min.js share/views/admintask.tt share/views/ajax/admintask/jobqueue.tt +share/views/ajax/admintask/nodemonitor.tt share/views/ajax/admintask/orphaned.tt share/views/ajax/admintask/orphaned_csv.tt share/views/ajax/admintask/performance.tt diff --git a/Netdisco/META.yml b/Netdisco/META.yml index 63fdac6f..02c6fdec 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -81,4 +81,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: '2.028008' +version: 2.028009_001 From 18ec29a522c07ea023e8a242b278700707d173ec Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Tue, 22 Jul 2014 22:38:41 +0100 Subject: [PATCH 37/41] release 2.028010 --- Netdisco/Changes | 8 ++++++-- Netdisco/META.yml | 2 +- Netdisco/lib/App/Netdisco.pm | 2 +- Netdisco/lib/App/Netdisco/AnyEvent/Nbtstat.pm | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index de8f5f24..d49189dd 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,8 +1,12 @@ -2.028009_001 +2.028010 - 2014-07-22 [NEW FEATURES] - * Node Monitor admin panel and netdisco-do action + * Node Monitor admin panel and netdisco-do action (beta) + + [BUG FIXES] + + * Hash deref in Nbtstat.pm 2.028008 - 2014-07-22 diff --git a/Netdisco/META.yml b/Netdisco/META.yml index 02c6fdec..f498134b 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -81,4 +81,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: 2.028009_001 +version: 2.028010 diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index c1c51fc1..92dd6057 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -4,7 +4,7 @@ use strict; use warnings; use 5.010_000; -our $VERSION = '2.028009_001'; +our $VERSION = '2.028010'; use App::Netdisco::Configuration; =head1 NAME diff --git a/Netdisco/lib/App/Netdisco/AnyEvent/Nbtstat.pm b/Netdisco/lib/App/Netdisco/AnyEvent/Nbtstat.pm index b8075481..f8746706 100644 --- a/Netdisco/lib/App/Netdisco/AnyEvent/Nbtstat.pm +++ b/Netdisco/lib/App/Netdisco/AnyEvent/Nbtstat.pm @@ -36,7 +36,7 @@ sub new { }; # Nbtstat tasks - $self->{_tasks} = {}; + $self->{_tasks} = {}; return $self; } @@ -60,7 +60,7 @@ sub nbtstat { $self->{_tasks}{ $request->{destination} } = $request; - my $delay = $self->interval * scalar keys $self->{_tasks}; + my $delay = $self->interval * scalar keys %{ $self->{_tasks} || {} }; # There's probably a better way to throttle the sends # but this will work for now since we currently don't support retries From af97767fcd280789468cb1c1816c8a99585a58ef Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Tue, 22 Jul 2014 22:46:54 +0100 Subject: [PATCH 38/41] Load Graph modules only if available --- Netdisco/Changes | 6 ++++++ Netdisco/bin/netdisco-do | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index d49189dd..9ba9c851 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,3 +1,9 @@ +2.028011 - 2014-07-22 + + [BUG FIXES] + + * Load Graph modules only if available + 2.028010 - 2014-07-22 [NEW FEATURES] diff --git a/Netdisco/bin/netdisco-do b/Netdisco/bin/netdisco-do index 89adafea..58bd329b 100755 --- a/Netdisco/bin/netdisco-do +++ b/Netdisco/bin/netdisco-do @@ -82,7 +82,8 @@ if (!length $action) { with 'App::Netdisco::Daemon::Worker::Interactive::DeviceActions'; with 'App::Netdisco::Daemon::Worker::Interactive::PortActions'; - use App::Netdisco::Util::Graph (); + use Module::Load (); + eval { Module::Load::load 'App::Netdisco::Util::Graph' }; sub graph { App::Netdisco::Util::Graph::graph(); return ('done', 'Generated graph data.'); From 9202044b8f55ada33086f014d0b8eb6399ac543c Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Tue, 22 Jul 2014 22:48:13 +0100 Subject: [PATCH 39/41] release 2.028011 --- Netdisco/META.yml | 2 +- Netdisco/lib/App/Netdisco.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Netdisco/META.yml b/Netdisco/META.yml index f498134b..db9d75e2 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -81,4 +81,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: 2.028010 +version: 2.028011 diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index 92dd6057..79125bc1 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -4,7 +4,7 @@ use strict; use warnings; use 5.010_000; -our $VERSION = '2.028010'; +our $VERSION = '2.028011'; use App::Netdisco::Configuration; =head1 NAME From df71cf946ebb193b9ac1f39baca870f983e0be50 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Tue, 22 Jul 2014 23:13:11 +0100 Subject: [PATCH 40/41] UTF8 decode the remote type string in neighbor protocol --- Netdisco/Changes | 6 ++++++ Netdisco/lib/App/Netdisco/Core/Discover.pm | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Netdisco/Changes b/Netdisco/Changes index 9ba9c851..117ba8f9 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,3 +1,9 @@ +2.028012 - 2014-07-22 + + [BUG FIXES] + + * UTF8 decode the remote type string in neighbor protocol + 2.028011 - 2014-07-22 [BUG FIXES] diff --git a/Netdisco/lib/App/Netdisco/Core/Discover.pm b/Netdisco/lib/App/Netdisco/Core/Discover.pm index 64d505ef..dbacbbdd 100644 --- a/Netdisco/lib/App/Netdisco/Core/Discover.pm +++ b/Netdisco/lib/App/Netdisco/Core/Discover.pm @@ -686,7 +686,7 @@ sub store_neighbors { 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_type = Encode::decode('UTF-8', $c_platform->{$entry} || ''); my $remote_id = Encode::decode('UTF-8', $c_id->{$entry}); my $remote_cap = $c_cap->{$entry} || []; From 18f507ec56dcbaa174c79377d202ac89bbde2441 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Tue, 22 Jul 2014 23:18:41 +0100 Subject: [PATCH 41/41] release 2.028012 --- Netdisco/META.yml | 2 +- Netdisco/lib/App/Netdisco.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Netdisco/META.yml b/Netdisco/META.yml index db9d75e2..fe65e38b 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -81,4 +81,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: 2.028011 +version: 2.028012 diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index 79125bc1..879e6765 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -4,7 +4,7 @@ use strict; use warnings; use 5.010_000; -our $VERSION = '2.028011'; +our $VERSION = '2.028012'; use App::Netdisco::Configuration; =head1 NAME