<h1 class="pagehead">Device Inventory</h1>
<A class="navbutton" HREF="#age">By Age</a>
<A class="navbutton" HREF="#model">By Model</a>
<A class="navbutton" HREF="#os">By OS</a>
<A class="navbutton" HREF="#name">By Name</a>
<A class="navbutton" HREF="#location">By Location</a>
<A class="navbutton" HREF="#ssid">Wireless SSID</a>
<A class="navbutton" HREF="#vlan">VLAN</a>
<h2 class="subheader"><A NAME="age">By Age</A></h2>
% $m->comp('SELF:show_age') if $cmd eq 'age';
<TABLE CLASS="inv-main">
<TR>
    <TH>Find Devices:</TH>
    <TD>
    <FORM ACTION="<%$r->uri%>" METHOD=GET>
    <DIV>
    <INPUT TYPE="hidden" NAME="cmd" VALUE="age">
    <SELECT NAME="age_type">
        <OPTION VALUE="first"<%$age_type eq 'first' ? ' SELECTED' : ''%>>First Discovered
        <OPTION VALUE="last"<%$age_type eq 'last' ? ' SELECTED' : ''%>>Last Updated
    </SELECT>
    <SELECT NAME="age_bool">
% for ('in the last','not in'){
        <OPTION VALUE="<%$_ | h %>"<%$age_bool eq $_ ? ' SELECTED' : ''%>><%$_%>
% }
    </SELECT>
    <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>
    <INPUT TYPE="submit" VALUE="Search" CLASS="navbutton">
    </DIV>
    </FORM>
    </TD>
</TR>
<TR>
    <TH>Find Devices:</TH>
    <TD>
    <FORM ACTION="<%$r->uri%>" METHOD=GET>
    <DIV>
    <INPUT TYPE="hidden" NAME="cmd" VALUE="age">
    <INPUT TYPE="hidden" NAME="age_type" VALUE="uptime">
    that have been up for at least
    <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>
    <INPUT TYPE="submit" VALUE="Search" CLASS="navbutton">
    </DIV>
    </FORM>
    </TD>
</TR>
</TABLE>

<h2 class="subheader"><A NAME="model">By Model</A></h2>
<TABLE CLASS="inv-cols">
<TR>
% my @m = sort {
%        # Sort by vendor then model
%        return $a->{vendor} cmp $b->{vendor} if $a->{vendor} ne $b->{vendor};
%        return lc($a->{model}) cmp lc($b->{model});
%    } @$model_count;
% $count = 0; $odd = 1;
% my $number = scalar @m;
% my $part2 = [splice(@m, int($number/2),$number)];
% my $part1 = [splice(@m, 0, int($number/2))];
% foreach my $part ($part1,$part2) {
    <TD>
        <TABLE class="inv-main">
        <TR>
            <TH>Vendor</TH>
            <TH>Model</TH>
            <TH>Count</TH>
        </TR>
<%perl>
    foreach my $row (@$part) {
        next unless defined($row);
        # Change class if we change vendor
        if ($old_vendor ne $row->{vendor}){
            $odd++;
            $old_vendor = $row->{vendor};
        }
        $count += $row->{count};
</%perl>
        <TR>
            <TD class="match-<%$odd%2%>"><A HREF="device_search.html?vendors=<%$row->{vendor}||'IS NULL'|u%>&amp;models=show&amp;os=show&amp;os_ver=show"><%$row->{vendor} || 'Unknown'|h%></A></TD>
            <TD class="match-<%$odd%2%>"><A HREF="device_search.html?vendors=<%$row->{vendor}||'IS NULL'|u%>&amp;models=<%$row->{model}||'IS NULL'|u%>&amp;boolean=and&amp;os=show&amp;os_ver=show"><%$row->{model} || 'Unknown'|h%></A></TD>
            <TD class="match-<%$odd%2%>"><%$row->{count}%></TD>
        </TR>
%   }
        </TABLE>
    </TD>
% }
</TR></TABLE>
Total: <%$count%>

<h2 class="subheader"><A NAME="os">By OS</A></h2>
<TABLE CLASS="inv-cols">
<TR>
% @m = sort {
%   # Sort by OS then Version
%   return $a->{os} cmp $b->{os} if $a->{os} ne $b->{os};
%   return $a->{os_ver} cmp $b->{os_ver};
% } @$os_count;
% $count = 0; $odd = 1;
% $number = scalar @m;
% $part2 = [splice(@m, int($number/2),$number)];
% $part1 = [splice(@m, 0, int($number/2))];
% foreach my $part ($part1,$part2) {
    <TD>
        <TABLE CLASS="inv-main">
        <TR>
            <TH>OS</TH>
            <TH>Version</TH>
            <TH>Count</TH>
        </TR>
<%perl>
    $odd = 1;
    my $old_os = '';
    foreach my $row (@$part) {
        next unless defined($row);
        if ($old_os ne $row->{os}) { 
            $odd++;
            $old_os = $row->{os};
        }
        $count += $row->{count};
        # need either os or os_ver defined
        next unless defined $row->{os} or defined $row->{os_ver};
</%perl>
        <TR>
            <TD CLASS="match-<%$odd%2%>"><A HREF="device_search.html?os=<%$row->{os}||'IS NULL'|u%>&amp;models=show&amp;vendors=show"><% $row->{os} || 'Unknown' |h%></A></TD>
            <TD CLASS="match-<%$odd%2%>"><A HREF="device_search.html?os=<%$row->{os}||'IS NULL'|u%>&amp;os_ver=<%$row->{os_ver}||'IS NULL'|u%>&amp;boolean=and&amp;models=show&amp;vendors=show"><% $row->{os_ver} || 'Unknown' |h%></a></TD>
            <TD CLASS="match-<%$odd%2%>"><% $row->{count} %></TD>
        </TR>
%   }
        </TABLE>
    </TD>
% }
</TR></TABLE>
Total: <%$count%>

<h2 class="subheader"><A NAME="name">By Name</A></h2>
% if ($show_name) {
<& SELF:show_name &>
% } else {
<A CLASS="navbutton" HREF="<%$r->uri%>?show_name=1#name">Show Inventory by Name</A>
% }

<h2 class="subheader"><A NAME="location">By Location</A></h2>
% if ($show_loc) {
<& SELF:show_loc &>
% } else {
<A CLASS="navbutton" HREF="<%$r->uri%>?show_loc=1#location">Show Inventory by Location</A>
% }

<h2 class="subheader"><A NAME="ssid">Wireless SSID Inventory</A></h2>
% if ($show_ssid) {
<& SELF:show_ssid &>
% } else {
<A CLASS="navbutton" HREF="<%$r->uri%>?show_ssid=1#ssid">Show Wireless SSID Inventory</a>
% }

<h2 class="subheader"><A NAME="vlan">VLAN Inventory</A></h2>
% if ($show_vlan) {
<& SELF:show_vlan &>
% } else {
<A CLASS="navbutton" HREF="<%$r->uri%>?show_vlan=1#vlan">Show VLAN Inventory</A>
% }
<%args>
$cmd      => undef
$age_type => 'last'
$age_val  => 2
$age_mul  => 'months'
$age_bool => 'not in'
$show_name => 0
$show_loc => 0
$show_ssid => 0
$show_vlan => 0
</%args>
%#
%#
%#
<%shared>
my $age = [];
my $domain = $netdisco::CONFIG{domain};
my $odd = 1;
my ($arg_cmd,$arg_age_type,$count,$arg_show_name,$arg_show_loc);
</%shared>
%#
%#
%#
<%init>
$arg_cmd = $cmd; $arg_age_type = $age_type; $arg_show_name = $show_name; $arg_show_loc = $show_loc;
my $old_vendor = '';
my $model_count = sql_rows('device',['vendor','model','count(*)'],
                           undef,undef,'GROUP by model,vendor');

my $os_count = sql_rows('device',['os','os_ver','count(*)'],
                        undef,undef,'GROUP by os,os_ver');

if ($cmd eq 'age'){
    # prevent stuffing
    $age_val = 3 unless $age_val =~ m/^\d+$/;
    $age_mul =~ s/[^a-z ]+//g;

    my $bool = '>=';
       $bool = '<=' if $age_bool =~ /last/;

    # Default - Last Discovered
    my $col = 'age(now(),last_discover)';
    my $val = "$bool interval '$age_val $age_mul'";
    
    # First Created
    if ($age_type eq 'first'){
        $col = 'age(now(),creation)';
        $val = "$bool interval '$age_val $age_mul'";
    } 
    # Uptime
    elsif ($age_type eq 'uptime') {
        $col = 'uptime';
        my $interval = 0;
        my $mult = 60*60*24;    # 1 day in seconds
        $mult    = $mult * 30  if ($age_mul eq 'months');
        $mult    = $mult * 365 if ($age_mul eq 'years'); 
        $mult    *= 100;    # uptime is in 100ths of seconds
        $interval = $age_val * $mult;
        $val = "$bool $interval"
    }

    $age = sql_rows('device',['ip','dns','name','vendor','model','uptime',
                              'age(now(),creation) as creation_age',
                              'age(now(),last_discover) as last_discover_age'
                             ],
                    {$col => \\$val}
                   );
}
</%init>
%#
%#  show_age() -  Process by age requests
%#
<%method show_age>
<h2 class="subheader">Results</h2>
% unless (scalar @$age) {
No Results Found.
% return; }
<TABLE CLASS="inv-main">
<TR>
    <TH>Device</TH>
    <TH>IP</TH>
    <TH>Vendor - Model</TH>
    <TH><%$arg_age_type eq 'uptime' ? 'Uptime' : 'First<BR>Discovered'%></TH>
    <TH>Last<BR>Updated</TH>
</TR>
<%perl>
my @results;
if ($arg_age_type eq 'uptime') {
    @results = sort { $b->{uptime} <=> $a->{uptime} } @$age;
} else {
    @results = sort {
        return $a->{dns} cmp $b->{dns} unless (! defined($a->{dns}) or ! defined $b->{dns});
        return $a->{ip} cmp $b->{ip};
    } @$age;
}
$odd = 1; $count = 0;
foreach my $dev (@results) {
    my $dns = $dev->{dns} || 'No DNS';
    $dns =~ s/\Q$domain\E//;
    my $creation_age = $m->comp("SELF:.trim_age", age => $dev->{creation_age});
    my $last_discover_age = $m->comp("SELF:.trim_age", age => $dev->{last_discover_age});
    my $uptime = $m->comp('device.html:uptime',uptime => $dev->{uptime});
    $odd++;
</%perl>
<TR>
    <TD CLASS="match-<%$odd %2%>"><A HREF="device.html?ip=<%$dev->{ip}|u%>"><%$dns|h%></A></TD>
    <TD CLASS="match-<%$odd %2%>"><A HREF="device.html?ip=<%$dev->{ip}|u%>"><%$dev->{ip}|h%></A></TD>
    <TD CLASS="match-<%$odd %2%>"><A HREF="device_search.html?vendors=<%$dev->{vendor}||'IS NULL'|u%>&amp;models=<%$dev->{model}||'IS NULL'|u%>&amp;boolean=and"><%$dev->{vendor} || "Unknown"%> - <%$dev->{model} || "Unknown"|h%></A></TD>
    <TD CLASS="match-<%$odd %2%>"><%$arg_age_type eq 'uptime' ? $uptime : $creation_age|h%></TD>
    <TD CLASS="match-<%$odd %2%>"><%$last_discover_age|h%></TD>
</TR>
% }
</TABLE>

Found <%scalar @$age%> devices.
</%method>
%#
%# .trim_age(age=>string) - Trim postgres ugly age string
%#
<%method .trim_age>
    <%args>
        $age => ''
    </%args>
    <%perl>
        $age =~ s/mon/month/;
        if ($age =~ /(day|month|year)/){
            $age =~ s/(-)?\d{2}:\d{2}.*$//;
        } else {
            $age =~ s/(\d{2}:\d{2}):\d{2}(\.\d*)$/$1/;
        }
        return $age;
    </%perl>
</%method>
%#
%# show_ssid() - Inventory based on SSID
%#
<%method show_ssid>
<TABLE class="inv-main">
<TR>
    <TH>SSID</TH>
    <TH>Broadcast?</TH>
    <TH>Count</TH>
</TR>
<%perl>
my $ssid_count = sql_rows('device_port_ssid',['ssid','broadcast','count(ssid)'],
                        undef,undef,'group by ssid,broadcast');
$count = 0; $odd = 1; my $old_loc = '';
foreach my $row (sort { $b->{count} <=> $a->{count} } @$ssid_count) {
$odd++;
</%perl>
<TR>
    <TD CLASS="match-<%$odd%2%>"><A HREF="device_search.html?ssid=<%$row->{ssid}|u%>&amp;specific=SSID&amp;exact=1" TITLE="All devices with this SSID"><%$row->{ssid}|h%></A></TD>
    <TD CLASS="match-<%$odd%2%>"><%$row->{broadcast} ? "Broadcast" : "Stealth"%></TD>
    <TD CLASS="match-<%$odd%2%>"><%$row->{count}%></TD>
</TR>
% } 
</TABLE>
<A HREF="<%$r->uri%>" CLASS="navbutton">Hide</A>
</%method>
%#
%# show_vlan() - Inventory based on VLAN
%#
<%method show_vlan>
<%perl>
my $vlan_count = sql_rows('device_vlan v, device_port_vlan pv',
                 ['v.vlan','v.description','count(distinct pv.ip) as dcount',
                    'count(pv.vlan) as pcount'],
                 { 'v.vlan' => \'pv.vlan',
                   'v.ip' => \'pv.ip'},
                 undef,'group by v.vlan,v.description');
my @m = sort { 
        $a->{vlan} <=> $b->{vlan} || 
        $a->{description} cmp $b->{description} 
    } @$vlan_count;
$count = 0; $odd = 1; my $old_vlan = -1;
</%perl>
<TABLE CLASS="inv-cols">
<TR>
% my $total = scalar @m;                     # Number of elements
% my $m_half = int($total / 2) + $total % 2; # Half the number, rounded (not truncated)
% foreach my $h (0,1) {
    <TD>
        <TABLE CLASS="inv-main">
        <TR>
            <TH>VLAN ID</TH>
            <TH>VLAN Name</TH>
            <TH>Device Count</TH>
            <TH>Port Count</TH>
        </TR>
% my $start = $m_half * $h;              # Start is: 0 for first column, half way for second
% my $end = ($h ? $total-1 : $m_half-1); # End is: half way - 1 for first column,  last element (zero based, so number of elements - 1) for second column

% foreach my $row (@m[$start .. $end]) {
%    # Change color if we change VLANs
%    if ($row->{vlan} != $old_vlan) {
%        $old_vlan = $row->{vlan};
%        $odd++;
%    }
        <TR CLASS="match-<%$odd%2%>">
            <TD><A HREF="device_search.html?specific=vlan_dev&vlan=<%$row->{vlan}%>" TITLE="All devices with this VLAN"><%$row->{vlan} |h%></A> [<A HREF="device_search.html?vlan=<%$row->{vlan}|u%>&amp;specific=vlan" TITLE="All ports with this vlan">ports</A>]</TD>
            <TD><%$row->{description}%></TD>
            <TD><%$row->{dcount}%></TD>
            <TD><%$row->{pcount}%></TD>
        </TR>
% }
        </TABLE>
    </TD>
% }
</TR></TABLE>
Total: <%scalar @m%>

<A HREF="<%$r->uri%>" CLASS="navbutton">Hide</A>
</%method>
%#
%# show_name() - Sorts and shows by name
%#
<%method show_name>
<TABLE class="inv-main">
<TR>
    <TH>sysName</TH>
    <TH>Location</TH>
    <TH>IP Address</TH>
    <TH>Vendor</TH>
    <TH>Model</TH>
</TR>
<%perl>
my $name_count = sql_rows('device',['location','dns','name','ip','vendor','model'],
                        undef,undef,undef);
$count = 0; $odd = 0; my $old_name = '';
foreach my $row (sort
    {
        # Sort by Name => Location => IP Address => Vendor => Model
        return $a->{name}     cmp $b->{name}     if $a->{name}     ne $b->{name};
        return $a->{location} cmp $b->{location} if $a->{location} ne $b->{location};
        return $a->{ip}       cmp $b->{ip}       if $a->{ip}       ne $b->{ip};
        return $a->{vendor}   cmp $b->{vendor}   if $a->{vendor}   ne $b->{vendor};
        return $a->{model}    cmp $b->{model}    if $a->{model}    ne $b->{model};
    } @$name_count
                ) {

        $count += $row->{count};
        $odd++ if $row->{name} ne $old_name;
        $old_name = $row->{name};
        my $ip = $row->{ip};
</%perl>
<TR>
    <TD CLASS="match-<%$odd%2%>"><A HREF="device.html?ip=<%$row->{ip}|u%>" TITLE="Click to View Device"><%$row->{name}|h%></A></TD>
    <TD CLASS="match-<%$odd%2%>"><A HREF="device_search.html?loc=<%$row->{location}||'IS NULL'|u%>&amp;e
xact=1" TITLE="All devices at this location"><%$row->{location} || '[Not Set]'%></A></TD>
    <TD CLASS="match-<%$odd%2%>"><A HREF="device.html?ip=<%$row->{ip}|u%>" TITLE="Click to View Device"><%$ip|h%></A></TD>
    <TD CLASS="match-<%$odd%2%>"><A HREF="device_search.html?vendors=<%$row->{vendor}||'IS NULL'|u%>" TITLE="All devices from this vendor"><%$row->{vendor} || "Unknown"|h%></A></TD>
    <TD CLASS="match-<%$odd%2%>"><A HREF="device_search.html?vendors=<%$row->{vendor}||'IS NULL'|u%>&amp;models=<%$row->{model}||'IS NULL'|u%>&amp;boolean=and" TITLE="All devices of this model"><%$row->{model} || "Unknown"|h%></A></TD>
</TR>
%                  } $odd++;
<TR>
    <TD CLASS="match-<%$odd%2%>" COLSPAN=3>&nbsp;</TD>
    <TD CLASS="match-<%$odd%2%>">Total:</TD>
    <TD CLASS="match-<%$odd%2%>"><%scalar(@$name_count)%></TD>
</TR>
</TABLE>
<A HREF="<%$r->uri%>" CLASS="navbutton">Hide</A>
</%method>
%#
%# show_loc() - Sorts and shows by location
%#
<%method show_loc>
<TABLE class="inv-main">
<TR>
    <TH>Location</TH>
    <TH>IP Address</TH>
    <TH>sysName</TH>
    <TH>Vendor</TH>
    <TH>Model</TH>
</TR>
<%perl>
my $location_count = sql_rows('device',['location','dns','name','ip','vendor','model'],
                        undef,undef,undef);
$count = 0; $odd = 0; my $old_loc = '';
foreach my $row (sort 
    {
        # Sort by Location => Name => IP Address => Vendor => Model
        return $a->{location} cmp $b->{location} if $a->{location} ne $b->{location};
        return $a->{name}     cmp $b->{name}     if $a->{name}     ne $b->{name};
        return $a->{ip}       cmp $b->{ip}       if $a->{ip}       ne $b->{ip};
        return $a->{vendor}   cmp $b->{vendor}   if $a->{vendor}   ne $b->{vendor};
        return $a->{model}    cmp $b->{model}    if $a->{model}    ne $b->{model};
    } @$location_count
                ) {

        $count += $row->{count};
        $odd++ if $row->{location} ne $old_loc;
        $old_loc = $row->{location};
        my $dns = $row->{dns} || $row->{ip};
        $dns =~ s/\Q$domain\E//;
</%perl>
<TR>
    <TD CLASS="match-<%$odd%2%>"><A HREF="device_search.html?loc=<%$row->{location}||'IS NULL'|u%>&amp;e
xact=1" TITLE="All devices at this location"><%$row->{location} || '[Not Set]'%></A></TD>
    <TD CLASS="match-<%$odd%2%>"><A HREF="device.html?ip=<%$row->{ip}|u%>" TITLE="Click to View Device"><%$dns|h%></A></TD>
    <TD CLASS="match-<%$odd%2%>"><A HREF="device.html?ip=<%$row->{ip}|u%>" TITLE="Click to View Device"><%$row->{name}|h%></A></TD>
    <TD CLASS="match-<%$odd%2%>"><A HREF="device_search.html?vendors=<%$row->{vendor}||'IS NULL'|u%>" TITLE="All devices from this vendor"><%$row->{vendor} || "Unknown"|h%></A></TD>
    <TD CLASS="match-<%$odd%2%>"><A HREF="device_search.html?vendors=<%$row->{vendor}||'IS NULL'|u%>&amp;models=<%$row->{model}||'IS NULL'|u%>&amp;boolean=and" TITLE="All devices of this model"><%$row->{model} || "Unknown"|h%></A></TD>
</TR>
%                  } $odd++;
<TR>
    <TD CLASS="match-<%$odd%2%>" COLSPAN=3>&nbsp;</TD>
    <TD CLASS="match-<%$odd%2%>">Total:</TD>
    <TD CLASS="match-<%$odd%2%>"><%scalar(@$location_count)%></TD>
</TR>
</TABLE>
<A HREF="<%$r->uri%>" CLASS="navbutton">Hide</A>
</%method>
%#
%# title()
%#
<%method title>
- Device Inventory \
</%method>
%# $Id$
%# vim:syntax=mason
