Merge branch 'master' of ssh://git.code.sf.net/p/netdisco/netdisco-ng
This commit is contained in:
		| @@ -1,5 +1,10 @@ | ||||
| 2.029015 | ||||
|  | ||||
|   [NEW FEATURES] | ||||
|  | ||||
|   * [#4] Allow comment on device port in the log, for any user | ||||
|   * [#18] Inventory by Model by OS Report | ||||
|  | ||||
|   [ENHANCEMENTS] | ||||
|  | ||||
|   * Request net-snmp-devel on Fedora/Red-Hat builds | ||||
| @@ -10,6 +15,9 @@ | ||||
|  | ||||
|   * Fix for node search on some formats of IPv6 addr being seen as MAC | ||||
|   * [#190] Ignore entries in the discover form that don't look like IP/hostname | ||||
|   * Delete all job queue entries (regardless of status) when deleting device | ||||
|   * [#15] Undiscovered Neighbor report JOIN fixed, and updated to include | ||||
|     the ports where Macsuck saw the MAC address of a known Device | ||||
|  | ||||
| 2.029014 - 2014-11-19 | ||||
|  | ||||
|   | ||||
| @@ -201,6 +201,7 @@ lib/App/Netdisco/Web/Plugin/Report/DeviceDnsMismatch.pm | ||||
| lib/App/Netdisco/Web/Plugin/Report/DevicePoeStatus.pm | ||||
| lib/App/Netdisco/Web/Plugin/Report/DuplexMismatch.pm | ||||
| lib/App/Netdisco/Web/Plugin/Report/HalfDuplex.pm | ||||
| lib/App/Netdisco/Web/Plugin/Report/InventoryByModelByOS.pm | ||||
| lib/App/Netdisco/Web/Plugin/Report/IpInventory.pm | ||||
| lib/App/Netdisco/Web/Plugin/Report/ModuleInventory.pm | ||||
| lib/App/Netdisco/Web/Plugin/Report/Netbios.pm | ||||
| @@ -341,6 +342,7 @@ share/views/ajax/report/generic_report.tt | ||||
| share/views/ajax/report/generic_report_csv.tt | ||||
| share/views/ajax/report/halfduplex.tt | ||||
| share/views/ajax/report/halfduplex_csv.tt | ||||
| share/views/ajax/report/inventorybymodelbyos.tt | ||||
| share/views/ajax/report/ipinventory.tt | ||||
| share/views/ajax/report/ipinventory_csv.tt | ||||
| share/views/ajax/report/moduleinventory.tt | ||||
|   | ||||
| @@ -7,11 +7,11 @@ build_requires: | ||||
|   ExtUtils::MakeMaker: 6.59 | ||||
|   Test::More: 0.88 | ||||
| configure_requires: | ||||
|   DBIx::Class: 0.082801 | ||||
|   DBIx::Class: '0.082801' | ||||
|   ExtUtils::MakeMaker: 6.59 | ||||
| distribution_type: module | ||||
| dynamic_config: 1 | ||||
| generated_by: 'Module::Install version 1.06' | ||||
| generated_by: 'Module::Install version 1.12' | ||||
| license: bsd | ||||
| meta-spec: | ||||
|   url: http://module-build.sourceforge.net/META-spec-v1.4.html | ||||
| @@ -39,7 +39,7 @@ requires: | ||||
|   DBIx::Class: 0.08281 | ||||
|   DBIx::Class::Helpers: 2.024 | ||||
|   Daemon::Control: 0.001006 | ||||
|   Dancer: 1.3132 | ||||
|   Dancer: '1.3132' | ||||
|   Dancer::Plugin::Auth::Extensible: 0.3 | ||||
|   Dancer::Plugin::DBIC: 0.2001 | ||||
|   Dancer::Plugin::Passphrase: 2.0.1 | ||||
| @@ -68,7 +68,7 @@ requires: | ||||
|   Plack::Middleware::ReverseProxy: 0.15 | ||||
|   Role::Tiny: 1.002005 | ||||
|   SNMP::Info: 3.18 | ||||
|   SQL::Translator: 0.11018 | ||||
|   SQL::Translator: '0.11018' | ||||
|   Sereal: 0 | ||||
|   Socket6: 0.23 | ||||
|   Starman: 0.4008 | ||||
| @@ -91,4 +91,4 @@ resources: | ||||
|   homepage: http://netdisco.org/ | ||||
|   license: http://opensource.org/licenses/bsd-license.php | ||||
|   repository: git://git.code.sf.net/p/netdisco/netdisco-ng | ||||
| version: 2.029014 | ||||
| version: '2.029014' | ||||
|   | ||||
| @@ -17,7 +17,7 @@ package Module::Install; | ||||
| #     3. The ./inc/ version of Module::Install loads | ||||
| # } | ||||
|  | ||||
| use 5.005; | ||||
| use 5.006; | ||||
| use strict 'vars'; | ||||
| use Cwd        (); | ||||
| use File::Find (); | ||||
| @@ -31,7 +31,7 @@ BEGIN { | ||||
| 	# This is not enforced yet, but will be some time in the next few | ||||
| 	# releases once we can make sure it won't clash with custom | ||||
| 	# Module::Install extensions. | ||||
| 	$VERSION = '1.06'; | ||||
| 	$VERSION = '1.12'; | ||||
|  | ||||
| 	# Storage for the pseudo-singleton | ||||
| 	$MAIN    = undef; | ||||
| @@ -156,10 +156,10 @@ END_DIE | ||||
| sub autoload { | ||||
| 	my $self = shift; | ||||
| 	my $who  = $self->_caller; | ||||
| 	my $cwd  = Cwd::cwd(); | ||||
| 	my $cwd  = Cwd::getcwd(); | ||||
| 	my $sym  = "${who}::AUTOLOAD"; | ||||
| 	$sym->{$cwd} = sub { | ||||
| 		my $pwd = Cwd::cwd(); | ||||
| 		my $pwd = Cwd::getcwd(); | ||||
| 		if ( my $code = $sym->{$pwd} ) { | ||||
| 			# Delegate back to parent dirs | ||||
| 			goto &$code unless $cwd eq $pwd; | ||||
| @@ -239,7 +239,7 @@ sub new { | ||||
|  | ||||
| 	# ignore the prefix on extension modules built from top level. | ||||
| 	my $base_path = Cwd::abs_path($FindBin::Bin); | ||||
| 	unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) { | ||||
| 	unless ( Cwd::abs_path(Cwd::getcwd()) eq $base_path ) { | ||||
| 		delete $args{prefix}; | ||||
| 	} | ||||
| 	return $args{_self} if $args{_self}; | ||||
| @@ -338,7 +338,7 @@ sub find_extensions { | ||||
| 		if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) { | ||||
| 			my $content = Module::Install::_read($subpath . '.pm'); | ||||
| 			my $in_pod  = 0; | ||||
| 			foreach ( split //, $content ) { | ||||
| 			foreach ( split /\n/, $content ) { | ||||
| 				$in_pod = 1 if /^=\w/; | ||||
| 				$in_pod = 0 if /^=cut/; | ||||
| 				next if ($in_pod || /^=cut/);  # skip pod text | ||||
| @@ -434,7 +434,7 @@ END_OLD | ||||
|  | ||||
| # _version is for processing module versions (eg, 1.03_05) not | ||||
| # Perl versions (eg, 5.8.1). | ||||
| sub _version ($) { | ||||
| sub _version { | ||||
| 	my $s = shift || 0; | ||||
| 	my $d =()= $s =~ /(\.)/g; | ||||
| 	if ( $d >= 2 ) { | ||||
| @@ -450,12 +450,12 @@ sub _version ($) { | ||||
| 	return $l + 0; | ||||
| } | ||||
|  | ||||
| sub _cmp ($$) { | ||||
| sub _cmp { | ||||
| 	_version($_[1]) <=> _version($_[2]); | ||||
| } | ||||
|  | ||||
| # Cloned from Params::Util::_CLASS | ||||
| sub _CLASS ($) { | ||||
| sub _CLASS { | ||||
| 	( | ||||
| 		defined $_[0] | ||||
| 		and | ||||
|   | ||||
| @@ -4,7 +4,7 @@ package Module::Install::Base; | ||||
| use strict 'vars'; | ||||
| use vars qw{$VERSION}; | ||||
| BEGIN { | ||||
| 	$VERSION = '1.06'; | ||||
| 	$VERSION = '1.12'; | ||||
| } | ||||
|  | ||||
| # Suspend handler for "redefined" warnings | ||||
|   | ||||
| @@ -8,7 +8,7 @@ use Module::Install::Base (); | ||||
|  | ||||
| use vars qw{$VERSION @ISA $ISCORE}; | ||||
| BEGIN { | ||||
| 	$VERSION = '1.06'; | ||||
| 	$VERSION = '1.12'; | ||||
| 	@ISA     = 'Module::Install::Base'; | ||||
| 	$ISCORE  = 1; | ||||
| } | ||||
|   | ||||
| @@ -6,7 +6,7 @@ use Module::Install::Base (); | ||||
|  | ||||
| use vars qw{$VERSION @ISA $ISCORE}; | ||||
| BEGIN { | ||||
| 	$VERSION = '1.06'; | ||||
| 	$VERSION = '1.12'; | ||||
| 	@ISA     = 'Module::Install::Base'; | ||||
| 	$ISCORE  = 1; | ||||
| } | ||||
|   | ||||
| @@ -8,7 +8,7 @@ use Fcntl qw/:flock :seek/; | ||||
|  | ||||
| use vars qw{$VERSION @ISA $ISCORE}; | ||||
| BEGIN { | ||||
| 	$VERSION = '1.06'; | ||||
| 	$VERSION = '1.12'; | ||||
| 	@ISA     = 'Module::Install::Base'; | ||||
| 	$ISCORE  = 1; | ||||
| } | ||||
| @@ -133,7 +133,7 @@ sub makemaker_args { | ||||
| 	return $args; | ||||
| } | ||||
|  | ||||
| # For mm args that take multiple space-seperated args, | ||||
| # For mm args that take multiple space-separated args, | ||||
| # append an argument to the current list. | ||||
| sub makemaker_append { | ||||
| 	my $self = shift; | ||||
|   | ||||
| @@ -6,7 +6,7 @@ use Module::Install::Base (); | ||||
|  | ||||
| use vars qw{$VERSION @ISA $ISCORE}; | ||||
| BEGIN { | ||||
| 	$VERSION = '1.06'; | ||||
| 	$VERSION = '1.12'; | ||||
| 	@ISA     = 'Module::Install::Base'; | ||||
| 	$ISCORE  = 1; | ||||
| } | ||||
| @@ -347,7 +347,7 @@ sub name_from { | ||||
| 		^ \s* | ||||
| 		package \s* | ||||
| 		([\w:]+) | ||||
| 		\s* ; | ||||
| 		[\s|;]* | ||||
| 		/ixms | ||||
| 	) { | ||||
| 		my ($name, $module_name) = ($1, $1); | ||||
| @@ -705,7 +705,7 @@ sub _write_mymeta_data { | ||||
| 	my @yaml = Parse::CPAN::Meta::LoadFile('META.yml'); | ||||
| 	my $meta = $yaml[0]; | ||||
|  | ||||
| 	# Overwrite the non-configure dependency hashs | ||||
| 	# Overwrite the non-configure dependency hashes | ||||
| 	delete $meta->{requires}; | ||||
| 	delete $meta->{build_requires}; | ||||
| 	delete $meta->{recommends}; | ||||
|   | ||||
| @@ -6,7 +6,7 @@ use Module::Install::Base (); | ||||
|  | ||||
| use vars qw{$VERSION @ISA $ISCORE}; | ||||
| BEGIN { | ||||
| 	$VERSION = '1.06'; | ||||
| 	$VERSION = '1.12'; | ||||
| 	@ISA     = 'Module::Install::Base'; | ||||
| 	$ISCORE  = 1; | ||||
| } | ||||
|   | ||||
| @@ -8,7 +8,7 @@ use ExtUtils::Manifest (); | ||||
|  | ||||
| use vars qw{$VERSION @ISA $ISCORE}; | ||||
| BEGIN { | ||||
| 	$VERSION = '1.06'; | ||||
| 	$VERSION = '1.12'; | ||||
| 	@ISA     = 'Module::Install::Base'; | ||||
| 	$ISCORE  = 1; | ||||
| } | ||||
|   | ||||
| @@ -6,7 +6,7 @@ use Module::Install::Base (); | ||||
|  | ||||
| use vars qw{$VERSION @ISA $ISCORE}; | ||||
| BEGIN { | ||||
| 	$VERSION = '1.06'; | ||||
| 	$VERSION = '1.12'; | ||||
| 	@ISA     = 'Module::Install::Base'; | ||||
| 	$ISCORE  = 1; | ||||
| } | ||||
|   | ||||
| @@ -6,7 +6,7 @@ use Module::Install::Base (); | ||||
|  | ||||
| use vars qw{$VERSION @ISA $ISCORE}; | ||||
| BEGIN { | ||||
| 	$VERSION = '1.06'; | ||||
| 	$VERSION = '1.12'; | ||||
| 	@ISA     = qw{Module::Install::Base}; | ||||
| 	$ISCORE  = 1; | ||||
| } | ||||
|   | ||||
| @@ -160,6 +160,16 @@ __PACKAGE__->has_many( active_nodes_with_age => 'App::Netdisco::DB::Result::Virt | ||||
|     cascade_copy => 0, cascade_update => 0, cascade_delete => 0 }, | ||||
| ); | ||||
|  | ||||
| =head2 logs | ||||
|  | ||||
| Returns the set of C<device_port_log> entries associated with this Port. | ||||
|  | ||||
| =cut | ||||
|  | ||||
| __PACKAGE__->has_many( logs => 'App::Netdisco::DB::Result::DevicePortLog', | ||||
|   { 'foreign.ip' => 'self.ip', 'foreign.port' => 'self.port' }, | ||||
| ); | ||||
|  | ||||
| =head2 power | ||||
|  | ||||
| Returns a row from the C<device_port_power> table if one refers to this | ||||
| @@ -345,4 +355,16 @@ Returns the C<mac> column instantiated into a L<NetAddr::MAC> object. | ||||
|  | ||||
| sub net_mac { return NetAddr::MAC->new(mac => (shift)->mac) } | ||||
|  | ||||
| =head2 last_comment | ||||
|  | ||||
| Returns the most recent comment from the logs for this device port. | ||||
|  | ||||
| =cut | ||||
|  | ||||
| sub last_comment { | ||||
|   my $row = (shift)->logs->search(undef, | ||||
|     { order_by => { -desc => 'creation' }, rows => 1 })->first; | ||||
|   return ($row ? $row->log : ''); | ||||
| } | ||||
|  | ||||
| 1; | ||||
|   | ||||
| @@ -22,14 +22,17 @@ __PACKAGE__->result_source_instance->view_definition(<<'ENDSQL'); | ||||
|                                      a.log, | ||||
|                                      a.finished | ||||
|     FROM device_port p | ||||
|     JOIN device d ON d.ip = p.ip | ||||
|     JOIN ADMIN a ON p.remote_ip = a.device | ||||
|     WHERE p.remote_ip NOT IN | ||||
|         (SELECT ALIAS | ||||
|          FROM device_ip) | ||||
|       AND a.action = 'discover' | ||||
|     ORDER BY p.remote_ip, | ||||
|              a.finished DESC | ||||
|     JOIN device d | ||||
|       ON d.ip = p.ip | ||||
|     LEFT JOIN admin a | ||||
|       ON (p.remote_ip = a.device AND a.action = 'discover') | ||||
|     WHERE | ||||
|         (p.remote_ip NOT IN (SELECT alias FROM device_ip)) | ||||
|       OR | ||||
|         ((p.remote_ip IS NULL) AND p.is_uplink) | ||||
|     ORDER BY | ||||
|       p.remote_ip ASC, | ||||
|       a.finished DESC | ||||
| ENDSQL | ||||
|  | ||||
| __PACKAGE__->add_columns( | ||||
|   | ||||
| @@ -594,7 +594,6 @@ sub delete { | ||||
|  | ||||
|   $schema->resultset('Admin')->search({ | ||||
|     device => { '-in' => $devices->as_query }, | ||||
|     action => { '-like' => 'queued%' }, | ||||
|   })->delete; | ||||
|  | ||||
|   $schema->resultset('Topology')->search({ | ||||
|   | ||||
| @@ -157,6 +157,7 @@ sub delete { | ||||
|     DevicePortVlan | ||||
|     DevicePortWireless | ||||
|     DevicePortSsid | ||||
|     DevicePortLog | ||||
|   /) { | ||||
|       $schema->resultset($set)->search( | ||||
|         { ip => { '-in' => $ports->as_query }}, | ||||
|   | ||||
| @@ -14,6 +14,7 @@ hook 'before' => sub { | ||||
|  | ||||
|   my @default_port_columns_right = ( | ||||
|     { name => 'c_descr',       label => 'Description',       default => ''   }, | ||||
|     { name => 'c_comment',     label => 'Last Comment',      default => ''   }, | ||||
|     { name => 'c_type',        label => 'Type',              default => ''   }, | ||||
|     { name => 'c_duplex',      label => 'Duplex',            default => ''   }, | ||||
|     { name => 'c_lastchange',  label => 'Last Change',       default => ''   }, | ||||
|   | ||||
| @@ -0,0 +1,30 @@ | ||||
| package App::Netdisco::Web::Plugin::Report::InventoryByModelByOS; | ||||
|  | ||||
| use Dancer ':syntax'; | ||||
| use Dancer::Plugin::DBIC; | ||||
| use Dancer::Plugin::Auth::Extensible; | ||||
|  | ||||
| use App::Netdisco::Web::Plugin; | ||||
|  | ||||
| register_report( | ||||
|     {   category => 'Device', | ||||
|         tag      => 'inventorybymodelbyos', | ||||
|         label    => 'Inventory by Model by OS', | ||||
|         provides_csv => 0, | ||||
|     } | ||||
| ); | ||||
|  | ||||
| get '/ajax/content/report/inventorybymodelbyos' => require_login sub { | ||||
|     my @results = schema('netdisco')->resultset('Device')->search(undef, { | ||||
|       columns => [qw/vendor model os os_ver/], | ||||
|       select => [ { count => 'os_ver' } ], | ||||
|       as => [qw/ os_ver_count /], | ||||
|       group_by => [qw/ vendor model os os_ver /], | ||||
|       order_by => ['vendor', 'model', { -desc => 'count' }, 'os_ver'], | ||||
|     })->hri->all; | ||||
|  | ||||
|     template 'ajax/report/inventorybymodelbyos.tt', { results => \@results, }, | ||||
|         { layout => undef }; | ||||
| }; | ||||
|  | ||||
| 1; | ||||
| @@ -14,6 +14,32 @@ register_report({ | ||||
|   hidden => true, | ||||
| }); | ||||
|  | ||||
| sub _sanity_ok { | ||||
|     return 0 unless | ||||
|       param('ip') =~ m/^[[:print:]]+$/ | ||||
|       and param('port') =~ m/^[[:print:]]+$/ | ||||
|       and param('log') =~ m/^[[:print:]]+$/; | ||||
|  | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| ajax '/ajax/control/report/portlog/add' => require_login sub { | ||||
|     send_error('Bad Request', 400) unless _sanity_ok(); | ||||
|  | ||||
|     schema('netdisco')->txn_do(sub { | ||||
|       my $user = schema('netdisco')->resultset('DevicePortLog') | ||||
|         ->create({ | ||||
|           ip => param('ip'), | ||||
|           port => param('port'), | ||||
|           reason => 'other', | ||||
|           log => param('log'), | ||||
|           username => session('logged_in_user'), | ||||
|           userip => request->remote_address, | ||||
|           action => 'comment', | ||||
|         }); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| ajax '/ajax/content/report/portlog' => require_role port_control => sub { | ||||
|     my $device = param('q'); | ||||
|     my $port = param('f'); | ||||
|   | ||||
| @@ -46,6 +46,7 @@ web_plugins: | ||||
|   - Report::HalfDuplex | ||||
|   - Report::DeviceAddrNoDNS | ||||
|   - Report::DeviceByLocation | ||||
|   - Report::InventoryByModelByOS | ||||
|   - Report::DeviceDnsMismatch | ||||
|   - Report::DevicePoeStatus | ||||
|   - Report::DuplexMismatch | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| <table id="data-table" class="table table-striped table-bordered" width="100%" cellspacing="0"> | ||||
|   <thead> | ||||
|     <tr> | ||||
|       <th>Device Location Triggering<br>Last Discovery Attempt</th> | ||||
|       <th>Observing Device</th> | ||||
|       <th>Undiscovered Neighbor</th>  | ||||
|       <th class="nd_center-cell">Last Discovery Attempt</th> | ||||
|       <th class="nd_center-cell">Last Discovery Log</th> | ||||
| @@ -12,11 +12,18 @@ | ||||
|     <tr> | ||||
|       <td><a href="[% device_ports %]&q=[% row.ip | uri %]&f=[% row.port | uri %]"> | ||||
|                [% row.dns || row.name || row.ip | html_entity %] ( [% row.port | html_entity %] ) </a></td> | ||||
|       <td><a href="[% search_node %]&q=[% row.remote_ip | uri %]"> | ||||
|                                  [% row.remote_ip | html_entity %]</a> | ||||
|                                  ([% row.remote_port | html_entity %]) | ||||
|                                  [% ' id: '_ row.remote_id IF row.remote_id %] | ||||
|                                  [% ' type: '_ row.remote_type IF row.remote_type %]</td> | ||||
|       <td> | ||||
|         [% IF row.remote_ip %] | ||||
|         <a href="[% search_node %]&q=[% row.remote_ip | uri %]"> | ||||
|           [% row.remote_ip | html_entity %] | ||||
|         </a> ([% row.remote_port | html_entity %]) | ||||
|         [% '<br />' IF row.remote_id or row.remote_type %] | ||||
|         [% ' id: '_ row.remote_id IF row.remote_id %] | ||||
|         [% ' type: '_ row.remote_type IF row.remote_type %] | ||||
|         [% ELSE %] | ||||
|         Known Device's MAC Address seen on this Port during Macsuck | ||||
|         [% END %] | ||||
|       </td> | ||||
|       <td class="nd_center-cell">[% row.finished | html_entity %]</td> | ||||
|       <td class="nd_center-cell">[% row.log | html_entity %]</td> | ||||
|     </tr> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| [% USE CSV -%] | ||||
| [% CSV.dump([ 'Device Triggering Last Discovery Attempt' 'Device Port' | ||||
| [% CSV.dump([ 'Observing Device' 'Device Port' | ||||
|               'Remote IP' 'Remote Port' 'Remote ID' 'Remote Type' | ||||
|               'Last Discovery Attempt' 'Discovery Log']) %] | ||||
|  | ||||
|   | ||||
| @@ -82,6 +82,10 @@ | ||||
|             rel="tooltip" data-placement="top" data-offset="3" | ||||
|             data-animation="" data-title="Enable Port"></i> | ||||
|         </span> | ||||
|       [% END %] | ||||
|       [% ELSE %] | ||||
|       <td nowrap class="nd_editable-cell" | ||||
|         data-order="[% row.port | html_entity %]" data-filter="[% row.port | html_entity %]"> | ||||
|       [% END %] | ||||
|         <a class="nd_log-icon" | ||||
|           href="[% uri_for('/report/portlog') %]?q=[% device.ip | uri %]&f=[% row.port | uri %]"> | ||||
| @@ -89,9 +93,6 @@ | ||||
|             rel="tooltip" data-placement="top" data-offset="3" | ||||
|             data-animation="" data-title="View Port Log"></i> | ||||
|         </a> | ||||
|       [% ELSE %] | ||||
|       <td nowrap data-order="[% row.port | html_entity %]" data-filter="[% row.port | html_entity %]"> | ||||
|       [% END %] | ||||
|         <a class="nd_this-port-only nd_port-only-first" href="[% uri_for('/device', | ||||
|             self_options) %]&q=[% params.q | uri %]&f=[% row.port | uri %]&prefer=port"> | ||||
|           [% IF row.is_master %] | ||||
| @@ -121,6 +122,10 @@ | ||||
|       <td nowrap>[% row.descr | html_entity %]</td> | ||||
|       [% END %] | ||||
|  | ||||
|       [% IF params.c_comment %] | ||||
|       <td nowrap>[% row.last_comment | html_entity %]</td> | ||||
|       [% END %] | ||||
|  | ||||
|       [% IF params.c_type %] | ||||
|       <td>[% row.type | html_entity %]</td> | ||||
|       [% END %] | ||||
|   | ||||
| @@ -61,6 +61,10 @@ | ||||
|     [% myport.push(row.descr) %] | ||||
|   [% END %] | ||||
|  | ||||
|   [% IF params.c_comment %] | ||||
|     [% myport.push(row.last_comment) %] | ||||
|   [% END %] | ||||
|  | ||||
|   [% IF params.c_type %] | ||||
|     [% myport.push(row.type) %] | ||||
|   [% END %] | ||||
|   | ||||
							
								
								
									
										71
									
								
								Netdisco/share/views/ajax/report/inventorybymodelbyos.tt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								Netdisco/share/views/ajax/report/inventorybymodelbyos.tt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| <table id="nsbi-data-table" class="table table-bordered table-hover" width="100%" cellspacing="0"> | ||||
|   <thead> | ||||
|     <tr> | ||||
|       <th>Model</th> | ||||
|       <th>Operating System Version</th> | ||||
|       <th>Count</th> | ||||
|     </tr> | ||||
|   </thead> | ||||
|   </tbody> | ||||
|     [% FOREACH row IN results %] | ||||
|     <tr> | ||||
|       <td> | ||||
|         <a href="[% search_device %]&q=[% row.model | uri %]&vendor=[% row.vendor | uri %]&model=[% row.model | uri %]"> | ||||
|             [% row.vendor.ucfirst | html_entity %] [% row.model | html_entity %]</a> | ||||
|             [% IF row.os %] running "[% row.os | html_entity %]"[% END %] | ||||
|       </td> | ||||
|       <td> | ||||
|         <a class="nd_linkcell" | ||||
|           href="[% search_device %]&q=[% row.os_ver | uri %]&vendor=[% row.vendor | uri %]&model=[% row.model | uri %]&os=[% row.os | uri %]&os_ver=[% row.os_ver | uri %]&matchall=on"> | ||||
|             [% row.os_ver | html_entity %]</a> | ||||
|       </td> | ||||
|       <td>[% row.os_ver_count | html_entity %]</td> | ||||
|     </tr> | ||||
|     [% END %] | ||||
|   </tbody> | ||||
| </table> | ||||
|  | ||||
| <style> | ||||
| tr.group, | ||||
| tr.group:hover { | ||||
|     background-color: #ddd !important; | ||||
| } | ||||
| </style> | ||||
|  | ||||
| <script> | ||||
| $(document).ready(function() { | ||||
|     var table = $('#nsbi-data-table').DataTable({ | ||||
|         "columnDefs": [ | ||||
|             { "visible": false, "targets": 0 } | ||||
|         ], | ||||
|         sort: false, | ||||
|         "drawCallback": function ( settings ) { | ||||
|             var api = this.api(); | ||||
|             var rows = api.rows( {page:'current'} ).nodes(); | ||||
|             var last=null; | ||||
|   | ||||
|             api.column(0, {page:'current'} ).data().each( function ( group, i ) { | ||||
|                 if ( last !== group ) { | ||||
|                     $(rows).eq( i ).before( | ||||
|                     '<tr class="group"><td colspan="2">'+group+'</td></tr>' | ||||
|                     ); | ||||
|  | ||||
|                     last = group; | ||||
|                 } | ||||
|             } ); | ||||
|         }, | ||||
| [% INCLUDE 'ajax/datatabledefaults.tt' -%] | ||||
|     } ); | ||||
|   | ||||
|     // Order by the grouping | ||||
|     $('#nsbi-data-table tbody').on( 'click', 'tr.group', function () { | ||||
|         var currentOrder = table.order()[0]; | ||||
|         if ( currentOrder[0] === 0 && currentOrder[1] === 'asc' ) { | ||||
|             table.order( [ 0, 'desc' ] ).draw(); | ||||
|         } | ||||
|         else { | ||||
|             table.order( [ 0, 'asc' ] ).draw(); | ||||
|         } | ||||
|     } ); | ||||
| } ); | ||||
| </script> | ||||
| @@ -4,8 +4,8 @@ | ||||
| [% FOREACH row IN results %] | ||||
|   [% mylist = [] %] | ||||
|   [% mylist.push(row.ip) %] | ||||
|   [% mylist.push(row.mac) %] | ||||
|   [% mylist.push(row.dns) %] | ||||
|   [% mylist.push(row.time_first) %] | ||||
|   [% mylist.push(row.time_last) %] | ||||
|   [% CSV.dump(mylist) %] | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,3 @@ | ||||
| [% IF results.count == 0 %] | ||||
| <div class="span2 alert alert-info">This port's log is empty.</div> | ||||
| [% ELSE %] | ||||
| <table id="data-table" class="table table-bordered table-condensed table-striped" width="100%" cellspacing="0"> | ||||
|   <thead> | ||||
|     <tr> | ||||
| @@ -10,9 +7,24 @@ | ||||
|       <th class="nd_center-cell">Action</th> | ||||
|       <th class="nd_center-cell">Reason</th> | ||||
|       <th class="nd_center-cell">Log</th> | ||||
|       <th class="nd_center-cell">Action</th> | ||||
|     </tr> | ||||
|   </thead> | ||||
|   </tbody> | ||||
|     <tr> | ||||
|       <td class="nd_center-cell">-</td> | ||||
|       <td class="nd_center-cell">[% session.logged_in_user | html_entity %]</td> | ||||
|       <td class="nd_center-cell">-</td> | ||||
|       <td class="nd_center-cell">comment</td> | ||||
|       <td class="nd_center-cell">Other</td> | ||||
|       <td class="nd_center-cell"><input data-form="add" class="span4" name="log" type="text" placeholder="Add comment..."></td> | ||||
|       <input data-form="add" name="ip" type="hidden" value="[% params.q | html_entity %]"> | ||||
|       <input data-form="add" name="port" type="hidden" value="[% params.f | html_entity %]"> | ||||
|       <td class="nd_center-cell"> | ||||
|         <button class="btn btn-small nd_adminbutton" name="add" type="submit"><i class="icon-plus-sign"></i> Add</button> | ||||
|       </td> | ||||
|     </tr> | ||||
|  | ||||
|     [% WHILE (row = results.next) %] | ||||
|     <tr> | ||||
|       <td class="nd_center-cell">[% row.creation_stamp | html_entity %]</td> | ||||
| @@ -21,15 +33,16 @@ | ||||
|       <td class="nd_center-cell">[% row.action   | html_entity %]</td> | ||||
|       <td class="nd_center-cell">[% settings.port_control_reasons.item(row.reason) || row.reason | html_entity %]</td> | ||||
|       <td class="nd_center-cell">[% row.log || '-' | html_entity %]</td> | ||||
|       <td class="nd_center-cell"></td> | ||||
|     </tr> | ||||
|     [% END %] | ||||
|   </tbody> | ||||
| </table> | ||||
| [% END %] | ||||
|  | ||||
| <script> | ||||
| $(document).ready(function() { | ||||
|   $('#data-table').dataTable({ | ||||
|     sort: false, | ||||
| [% INCLUDE 'ajax/datatabledefaults.tt' -%] | ||||
|   } ); | ||||
| } ); | ||||
|   | ||||
| @@ -52,4 +52,35 @@ | ||||
|       ,delay: 150 | ||||
|       ,minLength: 3 | ||||
|     }); | ||||
|  | ||||
|     // dynamically bind to all forms in the table | ||||
|     $('.content').on('click', '.nd_adminbutton', function(event) { | ||||
|       // stop form from submitting normally | ||||
|       event.preventDefault(); | ||||
|  | ||||
|       // what purpose - add/update/del | ||||
|       var mode = $(this).attr('name'); | ||||
|  | ||||
|       // submit the query and put results into the tab pane | ||||
|       $.ajax({ | ||||
|         type: 'POST' | ||||
|         ,async: true | ||||
|         ,dataType: 'html' | ||||
|         ,url: uri_base + '/ajax/control/report/' + tab + '/' + mode | ||||
|         ,data: $(this).closest('tr').find('input[data-form="' + mode + '"]').serializeArray() | ||||
|         ,beforeSend: function() { | ||||
|           $(target).html( | ||||
|             '<div class="span2 alert">Request submitted...</div>' | ||||
|           ); | ||||
|         } | ||||
|         ,success: function() { | ||||
|           $('#' + tab + '_form').trigger('submit'); | ||||
|         } | ||||
|         // skip any error reporting for now | ||||
|         // TODO: fix sanity_ok in Netdisco Web | ||||
|         ,error: function() { | ||||
|           $('#' + tab + '_form').trigger('submit'); | ||||
|         } | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user