# -*-perl-*-

# $Id$

# Return a structure of common sushi status information

# $Log$
# Revision 1.8  2010/03/22 23:32:31  koczan
# add type count to status
#
# Revision 1.7  2009/12/21 19:45:14  koczan
# make status go faster
#
# Revision 1.6  2008/11/24 20:41:16  koczan
# made status report consistent with how sushi behaves
#
# Revision 1.5  2003/06/09 14:45:11  thomas
# schema version of database
#
# Revision 1.4  2003/06/04 17:30:53  thomas
# *** empty log message ***
#
# Revision 1.3  2002/08/14 17:13:07  thomas
# 0.2.9 release
#
# Revision 1.2  2002/07/23 16:02:28  thomas
# version 0.2.5
#
# Revision 1.1  2002/07/10 14:01:29  thomas
# sushi-0.2.4
#

package Sushi::Status;

use strict;
use Time::localtime;
use Time::Local;
use Data::Dumper;

###############################################################################

sub status {
  my $args = shift;

  my $status = {};

  # Get group name
  if (!defined $args->{schedule_group}) {
    $args->{schedule_group} = Sushi::Misc::dailyGroup ();
  }

  $status->{schedule_group} = $args->{schedule_group};

  defined $args->{daemonStatus} and getDaemonStatus ($args, $status);
  defined $args->{workerStatus} and getWorkerStatus ($args, $status);
  defined $args->{groupStatus}  and getGroupStatus  ($args, $status);

  return $status;

}

###############################################################################

sub getDaemonStatus {
  my $args = shift;
  my $status = shift;

  # Get sushid info
  (
   $status->{sushid_host},
   $status->{sushid_pid},
   $status->{sushid_start_time}
  ) = $::sudbh->selectrow_array ("select host, pid, start_time from sushid_lock");

  if (!defined $status->{sushid_host}) {
    delete $status->{sushid_host};
    delete $status->{sushid_pid};
    return;
  } else {
    $status->{sushid_start_time} =
      Sushi::Misc::date2secs ($status->{sushid_start_time});
  }

  # last daemon log message
  my $timestamp = timelocal (0,
			     0,
			     0,
			     localtime->mday(),
			     localtime->mon(),
			     localtime->year()
			    );

  my $datetime = $::sudbh->quote(Sushi::Misc::secs2utc($timestamp));
  my $sth = $::sudbh->prepare("select max(timestamp) as lastlog from backend_log where (host, pid) = (select host, pid from sushid_lock) and timestamp > $datetime");
  $sth->execute();
  my $row = $sth->fetchrow_hashref;
  $status->{sushid_last_log_msg} = Sushi::Misc::date2secs ($row->{lastlog}) || 0;
  $sth->finish;
}

###############################################################################

sub getWorkerStatus {
  my $args = shift;
  my $status = shift;

  my $workers = {};
  my ($worker);
  $status->{workers} = $workers;

  # Try this a new way...
  foreach $worker (@{Sushi::GenDB::fetch (
					  { resource_alias => '@Worker@' },
					  {},
					  $::table_defs->{res_alias}
					 )}) {
    $workers->{$worker->{resource_name}} = 0;
  }

  # let's speed this up just a bit
  my $timestamp = timelocal (0,
			     0,
			     0,
			     localtime->mday(),
			     localtime->mon(),
			     localtime->year()
			    );

  my $datetime = $::sudbh->quote(Sushi::Misc::secs2utc($timestamp));
  my $sth = $::sudbh->prepare("select host, max(timestamp) as lastlog from backend_log where timestamp > $datetime group by host");
  $sth->execute();

  while (my $row = $sth->fetchrow_hashref) {
    $workers->{$row->{host}} = Sushi::Misc::date2secs ($row->{lastlog})
      if defined $workers->{$row->{host}};
  }

  $sth->finish;
}


###############################################################################

sub getGroupStatus {
  my $args = shift;
  my $status = shift;

  my $atom;
  my $atoms = {};
  foreach $atom (@{Sushi::Atom->readAtoms ( schedule_group => $args->{schedule_group} )}) {
    $atoms->{$atom->{id}} = $atom;
  }

  # Get information about first and last result time...
  my $res;
  foreach $res (@{Sushi::GenDB::fetch (
				       {
					schedule_group => $args->{schedule_group}
				       },
				       {},
				       $::table_defs->{view_atom_results}
				      )}) {
    # Set global first and last result times
    (!defined $status->{group_firststart} ||
     ($status->{group_firststart} > $res->{timestamp})) and
       $status->{group_firststart} = $res->{timestamp};
    (!defined $status->{group_laststart} ||
     ($status->{group_laststart} < $res->{timestamp})) and
       $status->{group_laststart} = $res->{timestamp};
    (!defined $status->{group_lastdone} ||
     ($res->{runtime} && ($status->{group_lastdone} < ($res->{timestamp} + $res->{runtime})))) and
       $status->{group_lastdone} = ($res->{timestamp} + $res->{runtime});

    # Set last result for each atom, except in cases of "Success"...
    # sushi always favors the first "Success" for restores and reschedules
    # Even if there was a later failure, success conquers all
    $atom = $atoms->{$res->{id}};
    if (!defined $atom->{result} || $atom->{timestamp} < $res->{timestamp}) {
      ($atom->{timestamp}, $atom->{result}) = ($res->{timestamp}, $res->{result}) if ($atom->{result} ne "Success");
    }
  }

  # Now, run though data and determine states and types
  my $id;
  my $state = {
	       Pending   => 0,
	       Scheduled => 0,
	       Active    => 0,
	       Success   => 0,
	       Fail      => 0,
	       Unknown   => 0
	      };

  # There are other types, but these are ones we want to have show up
  my $type = {
    Afs      => 0,
    LinuxTar => 0,
    Psql     => 0,
    Psql_all => 0,
    Sol      => 0,
  };

  foreach $id (keys %$atoms) {
    $atom = $atoms->{$id};
    if ($atom->{state} eq 'Pending'   ||
	$atom->{state} eq 'Scheduled' ||
	$atom->{state} eq 'Active') {
      $state->{$atom->{state}}++;
    } elsif ($atom->{state} eq 'Done') {
      if ($atom->{result} eq 'Success') {
	$state->{Success}++;
      } else {
	$state->{Fail}++;
      }
    } else {
      $state->{Unknown}++;
    }

    $type->{$atom->{type}} = 0 unless defined $type->{$atom->{type}};
    $type->{$atom->{type}}++;
  }
  $status->{group_state} = $state;
  $status->{group_type} = $type;
  $status->{group_total_atoms} = scalar (keys %$atoms);
}

###############################################################################

1;
