#!/usr/bin/perl
# $Id$
#
#  usage update 
#
# Location:
# $APL/<mod>/update
#            pre-update
#            post-update 
# Naming convention: <Major>.<Minor>.<Patch>-<Iteration>-<name>
#
# Sort: Numeric order will be applied to the prefix part and alphabetic 
#       sort order will be applied to the remaining script's file name portion

use strict;
use Data::Dumper;

# CONS ------------
my $APL=$ENV{APL}           || die "APL is not defined\n";
my $APL_VAR=$ENV{APL_VAR}   || die "APL_VAR  is not defined\n";
my $APL_ROLE=$ENV{APL_ROLE} || die "APL_ROLE is not defined\n";
my $LOGPATH="$APL_VAR/log/update";
my $LABEL=`$APL/vpatch/bin/vctl label 2>/dev/null`; chomp $LABEL;
my $MODULES="$APL/base/etc/modules_list.conf"; 

# VARS ------------
my $errors=0;
# ARGS ------------

# FUNC ------------

sub write2log {
   my $log=shift;
   if(open LOG, $log) {
     print LOG @_;
     close LOG;
   }
}

sub Log {
   write2log(">>$LOGPATH/update.log",@_);
} 

sub Fatal {
  $errors++;
  Log('FATAL ERROR:',@_);
  exit 1;
}

sub Error {
  $errors++;
  Log('ERROR:',@_);
}

#-------------------------------------------------------------------------------
# load_version gets version for particular module from $APL_VAR/vctl/ver.<MOD>
# if $APL_VAR/vctl/ver.<MOD> missing then VER will be get from 
# $APL_VAR/vctl/activated and appends 0 as itteration
#
#-------------------------------------------------------------------------------
sub load_version {
 my $mod=shift;
 my %ver;
 if(open(VER,"$APL_VAR/vctl/version.$mod")) {
    %ver=map{/(^\w+)=(.+)/} grep {/^\w+=.+/} <VER>;
    close VER;
    Fatal("incorrect VERSION: $ver{VERSION}\n")    if not $ver{VERSION}=~/^\d+\.\d+\.\d+$/;
    Fatal("incorrect ITERATION: $ver{ITERATION}\n")if not $ver{ITERATION}=~/^\d+$/;
 }
 if(not exists $ver{VERSION}) {
    if(open(VER,"$APL_VAR/vctl/activated")) {
      my %cfg=map{/(^\w+)=(.+)/} grep {/^\w+=.+/} <VER>;
      close VER;
      Fatal("incorrect VER: $cfg{VER}\n") if not $cfg{VER}=~/^(\d+\.\d+\.\d+)$/;
      %ver=(VERSION=>$cfg{VER}, ITERATION=>0);
     }else {
        # this is initial installation and activation never complited yet verinfo only option
        if(open(VER,"$APL_VAR/vctl/verinfo")) { 
           my %cfg=map{/(^\w+)=(.+)/} grep {/^\w+=.+/} <VER>;
           Fatal("incorrect VER: $cfg{VER}\n") if not $cfg{VER}=~/^(\d+\.\d+\.\d+)$/;
           %ver=(VERSION=>$cfg{VER}, ITERATION=>0);
        } else {
          Fatal("Cannot read $APL_VAR/vctl/verinfo\n");
        }
     } 
 }
 $ver{VERSION}=~/^(\d+)\.(\d+)\.(\d+)$/ || Fatal("Mark in version_ is not correct\n");
 %ver=(%ver,MAJOR=>$1,MINOR=>$2,PATCH=>$3);
 return %ver;
}

sub save_version {
 my ($mod,$mark)=@_;
 return if not $mark;    # skip emty marks
 Fatal("incorrect mark : $mark\n") if not $mark=~/^(\d+\.\d+\.\d+)-(\d+)$/;
 my ($version,$iteration)=($1,$2);
 open( VER,">$APL_VAR/vctl/version.$mod") || Fatal("Cannot write into $APL_VAR/vctl/ver.$mod");
 print(VER "VERSION=$version\nITERATION=$iteration\n") ||  Fatal("Cannot write into $APL_VAR/vctl/ver.$mod");
 close VER;
}

sub apply {
  my ($mod,$script)=@_;
  my $log="$LOGPATH/$mod/$LABEL-$script.log";
  my $cmd=qq{su apl -c '. $APL/base/etc/env.conf &&  $APL/$mod/update/$script 2>&1 > $log'};
  write2log(">$log","====COMMAND:====\n$cmd\n\n====OUTPUT:====\n\n");
  `chown $ENV{APL_USR} $log`;
  Log("APPLYING: $mod:$script ");
  my $ret=`$cmd`;
  my $err=$?;
  if($ret) {               # append log with output from the call
    open( LOG,">>$log");
    print LOG $ret;
    close LOG;
  }
  Fatal(" $mod:$script failed. See $log\n") if $err;
  Log "\t\t\tSUCCESS\n";
}

# MAIN ==================================
  Log scalar(localtime).": STARTED update \n";
  
  open(MODULES,$MODULES) || Fatal ("\n cannot read $MODULES\n");
  my @modules=grep {/^\w+/} <MODULES>; 
  close MODULES;

  foreach my $mod (@modules){
     chomp $mod;
     my $dir="$APL/$mod/update";
     next if not -d $dir;  
     my %ver=load_version($mod);
     Log "=== $mod === \n";
     Log(Dumper \%ver);
     opendir(UPDATE,$dir) || Fatal("Cannot read $dir");
     my @scripts= sort {
                  my @a=$a=~/^(\d+)\.(\d+)\.(\d+)-(\d+)-(.+)$/;
                  my @b=$b=~/^(\d+)\.(\d+)\.(\d+)-(\d+)-(.+)$/;
                  ($a[0] <=> $b[0]) || ($a[1] <=> $b[1]) || ($a[2] <=> $b[2]) || ($a[3] <=> $b[3]) || ($a[4] cmp $b[4])
               }  grep   {  /^(\d+)\.(\d+)\.(\d+)-(\d+)-(.+)$/ } readdir(UPDATE);
     close UPDATE;
     my $applied_mark='';
     apply($mod,'pre-update')        if -f "$dir/pre-update";
     apply($mod,'pre-update.MASTER') if -f "$dir/pre-update.MASTER" and $APL_ROLE eq 'MASTER';
     apply($mod,'pre-update.NODE')   if -f "$dir/pre-update.NODE"   and $APL_ROLE eq 'NODE';
     foreach my $script (@scripts) {
       if($script=~/\.(MASTER|NODE)$/) {    # master/node dependable sctips
          if($APL_ROLE ne $1) {             # chech if the same role
             Log "skipping: $script\n";
             next;
          }
       }
       $script=~/^(\d+)\.(\d+)\.(\d+)-(\d+)-(.+)$/;
       my $cmp=($1<=>$ver{MAJOR}) || ($2<=>$ver{MINOR}) || ($3<=>$ver{PATCH})  || ($4<=>$ver{ITERATION});
       if($cmp<=0){
         Log "skipping: $script\n";
         next;
       }
       my $mark="$1.$2.$3-$4"; 
       save_version($mod,$applied_mark) if $mark ne $applied_mark;
       apply($mod,$script);
       $applied_mark=$mark;
     }
     save_version($mod,$applied_mark);
     apply($mod,'post-update')        if -f "$dir/post-update";
     apply($mod,'post-update.MASTER') if -f "$dir/post-update.MASTER" and $APL_ROLE eq 'MASTER';
     apply($mod,'post-update.NODE')   if -f "$dir/post-update.NODE"   and $APL_ROLE eq 'NODE';
  }
# END ===================
 
END {
 my $err=($errors)?'with ERRORS':'successfully';
 Log scalar(localtime).": FINISHED update $err\n";
 my $exit_code = $?;
 `chown $ENV{APL_USR} $LOGPATH/update.log`;
 $? = $exit_code;
# print "FINISHED update $err\n";
}
