#68 Devices orphaned by missing topology info report
This commit is contained in:
		| @@ -0,0 +1,32 @@ | ||||
| package App::Netdisco::DB::Result::Virtual::OrphanedDevices; | ||||
|  | ||||
| use strict; | ||||
| use warnings; | ||||
|  | ||||
| use utf8; | ||||
| use base 'App::Netdisco::DB::Result::Device'; | ||||
|  | ||||
| __PACKAGE__->load_components('Helper::Row::SubClass'); | ||||
| __PACKAGE__->subclass; | ||||
|  | ||||
| __PACKAGE__->table_class('DBIx::Class::ResultSource::View'); | ||||
| __PACKAGE__->table('orphaned_devices'); | ||||
| __PACKAGE__->result_source_instance->is_virtual(1); | ||||
| __PACKAGE__->result_source_instance->view_definition(<<'ENDSQL'); | ||||
| SELECT * | ||||
| FROM device | ||||
| WHERE ip NOT IN | ||||
|     ( SELECT DISTINCT dp.ip AS ip | ||||
|      FROM | ||||
|        (SELECT device_port.ip, | ||||
|                device_port.remote_ip | ||||
|         FROM device_port | ||||
|         WHERE device_port.remote_port IS NOT NULL | ||||
|         GROUP BY device_port.ip, | ||||
|                  device_port.remote_ip | ||||
|         ORDER BY device_port.ip) dp | ||||
|      LEFT JOIN device_ip di ON dp.remote_ip = di.alias | ||||
|      WHERE di.ip IS NOT NULL) | ||||
| ENDSQL | ||||
|  | ||||
| 1; | ||||
							
								
								
									
										54
									
								
								Netdisco/lib/App/Netdisco/DB/Result/Virtual/UnDirEdgesAgg.pm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								Netdisco/lib/App/Netdisco/DB/Result/Virtual/UnDirEdgesAgg.pm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| package App::Netdisco::DB::Result::Virtual::UnDirEdgesAgg; | ||||
|  | ||||
| use strict; | ||||
| use warnings; | ||||
|  | ||||
| use base 'DBIx::Class::Core'; | ||||
|  | ||||
| __PACKAGE__->table_class('DBIx::Class::ResultSource::View'); | ||||
|  | ||||
| __PACKAGE__->table('undir_edges_agg'); | ||||
| __PACKAGE__->result_source_instance->is_virtual(1); | ||||
| __PACKAGE__->result_source_instance->view_definition(<<'ENDSQL'); | ||||
|    SELECT left_ip, | ||||
|           array_agg(right_ip) AS links | ||||
|    FROM | ||||
|      ( SELECT dp.ip AS left_ip, | ||||
|               di.ip AS right_ip | ||||
|       FROM | ||||
|         (SELECT device_port.ip, | ||||
|                 device_port.remote_ip | ||||
|          FROM device_port | ||||
|          WHERE device_port.remote_port IS NOT NULL | ||||
|          GROUP BY device_port.ip, | ||||
|                   device_port.remote_ip) dp | ||||
|       LEFT JOIN device_ip di ON dp.remote_ip = di.alias | ||||
|       WHERE di.ip IS NOT NULL | ||||
|       UNION SELECT di.ip AS left_ip, | ||||
|                    dp.ip AS right_ip | ||||
|       FROM | ||||
|         (SELECT device_port.ip, | ||||
|                 device_port.remote_ip | ||||
|          FROM device_port | ||||
|          WHERE device_port.remote_port IS NOT NULL | ||||
|          GROUP BY device_port.ip, | ||||
|                   device_port.remote_ip) dp | ||||
|       LEFT JOIN device_ip di ON dp.remote_ip = di.alias | ||||
|       WHERE di.ip IS NOT NULL ) AS foo | ||||
|    GROUP BY left_ip | ||||
|    ORDER BY left_ip | ||||
| ENDSQL | ||||
|  | ||||
| __PACKAGE__->add_columns( | ||||
|   'left_ip' => { | ||||
|     data_type => 'inet', | ||||
|   }, | ||||
|   'links' => { | ||||
|     data_type => 'inet[]', | ||||
|   } | ||||
| ); | ||||
|  | ||||
| __PACKAGE__->belongs_to('device', 'App::Netdisco::DB::Result::Device', | ||||
|   { 'foreign.ip' => 'self.left_ip' }); | ||||
|  | ||||
| 1; | ||||
| @@ -0,0 +1,82 @@ | ||||
| package App::Netdisco::Web::Plugin::AdminTask::OrphanedDevices; | ||||
|  | ||||
| use strict; | ||||
| use warnings; | ||||
| use Dancer ':syntax'; | ||||
| use Dancer::Plugin::DBIC; | ||||
| use Dancer::Plugin::Auth::Extensible; | ||||
|  | ||||
| use App::Netdisco::Web::Plugin; | ||||
|  | ||||
| register_admin_task( | ||||
|     {   tag          => 'orphaned', | ||||
|         label        => 'Orphaned Devices / Networks', | ||||
|         provides_csv => 1, | ||||
|     } | ||||
| ); | ||||
|  | ||||
| get '/ajax/content/admin/orphaned' => require_role admin => sub { | ||||
|  | ||||
|     my @tree = schema('netdisco')->resultset('Virtual::UnDirEdgesAgg') | ||||
|         ->search( undef, { prefetch => 'device' } )->hri->all; | ||||
|  | ||||
|     my @orphans | ||||
|         = schema('netdisco')->resultset('Virtual::OrphanedDevices')->search() | ||||
|         ->order_by('ip')->hri->all; | ||||
|  | ||||
|     return unless ( scalar @tree || scalar @orphans ); | ||||
|  | ||||
|     my @ordered; | ||||
|  | ||||
|     if ( scalar @tree ) { | ||||
|         my %tree = map { $_->{'left_ip'} => $_ } @tree; | ||||
|  | ||||
|         my $current_graph = 0; | ||||
|         my %visited       = (); | ||||
|         my @to_visit      = (); | ||||
|         foreach my $node ( keys %tree ) { | ||||
|             next if exists $visited{$node}; | ||||
|  | ||||
|             $current_graph++; | ||||
|             @to_visit = ($node); | ||||
|             while (@to_visit) { | ||||
|                 my $node_to_visit = shift @to_visit; | ||||
|  | ||||
|                 $visited{$node_to_visit} = $current_graph; | ||||
|  | ||||
|                 push @to_visit, | ||||
|                     grep { !exists $visited{$_} } | ||||
|                     @{ $tree{$node_to_visit}->{'links'} }; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         my @graphs = (); | ||||
|         foreach my $key ( keys %visited ) { | ||||
|             push @{ $graphs[ $visited{$key} - 1 ] }, $tree{$key}->{'device'}; | ||||
|         } | ||||
|  | ||||
|         @ordered = sort { scalar @{$b} <=> scalar @{$a} } @graphs; | ||||
|     } | ||||
|  | ||||
|     return if ( scalar @ordered < 2 && !scalar @tree ); | ||||
|  | ||||
|     if ( request->is_ajax ) { | ||||
|         template 'ajax/admintask/orphaned.tt', | ||||
|             { | ||||
|             orphans => \@orphans, | ||||
|             graphs  => \@ordered, | ||||
|             }, | ||||
|             { layout => undef }; | ||||
|     } | ||||
|     else { | ||||
|         header( 'Content-Type' => 'text/comma-separated-values' ); | ||||
|         template 'ajax/admintask/orphaned_csv.tt', | ||||
|             { | ||||
|             orphans => \@orphans, | ||||
|             graphs  => \@ordered, | ||||
|             }, | ||||
|             { layout => undef }; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| 1; | ||||
		Reference in New Issue
	
	Block a user