Compare commits
	
		
			10 Commits
		
	
	
		
			d428b82220
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					1d5e5ad8cb | ||
| 
						 | 
					12c4b22ea2 | ||
| 
						 | 
					ccd2de0651 | ||
| 
						 | 
					9128c0d50d | ||
| 
						 | 
					bc47b54f67 | ||
| 
						 | 
					b85ca1140f | ||
| 
						 | 
					a91e518f16 | ||
| 
						 | 
					a068960b51 | ||
| 
						 | 
					8397eabe50 | ||
| 
						 | 
					2afa56dde9 | 
							
								
								
									
										1
									
								
								Build.PL
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Build.PL
									
									
									
									
									
								
							@@ -79,6 +79,7 @@ Module::Build->new(
 | 
				
			|||||||
    'Plack::Middleware::ReverseProxy' => '0.15',
 | 
					    'Plack::Middleware::ReverseProxy' => '0.15',
 | 
				
			||||||
    'Pod::Usage' => 0,
 | 
					    'Pod::Usage' => 0,
 | 
				
			||||||
    'Regexp::Common' => 2017060201,
 | 
					    'Regexp::Common' => 2017060201,
 | 
				
			||||||
 | 
					    'Regexp::Common::net::CIDR' => 0,
 | 
				
			||||||
    'Role::Tiny' => '1.002005',
 | 
					    'Role::Tiny' => '1.002005',
 | 
				
			||||||
    'Scope::Guard' => 0,
 | 
					    'Scope::Guard' => 0,
 | 
				
			||||||
    'Sereal' => '0',
 | 
					    'Sereal' => '0',
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										32
									
								
								Changes
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								Changes
									
									
									
									
									
								
							@@ -1,3 +1,35 @@
 | 
				
			|||||||
 | 
					2.071001 - 2023-12-07
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [BUG FIXES]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  * update vlansneverconfigured report to skip vlan 1
 | 
				
			||||||
 | 
					  * better approach to HTML entity encoding in custom report searchable fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.071000 - 2023-12-07
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [NEW FEATURES]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  * device port external links support
 | 
				
			||||||
 | 
					  * new preset fields and custom fields support for device external links
 | 
				
			||||||
 | 
					  * custom reports returning array columns will be split over lines
 | 
				
			||||||
 | 
					  * default database in tenancies can have a friendly name
 | 
				
			||||||
 | 
					  * #1133 improve searchable generic report fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ENHANCEMENTS]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  * tidied up the report menus and report names
 | 
				
			||||||
 | 
					  * #830 subnets report can show all subnets to start with
 | 
				
			||||||
 | 
					  * #920 unused VLANs report
 | 
				
			||||||
 | 
					  * #999 device vlan count report
 | 
				
			||||||
 | 
					  * #1018 VLANs with Multiple Names report
 | 
				
			||||||
 | 
					  * #1022 VLANs Known but Not Configured report
 | 
				
			||||||
 | 
					  * #1023 ports with most vlans report
 | 
				
			||||||
 | 
					  * #1052 duplicate private networks report
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [BUG FIXES]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  * do not reverse the external links for IPs and Devices
 | 
				
			||||||
 | 
					
 | 
				
			||||||
2.070003 - 2023-11-24
 | 
					2.070003 - 2023-11-24
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  [BUG FIXES]
 | 
					  [BUG FIXES]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								MANIFEST
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								MANIFEST
									
									
									
									
									
								
							@@ -578,6 +578,8 @@ share/views/ajax/report/subnets.tt
 | 
				
			|||||||
share/views/ajax/report/subnets_csv.tt
 | 
					share/views/ajax/report/subnets_csv.tt
 | 
				
			||||||
share/views/ajax/report/vlaninventory.tt
 | 
					share/views/ajax/report/vlaninventory.tt
 | 
				
			||||||
share/views/ajax/report/vlaninventory_csv.tt
 | 
					share/views/ajax/report/vlaninventory_csv.tt
 | 
				
			||||||
 | 
					share/views/ajax/report/vlanmultiplenames.tt
 | 
				
			||||||
 | 
					share/views/ajax/report/vlanmultiplenames_csv.tt
 | 
				
			||||||
share/views/ajax/search/device.tt
 | 
					share/views/ajax/search/device.tt
 | 
				
			||||||
share/views/ajax/search/device_csv.tt
 | 
					share/views/ajax/search/device_csv.tt
 | 
				
			||||||
share/views/ajax/search/node_by_ip.tt
 | 
					share/views/ajax/search/node_by_ip.tt
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -93,6 +93,7 @@
 | 
				
			|||||||
            "Plack::Middleware::ReverseProxy" : "0.15",
 | 
					            "Plack::Middleware::ReverseProxy" : "0.15",
 | 
				
			||||||
            "Pod::Usage" : "0",
 | 
					            "Pod::Usage" : "0",
 | 
				
			||||||
            "Regexp::Common" : "2017060201",
 | 
					            "Regexp::Common" : "2017060201",
 | 
				
			||||||
 | 
					            "Regexp::Common::net::CIDR" : "0",
 | 
				
			||||||
            "Role::Tiny" : "1.002005",
 | 
					            "Role::Tiny" : "1.002005",
 | 
				
			||||||
            "SNMP::Info" : "3.95",
 | 
					            "SNMP::Info" : "3.95",
 | 
				
			||||||
            "SQL::Abstract" : "1.85",
 | 
					            "SQL::Abstract" : "1.85",
 | 
				
			||||||
@@ -138,7 +139,7 @@
 | 
				
			|||||||
   "provides" : {
 | 
					   "provides" : {
 | 
				
			||||||
      "App::Netdisco" : {
 | 
					      "App::Netdisco" : {
 | 
				
			||||||
         "file" : "lib/App/Netdisco.pm",
 | 
					         "file" : "lib/App/Netdisco.pm",
 | 
				
			||||||
         "version" : "2.070003"
 | 
					         "version" : "2.071001"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "App::Netdisco::AnyEvent::Nbtstat" : {
 | 
					      "App::Netdisco::AnyEvent::Nbtstat" : {
 | 
				
			||||||
         "file" : "lib/App/Netdisco/AnyEvent/Nbtstat.pm"
 | 
					         "file" : "lib/App/Netdisco/AnyEvent/Nbtstat.pm"
 | 
				
			||||||
@@ -961,6 +962,6 @@
 | 
				
			|||||||
      "x_IRC" : "irc://irc.libera.chat/#netdisco",
 | 
					      "x_IRC" : "irc://irc.libera.chat/#netdisco",
 | 
				
			||||||
      "x_MailingList" : "https://lists.sourceforge.net/lists/listinfo/netdisco-users"
 | 
					      "x_MailingList" : "https://lists.sourceforge.net/lists/listinfo/netdisco-users"
 | 
				
			||||||
   },
 | 
					   },
 | 
				
			||||||
   "version" : "2.070003",
 | 
					   "version" : "2.071001",
 | 
				
			||||||
   "x_serialization_backend" : "JSON::PP version 4.16"
 | 
					   "x_serialization_backend" : "JSON::PP version 4.16"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								META.yml
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								META.yml
									
									
									
									
									
								
							@@ -23,7 +23,7 @@ name: App-Netdisco
 | 
				
			|||||||
provides:
 | 
					provides:
 | 
				
			||||||
  App::Netdisco:
 | 
					  App::Netdisco:
 | 
				
			||||||
    file: lib/App/Netdisco.pm
 | 
					    file: lib/App/Netdisco.pm
 | 
				
			||||||
    version: '2.070003'
 | 
					    version: '2.071001'
 | 
				
			||||||
  App::Netdisco::AnyEvent::Nbtstat:
 | 
					  App::Netdisco::AnyEvent::Nbtstat:
 | 
				
			||||||
    file: lib/App/Netdisco/AnyEvent/Nbtstat.pm
 | 
					    file: lib/App/Netdisco/AnyEvent/Nbtstat.pm
 | 
				
			||||||
  App::Netdisco::Backend::Job:
 | 
					  App::Netdisco::Backend::Job:
 | 
				
			||||||
@@ -625,6 +625,7 @@ requires:
 | 
				
			|||||||
  Plack::Middleware::ReverseProxy: '0.15'
 | 
					  Plack::Middleware::ReverseProxy: '0.15'
 | 
				
			||||||
  Pod::Usage: '0'
 | 
					  Pod::Usage: '0'
 | 
				
			||||||
  Regexp::Common: '2017060201'
 | 
					  Regexp::Common: '2017060201'
 | 
				
			||||||
 | 
					  Regexp::Common::net::CIDR: '0'
 | 
				
			||||||
  Role::Tiny: '1.002005'
 | 
					  Role::Tiny: '1.002005'
 | 
				
			||||||
  SNMP::Info: '3.95'
 | 
					  SNMP::Info: '3.95'
 | 
				
			||||||
  SQL::Abstract: '1.85'
 | 
					  SQL::Abstract: '1.85'
 | 
				
			||||||
@@ -663,5 +664,5 @@ resources:
 | 
				
			|||||||
  homepage: http://netdisco.org/
 | 
					  homepage: http://netdisco.org/
 | 
				
			||||||
  license: http://opensource.org/licenses/BSD-3-Clause
 | 
					  license: http://opensource.org/licenses/BSD-3-Clause
 | 
				
			||||||
  repository: https://github.com/netdisco/netdisco
 | 
					  repository: https://github.com/netdisco/netdisco
 | 
				
			||||||
version: '2.070003'
 | 
					version: '2.071001'
 | 
				
			||||||
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
 | 
					x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ use strict;
 | 
				
			|||||||
use warnings;
 | 
					use warnings;
 | 
				
			||||||
use 5.010_000;
 | 
					use 5.010_000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
our $VERSION = '2.070003';
 | 
					our $VERSION = '2.071001';
 | 
				
			||||||
use App::Netdisco::Configuration;
 | 
					use App::Netdisco::Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
=head1 NAME
 | 
					=head1 NAME
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -247,7 +247,7 @@ hook after_error_render => sub { setting('layout' => 'main') };
 | 
				
			|||||||
                               tag => $_->{'tag'},
 | 
					                               tag => $_->{'tag'},
 | 
				
			||||||
                               path => config->{'url_base'}->with("/t/$_->{tag}")->path } ) }
 | 
					                               path => config->{'url_base'}->with("/t/$_->{tag}")->path } ) }
 | 
				
			||||||
            @{ setting('tenant_databases') },
 | 
					            @{ setting('tenant_databases') },
 | 
				
			||||||
            { tag => 'netdisco', displayname => 'Default' }
 | 
					            { tag => 'netdisco', displayname => (setting('database')->{displayname} || 'Default') }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    config->{'tenant_data'}->{'netdisco'}->{'path'}
 | 
					    config->{'tenant_data'}->{'netdisco'}->{'path'}
 | 
				
			||||||
      = URI::Based->new((config->{path} eq '/') ? '' : config->{path})->path;
 | 
					      = URI::Based->new((config->{path} eq '/') ? '' : config->{path})->path;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,10 @@ use Path::Class 'file';
 | 
				
			|||||||
use Storable 'dclone';
 | 
					use Storable 'dclone';
 | 
				
			||||||
use Safe;
 | 
					use Safe;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use HTML::Entities 'encode_entities';
 | 
				
			||||||
 | 
					use Regexp::Common qw( RE_net_IPv4 RE_net_IPv6 RE_net_MAC RE_net_domain );
 | 
				
			||||||
 | 
					use Regexp::Common::net::CIDR ();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
our ($config, @data);
 | 
					our ($config, @data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
foreach my $report (@{setting('reports')}) {
 | 
					foreach my $report (@{setting('reports')}) {
 | 
				
			||||||
@@ -72,6 +76,42 @@ foreach my $report (@{setting('reports')}) {
 | 
				
			|||||||
      my @results = ((-f $munger) ? $compartment->rdo( $munger ) : @data);
 | 
					      my @results = ((-f $munger) ? $compartment->rdo( $munger ) : @data);
 | 
				
			||||||
      return if $@ or (0 == scalar @results);
 | 
					      return if $@ or (0 == scalar @results);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # searchable field support..
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      my $recidr4 = $RE{net}{CIDR}{IPv4}{-keep}; #RE_net_CIDR_IPv4(-keep);
 | 
				
			||||||
 | 
					      my $rev4 = RE_net_IPv4(-keep);
 | 
				
			||||||
 | 
					      my $rev6 = RE_net_IPv6(-keep);
 | 
				
			||||||
 | 
					      my $remac = RE_net_MAC(-keep);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      foreach my $row (@results) {
 | 
				
			||||||
 | 
					          foreach my $col (@column_order) {
 | 
				
			||||||
 | 
					              next unless $column_config{$col}->{_searchable};
 | 
				
			||||||
 | 
					              my $fields = (ref $row->{$col} ? $row->{$col} : [$row->{$col}]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              foreach my $f (@$fields) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  encode_entities($f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  $f =~ s!\b${recidr4}\b!'<a href="'.
 | 
				
			||||||
 | 
					                    uri_for('/search', {q => "$1/$2"})->path_query
 | 
				
			||||||
 | 
					                    .qq{">$1/$2</a>}!gex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  if (not $1 and not $2) {
 | 
				
			||||||
 | 
					                      $f =~ s!\b${rev4}\b!'<a href="'.
 | 
				
			||||||
 | 
					                        uri_for('/search', {q => $1})->path_query .qq{">$1</a>}!gex;
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  $f =~ s!\b${rev6}\b!'<a href="'.
 | 
				
			||||||
 | 
					                    uri_for('/search', {q => $1})->path_query .qq{">$1</a>}!gex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  $f =~ s!\b${remac}\b!'<a href="'.
 | 
				
			||||||
 | 
					                    uri_for('/search', {q => $1})->path_query .qq{">$1</a>}!gex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  $row->{$col} = $f if not ref $row->{$col};
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (request->is_ajax) {
 | 
					      if (request->is_ajax) {
 | 
				
			||||||
          template 'ajax/report/generic_report.tt',
 | 
					          template 'ajax/report/generic_report.tt',
 | 
				
			||||||
              { results => \@results,
 | 
					              { results => \@results,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										149
									
								
								share/config.yml
									
									
									
									
									
								
							
							
						
						
									
										149
									
								
								share/config.yml
									
									
									
									
									
								
							@@ -16,6 +16,7 @@ logger_format: '[%P] %U %L %m'
 | 
				
			|||||||
include_paths:  []
 | 
					include_paths:  []
 | 
				
			||||||
template_paths: []
 | 
					template_paths: []
 | 
				
			||||||
site_local_files: false
 | 
					site_local_files: false
 | 
				
			||||||
 | 
					database: {}
 | 
				
			||||||
external_databases: []
 | 
					external_databases: []
 | 
				
			||||||
tenant_databases: []
 | 
					tenant_databases: []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -126,21 +127,22 @@ sidebar_defaults:
 | 
				
			|||||||
    c_name:         { label: 'Name', default: checked, idx: 7 }
 | 
					    c_name:         { label: 'Name', default: checked, idx: 7 }
 | 
				
			||||||
    c_tags:         { label: 'Tags', default: null, idx: 8 }
 | 
					    c_tags:         { label: 'Tags', default: null, idx: 8 }
 | 
				
			||||||
    c_speed_admin:  { label: 'Speed (configured)', default: null, idx: 9 }
 | 
					    c_speed_admin:  { label: 'Speed (configured)', default: null, idx: 9 }
 | 
				
			||||||
    c_speed:        { label: 'Speed (running)', default: null, idx: 10 }
 | 
					    c_links:        { label: 'External Links', default: checked, idx: 10 }
 | 
				
			||||||
    c_duplex_admin: { label: 'Duplex (configured)', default: null, idx: 11 }
 | 
					    c_speed:        { label: 'Speed (running)', default: null, idx: 11 }
 | 
				
			||||||
    c_duplex:       { label: 'Duplex (running)', default: null, idx: 12 }
 | 
					    c_duplex_admin: { label: 'Duplex (configured)', default: null, idx: 12 }
 | 
				
			||||||
    c_error:        { label: 'Error Message', default: null, idx: 13 }
 | 
					    c_duplex:       { label: 'Duplex (running)', default: null, idx: 13 }
 | 
				
			||||||
    c_mac:          { label: 'Port MAC', default: null, idx: 14 }
 | 
					    c_error:        { label: 'Error Message', default: null, idx: 14 }
 | 
				
			||||||
    c_mtu:          { label: 'MTU', default: null, idx: 15 }
 | 
					    c_mac:          { label: 'Port MAC', default: null, idx: 15 }
 | 
				
			||||||
    c_pvid:         { label: 'Native VLAN', default: checked, idx: 16 }
 | 
					    c_mtu:          { label: 'MTU', default: null, idx: 16 }
 | 
				
			||||||
    c_vmember:      { label: 'VLAN Membership', default: checked, idx: 17 }
 | 
					    c_pvid:         { label: 'Native VLAN', default: checked, idx: 17 }
 | 
				
			||||||
    c_power:        { label: 'PoE', default: null, idx: 18 }
 | 
					    c_vmember:      { label: 'VLAN Membership', default: checked, idx: 18 }
 | 
				
			||||||
    c_ssid:         { label: 'SSID', default: null, idx: 19 }
 | 
					    c_power:        { label: 'PoE', default: null, idx: 19 }
 | 
				
			||||||
    c_nac_summary:  { label: 'NAC/802.1X Status', default: null, idx: 20 }
 | 
					    c_ssid:         { label: 'SSID', default: null, idx: 20 }
 | 
				
			||||||
    c_nodes:        { label: 'Connected Nodes', default: null, idx: 21 }
 | 
					    c_nac_summary:  { label: 'NAC/802.1X Status', default: null, idx: 21 }
 | 
				
			||||||
    c_neighbors:    { label: 'Connected Devices', default: checked, idx: 22 }
 | 
					    c_nodes:        { label: 'Connected Nodes', default: null, idx: 22 }
 | 
				
			||||||
    c_stp:          { label: 'Spanning Tree', default: null, idx: 23 }
 | 
					    c_neighbors:    { label: 'Connected Devices', default: checked, idx: 23 }
 | 
				
			||||||
    c_up:           { label: 'Up/Down Status', default: null, idx: 24 }
 | 
					    c_stp:          { label: 'Spanning Tree', default: null, idx: 24 }
 | 
				
			||||||
 | 
					    c_up:           { label: 'Up/Down Status', default: null, idx: 25 }
 | 
				
			||||||
    mac_format:     { default: IEEE }
 | 
					    mac_format:     { default: IEEE }
 | 
				
			||||||
    n_inventory:    { label: 'Remote Inventory', default: checked, idx: 0 }
 | 
					    n_inventory:    { label: 'Remote Inventory', default: checked, idx: 0 }
 | 
				
			||||||
    n_detailed_inventory: { label: 'Remote Advertisement', default: null, idx: 1 }
 | 
					    n_detailed_inventory: { label: 'Remote Advertisement', default: null, idx: 1 }
 | 
				
			||||||
@@ -171,8 +173,8 @@ sidebar_defaults:
 | 
				
			|||||||
    age_num:      { default: 3 }
 | 
					    age_num:      { default: 3 }
 | 
				
			||||||
    age_unit:     { default: months }
 | 
					    age_unit:     { default: months }
 | 
				
			||||||
device_port_col_idx_left: 8
 | 
					device_port_col_idx_left: 8
 | 
				
			||||||
device_port_col_idx_mid: 20
 | 
					device_port_col_idx_mid: 21
 | 
				
			||||||
device_port_col_idx_right: 24
 | 
					device_port_col_idx_right: 25
 | 
				
			||||||
jobqueue_refresh: 5
 | 
					jobqueue_refresh: 5
 | 
				
			||||||
safe_password_store: true
 | 
					safe_password_store: true
 | 
				
			||||||
reports: []
 | 
					reports: []
 | 
				
			||||||
@@ -181,7 +183,7 @@ system_reports:
 | 
				
			|||||||
    label: 'Blocked - Error-Disabled'
 | 
					    label: 'Blocked - Error-Disabled'
 | 
				
			||||||
    category: Port
 | 
					    category: Port
 | 
				
			||||||
    columns:
 | 
					    columns:
 | 
				
			||||||
      - { ip: Device }
 | 
					      - { ip: Device, _searchable: true }
 | 
				
			||||||
      - { dns: DNS }
 | 
					      - { dns: DNS }
 | 
				
			||||||
      - { port: Port }
 | 
					      - { port: Port }
 | 
				
			||||||
      - { name: Description }
 | 
					      - { name: Description }
 | 
				
			||||||
@@ -257,14 +259,14 @@ system_reports:
 | 
				
			|||||||
        HAVING count(vlan) > COALESCE(NULLIF(?,''), '1') ::integer
 | 
					        HAVING count(vlan) > COALESCE(NULLIF(?,''), '1') ::integer
 | 
				
			||||||
        ORDER BY vlans DESC, ip ASC, port ASC
 | 
					        ORDER BY vlans DESC, ip ASC, port ASC
 | 
				
			||||||
  - tag: duplicateprivatenetworks
 | 
					  - tag: duplicateprivatenetworks
 | 
				
			||||||
    category: Port
 | 
					    category: IP
 | 
				
			||||||
    label: 'Duplicate Private Networks'
 | 
					    label: 'Duplicate Private Networks'
 | 
				
			||||||
    columns:
 | 
					    columns:
 | 
				
			||||||
      - { subnet: 'Subnet', _searchable: true }
 | 
					      - { subnet: 'Subnet', _searchable: true }
 | 
				
			||||||
      - { count: 'Instances' }
 | 
					      - { count: 'Instances' }
 | 
				
			||||||
      - { seen: 'Where Seen' }
 | 
					      - { seen: 'Where Seen', _searchable: true }
 | 
				
			||||||
    query: |
 | 
					    query: |
 | 
				
			||||||
      SELECT subnet, count(subnet), array_to_string(array_agg(host(alias)::text || ' on ' || host(ip)::text), ', ') AS seen
 | 
					      SELECT subnet, count(subnet), array_agg(host(alias)::text || ' on ' || host(ip)::text) AS seen
 | 
				
			||||||
        FROM device_ip
 | 
					        FROM device_ip
 | 
				
			||||||
        WHERE ip <> alias
 | 
					        WHERE ip <> alias
 | 
				
			||||||
          AND (masklen(subnet) <> 32 AND masklen(subnet) <> 128)
 | 
					          AND (masklen(subnet) <> 32 AND masklen(subnet) <> 128)
 | 
				
			||||||
@@ -282,64 +284,85 @@ system_reports:
 | 
				
			|||||||
    columns:
 | 
					    columns:
 | 
				
			||||||
      - { ip: 'Device IP', _searchable: true }
 | 
					      - { ip: 'Device IP', _searchable: true }
 | 
				
			||||||
      - { vlans: 'VLAN List' }
 | 
					      - { vlans: 'VLAN List' }
 | 
				
			||||||
 | 
					    bind_params: ['chunk_size']
 | 
				
			||||||
    query: |
 | 
					    query: |
 | 
				
			||||||
      SELECT ip, array_to_string(array_agg(DISTINCT vlan::integer ORDER BY vlan::integer ASC), ', ') AS vlans
 | 
					      SELECT ip, array_agg(vlans) AS vlans FROM (
 | 
				
			||||||
        FROM device_port_vlan dpv
 | 
					        SELECT ip, array_to_string(array_agg(vlan), ', ') AS vlans, (x / COALESCE(NULLIF(?,''), '20') ::integer) AS chunk FROM (
 | 
				
			||||||
        WHERE native IS false
 | 
					          SELECT *, (row_number() over (partition by ip)) AS x FROM (
 | 
				
			||||||
          AND vlan <> 1
 | 
					
 | 
				
			||||||
          AND (
 | 
					            SELECT DISTINCT ip, vlan
 | 
				
			||||||
            SELECT count(*) FROM device_port_vlan dpv2
 | 
					              FROM device_port_vlan dpv
 | 
				
			||||||
              WHERE dpv2.ip = dpv.ip
 | 
					              WHERE native IS false
 | 
				
			||||||
               AND dpv2.vlan = dpv.vlan
 | 
					                AND vlan <> 1
 | 
				
			||||||
               AND native IS true
 | 
					                AND (
 | 
				
			||||||
          ) = 0
 | 
					                  SELECT count(*) FROM device_port_vlan dpv2
 | 
				
			||||||
        GROUP BY ip
 | 
					                    WHERE dpv2.ip = dpv.ip
 | 
				
			||||||
        ORDER BY ip
 | 
					                     AND dpv2.vlan = dpv.vlan
 | 
				
			||||||
 | 
					                     AND native IS true
 | 
				
			||||||
 | 
					                ) = 0
 | 
				
			||||||
 | 
					            ORDER BY ip, vlan)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ) GROUP BY ip, chunk
 | 
				
			||||||
 | 
					      ) GROUP BY ip ORDER BY ip
 | 
				
			||||||
  - tag: vlansneverconfigured
 | 
					  - tag: vlansneverconfigured
 | 
				
			||||||
    category: VLAN
 | 
					    category: VLAN
 | 
				
			||||||
    label: 'VLANs Known but Not Configured'
 | 
					    label: 'VLANs Known but Not Configured'
 | 
				
			||||||
    columns:
 | 
					    columns:
 | 
				
			||||||
      - { ip: 'Device IP', _searchable: true }
 | 
					      - { ip: 'Device IP', _searchable: true }
 | 
				
			||||||
      - { vlans: 'VLAN List' }
 | 
					      - { vlans: 'VLAN List' }
 | 
				
			||||||
 | 
					    bind_params: ['chunk_size']
 | 
				
			||||||
    query: |
 | 
					    query: |
 | 
				
			||||||
      SELECT ip, array_to_string(array_agg(DISTINCT dv.vlan::integer ORDER BY dv.vlan::integer ASC), ', ') AS vlans
 | 
					      SELECT ip, array_agg(vlans) AS vlans FROM (
 | 
				
			||||||
        FROM device_vlan dv
 | 
					        SELECT ip, array_to_string(array_agg(vlan), ', ') AS vlans, (x / COALESCE(NULLIF(?,''), '20') ::integer) AS chunk FROM (
 | 
				
			||||||
        WHERE NOT EXISTS (
 | 
					          SELECT *, (row_number() over (partition by ip)) AS x FROM (
 | 
				
			||||||
          SELECT FROM device_port_vlan dpv
 | 
					
 | 
				
			||||||
            WHERE dpv.ip = dv.ip
 | 
					            SELECT DISTINCT ip, vlan
 | 
				
			||||||
              AND dpv.vlan = dv.vlan
 | 
					              FROM device_vlan dv
 | 
				
			||||||
          )
 | 
					              WHERE vlan <> 1
 | 
				
			||||||
          AND vlan NOT IN (1002, 1003, 1004, 1005)
 | 
					                AND NOT EXISTS (
 | 
				
			||||||
        GROUP BY ip
 | 
					                SELECT FROM device_port_vlan dpv
 | 
				
			||||||
        ORDER BY ip
 | 
					                  WHERE dpv.ip = dv.ip
 | 
				
			||||||
 | 
					                    AND dpv.vlan = dv.vlan
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                AND vlan NOT IN (1002, 1003, 1004, 1005)
 | 
				
			||||||
 | 
					              ORDER BY ip, vlan)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ) GROUP BY ip, chunk
 | 
				
			||||||
 | 
					      ) GROUP BY ip ORDER BY ip
 | 
				
			||||||
  - tag: vlansunused
 | 
					  - tag: vlansunused
 | 
				
			||||||
    category: VLAN
 | 
					    category: VLAN
 | 
				
			||||||
    label: 'VLANs No Longer Used'
 | 
					    label: 'VLANs No Longer Used'
 | 
				
			||||||
    columns:
 | 
					    columns:
 | 
				
			||||||
      - { ip: 'Device IP', _searchable: true }
 | 
					      - { ip: 'Device IP', _searchable: true }
 | 
				
			||||||
      - { vlans: 'VLAN List' }
 | 
					      - { vlans: 'VLAN List' }
 | 
				
			||||||
    bind_params: ['free']
 | 
					    bind_params: ['chunk_size', 'free']
 | 
				
			||||||
    query: |
 | 
					    query: |
 | 
				
			||||||
      SELECT dpv.ip, array_to_string(array_agg(DISTINCT dpv.vlan::integer ORDER BY dpv.vlan::integer ASC), ', ') AS vlans
 | 
					      SELECT ip, array_agg(vlans) AS vlans FROM (
 | 
				
			||||||
        FROM device_port_vlan dpv
 | 
					        SELECT ip, array_to_string(array_agg(vlan), ', ') AS vlans, (x / COALESCE(NULLIF(?,''), '20') ::integer) AS chunk FROM (
 | 
				
			||||||
        WHERE dpv.native IS false
 | 
					          SELECT *, (row_number() over (partition by ip)) AS x FROM (
 | 
				
			||||||
          AND dpv.vlan <> 1
 | 
					
 | 
				
			||||||
          AND (
 | 
					            SELECT DISTINCT ip, vlan
 | 
				
			||||||
            SELECT count(*) FROM device_port_vlan dpv2
 | 
					              FROM device_port_vlan dpv
 | 
				
			||||||
              LEFT JOIN device_port dp USING (ip, port)
 | 
					              WHERE dpv.native IS false
 | 
				
			||||||
              LEFT JOIN device d USING (ip)
 | 
					                AND dpv.vlan <> 1
 | 
				
			||||||
              WHERE dpv2.ip = dpv.ip
 | 
					 | 
				
			||||||
                AND dpv2.vlan = dpv.vlan
 | 
					 | 
				
			||||||
                AND native IS true
 | 
					 | 
				
			||||||
                AND (
 | 
					                AND (
 | 
				
			||||||
                  dp.up_admin = 'up'
 | 
					                  SELECT count(*) FROM device_port_vlan dpv2
 | 
				
			||||||
                  OR age( LOCALTIMESTAMP,
 | 
					                    LEFT JOIN device_port dp USING (ip, port)
 | 
				
			||||||
                          to_timestamp( extract( epoch FROM d.last_discover ) - ( d.uptime - dp.lastchange ) /100 ) ::timestamp )
 | 
					                    LEFT JOIN device d USING (ip)
 | 
				
			||||||
                    < COALESCE(NULLIF(?,''), '3 months') ::interval
 | 
					                    WHERE dpv2.ip = dpv.ip
 | 
				
			||||||
                )
 | 
					                      AND dpv2.vlan = dpv.vlan
 | 
				
			||||||
          ) = 0
 | 
					                      AND native IS true
 | 
				
			||||||
        GROUP BY dpv.ip
 | 
					                      AND (
 | 
				
			||||||
        ORDER BY dpv.ip
 | 
					                        dp.up_admin = 'up'
 | 
				
			||||||
 | 
					                        OR age( LOCALTIMESTAMP,
 | 
				
			||||||
 | 
					                                to_timestamp( extract( epoch FROM d.last_discover ) - ( d.uptime - dp.lastchange ) /100 ) ::timestamp )
 | 
				
			||||||
 | 
					                          < COALESCE(NULLIF(?,''), '3 months') ::interval
 | 
				
			||||||
 | 
					                      )
 | 
				
			||||||
 | 
					                ) = 0
 | 
				
			||||||
 | 
					              ORDER BY dpv.ip, dpv.vlan)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ) GROUP BY ip, chunk
 | 
				
			||||||
 | 
					      ) GROUP BY ip ORDER BY ip
 | 
				
			||||||
  - tag: devicevlancount
 | 
					  - tag: devicevlancount
 | 
				
			||||||
    category: VLAN
 | 
					    category: VLAN
 | 
				
			||||||
    label: 'VLAN Count per Device'
 | 
					    label: 'VLAN Count per Device'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					[% PROCESS 'externallinks.tt' -%]
 | 
				
			||||||
[% SET user_can_port_control = user_has_role('port_control', device) %]
 | 
					[% SET user_can_port_control = user_has_role('port_control', device) %]
 | 
				
			||||||
<table id="dp-data-table" class="table table-bordered table-striped" width="100%" cellspacing="0">
 | 
					<table id="dp-data-table" class="table table-bordered table-striped" width="100%" cellspacing="0">
 | 
				
			||||||
  <thead>
 | 
					  <thead>
 | 
				
			||||||
@@ -5,6 +6,7 @@
 | 
				
			|||||||
      <th></th>
 | 
					      <th></th>
 | 
				
			||||||
      [% FOREACH item IN settings.port_columns %]
 | 
					      [% FOREACH item IN settings.port_columns %]
 | 
				
			||||||
      [% NEXT IF item.name == 'c_admin'  %]
 | 
					      [% NEXT IF item.name == 'c_admin'  %]
 | 
				
			||||||
 | 
					      [% NEXT IF item.name == 'c_links' AND settings.external_links.device_port.empty %]
 | 
				
			||||||
      [% NEXT IF item.name == 'c_nodes' AND params.c_nodes AND params.c_neighbors %]
 | 
					      [% NEXT IF item.name == 'c_nodes' AND params.c_nodes AND params.c_neighbors %]
 | 
				
			||||||
        [% NEXT UNLESS params.${item.name} %]
 | 
					        [% NEXT UNLESS params.${item.name} %]
 | 
				
			||||||
        [% SET th_class = '' %]
 | 
					        [% SET th_class = '' %]
 | 
				
			||||||
@@ -177,6 +179,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      [% END %]
 | 
					      [% END %]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      [% IF params.c_links AND settings.external_links.device_port.size > 0 %]
 | 
				
			||||||
 | 
					      <td>
 | 
				
			||||||
 | 
					        [% INCLUDE external_device_port_links
 | 
				
			||||||
 | 
					          item = row
 | 
				
			||||||
 | 
					          d = device
 | 
				
			||||||
 | 
					        %]
 | 
				
			||||||
 | 
					      </td>
 | 
				
			||||||
 | 
					      [% END %]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      [% IF params.c_speed_admin %]
 | 
					      [% IF params.c_speed_admin %]
 | 
				
			||||||
      <td>[% row.speed_admin | html_entity %]</td>
 | 
					      <td>[% row.speed_admin | html_entity %]</td>
 | 
				
			||||||
      [% END %]
 | 
					      [% END %]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,13 +10,16 @@
 | 
				
			|||||||
    [% FOREACH row IN results %]
 | 
					    [% FOREACH row IN results %]
 | 
				
			||||||
    <tr>
 | 
					    <tr>
 | 
				
			||||||
      [% FOREACH col IN columns %]
 | 
					      [% FOREACH col IN columns %]
 | 
				
			||||||
        [% IF column_options.$col._searchable %]
 | 
					      <td>
 | 
				
			||||||
        <td>
 | 
					        [% FOREACH record IN row.item(col) %]
 | 
				
			||||||
          <a href="[% uri_for('/search') | none %]?q=[% row.item(col) | uri %]">[% row.item(col) | html_entity %]</a>
 | 
					          [% IF column_options.$col._searchable %]
 | 
				
			||||||
        </td>
 | 
					            [% record | none %]
 | 
				
			||||||
        [% ELSE %]
 | 
					          [% ELSE %]
 | 
				
			||||||
        <td>[% row.item(col) | html_entity %]</td>
 | 
					            [% record | html_entity %]
 | 
				
			||||||
 | 
					          [% END %]
 | 
				
			||||||
 | 
					          [% '<br />' IF loop.size > 1 %]
 | 
				
			||||||
        [% END %]
 | 
					        [% END %]
 | 
				
			||||||
 | 
					      </td>
 | 
				
			||||||
      [% END %]
 | 
					      [% END %]
 | 
				
			||||||
    </tr>
 | 
					    </tr>
 | 
				
			||||||
    [% END %]
 | 
					    [% END %]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
<a href="[% link.url | evaltt %]" target="_blank">
 | 
					<a href="[% link.url | evaltt %]" target="_blank">
 | 
				
			||||||
     <span class="label label-default"><i class="icon-external-link"></i> [% link.displayname | html_entity %]</span></a>
 | 
					     <span class="label label-default"><i class="icon-external-link"></i> [% link.displayname | html_entity %]</span></a>
 | 
				
			||||||
[% END %]
 | 
					[% END %]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[% BLOCK external_mac_links %]
 | 
					[% BLOCK external_mac_links %]
 | 
				
			||||||
  [% FOREACH link IN settings.external_links.node.reverse %]
 | 
					  [% FOREACH link IN settings.external_links.node.reverse %]
 | 
				
			||||||
    [% NEXT UNLESS link.for_mac %]
 | 
					    [% NEXT UNLESS link.for_mac %]
 | 
				
			||||||
@@ -11,6 +12,7 @@
 | 
				
			|||||||
    <span class="nd_node-ext-link"> </span>
 | 
					    <span class="nd_node-ext-link"> </span>
 | 
				
			||||||
  [% END %]
 | 
					  [% END %]
 | 
				
			||||||
[% END %]
 | 
					[% END %]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[% BLOCK external_ip_links %]
 | 
					[% BLOCK external_ip_links %]
 | 
				
			||||||
  [% FOREACH link IN settings.external_links.node %]
 | 
					  [% FOREACH link IN settings.external_links.node %]
 | 
				
			||||||
    [% NEXT UNLESS link.for_ip %]
 | 
					    [% NEXT UNLESS link.for_ip %]
 | 
				
			||||||
@@ -18,6 +20,7 @@
 | 
				
			|||||||
    [% PROCESS external_link %]
 | 
					    [% PROCESS external_link %]
 | 
				
			||||||
  [% END %]
 | 
					  [% END %]
 | 
				
			||||||
[% END %]
 | 
					[% END %]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[% BLOCK external_device_links %]
 | 
					[% BLOCK external_device_links %]
 | 
				
			||||||
  [% FOREACH link IN settings.external_links.device %]
 | 
					  [% FOREACH link IN settings.external_links.device %]
 | 
				
			||||||
    [% device   = item.ip FILTER uri %]
 | 
					    [% device   = item.ip FILTER uri %]
 | 
				
			||||||
@@ -33,3 +36,20 @@
 | 
				
			|||||||
    [% PROCESS external_link %]
 | 
					    [% PROCESS external_link %]
 | 
				
			||||||
  [% END %]
 | 
					  [% END %]
 | 
				
			||||||
[% END %]
 | 
					[% END %]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[% BLOCK external_device_port_links %]
 | 
				
			||||||
 | 
					  [% FOREACH link IN settings.external_links.device_port %]
 | 
				
			||||||
 | 
					    [% device   = d.ip FILTER uri %]
 | 
				
			||||||
 | 
					    [% fqdn     = d.dns FILTER uri %]
 | 
				
			||||||
 | 
					    [% sysname  = d.name FILTER uri %]
 | 
				
			||||||
 | 
					    [% hostname = d.dns.remove(settings.domain_suffix) FILTER uri %]
 | 
				
			||||||
 | 
					    [% port     = item.port FILTER uri %]
 | 
				
			||||||
 | 
					    [% mac      = item.mac FILTER uri %]
 | 
				
			||||||
 | 
					    [% ifindex  = item.ifindex FILTER uri %]
 | 
				
			||||||
 | 
					    [% FOREACH config IN settings._extra_device_port_cols %]
 | 
				
			||||||
 | 
					      [% NEXT UNLESS config.field.match('^cf_') %]
 | 
				
			||||||
 | 
					      [% ${config.field} = item.get_column(${config.field}) FILTER uri %]
 | 
				
			||||||
 | 
					    [% END %]
 | 
				
			||||||
 | 
					    [% PROCESS external_link %]
 | 
				
			||||||
 | 
					  [% END %]
 | 
				
			||||||
 | 
					[% END %]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -176,7 +176,7 @@
 | 
				
			|||||||
        <li class="dropdown">
 | 
					        <li class="dropdown">
 | 
				
			||||||
          <a href="#" class="dropdown-toggle" data-toggle="dropdown">
 | 
					          <a href="#" class="dropdown-toggle" data-toggle="dropdown">
 | 
				
			||||||
            <i class="icon-home text-success"></i>
 | 
					            <i class="icon-home text-success"></i>
 | 
				
			||||||
             [% settings.tenant_data.$tenant.displayname || 'Default' | html_entity %]
 | 
					             [% (settings.tenant_data.$tenant.displayname || settings.database.displayname || 'Default') | html_entity %]
 | 
				
			||||||
            <b class="caret"></b>
 | 
					            <b class="caret"></b>
 | 
				
			||||||
          </a>
 | 
					          </a>
 | 
				
			||||||
          <ul class="dropdown-menu">
 | 
					          <ul class="dropdown-menu">
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user