<h1 class="pagehead">Administration Panel</h1>
<& SELF:err &>
% if ($need_confirm) {
<& SELF:confirm &>
% } else {
<& SELF:dev_control &>
% }
<& SELF:list_queue &>

<FORM ACTION="<% $r->uri %>" METHOD=GET>
<DIV>
<INPUT TYPE="hidden" VALUE="list" NAME="cmd">
<TABLE CLASS="adm-search">
<TR>
    <TH>Finished Jobs:</TH>
    <TD><SELECT NAME="old_jobs">
% foreach my $num (qw/1 3 5 10 20 30 50 100/) {
        <OPTION VALUE="<%$num%>"<% $num eq $old_jobs ? ' SELECTED': ''%>><%$num%>
% }
        </SELECT>
    </TD>
</TR>
<TR>
    <TH>Ignore Actions<BR>of Type:</TH>
    <TD>
        <SELECT NAME="act_ignore" SIZE=3 MULTIPLE>
% foreach my $act (sort @$action_types) {
            <OPTION VALUE="<%$act|h%>"<% grep(/^$act$/,@act_ignore) ? ' SELECTED' : ''%>><%$act%>
% }
        </SELECT>
    </TD>
</TR>
<TR>
    <TH>&nbsp;</TH>
    <TD><INPUT TYPE="submit" CLASS="navbutton" VALUE="Refresh Queue Listing">
        <A CLASS="navbutton" HREF="<%$r->uri%>">Reset</a>
    </TD>
</TR>
</TABLE>
</DIV>
</FORM>
<%args>
$dev      => undef
$dev_text => undef
$discover => undef
$cmd      => 'add'
$debug    => ''
$action   => ''
@logs     => ()
$nodes    => ''
$confirm  => ''
$old_jobs => ''
$port     => ''
@act_ignore => ()
</%args>
<%shared>
my $user      = $m->session->{user};
my $domain    = $netdisco::CONFIG{domain};
my %actions = (
            # action       => [longname, confirm action,show]
             'discover'    => ['Discover Device',0,0],
             'discover_run'=> ['Discover Whole Network Starting from Device',1,0],
             'discover_new'=> ['Discover New Parts of Network from Device',1,0],
             'arpnip'      => ['ArpNip Device',0,1],
             'delete'      => ['Delete Device',1,1],
             'macsuck'     => ['MacSuck Device',0,1],
             'nodes-del'   => ['Delete Nodes',1,1],
             'nodes-arc'   => ['Archive Nodes',1,1],
             'refresh'     => ['Refresh Device',0,1],
             'expire_ips'  => ['Database Cleanup - Expire IPs [-I]',1,0],
             'graph'       => ['Regenerate Graph',1,0],
             'arpwalk'     => ['ArpNip whole network',1,0],
             'macwalk'     => ['MacSuck whole network',1,0],
             'nbtwalk'     => ['NBTWalk whole network',1,0],
             'backup'      => ['Run Nightly Backup',1,0],
             'clean_nodes' => ['Database Cleanup - Nodes [-K]',1,0],
             'clean_alias' => ['Database Cleanup - Aliases [-k]',1,0]
           );
# Globals
my ($arg_dev,$arg_debug,$db_dev,$arg_act,$devices,$queue,$subaction,$action_types,$arg_dev_text,$arg_discover,$arg_cmd);
my $arg_jobs;
my $need_confirm = 0; my $commit = 0; my $odd = 0;
my %logs = ();
my $err =  '';
my @act_ignore_default = qw/portcontrol test/;
my @arg_ignore;
</%shared>
%#
%#
<%init>
# Enforce secure + admin
$m->comp('admin.html:force_secure_admin');

# Move args to shared space for methods
$arg_dev = $dev; $arg_act = $action;
$arg_cmd = $cmd; @arg_ignore = @act_ignore;
$arg_jobs = $old_jobs;
$arg_debug = $debug; $arg_discover = $discover;
@act_ignore = @act_ignore_default unless scalar @act_ignore ;
%logs = map {$_ => 1} @logs;

my @devs;

#..................... Device Control .............................

# Load dev from db if given
if ($dev){
    if(ref($dev) eq "ARRAY"){
        for my $i (@$dev){
            my $dip=getip($i);
            push @devs,sql_hash('device',['ip','dns','layers'],{'ip'=>$dip}) if $dip;
        }
    } else {
        $dev = getip($dev);
        my $dns = hostname($dev);
        $dns =~ s/\Q$domain\E//;
        $arg_dev_text = $dns if (defined $dev and defined $dns);
        $db_dev = sql_hash('device',['ip','dns','layers'],{'ip'=>$dev}) if $dev;
    }
} elsif ($dev_text){
    # Resolve user entered device name to device_ip
    $dev=getip($dev_text);
    $arg_dev = $dev;
    $arg_dev_text = $dev_text if defined $dev;
    $db_dev = sql_hash('device',['ip','dns','layers'],{'ip'=>$dev}) if $dev;
}

# Confirm Given Action if required
if ($cmd eq 'add' and $actions{$action}->[1] and $confirm ne 'ok') {
    # Selecting multiple devices currently not supported on commands needing confirmation
    if (@devs) {
        $err .= "Selecting multiple devices not supported with $actions{$action}->[0] action.<BR>\n";
        $action = undef;
        $arg_dev = $dev = undef;
    } else {
        $need_confirm++;
    }
}

# Maintenance Commands from admin.html
if ($cmd eq 'add' and $action =~ /^(expire_ips|arpwalk|macwalk|nbtwalk|graph|backup|clean_nodes|clean_alias)$/ and !$need_confirm){
    $dev = undef;
    $commit++;
}

# Refresh / MacSuck / Arpnip
if ($cmd eq 'add' and $action =~ /^(refresh|macsuck|arpnip)$/ and !$need_confirm){
    if(@devs){
        my @devs_checked;
        for my $i (@devs){
            my $layers = $i->{layers};
    
            if ($action eq 'macsuck' and !&netdisco::has_layer($layers,2) ){
                $err .= sprintf("Device %s does not support mac_sucking, skipping.<BR>\n", $i->{ip});
            } elsif ($action eq 'arpnip' and !&netdisco::has_layer($layers,3) ){
                $err .= sprintf("Device %s does not support arp_niping, skipping.<BR>\n", $i->{ip});
            } else {
                push(@devs_checked, $i);
            }
        }
        # Add Entries to Queue
        @devs = @devs_checked;
        $commit += scalar @devs;
        @devs = $arg_dev = $dev = undef unless $commit;
    } else {
        # Verify device exists.
        unless (defined $db_dev->{ip}){
            $err .= "Device ($dev) not found in Database.<BR>\n";
            $action = undef;
            $arg_dev = $dev = undef;
        }
    
        # Verify macsuck/arpnip is possible on device.
        my $layers = $db_dev->{layers};
    
        if ($action eq 'macsuck' and !&netdisco::has_layer($layers,2) ){
            $err .= "Device does not support mac_sucking.<BR>\n";
            $action = undef;
            $arg_dev = $dev = undef;
        }
    
        if ($action eq 'arpnip' and !&netdisco::has_layer($layers,3) ){
            $err .= "Device does not support arp_niping.<BR>\n";
            $action = undef;
            $arg_dev = $dev = undef;
        }
        # Add Entry to Queue
        $commit++ unless $err;
    }
}

# Delete
if ($cmd eq 'add' and $action eq 'delete' and $confirm eq 'ok'){
    # Verify device exists.
    unless (defined $db_dev->{ip}){
        $err .= "Device not found in Database.<BR>\n";
        $action = undef;
    }
    # Add to queue.
    unless ($err) {
        $err .= "Device $dev marked for deletion.<BR>\n";
        $action = $nodes eq 'ok' ? 'delete+nodes' : 'delete';
        $commit++;
    }
}

# Archive/Delete Nodes
if ($cmd eq 'add' and $action =~ /^nodes/ and $confirm eq 'ok'){
    # Verify device exists.
    unless (defined $db_dev->{ip}){
        $err .= "Device ($dev) not found in Database.<BR>\n";
        $action = undef;
        $arg_dev = $dev = undef;
    }
    # Port Only
    if ($port !~ /^\s*$/){
        my $port = sql_hash('device_port',['port'],{'ip'=>$dev,'port'=>$port});
        unless (defined $port and $port->{port}){
            $err .= "$actions{$action}->[0] - $dev - Port $port not found!<BR>\n";
            $action = undef;
            $arg_dev = $dev = undef;
        } else {
            # Restict to Port
           $subaction = $port->{port};
        }
    }

    $commit++ unless $err;
}

# Discover Devices
if ($cmd eq 'add' and $action =~ /^(discover|discover_new|discover_run)$/ and !$need_confirm){
    # Check input
    unless (defined $discover and length($discover)){
        $err .= "Please enter the device IP or DNS name to discover.<BR>\n";
        $discover = $discover = undef;
    } else {
        $subaction = $discover;
        $dev = undef;
        $discover = $discover = undef;
        $commit++;
    }
}

# Add to Queue
if ($commit) {
    if(@devs){
        for my $i (@devs){
            next unless defined $i;
            $err .= "Action Queued - $action";
            $err .= " For Device $i->{ip}.";
            $err .= "<BR>\n";
            insert_or_update('admin',{},
                         {'device'=>$i->{ip},'action'=>$action, 'status' => 'queued',
                          'username' => $user, 'debug' => ($debug eq 'yes' ? 1 : 0),
                          'subaction' => $subaction }
                        );
        }
    } else {
        $err .= "Action Queued - $action";
        $err .= " For Device $dev." if $dev;
        $err .= "<BR>\n";
        insert_or_update('admin',{},
                         {'device'=>$dev,'action'=>$action, 'status' => 'queued',
                          'username' => $user, 'debug' => ($debug eq 'yes' ? 1 : 0),
                          'subaction' => $subaction }
                        );
    }
    @devs=undef;
    $action = undef;
    $arg_dev = $dev = undef;
}

#............ Job Queue ............

# job queue is after device control in case we just added to it.

# Load device list, unless device provided.
$devices = sql_rows('device',['ip','dns'], undef, undef,'order by ip asc') if not defined $arg_dev;

# Taint check (head between legs)
$old_jobs = 5 unless ($old_jobs =~ /^\d+$/);

# Fetch action types - Changed to static values, be sure to add new ones here
$action_types = ['arpnip', 'arpwalk', 'delete', 'delete+nodes', 'discover', 'graph', 'macsuck', 'macwalk', 'nbtwalk', 'portcontrol', 'refresh'];

# Create NOT IN list if we are ignoring certain types.
my $where = {};
my @ignore = @act_ignore;
$ignore[0] = "!$ignore[0]" if scalar @ignore;
$where->{action} = [\@ignore] if scalar @ignore;

# Fetch running jobs
$where->{status} = [['queued','running']];
$queue = sql_rows('admin',['job','extract(epoch from entered) as entered','extract(epoch from finished) as finished',
                           'extract(epoch from started) as started','device','action','status','username'],
                  $where);

# Fetch done jobs.
$where->{status} = [['done','error']];
my $done = sql_rows('admin',['job','extract(epoch from entered) as entered','extract(epoch from finished) as finished',
                             'extract(epoch from started) as started','device','action','status','username'],
                    $where,undef,"order by admin.entered desc limit $old_jobs");
push (@$queue,@$done);

# Log entries are fetched separately, as these can be rather large, and should
#  only be loaded if needed.
foreach my $item (@$queue){
    my $id = $item->{job};
    if (defined $logs{$id}){
        my $log = sql_scalar('admin',['log'],{'job'=>$id});
        $item->{log} = $log;
    }
}

</%init>
%#
%# err() - Prints out the error string if it exists.
%#
<%method err>
% return unless $err;
<h1 class="pagehead"><%$err%></h1>
</%method>
%#
%# confirm() - Creates a confirmation page for certain actions
%#
<%method confirm>
% my $act = $actions{$arg_act}->[0];
<h1 class="pagehead">Confirm <%$act%> <% $arg_dev ? "of $arg_dev" : ''%></h1>
<FORM ACTION="<% $r->uri %>" METHOD=POST>
<DIV>
<INPUT TYPE="HIDDEN" NAME="cmd" VALUE="add">
<INPUT TYPE="hidden" NAME="dev" VALUE="<%$arg_dev|h%>">
<INPUT TYPE="hidden" NAME="action" VALUE="<%$arg_act%>">
<INPUT TYPE="hidden" NAME="discover" VALUE="<%$arg_discover%>">
<INPUT TYPE="checkbox" NAME="confirm" VALUE="ok">
Check to Confirm - <%$act%>
% if ($arg_dev) {
 - <A HREF="device.html?ip=<%$arg_dev |u%>"><%$arg_dev%>
% }
</A>
<BR>
%# Action specific check options:
%#
%# Delete Device - Checkbox to Delete Nodes too
% if ($arg_act eq 'delete'){
<INPUT TYPE="checkbox" NAME="nodes" VALUE="ok"> Delete Nodes Too.
% }
%# Delete Nodes - Ability to select port
% if ($arg_act =~ /^nodes/) {
%   my $ports = sql_rows('device_port',['port'],{'ip'=>$arg_dev});
Restrict <%$act%> to
<SELECT NAME="port">
    <OPTION VALUE="" SELECTED>All Ports
%   foreach my $row (sort {$a->{port} cmp $b->{port}} @$ports){
%       my $port = $row->{port};
   <OPTION VALUE="<%$port|h%>"><%$port|h%>
%   }
</SELECT>
%}
<BR>
<INPUT TYPE="checkbox" NAME="debug" VALUE="yes"<%$arg_debug? ' CHECKED' : ''%>>
Debug Output
<P>
<INPUT TYPE="submit" CLASS="navbutton" VALUE="<%$act%><% $arg_dev ? "- $arg_dev" : '' %>">
</DIV>
</FORM>
</%method>
%#
%# dev_control() - Prints out the device control menu form.
%#
<%method dev_control>
<h2 class="subheader">Device Control</h2>
<FORM ACTION="<% $r->uri %>" METHOD=POST>
<DIV>
<INPUT TYPE="HIDDEN" NAME="cmd" VALUE="add">
<TABLE CLASS="adm-search">
<TR>
    <TH><% $arg_dev ? '&nbsp;' : 'Choose Device :'%></TH>
    <TD><% $arg_dev ? '&nbsp;' : $m->comp('SELF:list_dev')%></TD>
    <TH>Action:</TH>
    <TD><SELECT NAME="action" SIZE=4>
% foreach my $c (sort keys %actions){
%   next unless $c;
%   next unless $actions{$c}->[2];
            <OPTION VALUE="<%$c|h%>"><%$actions{$c}->[0]%>
% }
        </SELECT>
    </TD>
</TR>
<TR>
    <TH>Enter Device:</TH>
    <TD><INPUT TYPE="TEXT" VALUE="<%$arg_dev%>" NAME="dev_text">
        <% defined $arg_dev_text ? "<BR><span class=\"small\"><TT>($arg_dev_text)</TT></span>" : '' %>
    </TD>
    <TH>Debug:</TH>
    <TD><INPUT TYPE="checkbox" NAME="debug" VALUE="yes" <%$arg_debug ? 'CHECKED' : ''%>></TD>
</TR>
<TR>
    <TD>&nbsp;</TD>
    <TD COLSPAN=3>
        <INPUT TYPE="submit" CLASS="navbutton" VALUE="Queue New Job">
        <A CLASS="navbutton" HREF="<%$r->uri%>">Reset</A>
        <BR><SPAN CLASS="small">Use this box to control devices that are already in Netdisco's database.</SPAN>
    </TD>
</TR>
</TABLE>
</DIV>
</FORM>

<h2 class="subheader">Discover Devices</h2>
<FORM ACTION="<% $r->uri %>" METHOD=POST>
<DIV>
<INPUT TYPE="HIDDEN" NAME="cmd" VALUE="add">
<TABLE CLASS="adm-search">
<TR>
    <TH>Enter Device:</TH>
    <TD><INPUT TYPE="TEXT" VALUE="<%$arg_discover%>" NAME="discover" TITLE="IP or Host Name">
        <SELECT NAME="action" SIZE=1>
% foreach my $c (qw/discover discover_new discover_run/){
            <OPTION VALUE="<%$c|h%>"><%$actions{$c}->[0]%>
% }
        </SELECT>
    </TD>
</TR>
<TR>
    <TH>Debug:</TH>
    <TD><INPUT TYPE="checkbox" NAME="debug" VALUE="yes" <%$arg_debug ? 'CHECKED' : ''%>>
        <INPUT TYPE="submit" CLASS="navbutton" VALUE="Queue New Job">
        <A CLASS="navbutton" HREF="<%$r->uri%>">Reset</A>
        <BR>
        <SPAN CLASS="small">Use this box to add new devices to the network, or to re-add devices after deletion.</SPAN>
    </TD>
</TR>
</TABLE>
</DIV>
</FORM>
</%method>
%#
%# list_dev() - Create <SELECT> list for devices
%#
<%method list_dev>
<SELECT MULTIPLE="1" NAME="dev" SIZE=8>
% foreach my $dev (sort {$a->{dns} cmp $b->{dns}} @$devices){
%   my $view = defined $dev->{dns} ? $dev->{dns} : $dev->{ip};
%   $view =~ s/\Q$domain\E//;
            <OPTION VALUE="<%$dev->{ip}|h%>"><%$view%>
%}
        </SELECT>
</%method>
%#
%# list_queue() - List jobs in admin queue
%#
<%method list_queue>
<h2 class="subheader">Job Queue</h2>
% return unless scalar @$queue;
<TABLE CLASS="adm-q">
    <TR>
        <TH>Job</TH>
        <TH>Action</TH>
        <TH>Time</TH>
        <TH>Status</TH>
        <TH>View<BR>Log</TH>
% if (scalar keys %logs){
        <TH>Log</TH>
%}
    </TR>
<%perl>
$odd = 1;
foreach my $job (sort {$b->{entered} <=> $a->{entered} } @$queue){
    my $act    = $job->{action};
    my $entered = scalar localtime($job->{entered});
    my $start   = defined $job->{started} ? scalar localtime($job->{started}) : undef;
    my $end   = defined $job->{finished} ? scalar localtime($job->{finished}) : undef;
    my $time  = (defined $job->{started} and defined $job->{finished}) ?  $job->{finished} - $job->{started} : undef;
    my $timestamp = defined $time ?  sprintf("Ended: %s<BR> %-2.2f min.",$end,$time/60) : undef;
    $timestamp = "Start: $start" if (! defined $timestamp and defined $start);
    $timestamp = "Queued: $entered" if (! defined $timestamp and ! defined $start);
    my $status = $job->{status};
    my $dev    = $job->{device};
    my $id     = $job->{job};
    $act .= " (<A HREF=\"device.html?ip=$dev\">$dev</A>)" if $dev;
    $odd++;
    # TODO: these should probably be url_escape()'d
    my $args = '';
    $args .= "&cmd=$arg_cmd" if $arg_cmd ne 'add';
    $args .= "&old_jobs=$arg_jobs" if $arg_jobs;
    foreach my $i (@arg_ignore) {
        $args .= "&act_ignore=$i";
    }
    my $log_id = defined $logs{$id} ? '' : $id;
    my $img    = defined $logs{$id} ? "icon_down.gif" : "icon_note.gif";
</%perl>
    <TR>
        <TD CLASS="match-<%$odd%2%>"><%$id%> - <%$job->{username}%></TD>
        <TD CLASS="match-<%$odd%2%>"><%$act%></TD>
        <TD CLASS="match-<%$odd%2%>"><%$timestamp%></TD>
        <TD CLASS="match-<%$odd%2%>"><%$status%></TD>
        <TD CLASS="match-<%$odd%2%>">
            <A HREF="<%$r->uri%>?logs=<%$log_id|u%><%$args%>"><IMG SRC="<%$img%>" class="log"></A>
        </TD>
% if (scalar keys %logs) {
        <TD CLASS="log-<%$odd%2%>">
%   if (defined $logs{$id} and !defined $job->{log}) {
            [ No Log Available ]
%   } elsif (defined $logs{$id} and defined $job->{log}) {
            <PRE CLASS="log"><%$job->{log}%></PRE>
%   } else {
            &nbsp;
%   }
        </TD>
% }
    </TR>
% }
</TABLE>
</%method>
<%method title>
- Admin Panel \
</%method>
%# $Id$
%# vim:syntax=mason
