diff --git a/lib/App/Netdisco/DB/Result/Virtual/PortVLANMismatch.pm b/lib/App/Netdisco/DB/Result/Virtual/PortVLANMismatch.pm
index 5e201cef..f5c54876 100644
--- a/lib/App/Netdisco/DB/Result/Virtual/PortVLANMismatch.pm
+++ b/lib/App/Netdisco/DB/Result/Virtual/PortVLANMismatch.pm
@@ -23,10 +23,14 @@ __PACKAGE__->result_source_instance->view_definition(<<'ENDSQL');
WHERE vlan::text NOT IN (?, ?, ?, ?) GROUP BY ip, port)
SELECT CASE WHEN length(ld.dns) > 0 THEN ld.dns ELSE host(ld.ip) END AS left_device,
+ ld.name AS left_name,
lp.port AS left_port,
+ lp.name AS left_portname,
(SELECT vlist FROM all_vlans WHERE ip=lp.ip AND port=lp.port) AS left_vlans,
CASE WHEN length(rd.dns) > 0 THEN rd.dns ELSE host(rd.ip) END AS right_device,
+ rd.name AS right_name,
rp.port AS right_port,
+ rp.name AS right_portname,
(SELECT vlist FROM all_vlans WHERE ip=rp.ip AND port=rp.port) AS right_vlans
FROM device ld
JOIN device_port lp USING (ip)
@@ -40,13 +44,17 @@ __PACKAGE__->result_source_instance->view_definition(<<'ENDSQL');
ENDSQL
__PACKAGE__->add_columns(
- 'left_device' => { data_type => 'text' },
- 'left_port' => { data_type => 'text' },
- 'left_vlans' => { data_type => 'text' },
+ 'left_device' => { data_type => 'text' },
+ 'left_name' => { data_type => 'text' },
+ 'left_port' => { data_type => 'text' },
+ 'left_portname' => { data_type => 'text' },
+ 'left_vlans' => { data_type => 'text' },
- 'right_device' => { data_type => 'text' },
- 'right_port' => { data_type => 'text' },
- 'right_vlans' => { data_type => 'text' },
+ 'right_device' => { data_type => 'text' },
+ 'right_name' => { data_type => 'text' },
+ 'right_port' => { data_type => 'text' },
+ 'right_portname' => { data_type => 'text' },
+ 'right_vlans' => { data_type => 'text' },
);
1;
diff --git a/lib/App/Netdisco/Web/Plugin/Report/PortVLANMismatch.pm b/lib/App/Netdisco/Web/Plugin/Report/PortVLANMismatch.pm
index 147ae640..3f11371b 100644
--- a/lib/App/Netdisco/Web/Plugin/Report/PortVLANMismatch.pm
+++ b/lib/App/Netdisco/Web/Plugin/Report/PortVLANMismatch.pm
@@ -5,6 +5,7 @@ use Dancer::Plugin::DBIC;
use Dancer::Plugin::Auth::Extensible;
use App::Netdisco::Web::Plugin;
+use List::MoreUtils qw/listcmp sort_by/;
register_report(
{ category => 'Port',
@@ -24,6 +25,22 @@ get '/ajax/content/report/portvlanmismatch' => require_login sub {
})
->hri->all;
+ # note that the generated list is rendered without HTML escape,
+ # so we MUST sanitise here with the grep
+ foreach my $res (@results) {
+ my @left = grep {m/^(?:n:)?\d+$/} map {s/\s//g; $_} split ',', $res->{left_vlans};
+ my @right = grep {m/^(?:n:)?\d+$/} map {s/\s//g; $_} split ',', $res->{right_vlans};
+
+ my %new = (0 => [], 1 => []);
+ my %cmp = listcmp @left, @right;
+ foreach my $vlan (keys %cmp) {
+ map { push @{ $new{$_} }, ( (2 == scalar @{ $cmp{$vlan} }) ? $vlan : "$vlan" ) } @{ $cmp{$vlan} };
+ }
+
+ $res->{left_vlans} = join ', ', sort_by { (my $a = $_) =~ s/\D//g; sprintf "%04d", $a } @{ $new{0} };
+ $res->{right_vlans} = join ', ', sort_by { (my $a = $_) =~ s/\D//g; sprintf "%04d", $a } @{ $new{1} };
+ }
+
if (request->is_ajax) {
my $json = to_json (\@results);
template 'ajax/report/portvlanmismatch.tt', { results => $json }, { layout => 'noop' };
diff --git a/share/views/ajax/report/portvlanmismatch.tt b/share/views/ajax/report/portvlanmismatch.tt
index ddb5affa..7637040a 100644
--- a/share/views/ajax/report/portvlanmismatch.tt
+++ b/share/views/ajax/report/portvlanmismatch.tt
@@ -20,27 +20,31 @@ $(document).ready(function() {
{
"data": 'left_device',
"render": function(data, type, row, meta) {
- return '' + he.encode(data || '') + ''; }
+ return '' + he.encode(data || '') + ''
+ + '
' + he.encode(row.left_name || ''); }
}, {
"data": 'left_port',
"type": 'portsort',
"render": function(data, type, row, meta) {
return type === 'display' ?
- '' + he.encode(data || '') + '' :
- he.encode(data || ''); }
+ '' + he.encode(data || '') + ''
+ + '
' + he.encode(row.left_portname || '')
+ : he.encode(data || ''); }
}, {
"data": 'left_vlans'
}, {
"data": 'right_device',
"render": function(data, type, row, meta) {
- return '' + he.encode(data || '') + ''; }
+ return '' + he.encode(data || '') + ''
+ + '
' + he.encode(row.right_name || ''); }
}, {
"data": 'right_port',
"type": 'portsort',
"render": function(data, type, row, meta) {
return type === 'display' ?
- '' + he.encode(data || '') + '' :
- he.encode(data || ''); }
+ '' + he.encode(data || '') + ''
+ + '
' + he.encode(row.right_portname || '')
+ : he.encode(data || ''); }
}, {
"data": 'right_vlans'
}