#!/usr/bin/perl
#
#  $Id$
# -----------------------------------------------------------------------------
#  SM_SPACE - small tool for space usage stat
# -----------------------------------------------------------------------------
#  Author: Alex Titov
#  QA by:
#  Copyright: videoNEXT LLC
# -----------------------------------------------------------------------------
#
# for REQ see: Rally TA4799
# notes:
#   1. command line tool
#
# usage: 
#  sm_space inspect <uuid|name>
#  sm_space rescan  <uuid|name>
#  sm_space reload  <uuid|name>

use warnings;
use strict;
use Data::Dumper;
use File::Basename qw(dirname);    
use lib dirname(__FILE__).'/../lib';              # find  SM::Config here
use SKM::DB;
use SM::Config ':all';
use Node::Conf;


# CONS ------------------------------------------------------------------------
my $SPACE   =(split(/\//,$0))[-1];	          # the actual name of the prog
# VARS ------------------------------------------------------------------------
my $vol=SM_Wheels(); #configs of active volumes ex: $vol->{$uuid}->{LIMIT_WRITE}
my $dbm;                                        # master db handler
my %dbs;                                        # statements
my $NODEID=UNI;                                 # char22 nodeid in domain
my $SM_VER  =SM_VER;                            # ex va-2.7.1

#print Dumper($vol);

my $head_long ="Volume-Id/uuid                        Label    Configured Status     Size GB Free GB[%%]  W-Limit W-now  await util\n"
              ."------------------------------------ --------  ---------- ---------  ------- -----------  ------- ------ ----- -----\n";
my $tail_long ="------------------------------------ --------  ---------- ---------  ------- -----------  ------- ------ ----- -----\n"
              ."Total:                                                                %4d  %4d [%2d%%]           %6.2f\n";
my $form_long ="%-36.36s %-8.8s   %-8.8s  %-8.8s    %4d  %4d [%2d%%]  %6d   %6.6s  %5.5s %5.5s\n";

my $head_short="shortID   CST    Status   Size GB Free GB[%%] Wlim W-now  await util\n"
              ."-------- ------- -------- ------- ----------- ---- ------ ----- -----\n";
my $tail_short="-------- ------- -------- ------- ----------- ---- ------ ----- -----\n"
              ."Total:                       %4d  %4d [%2d%%]      %6.2f\n";
my $form_short="%-8.8s %-0.0s%-7.7s %-8.8s    %4d  %4d [%2d%%] %4d %6.6s %5.5s %5.5s\n";


# SUBS ------------------------------------------------------------------------

#--------------------------------------------------
# $manage DB staff
#--------------------------------------------------

sub db_master{
 eval { $dbm->disconnect() if $dbm; $dbm=''; }; # disconnect if defined
 for (my $i=1;$i<6;$i++) {
   eval {
     $dbm=DBMaster({PrintError=>1,'RaiseError' => 1});
     $dbs{INSERT_SPREAD}=$dbm->prepare("insert into SM_SPACE_SPREAD(NODEID,ID,OBJID,DAY,SPACE) values('$NODEID',?,?,?,?)")
                       or die "Cannot prepare INSERT_SPREAD";
     $dbs{DELETE_SPREAD}=$dbm->prepare('delete from SM_SPACE_SPREAD where ID=? and OBJID=? and DAY=?')
                       or die "Cannot prepare DELETE_SPREAD";
     $dbs{INSERT_SPACE}=$dbm->prepare('insert into SM_SPACE(NODEID,OBJID,SPACE) '
                                     ."select '$NODEID',objid,sum(space) from sm_space_spread "
                                     ."where  NODEID='$NODEID' group by objid")
                       or die "Cannot prepare INSERT_SPACE";
     $dbs{DELETE_SPACE}=$dbm->prepare("delete from SM_SPACE where NODEID='$NODEID' and SPACE!=0")
                       or die "Cannot prepare DELETE_SPACE";
     $dbs{DELETE_ZERRO}=$dbm->prepare("delete from SM_SPACE s where NODEID='$NODEID' and SPACE=0 "
                                     ." and  exists (select space from SM_SPACE_SPREAD p  where s.nodeid=p.nodeid and s.objid=p.objid)")
                       or die "Cannot prepare DELETE_ZERRO";
   };
   if($@) {
     die ("Attempt $i (final). Cannot connect to master: $@") if $i>=5;
     print STDERR "Attempt $i. Cannot connect to master: $@";
     SM_error('',  "Cannot connect to master:$@");
     sleep($i*10);
   } else {
     last;                                   # exit cycle if OK
   }
 }
 $dbm->{FetchHashKeyName} = 'NAME_uc';
 $dbm->{ShowErrorStatement}=1;
}




sub inspect {
 my $task=shift;
 my ($t_size,$t_free,$t_write)=(0,0,0);
 my ($head,$tail,$form)=($task eq 'ins')?($head_short,$tail_short,$form_short)
                                        :($head_long, $tail_long, $form_long);
 print $head;
 foreach(sort keys %$vol) {
   my $disk=$vol->{$_};
#   print Dumper($disk);
   my $space=int($disk->{stat_FREE}/1000);
   $space=-1 if $disk->{stat_FREE} == -1;                   # this is undef
   if($disk->{ost}=~/^(Online|Degraded|Full)$/) {           # include in total only active volumes
     $t_size +=$disk->{stat_SIZE};
     $t_free +=$disk->{stat_FREE}  if $space>=0;
     $t_write+=$disk->{stat_WRITE} if $space>=0;
   }
   printf $form,$_,$disk->{NAME},$disk->{cst},$disk->{ost},
          int($disk->{stat_SIZE}/1000),$space,$disk->{stat_freeprc},
          $disk->{LIMIT_WRITE},$disk->{stat_WRITE},$disk->{stat_AWAIT},$disk->{stat_UTIL};
 }
 printf $tail,int($t_size/1000),int($t_free/1000),int(100*$t_free/(($t_size)?$t_size:1)),$t_write;
 0;
}


sub publish_stat { #($stat,$total,$hist,$uuid,$obj,$day)
   my $path=shift;
   my $total=shift;
   my $hist=shift;
   my $uuid=shift;
   my $obj=shift;
   my $day=shift;
   if(open(STAT,">$path")) {
     print "$path $total\n";
     print STAT "SIZE=$total\nhistory:\n$hist";
     close STAT;
     $dbs{DELETE_SPREAD}->execute($uuid,$obj,$day);
     $dbs{INSERT_SPREAD}->execute($uuid,$obj,$day,$total);
   }
}

sub rescan {
   my $uuid=shift;
   my ($today)=SM_DateSplit(time);
   my $vol="/vasm/$uuid/$SM_VER";
   open(SCAN, "cd $vol && du -k 2>/dev/null|")
                                     || die "cannot do du in $vol";
   my %dirs = map {m|(\d+)\s+\./(a*\d+/\d{6}(/\d\d\.\d{4}-\d\d)*)|;($2,$1)} 
                           grep {m|(\d+)\s+\./(a*\d+/\d{6}(/\d\d\.\d{4}-\d\d)*)|} <SCAN>;
   close FIND;
   my ($dir,$size,$obj,$day,$hour);
   my ($current,$hist,$total,$curobj,$stat)=(0,'',0);
   foreach (sort keys %dirs) {
      next if not m|a*(\d+)/(\d{6})(/\d\d\.\d{4}-\d\d)*|;
      ($dir,$size,$obj,$day,$hour)=($_,$dirs{$_},$1,$2,$3);
      if($day ne $current) {
        publish_stat($stat,$total,$hist,$uuid,$curobj,$current) if $current;
        ($current,$hist,$total,$stat,$curobj)=($day,'',$size,"$vol/$dir.stat",$obj);
      }
      if($day==$today){
#         print "$vol/$dir/.stat\n";
         if(defined $hour and ! -f "$vol/$dir/.stat") { #exclude hours without stat
            $hist.=sprintf( "-$size\t".time." du\t/vasm/$uuid/$SM_VER/$dir\n");
            $total-=$size;
            next;
         }
      }
      $hist.=sprintf( "$size\t".time." DU\t/vasm/$uuid/$SM_VER/$dir\n");
   }
   publish_stat($stat,$total,$hist,$uuid,$obj,$day) if $current;
   db_summary();
}


sub db_summary {
   $dbs{DELETE_SPACE}->execute();
   $dbs{DELETE_ZERRO}->execute();
   $dbs{INSERT_SPACE}->execute();
}

sub reload {
   my $uuid=shift;
   open(FIND, "find /vasm/$uuid/$SM_VER -mindepth 2 -maxdepth 2 -type f 2>/dev/null|")
                                     || die "cannot do find in /vasm/$uuid/$SM_VER";
   my @files = grep {m|\d+/\d{6}\.stat|} <FIND>;
   close FIND;
   foreach (@files) {
     chomp;
     next if not m|/vasm/\w{8}-\w{4}-\w{4}-\w{4}-\w{12}/va-\d\.\d.\d/a*(\d+)/(\d{6}).stat|;
     my ($file,$obj,$day)=($_,$1,$2);
     my $size;
     if (open(STAT,$file)) {
        $_=<STAT>;
        ($size)=/SIZE=(-*\d+)/;
        close STAT;
     }
     $size=0 if not defined $size;
     print "count: $uuid,$obj,$day,$size\n";
     $dbs{DELETE_SPREAD}->execute($uuid,$obj,$day);
     $dbs{INSERT_SPREAD}->execute($uuid,$obj,$day,$size);
   }
}

sub get_task {
 return ('') if not defined $ARGV[0];
 my $task=$ARGV[0];
 return ('') if not $task=~/^(inspect|rescan|reload)$/;
 return ('') if not defined $ARGV[1];
 my $uuid=$ARGV[1];
 foreach(keys %$vol) { 
   $uuid=$_,last if /^$uuid/; # short format match found
   $uuid=$_,last if $vol->{$_}->{NAME} eq $uuid; # name matches
 }
 print "Note: '$ARGV[1]' substituted by $uuid\n" if $uuid ne $ARGV[1];
 if(not defined $vol->{$uuid}) {
   print "Volume '$uuid' does not found in wheels group\n";
   return ('');
 }
 return ($task,$uuid);
}

# MAIN =======================================================================


my ($task,$uuid)=get_task;
if(not $task) {
  print "\n\nUsage:\n\t$SPACE inspect <uuid|name>\n\t$SPACE rescan  <uuid|name>\n"
       ."\t$SPACE reload  <uuid|name>\n\n";
  exit 1;
}
db_master;
exit(inspect($task,$uuid)) if $task eq 'inspect';
exit(rescan ($uuid)) if $task eq 'rescan';
exit(reload ($uuid)) if $task eq 'reload';
