<h3>IP Inventory</h3>
<% $err %>
% $m->comp('SELF:show_age') if $cmd eq 'age';
<FORM ACTION="<%$r->uri%>" METHOD="GET">
<INPUT TYPE="hidden" NAME="cmd" VALUE="age">
<TABLE CLASS="box" BORDER=0>
<TR>
    <TD>IPs not used in :</TD>
    <TD>
        <SELECT NAME="age_val">
% for (1..12) {
            <OPTION VALUE="<%$_%>"<%$age_val eq $_ ? ' SELECTED' : ''%>><%$_%>
% }
        </SELECT>
        <SELECT NAME="age_mul">
% for (qw/days months years/) {
            <OPTION VALUE="<%$_%>"<%$age_mul eq $_ ? ' SELECTED' : ''%>><%$_%>
% }
        </SELECT>
    <TD>Sort By :</TD>
    <TD><SELECT NAME="sort" SIZE=1>
%       foreach my $s (sort @sorts){
            <OPTION VALUE="<%$s%>" <% $sort eq $s ? 'SELECTED' : ''%>><%$s%>
%       } 
    </TD>
</TR>
<TR>
    <TD>Subnet :</TD>
    <TD><INPUT TYPE="text" SIZE=20 NAME="subnet" VALUE="<%$subnet|h%>" TITLE="CIDR Format : 192.168.0.0/24">
        <BR><font size=-2>(CIDR Format : <tt>192.168.0.0/24</tt>)</font>
    </TD>
    <TD>List IPs<BR>Never Discovered :</TD>
    <TD><INPUT TYPE="checkbox" NAME="never" VALUE=1<%$never ? ' CHECKED' : ''%>></TD>
</TR>
<TR>
    <TD>Resolve IPs:</TD>
    <TD><INPUT TYPE="radio" NAME="dns" VALUE=1<%$dns ? ' CHECKED': ''%>>On
        <INPUT TYPE="radio" NAME="dns" VALUE=0<%!$dns ? ' CHECKED': ''%>>Off</TD>
    <TD>Limit :</TD>
    <TD><SELECT NAME="max" SIZE=1>
% for (qw/10 50 100 200 500 1000 5000 100000/){
        <OPTION VALUE="<%$_%>"<%$max eq $_ ? ' SELECTED' : ''%>><%$_%>
%}
    </TD>
</TR>
<TR>
    <TD COLSPAN=4>
        <INPUT TYPE="submit" VALUE="Find IPs">
    </TD>
</TR>
</TABLE>
</FORM>
Finds IP Addresses used by nodes.
<BR>To find IP addresses that have never been used in a subnet, select "12 Years" and Check the Checkbox.
<P>
<h3>Netbios Inventory</h3>
% $m->comp('SELF:show_nbt') if $cmd eq 'nbt';
<FORM ACTION="<%$r->uri%>" METHOD=GET>
<TABLE CLASS="box" BORDER=0>
<INPUT TYPE="hidden" NAME="cmd" VALUE="nbt">
<TR>
    <TD>Domain:</TD>
    <TD><SELECT NAME="domain" MULTIPLE SIZE=8>
        <OPTION VALUE="all"<%!scalar(@domain) ? ' SELECTED' : ''%>>[All]
        <OPTION VALUE="blank" <%grep(/^\Qblank\E$/,@domain)?' SELECTED':''%>>(Blank Domain)
% foreach my $d (sort keys %$nbt_domains){
        <OPTION VALUE="<%$d|u%>"<%grep(/^\Q$d\E$/,@domain)?' SELECTED':''%>><%$d|h%>
% }
    </TD>
</TR>
<TR>
    <TD>Archive Data:</TD>
    <TD><INPUT TYPE="checkbox" NAME="archive" VALUE=1<%$archive ? ' CHECKED' : ''%>></TD>
</TR>
<TR>
    <TD COLSPAN=2><INPUT TYPE="submit" VALUE="Search">
        [ <A HREF="<%$r->uri%>">Reset</A> ]
    </TD>
</TR>
</TABLE>
</FORM>
<%args>
$subnet  => ''
$sort    => 'IP'
$age_val => 2
$age_mul => 'months'
$cmd     => ''
$max     => 200
@domain  => ()
$archive => 0
$never   => 0
$dns     => 1
</%args>
<%shared>
my @sorts = qw/IP Age/;
my ($ips,$arg_sort,$odd,$arg_max,$arg_domain,$addrs,$err);
my $nodes = [];  my $domain_count = {}; my $nbt_domains={};
my $domain = $netdisco::CONFIG{domain} || '';
</%shared>
<%init>
$arg_sort = $sort; $arg_max = $max; $arg_domain = \@domain;

# get some form filler
$nbt_domains = sql_column('node_nbt',['distinct(domain)','true']);
delete $nbt_domains->{''};

if ($cmd eq 'age' and !$subnet){
    $err = "Subnet Required.";
    $cmd = '';
}

if ($cmd eq 'age'){
    $age_val = 2 unless $age_val =~ m/^\d+$/;
    $age_mul =~ s/[^a-z ]+//g;
    my $where =  {'age(now(),time_last)' => \\">= interval '$age_val $age_mul'"};

    $subnet .= '/24' unless $subnet =~ /\//;
    my $safenet = dbh_quote($subnet);
    $where->{ip} = \\"<< inet $safenet";
    $where->{active} = \'true'; 
    # Grab all active node_ip rows in this subnet that are x old
    $ips = sql_rows('node_ip',['age(now(),time_last) as age','extract(epoch from time_first) as time_first',
                               'extract(epoch from time_last) as time_last','ip','active'],
                    $where
                   ); 


    # Find all active IPs
    my $active_ips = sql_rows('node_ip',['distinct(ip)'],{'ip' => \\"<< inet $safenet",
                              'active' => \'true'});
    my @active_ips = map { $_->{ip} } @$active_ips;

    # Find IPs that are not active but still in subnet
    if (scalar @active_ips){
        $active_ips[0] = "!$active_ips[0]";
        # Question: Is there any limit to IN (LIST) with DBD::Pg / Postgres?
        $where = {'ip' => [\\"<< inet $safenet",[@active_ips]], active => \'false' };
    } else {
        $where = {'ip' => \\"<< inet $safenet", active => \'false' };
    }
    my $archive_ips = sql_rows('node_ip',['age(now(),time_last) as age','extract(epoch from time_first) as time_first',
                               'extract(epoch from time_last) as time_last','ip','active'],
                    $where
                   ); 

    # Find the newest archived data
    my %seen; my %results;
    foreach my $r (@$archive_ips){
        my $ip = $r->{ip};
        # First entry
        unless (defined $results{$ip} ){
            $results{$ip}=$r;
            $seen{$ip}=$r->{time_last};
            next;
        }
        next unless $r->{time_last} > $seen{$ip};
        $results{$ip}=$r;
        $seen{$ip}=$r->{time_last};
    }

    push (@$ips,values %results);

    # Look for holes in our IP Space and report on that.
    if ($never){
        # Get all the possible IPs in a subnet
        $addrs = dump_subnet($subnet) || {};

        # Remove all the nodes seen in the subnet
        my $nodes_seen = sql_rows('node_ip',['distinct(ip)'], { 'ip' => \\"<< inet $safenet"});

        foreach my $row (@$nodes_seen){
            my $ip = $row->{ip};
            delete $addrs->{$ip};
        }

        # Remove all the device / aliases seen
        my $devs_seen = sql_rows('device', ['distinct(ip)'], { 'ip' => \\"<< inet $safenet"});
        my $aliases = sql_rows('device_ip', ['distinct(alias)'], { 'alias' => \\"<< inet $safenet"});
        push(@$devs_seen,@$aliases);

        foreach my $row (@$devs_seen){
            my $ip = $row->{alias} || $row->{ip};
            delete $addrs->{$ip};
        }
        
        # Add our missing IPs to the results from above.
        foreach my $ip (keys %$addrs){
            push @$ips, { 'age' => 'Never', 'time_first' => 'Never',
                          'ip' => $ip, 'active' => 1, 'time_last' => 'Never',
                        };
        }
    }

    # Do DNS lookups on results
    if ($dns){
        foreach my $i (@$ips){
            my $ip = $i->{ip};
            $i->{dns} = hostname($ip);
        }
    }

}

if ($cmd eq 'nbt'){
    my $where = {};
    foreach my $d (@domain) {
        $d = '' if $d eq 'blank';
    }
    if (grep(/^all$/,@domain)){
        @domain = ();
    } else {
        $where = {'domain' => [[@domain]]};
    }
    $where->{active} = 1 unless $archive;
    $nodes = sql_rows('node_nbt',['ip','mac','nbname','domain','server','nbuser','active',
                                  'extract(epoch from time_first) as time_first','extract(epoch from time_last) as time_last'],
                                 $where);
    # count domains
    foreach my $n (@$nodes){
        my $d = $n->{domain};
        $domain_count->{$d}++;
    }
}
</%init>
%#
%# show_nbt() - Show results from Netbios Search
%#
<%method show_nbt>
<h4>Results</h4>
% unless (scalar @$nodes) {
No Results Found.
% return; }
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
    <TR>
        <TH>Domain</TH>
        <TH>Count</TH>
        <TH>Domain</TH>
        <TH>Count</TH>
    </TR>
% my $cnt = 0;
% foreach my $d (sort keys %$domain_count){
% $cnt++;
% if ($cnt%2){
<TR CLASS="match-<%++$odd%2%>">
%}
    <TD><A HREF="<%$r->uri%>?domain=<%$d||'blank'%>"><%$d || '[Blank]'%></A></TD>
    <TD><%$domain_count->{$d}%></TD>
% if (($cnt+1)%2) {
</TR>
%}
%}
</TABLE>
<% scalar(keys %$domain_count) %> Domains.
<P>
<TABLE WIDTH="100%" BORDER=0 CELLSPACING=0>
    <TR>
        <TH>Domain</TH>
        <TH>Node</TH>
        <TH>Name</TH>
        <TH>User</TH>
        <TH>First Seen</TH>
        <TH>Last Seen</TH>
    </TR>
% my $last_domain = '';
% foreach my $n (sort {($a->{domain} cmp $b->{domain}) or 
%                      (lc($a->{nbname}) cmp lc($b->{nbname}) )} 
%                @$nodes){
    <TR CLASS="match-<%++$odd%2%>">
        <TD><%$last_domain ne $n->{domain} ? $n->{domain} : '&nbsp;'%></TD>
        <TD><A HREF="node.html?node=<%$n->{mac}|u%>"><%$n->{mac}%></A></TD>
        <TD><%$n->{domain} ? "\\\\$n->{domain}\\" : '' %><A HREF="node.html?node=<%$n->{nbname} |u%>"><%$n->{nbname} || '[None]'%></A></TD>
        <TD><%$n->{nbuser} || '[No User]' %> @
            <A HREF="node.html?node=<%$n->{ip} |u%>"><%$n->{ip}%></A>
        </TD>
        <TD><& 'node.html:format_date', date=>$n->{time_first}&></TD>
        <TD><& 'node.html:format_date', date=>$n->{time_last}&></TD>
    </TR>
%   $last_domain = $n->{domain};
% }
</TABLE>
<% scalar(@$nodes) %> Nodes.
<P>
</%method>
%#
%# show_age() - Show results from IP age inventory
%#
<%method show_age>
<H4>Results</H4>
% unless (scalar @$ips) {
No Results Found.
% return; }
<TABLE WIDTH="100%" BORDER=0 CELLSPACING=0>
    <TR>
        <TH>Node</TH>
        <TH>Host Name</TH>
        <TH>Last Used</TH>
        <TH>First<BR>Discovered</TH>
    </TR>
<%perl>
my @rows;
if ($arg_sort eq 'Age'){
    @rows = sort { $a->{time_last} <=> $b->{time_last} } @$ips;
} else {
    @rows = sort sort_ip @$ips;
}

# Truncate the list if too long (too many results)
if (scalar @rows > $arg_max) {
    splice(@rows,$arg_max);
    $m->out("Found ".scalar(@$ips) ." matching IPs.  Only showing $arg_max.<BR>\n");
}
foreach my $row (@rows){
    my $ip = $row->{ip};
    my $dns = $row->{dns} || '&nbsp;';
    $dns =~ s/\Q$domain\E//;
    my $age = $m->comp('device_inv.html:.trim_age', age => $row->{age});
    my $time_first = scalar(localtime($row->{time_first}));
    my $active = $row->{active};
</%perl>
<TR CLASS="match-<%++$odd%2%>">
    <TD><A HREF="node.html?node=<%$ip|u%><%!$active ? '&archive=1' : ''%>"><%$ip%></A><%!$active ? '*' : ''%></TD>
    <TD><A HREF="node.html?node=<%$dns|u%><%!$active ? '&archive=1' : ''%>"><%$dns%></A><%!$active ? '*' : ''%></TD>
    <TD><%$age%><%!$active ? '*' : ''%></TD>
    <TD><%$time_first%></TD>
</TR>
%}
</TABLE>
<% scalar @rows %> IPs Shown.
<BR><B>*</B> denotes archived data (not currently in use, but seen).
</%method>
<%method title>
- Node Inventory \
</%method>
%# $Id: ip_search.html,v 1.6 2004/11/08 18:07:08 maxbaker Exp $
%# vim:syntax=mason
