From 4e5b544b9c2b8746d787cf261afdbdfb36cd1ebc Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 9 Jul 2017 10:03:52 +0100 Subject: [PATCH] =?UTF-8?q?#325=20significant=20speed-up=20to=20Device=20>?= =?UTF-8?q?=20Ports=20tab=20(thx=20to=20T.=20Ter=C3=A4s)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Changes | 1 + lib/App/Netdisco/DB/Result/DevicePort.pm | 23 ++------ lib/App/Netdisco/DB/ResultSet/Device.pm | 2 +- lib/App/Netdisco/Web/Plugin/Device/Ports.pm | 64 ++++++++++++++------- share/views/ajax/device/ports.tt | 25 ++++---- share/views/ajax/device/ports_csv.tt | 23 +++----- 6 files changed, 69 insertions(+), 69 deletions(-) diff --git a/Changes b/Changes index d7de6ea9..40352c55 100644 --- a/Changes +++ b/Changes @@ -4,6 +4,7 @@ * Documentation note on OS upgrade * #324 use a (better) host group for internal localnet filter + * #325 significant speed-up to Device > Ports tab (thx to T. Teräs) 2.036005 - 2017-07-05 diff --git a/lib/App/Netdisco/DB/Result/DevicePort.pm b/lib/App/Netdisco/DB/Result/DevicePort.pm index 32776d75..6d8db873 100644 --- a/lib/App/Netdisco/DB/Result/DevicePort.pm +++ b/lib/App/Netdisco/DB/Result/DevicePort.pm @@ -91,26 +91,11 @@ __PACKAGE__->belongs_to( device => 'App::Netdisco::DB::Result::Device', 'ip' ); Returns the set of C entries associated with this Port. These will be both tagged and untagged. Use this relation in search conditions. -See also C. - =cut __PACKAGE__->has_many( port_vlans => 'App::Netdisco::DB::Result::DevicePortVlan', { 'foreign.ip' => 'self.ip', 'foreign.port' => 'self.port' } ); -=head2 all_port_vlans - -Returns the set of C entries associated with this Port. -These will be both tagged and untagged. Use this relation when prefetching related -C rows. - -See also C. - -=cut - -__PACKAGE__->has_many( all_port_vlans => 'App::Netdisco::DB::Result::DevicePortVlan', - { 'foreign.ip' => 'self.ip', 'foreign.port' => 'self.port' } ); - =head2 nodes / active_nodes / nodes_with_age / active_nodes_with_age Returns the set of Nodes whose MAC addresses are associated with this Device @@ -241,10 +226,10 @@ __PACKAGE__->belongs_to( neighbor_alias => 'App::Netdisco::DB::Result::DeviceIp' sub { my $args = shift; return { - "$args->{foreign_alias}.ip" => { '=' => + "$args->{foreign_alias}.alias" => { '=' => $args->{self_resultsource}->schema->resultset('DeviceIp') ->search({alias => { -ident => "$args->{self_alias}.remote_ip"}}, - {rows => 1, columns => 'ip', alias => 'devipsub'})->as_query } + {rows => 1, columns => 'alias', alias => 'devipsub'})->as_query } }; }, { join_type => 'LEFT' }, @@ -252,7 +237,7 @@ __PACKAGE__->belongs_to( neighbor_alias => 'App::Netdisco::DB::Result::DeviceIp' =head2 vlans -As compared to C, this relationship returns a set of VLAN +As compared to C, this relationship returns a set of Device VLAN row objects for the VLANs on the given port, which might be more useful if you want to find out details such as the VLAN name. @@ -260,7 +245,7 @@ See also C. =cut -__PACKAGE__->many_to_many( vlans => 'all_port_vlans', 'vlan' ); +__PACKAGE__->many_to_many( vlans => 'port_vlans', 'vlan' ); =head2 oui diff --git a/lib/App/Netdisco/DB/ResultSet/Device.pm b/lib/App/Netdisco/DB/ResultSet/Device.pm index 2e8820f0..ea582bf9 100644 --- a/lib/App/Netdisco/DB/ResultSet/Device.pm +++ b/lib/App/Netdisco/DB/ResultSet/Device.pm @@ -96,7 +96,7 @@ sub search_aliases { { order_by => [qw/ me.dns me.ip /], join => 'device_ips', - distinct => 1, + group_by => 'me.ip', } ); } diff --git a/lib/App/Netdisco/Web/Plugin/Device/Ports.pm b/lib/App/Netdisco/Web/Plugin/Device/Ports.pm index e67477e0..c791979a 100644 --- a/lib/App/Netdisco/Web/Plugin/Device/Ports.pm +++ b/lib/App/Netdisco/Web/Plugin/Device/Ports.pm @@ -113,7 +113,32 @@ get '/ajax/content/device/ports' => require_login sub { $set = $set->search({-or => \@combi}); } - # get aggregate master status + # so far only the basic device_port data + # now begin to join tables depending on the selected columns/options + + # get vlans on the port + # leave this query dormant (lazy) unless c_vmember is set + my $vlans = $set->search({}, { + select => [ + 'port', + { array_agg => 'port_vlans.vlan', -as => 'vlan_set' }, + { count => 'port_vlans.vlan', -as => 'vlan_count' }, + ], + join => 'port_vlans', + group_by => 'me.port', + }); + + if (param('c_vmember')) { + $vlans = { map {( + $_->port => { + # DBIC smart enough to work out this should be an arrayref :) + vlan_set => $_->get_column('vlan_set'), + vlan_count => $_->get_column('vlan_count'), + }, + )} $vlans->all }; + } + + # get aggregate master status (self join) $set = $set->search({}, { 'join' => 'agg_master', '+select' => [qw/agg_master.up_admin agg_master.up/], @@ -123,17 +148,6 @@ get '/ajax/content/device/ports' => require_login sub { # make sure query asks for formatted timestamps when needed $set = $set->with_times if param('c_lastchange'); - # get vlans on the port, if there aren't too many - my $port_cnt = $device->ports->count() || 1; - my $vlan_cnt = $device->port_vlans->count() || 1; - my $vmember_ok = - (($vlan_cnt / $port_cnt) <= setting('devport_vlan_limit')); - - if ($vmember_ok) { - $set = $set->search_rs({}, { prefetch => 'all_port_vlans' })->with_vlan_count - if param('c_vmember'); - } - # what kind of nodes are we interested in? my $nodes_name = (param('n_archived') ? 'nodes' : 'active_nodes'); $nodes_name .= '_with_age' if param('n_age'); @@ -144,28 +158,35 @@ get '/ajax/content/device/ports' => require_login sub { if (param('c_nodes')) { # retrieve active/all connected nodes, if asked for - $set = $set->search_rs({}, { prefetch => [{$nodes_name => $ips_name}] }); - $set = $set->search_rs({}, { order_by => ["${nodes_name}.vlan", "${nodes_name}.mac", "${ips_name}.ip"] }); + $set = $set->search({}, { prefetch => [{$nodes_name => $ips_name}] }); + $set = $set->search({}, { order_by => ["${nodes_name}.vlan", "${nodes_name}.mac", "${ips_name}.ip"] }); # retrieve wireless SSIDs, if asked for - $set = $set->search_rs({}, { prefetch => [{$nodes_name => 'wireless'}] }) + $set = $set->search({}, { prefetch => [{$nodes_name => 'wireless'}] }) if param('n_ssid'); # retrieve NetBIOS, if asked for - $set = $set->search_rs({}, { prefetch => [{$nodes_name => 'netbios'}] }) + $set = $set->search({}, { prefetch => [{$nodes_name => 'netbios'}] }) if param('n_netbios'); # retrieve vendor, if asked for - $set = $set->search_rs({}, { prefetch => [{$nodes_name => 'oui'}] }) + $set = $set->search({}, { prefetch => [{$nodes_name => 'oui'}] }) if param('n_vendor'); } # retrieve SSID, if asked for - $set = $set->search({}, { prefetch => 'ssid' }) if param('c_ssid'); + $set = $set->search({}, { prefetch => 'ssid' }) + if param('c_ssid'); # retrieve neighbor devices, if asked for - $set = $set->search_rs({}, { prefetch => [{neighbor_alias => 'device'}] }) - if param('c_neighbors'); + #$set = $set->search({}, { prefetch => [{neighbor_alias => 'device'}] }) + # if param('c_neighbors'); + # retrieve neighbor devices, if asked for + $set = $set->search({}, { + join => 'neighbor_alias', + '+select' => ['neighbor_alias.ip', 'neighbor_alias.dns'], + '+as' => ['neighbor_ip', 'neighbor_dns'], + }) if param('c_neighbors'); # sort ports (empty set would be a 'no records' msg) my $results = [ sort { &App::Netdisco::Util::Web::sort_port($a->port, $b->port) } $set->all ]; @@ -177,7 +198,7 @@ get '/ajax/content/device/ports' => require_login sub { nodes => $nodes_name, ips => $ips_name, device => $device, - vmember_ok => $vmember_ok, + vlans => $vlans, }, { layout => undef }; } else { @@ -187,6 +208,7 @@ get '/ajax/content/device/ports' => require_login sub { nodes => $nodes_name, ips => $ips_name, device => $device, + vlans => $vlans, }, { layout => undef }; } }; diff --git a/share/views/ajax/device/ports.tt b/share/views/ajax/device/ports.tt index 7c3b51ee..1937c7fd 100644 --- a/share/views/ajax/device/ports.tt +++ b/share/views/ajax/device/ports.tt @@ -19,11 +19,12 @@ [% FOREACH row IN results %] + [% SET portname = row.port %] [% IF row.up_admin != 'up' %] - [% ELSIF row.up == 'up' AND row.stp == 'blocking' AND row.vlan_count < 2 %] + [% ELSIF row.up == 'up' AND row.stp == 'blocking' AND vlans.$portname.vlan_count < 2 %] [% ELSIF row.has_column_loaded('is_free') AND row.is_free %] @@ -190,27 +191,23 @@ [% IF params.c_vmember %] - [% IF vmember_ok %] - [% IF row.vlan_count %] + [% IF vlans.$portname.vlan_count <= settings.devport_vlan_limit %] [% SET output = '' %] - [% SET vlanlist = [] %] - [% FOREACH vlan IN row.all_port_vlans %][% vlanlist.push(vlan.get_column('vlan')) %][% END %] - [% FOREACH vlan IN vlanlist.nsort %] + [% FOREACH vlan IN vlans.$portname.vlan_set.nsort %] [% SET output = output _ '' _ vlan _ '' %] [% SET output = output _ ', ' IF NOT loop.last %] [% END %] - [% IF row.vlan_count > 10 %] [%# TODO make this a settable variable %] - [% SET output = '
(' _ row.vlan_count + [% IF vlans.$portname.vlan_count > 10 %] [%# TODO make this a settable variable %] + [% SET output = '
(' _ vlans.$portname.vlan_count _ ')
Show VLANs
' _ output %] [% SET output = output _ '
' %] [% END %] [% output %] - [% END %] [% ELSE %] - (too many to list) + ([% vlans.$portname.vlan_count %] is too many to list) [% END %] [% END %] @@ -264,18 +261,18 @@ [% IF params.c_nodes OR params.c_neighbors %] [% IF params.c_neighbors AND (row.remote_ip OR row.is_uplink) %] - [% IF row.neighbor %] + [% IF row.get_column('neighbor_ip') %] [% IF row.remote_type AND row.remote_type.match('(?i)ip.phone') %]   [% ELSIF row.remote_type AND row.remote_type.match('^AP:\s') %]   [% END %] - - [% row.neighbor.dns.remove(settings.domain_suffix) || row.neighbor.ip | html_entity %] + + [% row.get_column('neighbor_dns').remove(settings.domain_suffix) || row.get_column('neighbor_ip') | html_entity %] [% IF row.remote_port %] - - + [% row.remote_port | html_entity %] [% END %]
diff --git a/share/views/ajax/device/ports_csv.tt b/share/views/ajax/device/ports_csv.tt index 72ee06a4..81a31fe5 100644 --- a/share/views/ajax/device/ports_csv.tt +++ b/share/views/ajax/device/ports_csv.tt @@ -104,18 +104,13 @@ [% END %] [% IF params.c_vmember %] - [% IF row.vlan_count %] - [% SET output = '' %] - [% SET vlanlist = [] %] - [% FOREACH vlan IN row.all_port_vlans %][% vlanlist.push(vlan.get_column('vlan')) %][% END %] - [% FOREACH vlan IN vlanlist.nsort %] - [% SET output = output _ ',' IF NOT loop.first %] - [% SET output = output _ vlan %] - [% END %] - [% myport.push(output) %] - [% ELSE %] - [% myport.push('') %] + [% SET portname = row.port %] + [% SET output = '' %] + [% FOREACH vlan IN vlans.$portname.vlan_set.nsort %] + [% SET output = output _ ',' IF NOT loop.first %] + [% SET output = output _ vlan %] [% END %] + [% myport.push(output) %] [% END %] [% IF params.c_power %] @@ -139,9 +134,9 @@ [% IF params.c_neighbors %] [% IF (row.remote_ip OR row.is_uplink) %] - [% IF row.neighbor %] - [% myport.push( row.neighbor.ip ) %] - [% myport.push( row.neighbor.dns.remove(settings.domain_suffix) ) %] + [% IF row.get_column('neighbor_ip') %] + [% myport.push( row.get_column('neighbor_ip') ) %] + [% myport.push( row.get_column('neighbor_dns').remove(settings.domain_suffix) ) %] [% ELSIF row.remote_ip AND row.remote_port %] [% myport.push( row.remote_ip ) %] [% myport.push('') %]