#!/usr/bin/env perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT_plugin::notification;
BEGIN
{
    $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl";
use xCAT::Table;
use xCAT::NotifHandler;
1;

#-------------------------------------------------------------------------------
=head1  xCAT_plugin:notification
=head2    Package Description
  xCAT notification plugini module. This mondule allows users to register and
  unregister for the xCAT database table changes. 
=cut
#-------------------------------------------------------------------------------


#--------------------------------------------------------------------------------
=head3   handled_commands
      It returns a list of commands handled by this plugin.
    Arguments:
        none
    Returns:
        a list of commands.
=cut
#-------------------------------------------------------------------------------
sub handled_commands {
  return {
    regnotif => "notification",
    unregnotif => "notification",
    refnotif => "notification",
    lsnotif => "notification",
    enablenotif => "notification",
    disablenotif => "notification",
  }
}


#--------------------------------------------------------------------------------
=head3   process_request
      It processes the monitoring control commands.
    Arguments:
      request -- a hash table which contains the command name.
      callback -- a callback pointer to return the response to.
      args -- a list of arguments that come with the command.
    Returns:
        0 for success. The output is returned through the callback pointer.
        1. for unsuccess. The error messages are returns through the callback pointer.
=cut
#-------------------------------------------------------------------------------
sub process_request {
  use Getopt::Long;
  # options can be bundled up like -vV
  Getopt::Long::Configure("bundling") ;
  $Getopt::Long::ignorecase=0;
  
  my $request = shift;
  my $callback = shift;
  my $command = $request->{command}->[0];
  my $args=$request->{arg};

  if ($command eq "regnotif") {
    my ($ret, $msg) = regNotification($args, $callback);
    if ($msg) {
      my %rsp=();
      $rsp->{dara}->[0]= $msg;
      $callback->($rsp);
    }
    return $ret;
  } 
  elsif ($command eq "unregnotif") {
    my ($ret, $msg) = unregNotification($args, $callback);
    if ($msg) {
      my %rsp=();
      $rsp->{data}->[0]= $msg;
      $callback->($rsp);
    }
    return $ret;
  } 
  else {
    my %rsp=();
    $rsp->{data}->[0]= "unsupported command: $command.";
    $callback->($rsp);
    return 1;
  }
}


#--------------------------------------------------------------------------------
=head3   regNotification
      It registers a notification routine or a command which
      will be called when there are changes in the data base tables
      that the routine/command is interested in.
    Arguments:
      args - The format of the args is:
         [-h|--help|-v|--version] or
          filename tablename[,tablename]... [-o|--operation tableop[,tableop][,tableop]]]
        where
          tablename - string. a list of comma seperated names of the tables whose changes
              will be watched.
          filename - string. The name of the module (e.g. /usr/lib/xxx.pm) or
              command (e.g. /usr/bin/xxx) that handles the notification.
              If it is a perl module, the module  must implement the following routine:
                processTableChanges(action, table_name, old_data, new_data)
              Refer to notify() subroutine for the meaning of the parameters.
              If it is a command, the command format should be:
                command [-d|-a|-u] tablename {[colnames][rowvalues][rowvalues]...} {[colnames][rowvalues]}
          tableop - it can be 'a' for add, 'd' for delete and 'u' for update.
      callback - the pointer to the callback function.
    Returns:
      none
    Comments:
       If the module or the command already exists in the notification table, this subroutine
       will replace it.
=cut
#-------------------------------------------------------------------------------
sub regNotification {
  my $args=shift;
  my $callback=shift;
  my $VERSION;
  my $HELP;
  my $tableops;

  # subroutine to display the usage
  sub regnotif_usage
  {
    my $callbk=shift;
    if (! $callbk) { return;}

    my %rsp;
    $rsp->{data}->[0]= "Usage:";
    $rsp->{data}->[1]= "  regnotif filename tablename[,tablename]... [-o|--operation tableop[,tableop][,tableop]]]";
    $rsp->{data}->[2]= "       where tableop can be 'a' for add, 'd' for delete and 'u' for update";
    $rsp->{data}->[3]= "  regnotif [-h|--help|-v|--version]";
    $callbk->($rsp);
  }
  
  @ARGV=@{$args};
  # parse the options
  if(!GetOptions(
      'o|operation=s' => \$tableops,
      'h|help'     => \$::HELP,
      'v|version'  => \$::VERSION,))
  {
    &regnotif_usage($callback);
    return;
  }

  # display the usage if -h or --help is specified
  if ($::HELP) { 
    &regnotif_usage($callback);
    return;
  }

  # display the version statement if -v or --verison is specified
  if ($::VERSION)
  {
    my %rsp;
    $rsp->{data}->[0]= "regnotif version 1.0";
    if ($callback) {
      $callback->($rsp);
    }
    return;
  }

  # must specify the file name and table names
  if (@ARGV < 2)
  {
    &regnotif_usage($callback);
    return;
  }

  #table operations must be a,d or u seperated by ','
  if ($tableops)
  {
    if ($tableops !~ m/^(a|d|u)(,(a|d|u)){0,2}$/) {
      my %rsp;
      $rsp->{data}->[0]= "Invalid table operations: $tableops";
      if ($callback) {
        $callback->($rsp);
      }
      return;
    }
  }
  else
  {
     $tableops="a,d,u";
  }
 
  my $fname=shift(@ARGV);
  my $table_names=shift(@ARGV);
  
  my $table=xCAT::Table->new("notification", -create => 1,-autocommit => 0);
  if ($table) {
    my %key_col = (filename=>$fname);
    my %tb_cols=(tables=>$table_names, tableops=>$tableops);
    $table->setAttribs(\%key_col, \%tb_cols);
    $table->commit;
  }

  #update notification cache
  xCAT::NotifHandler::sendNotifSignal();
  return;
}

#--------------------------------------------------------------------------------
=head3   unregNotification 
      It unregisters a notification routine or a command.
    Arguments:
      args - the format of the ares is:
          [-h|--help|-v|--version] or
          filename
        where
          filename - string. The name of the module or command that handles the notification.
      callback - the pointer to the callback funtion.
    Returns:
      none
=cut
#-------------------------------------------------------------------------------
sub unregNotification {
  my $args=shift;
  my $callback=shift;
  my $VERSION;
  my $HELP;

  # subroutine to display the usage
  sub unregnotif_usage
  {
    my $callbk=shift;
    if (! $callbk) { return;}

    my %rsp;
    $rsp->{data}->[0]= "Usage:";
    $rsp->{data}->[1]= "  unregnotif filename";
    $rsp->{data}->[2]= "  unregnotif [-h|--help|-v|--version]";
    $callbk->($rsp);
  }

  @ARGV=@{$args};
  # parse the options
  if(!GetOptions(
      'h|help'     => \$::HELP,
      'v|version'  => \$::VERSION,))
  {
    &unregnotif_usage($callback);
    return;
  }

  # display the usage if -h or --help is specified
  if ($::HELP) { 
    &unregnotif_usage($callback);
    return;
  }

  # display the version statement if -v or --verison is specified
  if ($::VERSION)
  {
    my %rsp;
    $rsp->{data}->[0]= "unregnotif version 1.0";
    if ($callback) {
      $callback->($rsp);
    }
    return;
  }

  # must specify the node range
  if (@ARGV < 1) {
    &unregnotif_usage($callback);
    return;
  }


  my $fname=shift(@ARGV);

  my $table=xCAT::Table->new("notification", -create => 1,-autocommit => 0);
  if ($table) {
    my %key_col = (filename=>$fname);
    $table->delEntries(\%key_col);
    $table->commit;
  }

  #update notification cache
  xCAT::NotifHandler::sendNotifSignal();
  return;
}