diff --git a/Netdisco/lib/App/Netdisco/Web.pm b/Netdisco/lib/App/Netdisco/Web.pm index c3527b47..347ed3dc 100644 --- a/Netdisco/lib/App/Netdisco/Web.pm +++ b/Netdisco/lib/App/Netdisco/Web.pm @@ -12,6 +12,7 @@ use URI::QueryParam (); # part of URI, to add helper methods use App::Netdisco::Web::AuthN; use App::Netdisco::Web::Search; use App::Netdisco::Web::Device; +use App::Netdisco::Web::TypeAhead; use App::Netdisco::Web::PortControl; sub _load_web_plugins { diff --git a/Netdisco/lib/App/Netdisco/Web/Device.pm b/Netdisco/lib/App/Netdisco/Web/Device.pm index b8d4247f..dd84dcab 100644 --- a/Netdisco/lib/App/Netdisco/Web/Device.pm +++ b/Netdisco/lib/App/Netdisco/Web/Device.pm @@ -5,7 +5,6 @@ use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; use NetAddr::IP::Lite ':lower'; -use App::Netdisco::Util::Web (); # for sort_port hook 'before' => sub { # list of port detail columns @@ -87,206 +86,6 @@ hook 'before_template' => sub { $tokens->{self_options} = $self_uri->query_form_hash; }; -ajax '/ajax/content/device/:thing' => sub { - return "
Hello, this is where the ". param('thing') ." content goes.
"; -}; - -ajax '/ajax/content/device/netmap' => sub { - content_type('text/html'); - template 'ajax/device/netmap.tt', {}, { layout => undef }; -}; - -sub _get_name { - my $ip = shift; - my $domain = quotemeta( setting('domain_suffix') || '' ); - - (my $dns = (var('devices')->{$ip} || '')) =~ s/$domain$//; - return ($dns || $ip); -} - -sub _add_children { - my ($ptr, $childs) = @_; - my @legit = (); - - foreach my $c (@$childs) { - next if exists var('seen')->{$c}; - var('seen')->{$c}++; - push @legit, $c; - push @{$ptr}, { name => _get_name($c), ip => $c }; - } - - for (my $i = 0; $i < @legit; $i++) { - $ptr->[$i]->{children} = []; - _add_children($ptr->[$i]->{children}, var('links')->{$legit[$i]}); - } -} - -# d3 seems not to use proper ajax semantics, so get instead of ajax -get '/ajax/data/device/netmap' => sub { - my $start = param('q'); - return unless $start; - - my @devices = schema('netdisco')->resultset('Device')->search({}, { - result_class => 'DBIx::Class::ResultClass::HashRefInflator', - columns => ['ip', 'dns'], - })->all; - var(devices => { map { $_->{ip} => $_->{dns} } @devices }); - - var(links => {}); - my $rs = schema('netdisco')->resultset('Virtual::DeviceLinks')->search({}, { - result_class => 'DBIx::Class::ResultClass::HashRefInflator', - }); - - while (my $l = $rs->next) { - var('links')->{ $l->{left_ip} } ||= []; - push @{ var('links')->{ $l->{left_ip} } }, $l->{right_ip}; - } - - my %tree = ( - ip => $start, - name => _get_name($start), - children => [], - ); - - var(seen => {$start => 1}); - _add_children($tree{children}, var('links')->{$start}); - - content_type('application/json'); - return to_json(\%tree); -}; - -ajax '/ajax/data/device/alldevicelinks' => sub { - my @devices = schema('netdisco')->resultset('Device')->search({}, { - result_class => 'DBIx::Class::ResultClass::HashRefInflator', - columns => ['ip', 'dns'], - })->all; - var(devices => { map { $_->{ip} => $_->{dns} } @devices }); - - my $rs = schema('netdisco')->resultset('Virtual::DeviceLinks')->search({}, { - result_class => 'DBIx::Class::ResultClass::HashRefInflator', - }); - - my %tree = (); - while (my $l = $rs->next) { - push @{ $tree{ _get_name($l->{left_ip} )} }, - _get_name($l->{right_ip}); - } - - content_type('application/json'); - return to_json(\%tree); -}; - -# device interface addresses -ajax '/ajax/content/device/addresses' => sub { - my $ip = param('q'); - return unless $ip; - - my $set = schema('netdisco')->resultset('DeviceIp') - ->search({ip => $ip}, {order_by => 'alias'}); - return unless $set->count; - - content_type('text/html'); - template 'ajax/device/addresses.tt', { - results => $set, - }, { layout => undef }; -}; - -# device ports with a description (er, name) matching -ajax '/ajax/content/device/ports' => sub { - my $ip = param('q'); - return unless $ip; - - my $set = schema('netdisco')->resultset('DevicePort') - ->search({'me.ip' => $ip}); - - # refine by ports if requested - my $q = param('f'); - if ($q) { - if ($q =~ m/^\d+$/) { - $set = $set->search({ - -or => { - 'me.vlan' => $q, - 'port_vlans_tagged.vlan' => $q, - }, - }, { join => 'port_vlans_tagged' }); - return unless $set->count; - } - else { - $q =~ s/\*/%/g if index($q, '*') >= 0; - $q =~ s/\?/_/g if index($q, '?') >= 0; - $q = { '-ilike' => $q }; - - if ($set->search({'me.port' => $q})->count) { - $set = $set->search({'me.port' => $q}); - } - else { - $set = $set->search({'me.name' => $q}); - return unless $set->count; - } - } - } - - # filter for free ports if asked - my $free_filter = (param('free') ? 'only_free_ports' : 'with_is_free'); - $set = $set->$free_filter({ - age_num => (param('age_num') || 3), - age_unit => (param('age_unit') || 'months') - }); - - # make sure query asks for formatted timestamps when needed - $set = $set->with_times if param('c_lastchange'); - - # get number of vlans on the port to control whether to list them or not - $set = $set->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('c_nodes') and param('n_age'); - - # retrieve active/all connected nodes, if asked for - $set = $set->search_rs({}, { prefetch => [{$nodes_name => 'ips'}] }) - if param('c_nodes'); - - # retrieve neighbor devices, if asked for - $set = $set->search_rs({}, { prefetch => [{neighbor_alias => 'device'}] }) - 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 ]; - return unless scalar @$results; - - content_type('text/html'); - template 'ajax/device/ports.tt', { - results => $results, - nodes => $nodes_name, - device => $ip, - }, { layout => undef }; -}; - -# device details table -ajax '/ajax/content/device/details' => sub { - my $ip = param('q'); - return unless $ip; - - my $device = schema('netdisco')->resultset('Device') - ->with_times()->find($ip); - return unless $device; - - content_type('text/html'); - template 'ajax/device/details.tt', { - d => $device, - }, { layout => undef }; -}; - -# support typeahead with simple AJAX query for device names -ajax '/ajax/data/device/typeahead' => sub { - my $q = param('query'); - my $set = schema('netdisco')->resultset('Device')->search_fuzzy($q); - - content_type 'application/json'; - return to_json [map {$_->dns || $_->name || $_->ip} $set->all]; -}; - get '/device' => sub { my $ip = NetAddr::IP::Lite->new(param('q')); if (! $ip) { diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Addresses.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Addresses.pm new file mode 100644 index 00000000..336aca3e --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Addresses.pm @@ -0,0 +1,26 @@ +package App::Netdisco::Web::Plugin::Device::Addresses; + +use Dancer ':syntax'; +use Dancer::Plugin::Ajax; +use Dancer::Plugin::DBIC; + +use App::Netdisco::Web::Plugin; + +register_device_tab({ id => 'addresses', label => 'Addresses' }); + +# device interface addresses +ajax '/ajax/content/device/addresses' => sub { + my $ip = param('q'); + return unless $ip; + + my $set = schema('netdisco')->resultset('DeviceIp') + ->search({ip => $ip}, {order_by => 'alias'}); + return unless $set->count; + + content_type('text/html'); + template 'ajax/device/addresses.tt', { + results => $set, + }, { layout => undef }; +}; + +true; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Details.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Details.pm new file mode 100644 index 00000000..ab2ffc34 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Details.pm @@ -0,0 +1,26 @@ +package App::Netdisco::Web::Plugin::Device::Details; + +use Dancer ':syntax'; +use Dancer::Plugin::Ajax; +use Dancer::Plugin::DBIC; + +use App::Netdisco::Web::Plugin; + +register_device_tab({ id => 'details', label => 'Details' }); + +# device details table +ajax '/ajax/content/device/details' => sub { + my $ip = param('q'); + return unless $ip; + + my $device = schema('netdisco')->resultset('Device') + ->with_times()->find($ip); + return unless $device; + + content_type('text/html'); + template 'ajax/device/details.tt', { + d => $device, + }, { layout => undef }; +}; + +true; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Modules.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Modules.pm new file mode 100644 index 00000000..4a9850d3 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Modules.pm @@ -0,0 +1,14 @@ +package App::Netdisco::Web::Plugin::Device::Modules; + +use Dancer ':syntax'; +use Dancer::Plugin::Ajax; + +use App::Netdisco::Web::Plugin; + +register_device_tab({ id => 'modules', label => 'Modules' }); + +ajax '/ajax/content/device/:thing' => sub { + return "Hello, this is where the ". param('thing') ." content goes.
"; +}; + +true; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm new file mode 100644 index 00000000..b484c2c9 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm @@ -0,0 +1,96 @@ +package App::Netdisco::Web::Plugin::Device::Neighbors; + +use Dancer ':syntax'; +use Dancer::Plugin::Ajax; +use Dancer::Plugin::DBIC; + +use App::Netdisco::Web::Plugin; + +register_device_tab({ id => 'netmap', label => 'Neighbors' }); + +ajax '/ajax/content/device/netmap' => sub { + content_type('text/html'); + template 'ajax/device/netmap.tt', {}, { layout => undef }; +}; + +sub _get_name { + my $ip = shift; + my $domain = quotemeta( setting('domain_suffix') || '' ); + + (my $dns = (var('devices')->{$ip} || '')) =~ s/$domain$//; + return ($dns || $ip); +} + +sub _add_children { + my ($ptr, $childs) = @_; + my @legit = (); + + foreach my $c (@$childs) { + next if exists var('seen')->{$c}; + var('seen')->{$c}++; + push @legit, $c; + push @{$ptr}, { name => _get_name($c), ip => $c }; + } + + for (my $i = 0; $i < @legit; $i++) { + $ptr->[$i]->{children} = []; + _add_children($ptr->[$i]->{children}, var('links')->{$legit[$i]}); + } +} + +# d3 seems not to use proper ajax semantics, so get instead of ajax +get '/ajax/data/device/netmap' => sub { + my $start = param('q'); + return unless $start; + + my @devices = schema('netdisco')->resultset('Device')->search({}, { + result_class => 'DBIx::Class::ResultClass::HashRefInflator', + columns => ['ip', 'dns'], + })->all; + var(devices => { map { $_->{ip} => $_->{dns} } @devices }); + + var(links => {}); + my $rs = schema('netdisco')->resultset('Virtual::DeviceLinks')->search({}, { + result_class => 'DBIx::Class::ResultClass::HashRefInflator', + }); + + while (my $l = $rs->next) { + var('links')->{ $l->{left_ip} } ||= []; + push @{ var('links')->{ $l->{left_ip} } }, $l->{right_ip}; + } + + my %tree = ( + ip => $start, + name => _get_name($start), + children => [], + ); + + var(seen => {$start => 1}); + _add_children($tree{children}, var('links')->{$start}); + + content_type('application/json'); + return to_json(\%tree); +}; + +ajax '/ajax/data/device/alldevicelinks' => sub { + my @devices = schema('netdisco')->resultset('Device')->search({}, { + result_class => 'DBIx::Class::ResultClass::HashRefInflator', + columns => ['ip', 'dns'], + })->all; + var(devices => { map { $_->{ip} => $_->{dns} } @devices }); + + my $rs = schema('netdisco')->resultset('Virtual::DeviceLinks')->search({}, { + result_class => 'DBIx::Class::ResultClass::HashRefInflator', + }); + + my %tree = (); + while (my $l = $rs->next) { + push @{ $tree{ _get_name($l->{left_ip} )} }, + _get_name($l->{right_ip}); + } + + content_type('application/json'); + return to_json(\%tree); +}; + +true; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Ports.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Ports.pm new file mode 100644 index 00000000..fa30a353 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Ports.pm @@ -0,0 +1,85 @@ +package App::Netdisco::Web::Plugin::Device::Ports; + +use Dancer ':syntax'; +use Dancer::Plugin::Ajax; +use Dancer::Plugin::DBIC; + +use App::Netdisco::Util::Web (); # for sort_port + +use App::Netdisco::Web::Plugin; + +register_device_tab({ id => 'ports', label => 'Ports' }); + +# device ports with a description (er, name) matching +ajax '/ajax/content/device/ports' => sub { + my $ip = param('q'); + return unless $ip; + + my $set = schema('netdisco')->resultset('DevicePort') + ->search({'me.ip' => $ip}); + + # refine by ports if requested + my $q = param('f'); + if ($q) { + if ($q =~ m/^\d+$/) { + $set = $set->search({ + -or => { + 'me.vlan' => $q, + 'port_vlans_tagged.vlan' => $q, + }, + }, { join => 'port_vlans_tagged' }); + return unless $set->count; + } + else { + $q =~ s/\*/%/g if index($q, '*') >= 0; + $q =~ s/\?/_/g if index($q, '?') >= 0; + $q = { '-ilike' => $q }; + + if ($set->search({'me.port' => $q})->count) { + $set = $set->search({'me.port' => $q}); + } + else { + $set = $set->search({'me.name' => $q}); + return unless $set->count; + } + } + } + + # filter for free ports if asked + my $free_filter = (param('free') ? 'only_free_ports' : 'with_is_free'); + $set = $set->$free_filter({ + age_num => (param('age_num') || 3), + age_unit => (param('age_unit') || 'months') + }); + + # make sure query asks for formatted timestamps when needed + $set = $set->with_times if param('c_lastchange'); + + # get number of vlans on the port to control whether to list them or not + $set = $set->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('c_nodes') and param('n_age'); + + # retrieve active/all connected nodes, if asked for + $set = $set->search_rs({}, { prefetch => [{$nodes_name => 'ips'}] }) + if param('c_nodes'); + + # retrieve neighbor devices, if asked for + $set = $set->search_rs({}, { prefetch => [{neighbor_alias => 'device'}] }) + 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 ]; + return unless scalar @$results; + + content_type('text/html'); + template 'ajax/device/ports.tt', { + results => $results, + nodes => $nodes_name, + device => $ip, + }, { layout => undef }; +}; + +true; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Device.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Device.pm new file mode 100644 index 00000000..dfdbb7b8 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Device.pm @@ -0,0 +1,36 @@ +package App::Netdisco::Web::Plugin::Search::Device; + +use Dancer ':syntax'; +use Dancer::Plugin::Ajax; +use Dancer::Plugin::DBIC; + +use List::MoreUtils (); + +use App::Netdisco::Web::Plugin; + +register_search_tab({id => 'device', label => 'Device'}); + +# device with various properties or a default match-all +ajax '/ajax/content/search/device' => sub { + my $has_opt = List::MoreUtils::any {param($_)} + qw/name location dns ip description model os_ver vendor/; + my $set; + + if ($has_opt) { + $set = schema('netdisco')->resultset('Device')->search_by_field(scalar params); + } + else { + my $q = param('q'); + return unless $q; + + $set = schema('netdisco')->resultset('Device')->search_fuzzy($q); + } + return unless $set->count; + + content_type('text/html'); + template 'ajax/search/device.tt', { + results => $set, + }, { layout => undef }; +}; + +true; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Node.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Node.pm new file mode 100644 index 00000000..ab49509f --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Node.pm @@ -0,0 +1,71 @@ +package App::Netdisco::Web::Plugin::Search::Node; + +use Dancer ':syntax'; +use Dancer::Plugin::Ajax; +use Dancer::Plugin::DBIC; + +use NetAddr::IP::Lite ':lower'; +use Net::MAC (); + +use App::Netdisco::Web::Plugin; + +register_search_tab({ id => 'node', label => 'Node' }); + +# nodes matching the param as an IP or DNS hostname or MAC +ajax '/ajax/content/search/node' => sub { + my $node = param('q'); + return unless $node; + content_type('text/html'); + + my $mac = Net::MAC->new(mac => $node, 'die' => 0, verbose => 0); + my @active = (param('archived') ? () : (-bool => 'active')); + + if (! $mac->get_error) { + my $sightings = schema('netdisco')->resultset('Node') + ->search_by_mac({mac => $mac->as_IEEE, @active}); + + my $ips = schema('netdisco')->resultset('NodeIp') + ->search_by_mac({mac => $mac->as_IEEE, @active}); + + my $ports = schema('netdisco')->resultset('DevicePort') + ->search({mac => $mac->as_IEEE}); + + return unless $sightings->count + or $ips->count + or $ports->count; + + template 'ajax/search/node_by_mac.tt', { + ips => $ips, + sightings => $sightings, + ports => $ports, + }, { layout => undef }; + } + else { + my $set; + + if (my $ip = NetAddr::IP::Lite->new($node)) { + # search_by_ip() will extract cidr notation if necessary + $set = schema('netdisco')->resultset('NodeIp') + ->search_by_ip({ip => $ip, @active}); + } + else { + if (param('partial')) { + $node = "\%$node\%"; + } + elsif (setting('domain_suffix')) { + $node .= setting('domain_suffix') + if index($node, setting('domain_suffix')) == -1; + } + $set = schema('netdisco')->resultset('NodeIp') + ->search_by_dns({dns => $node, @active}); + } + return unless $set and $set->count; + + template 'ajax/search/node_by_ip.tt', { + macs => $set, + archive_filter => {@active}, + }, { layout => undef }; + } +}; + +true; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Port.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Port.pm new file mode 100644 index 00000000..6a18fdb8 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Port.pm @@ -0,0 +1,31 @@ +package App::Netdisco::Web::Plugin::Search::Port; + +use Dancer ':syntax'; +use Dancer::Plugin::Ajax; +use Dancer::Plugin::DBIC; + +use App::Netdisco::Web::Plugin; + +register_search_tab({ id => 'port', label => 'Port' }); + +# device ports with a description (er, name) matching +ajax '/ajax/content/search/port' => sub { + my $q = param('q'); + return unless $q; + my $set; + + if ($q =~ m/^\d+$/) { + $set = schema('netdisco')->resultset('DevicePort')->search({vlan => $q}); + } + else { + $set = schema('netdisco')->resultset('DevicePort')->search({name => $q}); + } + return unless $set->count; + + content_type('text/html'); + template 'ajax/search/port.tt', { + results => $set, + }, { layout => undef }; +}; + +true; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Search/VLAN.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/VLAN.pm new file mode 100644 index 00000000..c8f689bb --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/VLAN.pm @@ -0,0 +1,31 @@ +package App::Netdisco::Web::Plugin::Search::VLAN; + +use Dancer ':syntax'; +use Dancer::Plugin::Ajax; +use Dancer::Plugin::DBIC; + +use App::Netdisco::Web::Plugin; + +register_search_tab({ id => 'vlan', label => 'VLAN' }); + +# devices carrying vlan xxx +ajax '/ajax/content/search/vlan' => sub { + my $q = param('q'); + return unless $q; + my $set; + + if ($q =~ m/^\d+$/) { + $set = schema('netdisco')->resultset('Device')->carrying_vlan({vlan => $q}); + } + else { + $set = schema('netdisco')->resultset('Device')->carrying_vlan_name({name => $q}); + } + return unless $set->count; + + content_type('text/html'); + template 'ajax/search/vlan.tt', { + results => $set, + }, { layout => undef }; +}; + +true; diff --git a/Netdisco/lib/App/Netdisco/Web/PortControl.pm b/Netdisco/lib/App/Netdisco/Web/PortControl.pm index acbb966c..ef126dd1 100644 --- a/Netdisco/lib/App/Netdisco/Web/PortControl.pm +++ b/Netdisco/lib/App/Netdisco/Web/PortControl.pm @@ -3,6 +3,7 @@ package App::Netdisco::Web::PortControl; use Dancer ':syntax'; use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; + use Try::Tiny; ajax '/ajax/portcontrol' => sub { diff --git a/Netdisco/lib/App/Netdisco/Web/Search.pm b/Netdisco/lib/App/Netdisco/Web/Search.pm index 3f88d536..80fc9bca 100644 --- a/Netdisco/lib/App/Netdisco/Web/Search.pm +++ b/Netdisco/lib/App/Netdisco/Web/Search.pm @@ -5,8 +5,6 @@ use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; use NetAddr::IP::Lite ':lower'; -use Net::MAC (); -use List::MoreUtils (); hook 'before' => sub { # view settings for node options @@ -63,126 +61,6 @@ hook 'before_template' => sub { } }; -# device with various properties or a default match-all -ajax '/ajax/content/search/device' => sub { - my $has_opt = List::MoreUtils::any {param($_)} - qw/name location dns ip description model os_ver vendor/; - my $set; - - if ($has_opt) { - $set = schema('netdisco')->resultset('Device')->search_by_field(scalar params); - } - else { - my $q = param('q'); - return unless $q; - - $set = schema('netdisco')->resultset('Device')->search_fuzzy($q); - } - return unless $set->count; - - content_type('text/html'); - template 'ajax/search/device.tt', { - results => $set, - }, { layout => undef }; -}; - -# nodes matching the param as an IP or DNS hostname or MAC -ajax '/ajax/content/search/node' => sub { - my $node = param('q'); - return unless $node; - content_type('text/html'); - - my $mac = Net::MAC->new(mac => $node, 'die' => 0, verbose => 0); - my @active = (param('archived') ? () : (-bool => 'active')); - - if (! $mac->get_error) { - my $sightings = schema('netdisco')->resultset('Node') - ->search_by_mac({mac => $mac->as_IEEE, @active}); - - my $ips = schema('netdisco')->resultset('NodeIp') - ->search_by_mac({mac => $mac->as_IEEE, @active}); - - my $ports = schema('netdisco')->resultset('DevicePort') - ->search({mac => $mac->as_IEEE}); - - return unless $sightings->count - or $ips->count - or $ports->count; - - template 'ajax/search/node_by_mac.tt', { - ips => $ips, - sightings => $sightings, - ports => $ports, - }, { layout => undef }; - } - else { - my $set; - - if (my $ip = NetAddr::IP::Lite->new($node)) { - # search_by_ip() will extract cidr notation if necessary - $set = schema('netdisco')->resultset('NodeIp') - ->search_by_ip({ip => $ip, @active}); - } - else { - if (param('partial')) { - $node = "\%$node\%"; - } - elsif (setting('domain_suffix')) { - $node .= setting('domain_suffix') - if index($node, setting('domain_suffix')) == -1; - } - $set = schema('netdisco')->resultset('NodeIp') - ->search_by_dns({dns => $node, @active}); - } - return unless $set and $set->count; - - template 'ajax/search/node_by_ip.tt', { - macs => $set, - archive_filter => {@active}, - }, { layout => undef }; - } -}; - -# devices carrying vlan xxx -ajax '/ajax/content/search/vlan' => sub { - my $q = param('q'); - return unless $q; - my $set; - - if ($q =~ m/^\d+$/) { - $set = schema('netdisco')->resultset('Device')->carrying_vlan({vlan => $q}); - } - else { - $set = schema('netdisco')->resultset('Device')->carrying_vlan_name({name => $q}); - } - return unless $set->count; - - content_type('text/html'); - template 'ajax/search/vlan.tt', { - results => $set, - }, { layout => undef }; -}; - -# device ports with a description (er, name) matching -ajax '/ajax/content/search/port' => sub { - my $q = param('q'); - return unless $q; - my $set; - - if ($q =~ m/^\d+$/) { - $set = schema('netdisco')->resultset('DevicePort')->search({vlan => $q}); - } - else { - $set = schema('netdisco')->resultset('DevicePort')->search({name => $q}); - } - return unless $set->count; - - content_type('text/html'); - template 'ajax/search/port.tt', { - results => $set, - }, { layout => undef }; -}; - get '/search' => sub { my $q = param('q'); if (not param('tab')) { diff --git a/Netdisco/lib/App/Netdisco/Web/TypeAhead.pm b/Netdisco/lib/App/Netdisco/Web/TypeAhead.pm new file mode 100644 index 00000000..8be780e0 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Web/TypeAhead.pm @@ -0,0 +1,16 @@ +package App::Netdisco::Web::TypeAhead; + +use Dancer ':syntax'; +use Dancer::Plugin::Ajax; +use Dancer::Plugin::DBIC; + +# support typeahead with simple AJAX query for device names +ajax '/ajax/data/device/typeahead' => sub { + my $q = param('query'); + my $set = schema('netdisco')->resultset('Device')->search_fuzzy($q); + + content_type 'application/json'; + return to_json [map {$_->dns || $_->name || $_->ip} $set->all]; +}; + +true; diff --git a/Netdisco/share/config.yml b/Netdisco/share/config.yml index 3e7626fb..35ec0ab3 100644 --- a/Netdisco/share/config.yml +++ b/Netdisco/share/config.yml @@ -32,15 +32,15 @@ engines: web_plugins: - Inventory # - Report::WirelessDevices -# - Search::Device -# - Search::Node -# - Search::VLAN -# - Search::Port -# - Device::Details -# - Device::Ports + - Search::Device + - Search::Node + - Search::VLAN + - Search::Port + - Device::Details + - Device::Ports # - Device::Modules -# - Device::Neighbors -# - Device::Addresses + - Device::Neighbors + - Device::Addresses discover_no: [] discover_only: []