From 4e94c248e3ca3d031f5fcd81090947566a60af01 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Mon, 9 Jan 2012 00:51:45 +0000 Subject: [PATCH] implement first device tab - details --- Netdisco/lib/Netdisco/DB/ResultSet/Device.pm | 17 +++++ Netdisco/lib/Netdisco/Web.pm | 54 +++++++++++-- Netdisco/views/ajax/device/details.tt | 69 +++++++++++++++++ Netdisco/views/ajax/{ => search}/device.tt | 0 .../views/ajax/{ => search}/node_by_ip.tt | 0 .../views/ajax/{ => search}/node_by_mac.tt | 0 Netdisco/views/ajax/{ => search}/port.tt | 2 +- Netdisco/views/ajax/{ => search}/vlan.tt | 0 Netdisco/views/device.tt | 32 ++++++++ Netdisco/views/inc/device/addresses.tt | 4 + Netdisco/views/inc/device/details.tt | 4 + Netdisco/views/inc/device/modules.tt | 4 + Netdisco/views/inc/device/ports.tt | 12 +++ Netdisco/views/inc/js/device.js | 75 +++++++++++++++++++ Netdisco/views/index.tt | 6 ++ 15 files changed, 273 insertions(+), 6 deletions(-) create mode 100644 Netdisco/views/ajax/device/details.tt rename Netdisco/views/ajax/{ => search}/device.tt (100%) rename Netdisco/views/ajax/{ => search}/node_by_ip.tt (100%) rename Netdisco/views/ajax/{ => search}/node_by_mac.tt (100%) rename Netdisco/views/ajax/{ => search}/port.tt (82%) rename Netdisco/views/ajax/{ => search}/vlan.tt (100%) create mode 100644 Netdisco/views/device.tt create mode 100644 Netdisco/views/inc/device/addresses.tt create mode 100644 Netdisco/views/inc/device/details.tt create mode 100644 Netdisco/views/inc/device/modules.tt create mode 100644 Netdisco/views/inc/device/ports.tt create mode 100644 Netdisco/views/inc/js/device.js diff --git a/Netdisco/lib/Netdisco/DB/ResultSet/Device.pm b/Netdisco/lib/Netdisco/DB/ResultSet/Device.pm index fefb7efb..8d7333ab 100644 --- a/Netdisco/lib/Netdisco/DB/ResultSet/Device.pm +++ b/Netdisco/lib/Netdisco/DB/ResultSet/Device.pm @@ -3,6 +3,23 @@ use base 'DBIx::Class::ResultSet'; use NetAddr::IP::Lite ':lower'; +# override the built-in so we can munge some columns +sub find { + my ($set, $ip) = @_; + + return $set->SUPER::find($ip, + { + '+select' => [ + \"replace(age(timestamp 'epoch' + uptime / 100 * interval '1 second', timestamp '1970-01-01 00:00:00-00')::text, 'mons', 'months')", + \"to_char(last_discover, 'YYYY-MM-DD HH24:MI')", + \"to_char(last_macsuck, 'YYYY-MM-DD HH24:MI')", + \"to_char(last_arpnip, 'YYYY-MM-DD HH24:MI')", + ], + '+as' => [qw/ uptime last_discover last_macsuck last_arpnip /], + } + ); +} + # finds distinct values of a col for use in form selections sub get_distinct { my ($set, $col) = @_; diff --git a/Netdisco/lib/Netdisco/Web.pm b/Netdisco/lib/Netdisco/Web.pm index fced281b..11ce7908 100644 --- a/Netdisco/lib/Netdisco/Web.pm +++ b/Netdisco/lib/Netdisco/Web.pm @@ -6,6 +6,7 @@ use Dancer::Plugin::DBIC; use Digest::MD5 (); use Socket6 (); # to ensure dependency is met +use HTML::Entities (); # to ensure dependency is met use NetAddr::IP::Lite ':lower'; use Net::MAC (); use List::MoreUtils (); @@ -32,6 +33,49 @@ hook 'before' => sub { for qw/stamps vendor archived partial/; }; +ajax '/ajax/content/device/:thing' => sub { + return "

Hello ". param('thing') ."

"; +}; + +# device ports with a description (er, name) matching +ajax '/ajax/content/device/details' => sub { + my $ip = param('ip'); + return unless $ip; + + my $device = schema('netdisco')->resultset('Device')->find($ip); + return unless $device; + + content_type('text/html'); + template 'ajax/device/details.tt', { + d => $device, + }, { layout => undef }; +}; + +get '/device' => sub { + my $ip = NetAddr::IP::Lite->new(param('ip')); + if (! $ip) { + redirect '/?nosuchdevice=1'; + return; + } + + my $device = schema('netdisco')->resultset('Device')->find($ip->addr); + if (! $device) { + redirect '/?nosuchdevice=1'; + return; + } + + # list of tabs + var('tabs' => [ + { id => 'details', label => 'Details' }, + { id => 'ports', label => 'Ports' }, + { id => 'modules', label => 'Modules' }, + { id => 'addresses', label => 'Addresses' }, + ]); + + params->{'tab'} ||= 'details'; + template 'device', { d => $device }; +}; + # device with various properties or a default match-all ajax '/ajax/content/search/device' => sub { my $has_opt = List::MoreUtils::any {param($_)} @@ -50,7 +94,7 @@ ajax '/ajax/content/search/device' => sub { return unless $set->count; content_type('text/html'); - template 'ajax/device.tt', { + template 'ajax/search/device.tt', { results => $set, }, { layout => undef }; }; @@ -74,7 +118,7 @@ ajax '/ajax/content/search/node' => sub { my $ports = schema('netdisco')->resultset('DevicePort') ->by_mac($mac->as_IEEE); - template 'ajax/node_by_mac.tt', { + template 'ajax/search/node_by_mac.tt', { ips => $ips, sightings => $sightings, ports => $ports, @@ -95,7 +139,7 @@ ajax '/ajax/content/search/node' => sub { } return unless $set->count; - template 'ajax/node_by_ip.tt', { + template 'ajax/search/node_by_ip.tt', { results => $set, }, { layout => undef }; } @@ -116,7 +160,7 @@ ajax '/ajax/content/search/vlan' => sub { return unless $set->count; content_type('text/html'); - template 'ajax/vlan.tt', { + template 'ajax/search/vlan.tt', { results => $set, }, { layout => undef }; }; @@ -130,7 +174,7 @@ ajax '/ajax/content/search/port' => sub { return unless $set->count; content_type('text/html'); - template 'ajax/port.tt', { + template 'ajax/search/port.tt', { results => $set, }, { layout => undef }; }; diff --git a/Netdisco/views/ajax/device/details.tt b/Netdisco/views/ajax/device/details.tt new file mode 100644 index 00000000..eaf0bf19 --- /dev/null +++ b/Netdisco/views/ajax/device/details.tt @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
System Name[% d.name %]
Location + [% d.location %]
Contact[% d.contact %]
Model + [% d.model %]
OS / Version[% d.os %] / + [% d.os_ver %] +
Serial Number[% d.serial %]
Description[% d.description.replace(', ',",
") %]
Uptime[% d.uptime %]
Last Discover[% d.last_discover %]
Last Arpnip[% d.last_arpnip %]
Last Macsuck[% d.last_macsuck %]
Hardware StatusFan: [% d.fan %] +
PS1 [[% d.ps1_type %]]: [% d.ps1_status %] +
PS2 [[% d.ps2_type %]]: [% d.ps2_status %]
MAC Address[% d.mac %]
VTP Domain[% d.vtp_domain %]
diff --git a/Netdisco/views/ajax/device.tt b/Netdisco/views/ajax/search/device.tt similarity index 100% rename from Netdisco/views/ajax/device.tt rename to Netdisco/views/ajax/search/device.tt diff --git a/Netdisco/views/ajax/node_by_ip.tt b/Netdisco/views/ajax/search/node_by_ip.tt similarity index 100% rename from Netdisco/views/ajax/node_by_ip.tt rename to Netdisco/views/ajax/search/node_by_ip.tt diff --git a/Netdisco/views/ajax/node_by_mac.tt b/Netdisco/views/ajax/search/node_by_mac.tt similarity index 100% rename from Netdisco/views/ajax/node_by_mac.tt rename to Netdisco/views/ajax/search/node_by_mac.tt diff --git a/Netdisco/views/ajax/port.tt b/Netdisco/views/ajax/search/port.tt similarity index 82% rename from Netdisco/views/ajax/port.tt rename to Netdisco/views/ajax/search/port.tt index 2735926d..c505feef 100644 --- a/Netdisco/views/ajax/port.tt +++ b/Netdisco/views/ajax/search/port.tt @@ -11,7 +11,7 @@ [% WHILE (row = results.next) %] [% row.name %] - [% row.ip %] [ [% row.port %] ] + [% row.ip %] [ [% row.port %] ] [% ' (' _ row.device.dns.remove(settings.domain_suffix) _ ')' IF row.device.dns %] [% row.descr %] diff --git a/Netdisco/views/ajax/vlan.tt b/Netdisco/views/ajax/search/vlan.tt similarity index 100% rename from Netdisco/views/ajax/vlan.tt rename to Netdisco/views/ajax/search/vlan.tt diff --git a/Netdisco/views/device.tt b/Netdisco/views/device.tt new file mode 100644 index 00000000..e93928fe --- /dev/null +++ b/Netdisco/views/device.tt @@ -0,0 +1,32 @@ +
+ + +
+ +
+ [% FOREACH tab IN vars.tabs %] +
+ [% END %] +
+
+ + diff --git a/Netdisco/views/inc/device/addresses.tt b/Netdisco/views/inc/device/addresses.tt new file mode 100644 index 00000000..e9983aab --- /dev/null +++ b/Netdisco/views/inc/device/addresses.tt @@ -0,0 +1,4 @@ + +
+ +
diff --git a/Netdisco/views/inc/device/details.tt b/Netdisco/views/inc/device/details.tt new file mode 100644 index 00000000..e9983aab --- /dev/null +++ b/Netdisco/views/inc/device/details.tt @@ -0,0 +1,4 @@ + +
+ +
diff --git a/Netdisco/views/inc/device/modules.tt b/Netdisco/views/inc/device/modules.tt new file mode 100644 index 00000000..e9983aab --- /dev/null +++ b/Netdisco/views/inc/device/modules.tt @@ -0,0 +1,4 @@ + +
+ +
diff --git a/Netdisco/views/inc/device/ports.tt b/Netdisco/views/inc/device/ports.tt new file mode 100644 index 00000000..20e3e509 --- /dev/null +++ b/Netdisco/views/inc/device/ports.tt @@ -0,0 +1,12 @@ + +
+ + + +
diff --git a/Netdisco/views/inc/js/device.js b/Netdisco/views/inc/js/device.js new file mode 100644 index 00000000..25b8d6d0 --- /dev/null +++ b/Netdisco/views/inc/js/device.js @@ -0,0 +1,75 @@ + $(document).ready(function() { + // parameterised for the active tab - submits search form and injects + // HTML response into the tab pane, or an error/empty-results message + function do_search (event, tab) { + var form = '#' + tab + '_form'; + var target = '#' + tab + '_pane'; + var mark = '#' + tab + '_bookmark'; + + // stop form from submitting normally + event.preventDefault(); + + // get the form params + var query = $(form).serialize(); + + // in case of slow data load, let the user know + $(target).html( + '

Waiting for results...

' + ); + + // submit the query and put results into the tab pane + $(target).load( '/ajax/content/device/' + tab + '?' + query, + function(response, status, xhr) { + if (status !== "success") { + $(target).html( + '
' + + '

Search failed! Please contact your site administrator.

' + ); + return; + } + if (response === "") { + $(target).html( + '

No matching records.

' + ); + } + // looks good, update the bookmark for this search + $(mark).attr('href', '/device?' + query); + } + ); + } + + // search hook for each tab + [% FOREACH tab IN vars.tabs %] + $('[% "#${tab.id}_form" %]').submit(function(event){ do_search(event, '[% tab.id %]'); }); + [% END %] + + // on page load, load the content for the active tab + [% IF params.tab %] + $('#[% params.tab %]_form').trigger("submit"); + [% END %] + + // on tab change, hide previous tab's search form and show new tab's + // search form. also trigger to load the content for the newly active tab. + $('#search_results').bind('change', function(e) { + var to = $(e.target).attr('href').replace(/^#/,"").replace(/_pane$/,""); + var from = $(e.relatedTarget).attr('href').replace(/^#/,"").replace(/_pane$/,""); + + $('#' + from + '_search').toggleClass('active'); + $('#' + to + '_search').toggleClass('active'); + + var to_form = '#' + to + '_form'; + var from_form = '#' + from + '_form'; + // copy current search string to new form's input box + $(to_form).find("input[name=q]").val( + $(from_form).find("input[name=q]").val() + ); + $(to_form).trigger("submit"); + }); + + // fix green background on search checkboxes + // https://github.com/twitter/bootstrap/issues/742 + syncCheckBox = function() { + $(this).parents('.add-on').toggleClass('active', $(this).is(':checked')); + }; + $('.add-on :checkbox').each(syncCheckBox).click(syncCheckBox); + }); diff --git a/Netdisco/views/index.tt b/Netdisco/views/index.tt index 09c04c0f..fd8d5a40 100644 --- a/Netdisco/views/index.tt +++ b/Netdisco/views/index.tt @@ -13,6 +13,12 @@

You are now logged out.

[% END %] + [% IF params.nosuchdevice %] +
+ × +

Sorry, no such device is known.

+
+ [% END %] [% IF vars.notfound %]
×