Node search by NetBIOS name. Update documentation.
This commit is contained in:
		| @@ -98,6 +98,17 @@ See also the C<node_sightings> helper routine, below. | ||||
| __PACKAGE__->has_many( nodes => 'App::Netdisco::DB::Result::Node', | ||||
|   { 'foreign.mac' => 'self.mac' } ); | ||||
|  | ||||
| =head2 netbios | ||||
|  | ||||
| Returns the set of C<node_nbt> entries associated with the MAC of this IP. | ||||
| That is, all the NetBIOS entries recorded which shared the same MAC with this | ||||
| IP Address. | ||||
|  | ||||
| =cut | ||||
|  | ||||
| __PACKAGE__->has_many( netbios => 'App::Netdisco::DB::Result::NodeNbt', | ||||
|   { 'foreign.mac' => 'self.mac' } ); | ||||
|  | ||||
| my $search_attr = { | ||||
|     order_by => {'-desc' => 'time_last'}, | ||||
|     '+columns' => { | ||||
|   | ||||
| @@ -45,6 +45,141 @@ __PACKAGE__->set_primary_key("mac"); | ||||
| # Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02 | ||||
| # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:XFpxaGAWE13iizQIuVOP3g | ||||
|  | ||||
| =head1 RELATIONSHIPS | ||||
|  | ||||
| =head2 oui | ||||
|  | ||||
| Returns the C<oui> table entry matching this Node. You can then join on this | ||||
| relation and retrieve the Company name from the related table. | ||||
|  | ||||
| The JOIN is of type LEFT, in case the OUI table has not been populated. | ||||
|  | ||||
| =cut | ||||
|  | ||||
| __PACKAGE__->belongs_to( oui => 'App::Netdisco::DB::Result::Oui', | ||||
|     sub { | ||||
|         my $args = shift; | ||||
|         return { | ||||
|             "$args->{foreign_alias}.oui" => | ||||
|               { '=' => \"substring(cast($args->{self_alias}.mac as varchar) for 8)" } | ||||
|         }; | ||||
|     }, | ||||
|     { join_type => 'LEFT' } | ||||
| ); | ||||
|  | ||||
| =head2 nodes | ||||
|  | ||||
| Returns the set of C<node> entries associated with this IP. That is, all the | ||||
| MAC addresses recorded which have ever hosted this IP Address. | ||||
|  | ||||
| Remember you can pass a filter to this method to find only active or inactive | ||||
| nodes, but do take into account that both the C<node> and C<node_nbt> tables | ||||
| include independent C<active> fields. | ||||
|  | ||||
| See also the C<node_sightings> helper routine, below. | ||||
|  | ||||
| =cut | ||||
|  | ||||
| __PACKAGE__->has_many( nodes => 'App::Netdisco::DB::Result::Node', | ||||
|   { 'foreign.mac' => 'self.mac' } ); | ||||
|  | ||||
|  | ||||
| =head2 nodeips | ||||
|  | ||||
| Returns the set of C<node_ip> entries associated with this NetBIOS entry. | ||||
| That is, the IP addresses which the same MAC address at the time of discovery. | ||||
|  | ||||
| Note that the Active status of the returned IP entries will all be the same | ||||
| as the current NetBIOS entry. | ||||
|  | ||||
| =cut | ||||
|  | ||||
| __PACKAGE__->has_many( nodeips => 'App::Netdisco::DB::Result::NodeIp', | ||||
|   { 'foreign.mac' => 'self.mac', 'foreign.active' => 'self.active' } ); | ||||
|  | ||||
|  | ||||
| my $search_attr = { | ||||
|     order_by => {'-desc' => 'time_last'}, | ||||
|     '+columns' => { | ||||
|       time_first_stamp => \"to_char(time_first, 'YYYY-MM-DD HH24:MI')", | ||||
|       time_last_stamp => \"to_char(time_last, 'YYYY-MM-DD HH24:MI')", | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| =head2 node_sightings( \%cond, \%attrs? ) | ||||
|  | ||||
| Returns the set of C<node> entries associated with this IP. That is, all the | ||||
| MAC addresses recorded which have ever hosted this IP Address. | ||||
|  | ||||
| Remember you can pass a filter to this method to find only active or inactive | ||||
| nodes, but do take into account that both the C<node> and C<node_ip> tables | ||||
| include independent C<active> fields. | ||||
|  | ||||
| =over 4 | ||||
|  | ||||
| =item * | ||||
|  | ||||
| Results are ordered by time last seen. | ||||
|  | ||||
| =item * | ||||
|  | ||||
| Additional columns C<time_first_stamp> and C<time_last_stamp> provide | ||||
| preformatted timestamps of the C<time_first> and C<time_last> fields. | ||||
|  | ||||
| =item * | ||||
|  | ||||
| A JOIN is performed on the Device table and the Device DNS column prefetched. | ||||
|  | ||||
| =back | ||||
|  | ||||
| =cut | ||||
|  | ||||
| sub node_sightings { | ||||
|     my ($row, $cond, $attrs) = @_; | ||||
|  | ||||
|     return $row | ||||
|       ->nodes({}, { | ||||
|         '+columns' => [qw/ device.dns /], | ||||
|         join => 'device', | ||||
|       }) | ||||
|       ->search_rs({}, $search_attr) | ||||
|       ->search($cond, $attrs); | ||||
| } | ||||
|  | ||||
| =head1 ADDITIONAL COLUMNS | ||||
|  | ||||
| =head2 time_first_stamp | ||||
|  | ||||
| Formatted version of the C<time_first> field, accurate to the minute. | ||||
|  | ||||
| The format is somewhat like ISO 8601 or RFC3339 but without the middle C<T> | ||||
| between the date stamp and time stamp. That is: | ||||
|  | ||||
|  2012-02-06 12:49 | ||||
|  | ||||
| =cut | ||||
|  | ||||
| sub time_first_stamp { return (shift)->get_column('time_first_stamp') } | ||||
|  | ||||
| =head2 time_last_stamp | ||||
|  | ||||
| Formatted version of the C<time_last> field, accurate to the minute. | ||||
|  | ||||
| The format is somewhat like ISO 8601 or RFC3339 but without the middle C<T> | ||||
| between the date stamp and time stamp. That is: | ||||
|  | ||||
|  2012-02-06 12:49 | ||||
|  | ||||
| =cut | ||||
|  | ||||
| sub time_last_stamp  { return (shift)->get_column('time_last_stamp')  } | ||||
|  | ||||
| =head2 net_mac | ||||
|  | ||||
| Returns the C<mac> column instantiated into a L<Net::MAC> object. | ||||
|  | ||||
| =cut | ||||
|  | ||||
| sub net_mac { return Net::MAC->new(mac => (shift)->mac) } | ||||
|  | ||||
| # You can replace this text with custom code or comments, and it will be preserved on regeneration | ||||
| 1; | ||||
|   | ||||
							
								
								
									
										189
									
								
								Netdisco/lib/App/Netdisco/DB/ResultSet/NodeNbt.pm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								Netdisco/lib/App/Netdisco/DB/ResultSet/NodeNbt.pm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,189 @@ | ||||
| package App::Netdisco::DB::ResultSet::NodeNbt; | ||||
| use base 'App::Netdisco::DB::ResultSet'; | ||||
|  | ||||
| use strict; | ||||
| use warnings FATAL => 'all'; | ||||
|  | ||||
| __PACKAGE__->load_components(qw/ | ||||
|   +App::Netdisco::DB::ExplicitLocking | ||||
| /); | ||||
|  | ||||
| my $search_attr = { | ||||
|     order_by => {'-desc' => 'time_last'}, | ||||
|     '+columns' => [ | ||||
|       'oui.company', | ||||
|       { time_first_stamp => \"to_char(time_first, 'YYYY-MM-DD HH24:MI')" }, | ||||
|       { time_last_stamp =>  \"to_char(time_last, 'YYYY-MM-DD HH24:MI')" }, | ||||
|     ], | ||||
|     join => 'oui' | ||||
| }; | ||||
|  | ||||
| =head1 with_times | ||||
|  | ||||
| This is a modifier for any C<search()> (including the helpers below) which | ||||
| will add the following additional synthesized columns to the result set: | ||||
|  | ||||
| =over 4 | ||||
|  | ||||
| =item time_first_stamp | ||||
|  | ||||
| =item time_last_stamp | ||||
|  | ||||
| =back | ||||
|  | ||||
| =cut | ||||
|  | ||||
| sub with_times { | ||||
|   my ($rs, $cond, $attrs) = @_; | ||||
|  | ||||
|   return $rs | ||||
|     ->search_rs({}, $search_attr) | ||||
|     ->search($cond, $attrs); | ||||
| } | ||||
|  | ||||
| =head1 search_by_ip( \%cond, \%attrs? ) | ||||
|  | ||||
|  my $set = $rs->search_by_ip({ip => '192.0.2.1', active => 1}); | ||||
|  | ||||
| Like C<search()>, this returns a ResultSet of matching rows from the | ||||
| NodeNbt table. | ||||
|  | ||||
| =over 4 | ||||
|  | ||||
| =item * | ||||
|  | ||||
| The C<cond> parameter must be a hashref containing a key C<ip> with the value | ||||
| to search for. Value can either be a simple string of IPv4 or IPv6, or a | ||||
| L<NetAddr::IP::Lite> object in which case all results within the CIDR/Prefix | ||||
| will be retrieved. | ||||
|  | ||||
| =item * | ||||
|  | ||||
| Results are ordered by time last seen. | ||||
|  | ||||
| =item * | ||||
|  | ||||
| Additional columns C<time_first_stamp> and C<time_last_stamp> provide | ||||
| preformatted timestamps of the C<time_first> and C<time_last> fields. | ||||
|  | ||||
| =item * | ||||
|  | ||||
| A JOIN is performed on the OUI table and the OUI C<company> column prefetched. | ||||
|  | ||||
| =back | ||||
|  | ||||
| To limit results only to active IPs, set C<< {active => 1} >> in C<cond>. | ||||
|  | ||||
| =cut | ||||
|  | ||||
| sub search_by_ip { | ||||
|     my ($rs, $cond, $attrs) = @_; | ||||
|  | ||||
|     die "ip address required for search_by_ip\n" | ||||
|       if ref {} ne ref $cond or !exists $cond->{ip}; | ||||
|  | ||||
|     # handle either plain text IP or NetAddr::IP (/32 or CIDR) | ||||
|     my ($op, $ip) = ('=', delete $cond->{ip}); | ||||
|  | ||||
|     if ('NetAddr::IP::Lite' eq ref $ip and $ip->num > 1) { | ||||
|         $op = '<<='; | ||||
|         $ip = $ip->cidr; | ||||
|     } | ||||
|     $cond->{ip} = { $op => $ip }; | ||||
|  | ||||
|     return $rs | ||||
|       ->search_rs({}, $search_attr) | ||||
|       ->search($cond, $attrs); | ||||
| } | ||||
|  | ||||
| =head1 search_by_name( \%cond, \%attrs? ) | ||||
|  | ||||
|  my $set = $rs->search_by_name({nbname => 'MYNAME', active => 1}); | ||||
|  | ||||
| Like C<search()>, this returns a ResultSet of matching rows from the | ||||
| NodeNbt table. | ||||
|  | ||||
| =over 4 | ||||
|  | ||||
| =item * | ||||
|  | ||||
| The C<cond> parameter must be a hashref containing a key C<nbname> with the | ||||
| value to search for. The value may optionally include SQL wildcard characters. | ||||
|  | ||||
| =item * | ||||
|  | ||||
| Results are ordered by time last seen. | ||||
|  | ||||
| =item * | ||||
|  | ||||
| Additional columns C<time_first_stamp> and C<time_last_stamp> provide | ||||
| preformatted timestamps of the C<time_first> and C<time_last> fields. | ||||
|  | ||||
| =item * | ||||
|  | ||||
| A JOIN is performed on the OUI table and the OUI C<company> column prefetched. | ||||
|  | ||||
| =back | ||||
|  | ||||
| To limit results only to active IPs, set C<< {active => 1} >> in C<cond>. | ||||
|  | ||||
| =cut | ||||
|  | ||||
| sub search_by_name { | ||||
|     my ($rs, $cond, $attrs) = @_; | ||||
|  | ||||
|     die "nbname field required for search_by_name\n" | ||||
|       if ref {} ne ref $cond or !exists $cond->{nbname}; | ||||
|  | ||||
|     $cond->{nbname} = { '-ilike' => delete $cond->{nbname} }; | ||||
|  | ||||
|     return $rs | ||||
|       ->search_rs({}, $search_attr) | ||||
|       ->search($cond, $attrs); | ||||
| } | ||||
|  | ||||
| =head1 search_by_mac( \%cond, \%attrs? ) | ||||
|  | ||||
|  my $set = $rs->search_by_mac({mac => '00:11:22:33:44:55', active => 1}); | ||||
|  | ||||
| Like C<search()>, this returns a ResultSet of matching rows from the | ||||
| NodeNbt table. | ||||
|  | ||||
| =over 4 | ||||
|  | ||||
| =item * | ||||
|  | ||||
| The C<cond> parameter must be a hashref containing a key C<mac> with the value | ||||
| to search for. | ||||
|  | ||||
| =item * | ||||
|  | ||||
| Results are ordered by time last seen. | ||||
|  | ||||
| =item * | ||||
|  | ||||
| Additional columns C<time_first_stamp> and C<time_last_stamp> provide | ||||
| preformatted timestamps of the C<time_first> and C<time_last> fields. | ||||
|  | ||||
| =item * | ||||
|  | ||||
| A JOIN is performed on the OUI table and the OUI C<company> column prefetched. | ||||
|  | ||||
| =back | ||||
|  | ||||
| To limit results only to active IPs, set C<< {active => 1} >> in C<cond>. | ||||
|  | ||||
| =cut | ||||
|  | ||||
| sub search_by_mac { | ||||
|     my ($rs, $cond, $attrs) = @_; | ||||
|  | ||||
|     die "mac address required for search_by_mac\n" | ||||
|       if ref {} ne ref $cond or !exists $cond->{mac}; | ||||
|  | ||||
|     return $rs | ||||
|       ->search_rs({}, $search_attr) | ||||
|       ->search($cond, $attrs); | ||||
| } | ||||
|  | ||||
| 1; | ||||
| @@ -49,6 +49,9 @@ ajax '/ajax/content/search/node' => require_login sub { | ||||
|         my $ips = schema('netdisco')->resultset('NodeIp') | ||||
|           ->search_by_mac({mac => $mac->as_IEEE, @active, @times}); | ||||
|  | ||||
|         my $netbios = schema('netdisco')->resultset('NodeNbt') | ||||
|           ->search_by_mac({mac => $mac->as_IEEE, @active, @times}); | ||||
|  | ||||
|         my $ports = schema('netdisco')->resultset('DevicePort') | ||||
|           ->search({mac => $mac->as_IEEE}); | ||||
|  | ||||
| @@ -62,17 +65,6 @@ ajax '/ajax/content/search/node' => require_login sub { | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         my $netbios = schema('netdisco')->resultset('NodeNbt')->search( | ||||
|             { mac => $mac->as_IEEE }, | ||||
|             { order_by   => { '-desc' => 'time_last' }, | ||||
|               '+columns' => [ | ||||
|                 { | ||||
|                   time_first_stamp => \"to_char(time_first, 'YYYY-MM-DD HH24:MI')", | ||||
|                   time_last_stamp  => \"to_char(time_last,  'YYYY-MM-DD HH24:MI')" | ||||
|                 }] | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         return unless $sightings->has_rows | ||||
|             or $ips->has_rows | ||||
|             or $ports->has_rows | ||||
| @@ -89,25 +81,33 @@ ajax '/ajax/content/search/node' => require_login sub { | ||||
|     else { | ||||
|         my $set; | ||||
|  | ||||
|         my $name = $node; | ||||
|  | ||||
|         if (param('partial')) { | ||||
|             $name = "\%$name\%" if $name !~ m/%/; | ||||
|         } | ||||
|  | ||||
|         $set = schema('netdisco')->resultset('NodeNbt') | ||||
|             ->search_by_name({nbname => $name, @active, @times}); | ||||
|  | ||||
|         unless ( $set->has_rows ) { | ||||
|         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, @times}); | ||||
|         } | ||||
|         else { | ||||
|             if (param('partial')) { | ||||
|                 $node = "\%$node\%" if $node !~ m/%/; | ||||
|             } | ||||
|             elsif (setting('domain_suffix')) { | ||||
|                 $node .= setting('domain_suffix') | ||||
|                     if index($node, setting('domain_suffix')) == -1; | ||||
|  | ||||
|             if ($name !~ m/%/ and setting('domain_suffix')) { | ||||
|                 $name .= setting('domain_suffix') | ||||
|                     if index($name, setting('domain_suffix')) == -1; | ||||
|             } | ||||
|             $set = schema('netdisco')->resultset('NodeIp') | ||||
|               ->search_by_dns({dns => $node, @active, @times}); | ||||
|  | ||||
|             # if the user selects Vendor search opt, then | ||||
|             # we'll try the OUI company name as a fallback | ||||
|             if (not $set->count and param('show_vendor')) { | ||||
|             if (not $set->has_rows and param('show_vendor')) { | ||||
|                 $node = param('q'); | ||||
|                 $set = schema('netdisco')->resultset('NodeIp') | ||||
|                   ->with_times | ||||
| @@ -117,7 +117,8 @@ ajax '/ajax/content/search/node' => require_login sub { | ||||
|                   ); | ||||
|             } | ||||
|         } | ||||
|         return unless $set and $set->count; | ||||
|         return unless $set and $set->has_rows; | ||||
|         } | ||||
|         $set = $set->search_rs({}, { order_by => 'me.mac' }); | ||||
|  | ||||
|         template 'ajax/search/node_by_ip.tt', { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user