#!/usr/bin/perl
#  $Id: sm_engine 23198 2011-08-02 15:07:45Z atsybulnik $
# -----------------------------------------------------------------------------
#  SM_ENGINE  - forks spindles to handle uuids in wheels group
#             - keep spindle running till conf exists in wheels group
#             - start and keep running iostat monitor 
# -----------------------------------------------------------------------------
#  Author: Alex Titov
#  QA by:
#  Copyright: videoNEXT LLC
# -----------------------------------------------------------------------------
# usage:   sm_engine
#
# purpouse:
#  1. sm_engine starts sm_iomon for iostat monitoring
#  2. sm_engine sm_iomon if it dies
#  3. sm_engine checks  $APL/var/conf/sm/wheels and start a spindle for each
#  4. sm_engine shops spindle if it conf is gone from $APL/var/conf/sm/wheels
#  5. sm_engine restart spindle if it dies
#  6. sm_engine starts sm_keeper and restart if it dies
#  7. sm_engine starts sm_devstat and restart if it dies
#  8. sm_engine starts sm_dblog and restart if it dies
#  9. sm_engine starts sm_nfs and restart if it dies
# 10. sm_engine starts sm_status and restart if it dies
# 11. sm_engine starts sm_writer and restart if it dies
# 11a.sm_engine starts sm_marker and restart if it dies
# 12. sm_engine mounts store if not mounted yet (Darwin)
# 13. sm_engine initiates MRTG resorce rescan when started
# 14. sm_engine stop all sm process if running prior its start 
#
# for REQ see: Rally S623:Storage Manager TA923: SM Engine

use warnings;
use strict;
use Data::Dumper;
use POE qw(Wheel::Run Wheel::FollowTail);
use File::Basename qw(dirname);    
use lib dirname(__FILE__).'/../lib';             # find  SM::Config here
use SM::Config qw(SM_CONF SM_WritePid SM_LOG SM_StopAll);

# CONS ------------------------------------------------------------------------
my $ENGINE =(split(/\//,$0))[-1];                # the actual name of the prog
my $APL    =$ENV{APL};
my $KEEPER_NAME='sm_keeper';
my $KEEPER=dirname(__FILE__)."/$KEEPER_NAME";    # space keeper
my $IOMON_NAME='sm_iomon';
my $IOMON=dirname(__FILE__)."/$IOMON_NAME";      # io monitoring
my $SPINDLE_NAME='sm_spindle';
my $SPINDLE=dirname(__FILE__)."/$SPINDLE_NAME";  # name with path
my $DEVSTAT_NAME='sm_devstat';
my $DEVSTAT=dirname(__FILE__)."/$DEVSTAT_NAME";  # WP stat for devices
my $DBLOG_NAME='sm_dblog';
my $DBLOG=dirname(__FILE__)."/$DBLOG_NAME";      # log uploader
my $SMDB_NAME='sm_db';
my $SMDB=dirname(__FILE__)."/$SMDB_NAME";        # db sync
my $SMST_NAME='sm_status';
my $SMST=dirname(__FILE__)."/$SMST_NAME";        # status manager
my $WRITER_NAME='sm_writer';
my $WRITER=dirname(__FILE__)."/$WRITER_NAME";    # transfers from cache to storage
my $MARKER_NAME='sm_marker';
my $MARKER=dirname(__FILE__)."/$MARKER_NAME";    # mark events in cache
my $STORE_NAME='sm_store';
my $STORE=dirname(__FILE__)."/$STORE_NAME";      # store
                                                 #initiate MRTG scan volumes
#my $GRAPH="nohup sudo $APL/net/bin/conf_snmp_and_mrtg.pl >/dev/null 2>&1 &";


SM_WritePid($ENGINE);                            # create pid asap
SM_StopAll;                                      # stop sm_processes if running

# STATES ======================================================================


#------------------------------------------------------------------------------
# Make startup preparation and stop spindles if any running without control
# ! TBD killall can be substituted with killall from perl-Proc-ProcessTable
sub startup {
  # `killall    $SPINDLE_NAME >/dev/null 2>&1`; killall does not work with Darwin
  `ps ax | grep $SPINDLE_NAME | grep -v grep | cut -c 1-6 |xargs kill >/dev/null 2>&1`;
  sleep 1;
  #`killall -9 $SPINDLE_NAME >/dev/null 2>&1`;
  `ps ax | grep $SPINDLE_NAME | grep -v grep | cut -c 1-6 |xargs kill -9 >/dev/null 2>&1`;
  $_[KERNEL]->alias_set('ENGINE');
  $_[KERNEL]->yield('spindle_twist');
}

#---------------------------------------------------------------------------
# use Wheel::Run to start a spindle process for each avaiable configuration
sub spindle_twist {
    my $heap = $_[HEAP];
    #------------------------------------- get configurations
    opendir(DIR,SM_CONF."/wheels");
    my %new_conf=map{($_,1)} grep{/^\w{8}-(\w{4}-){3}\w{12}$/} readdir(DIR); 
    closedir DIR;
    my %cur_conf;                        # keys uuid, values Wheels id
    %cur_conf=reverse (%{$heap->{uuid}}) if defined $heap->{uuid}; 
    #------------------------------------- stop section
    foreach (keys %cur_conf) { 
      next if defined $new_conf{$_};      # skip if conf still present
      my $spindle=$heap->{spindle}->{$cur_conf{$_}};
      $spindle->kill();                   # kill the spindle if conf gone
    }
    #------------------------------------- start section
    foreach my $uuid (keys %new_conf) {
      next if defined $cur_conf{$uuid};   # skip if alredy running
      print "Starting spindle $uuid\n";
      my $spindle= POE::Wheel::Run->new (
        Program =>  "$SPINDLE $uuid",    # spindle to run 
        StderrEvent  => "spindle_debug",
        CloseEvent   => "spindle_done"
      );
      $heap->{spindle}->{$spindle->ID} = $spindle;
      $heap->{uuid}->{$spindle->ID} = $uuid;
    }
    #----------------------------------- run io-mon if none yet
    if(not defined $heap->{iomon}) {
       print "Starting iomon\n";
       my $iomon=POE::Wheel::Run->new (
          Program =>  "$IOMON",
          StderrEvent  => "spindle_debug",
          CloseEvent   => "iomon_done"
       );
       $heap->{iomon}=$iomon;
    }
    #----------------------------------- run keeper if none yet
    if(not defined $heap->{keeper}) {
       print "Starting keeper\n";
       my $keeper=POE::Wheel::Run->new (
          Program =>  "$KEEPER",
          StderrEvent  => "spindle_debug",
          CloseEvent   => "keeper_done"
       );
       $heap->{keeper}=$keeper;
    }
    #----------------------------------- run devstat if none yet
    if(not defined $heap->{devstat}) {
       print "Starting devstat\n";
       my $devstat=POE::Wheel::Run->new (
          Program =>  "$DEVSTAT",
          StderrEvent  => "spindle_debug",
          CloseEvent   => "devstat_done"
       );
       $heap->{devstat}=$devstat;
    }
    #----------------------------------- run dblog if none yet
    if(not defined $heap->{dblog}) {
       print "Starting dblog\n";
       my $dblog=POE::Wheel::Run->new (
          Program =>  "$DBLOG",
          StderrEvent  => "spindle_debug",
          CloseEvent   => "dblog_done"
       );
       $heap->{dblog}=$dblog;
    }
    #----------------------------------- run smdb if none yet
    if(not defined $heap->{smdb}) {
     if($^O ne 'darwin' or $ENV{APL_MOD} ne 'CIRRUS') { #do not start for cirrus
       print "Starting smdb\n";
       my $smdb=POE::Wheel::Run->new (
          Program =>  "$SMDB",
          StderrEvent  => "spindle_debug",
          CloseEvent   => "smdb_done"
       );
       $heap->{smdb}=$smdb;
     }
    }
    #----------------------------------- run smst if none yet
    if(not defined $heap->{smst}) {
       print "Starting smst\n";
       my $smst=POE::Wheel::Run->new (
          Program =>  "$SMST",
          StderrEvent  => "spindle_debug",
          CloseEvent   => "smst_done"
       );
       $heap->{smst}=$smst;
    }
    #----------------------------------- run writer if none yet
    if(not defined $heap->{writer}) {
       print "Starting writer\n";
       my $writer=POE::Wheel::Run->new (
          Program =>  "$WRITER",
          StderrEvent  => "spindle_debug",
          CloseEvent   => "writer_done"
       );
       $heap->{writer}=$writer;
    }
    #----------------------------------- run marker if none yet
    if(not defined $heap->{marker}) {
       print "Starting marker\n";
       my $marker=POE::Wheel::Run->new (
          Program =>  "$MARKER",
          StderrEvent  => "spindle_debug",
          CloseEvent   => "marker_done"
       );
       $heap->{marker}=$marker;
    }
    #----------------------------------- goto sleep
    SM_WritePid($ENGINE);
    $_[KERNEL]->delay(spindle_twist=>5)
}

#----------------------------------------------------------------------------
# Catch and display information from the child's STDERR
sub handle_spindle_debug {
    my $result = $_[ARG0];
    print "Debug: $result\n";
}

#----------------------------------------------------------------------------
# The spindle is done.  Delete the spindle's wheel, clean vars
sub handle_spindle_done {
    my ( $kernel, $heap, $spindle_id ) = @_[ KERNEL, HEAP, ARG0 ];
    print "spindle $heap->{uuid}->{$spindle_id} finished\n";
    delete $heap->{spindle}->{$spindle_id};
    delete $heap->{uuid}->{$spindle_id};
}

#----------------------------------------------------------------------------
# The iomon is done (should not be like this)  Cleanup. It will be restarted
sub handle_iomon_done {
    my ( $kernel, $heap, $id ) = @_[ KERNEL, HEAP, ARG0 ];
    print "iomon finished\n";
    delete $heap->{iomon};
}

#----------------------------------------------------------------------------
# The keeper is done (should not be like this)  Cleanup. It will be restarted
sub handle_keeper_done {
    my ( $kernel, $heap, $id ) = @_[ KERNEL, HEAP, ARG0 ];
    print "keeper finished\n";
    delete $heap->{keeper};
}
#----------------------------------------------------------------------------
# The devstat is done (should not be like this)  Cleanup. It will be restarted
sub handle_devstat_done {
    my ( $kernel, $heap, $id ) = @_[ KERNEL, HEAP, ARG0 ];
    print "devstat finished\n";
    delete $heap->{devstat};
}
#----------------------------------------------------------------------------
# The dblog is done (should not be like this)  Cleanup. It will be restarted
sub handle_dblog_done {
    my ( $kernel, $heap, $id ) = @_[ KERNEL, HEAP, ARG0 ];
    print "dblog finished\n";
    delete $heap->{dblog};
}
#----------------------------------------------------------------------------
# The smdb is done (should not be like this)  Cleanup. It will be restarted
sub handle_smdb_done {
    my ( $kernel, $heap, $id ) = @_[ KERNEL, HEAP, ARG0 ];
    print "smdb finished\n";
    delete $heap->{smdb};
}
#----------------------------------------------------------------------------
# The smst is done (should not be like this)  Cleanup. It will be restarted
sub handle_smst_done {
    my ( $kernel, $heap, $id ) = @_[ KERNEL, HEAP, ARG0 ];
    print "smst finished\n";
    delete $heap->{smst};
}
#----------------------------------------------------------------------------
# The writer is done (should not be like this)  Cleanup. It will be restarted
sub handle_writer_done {
    my ( $kernel, $heap, $id ) = @_[ KERNEL, HEAP, ARG0 ];
    print "writer finished\n";
    delete $heap->{writer};
}
#----------------------------------------------------------------------------
# The marker is done (should not be like this)  Cleanup. It will be restarted
sub handle_marker_done {
    my ( $kernel, $heap, $id ) = @_[ KERNEL, HEAP, ARG0 ];
    print "marker finished\n";
    delete $heap->{marker};
}
#----------------------------------------------------------------------------
# Stop any spindles if still roll
sub shutdown {
   my $heap=$_[HEAP];
   foreach (keys %{$heap->{spindle}}) {
      $heap->{spindle}->{$_}->kill();
      print "Shutdown spindle ". $heap->{uuid}->{$_} ."\n";
      delete $heap->{uuid}->{$_};
      delete $heap->{spindle}->{$_};
   }
   if(defined $heap->{iomon}) {
      print "Shutdown iomon\n";
      $heap->{iomon}->kill();
      delete $heap->{iomon};
   }
   if(defined $heap->{keeper}) {
      print "Shutdown Keeper\n";
      $heap->{keeper}->kill();
      delete $heap->{keeper};
   }
   if(defined $heap->{devstat}) {
      print "Shutdown Devstat\n";
      $heap->{devstat}->kill();
      delete $heap->{devstat};
   }
   if(defined $heap->{dblog}) {
      print "Shutdown dblog\n";
      $heap->{dblog}->kill();
      delete $heap->{dblog};
   }
   if(defined $heap->{smdb}) {
      print "Shutdown smdb\n";
      $heap->{smdb}->kill();
      delete $heap->{smdb};
   }
   if(defined $heap->{smst}) {
      print "Shutdown smst\n";
      $heap->{smst}->kill();
      delete $heap->{smst};
   }
   if(defined $heap->{writer}) {
      print "Shutdown writer\n";
      $heap->{writer}->kill();
      delete $heap->{writer};
   }
   if(defined $heap->{marker}) {
      print "Shutdown marker\n";
      $heap->{marker}->kill();
      delete $heap->{marker};
   }
   $_[KERNEL]->alias_remove( 'ENGINE' );
}

# SIGN ======================================================================
$poe_kernel->sig(INT  =>sub {$poe_kernel->post(ENGINE=>'shutdown');
                             $poe_kernel->sig_handled();});
$poe_kernel->sig(TERM =>sub {$poe_kernel->post(ENGINE=>'shutdown');
                             $poe_kernel->sig_handled();});
$poe_kernel->sig(CHLD =>sub {$poe_kernel->sig_handled()});
# MAIN ======================================================================
#                           # TBD: special wheel for monitoring STORE
`$STORE`;                   #mount /vasm/store if not mounted. 
SM_LOG->logdie("Store is not mounted. Cannot manage SM\n") if $?;
#`$GRAPH`;                   # initiate MRTG rescan volumes

# Start the session that will manage all the children.
POE::Session->create (
  inline_states =>
      { _start            => \&startup,
        _stop             => \&shutdown,
        shutdown          => \&shutdown,
        spindle_twist     => \&spindle_twist,
        spindle_done      => \&handle_spindle_done,
        spindle_debug     => \&handle_spindle_debug,
        iomon_done        => \&handle_iomon_done,
        keeper_done       => \&handle_keeper_done,
        devstat_done      => \&handle_devstat_done,
        dblog_done        => \&handle_dblog_done,
        smdb_done         => \&handle_smdb_done,
        smst_done         => \&handle_smst_done,
        writer_done       => \&handle_writer_done,
        marker_done       => \&handle_marker_done,
      }
);

$poe_kernel->run();
exit 0;
