#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