git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@5648 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
		
			
				
	
	
		
			367 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			367 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
#!/usr/bin/env perl
 | 
						|
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
 | 
						|
package xCAT::NotifHandler;
 | 
						|
BEGIN
 | 
						|
{
 | 
						|
    $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr';
 | 
						|
}
 | 
						|
use lib "$::XCATROOT/lib/perl";
 | 
						|
 | 
						|
use File::Basename qw(fileparse);
 | 
						|
use xCAT::Utils;
 | 
						|
use Data::Dumper;
 | 
						|
 | 
						|
#%notif is a cache that holds the info from the "notification" table.
 | 
						|
#the format of it is:
 | 
						|
#   {tablename=>{'a'=>[filename,...]
 | 
						|
#                'u'=>[filename,,..]
 | 
						|
#                'd'=>[filename,...]
 | 
						|
#                }
 | 
						|
#   }
 | 
						|
my %notif;
 | 
						|
my $masterpid;
 | 
						|
my $dbworkerid;
 | 
						|
 | 
						|
1;
 | 
						|
 | 
						|
#-------------------------------------------------------------------------------
 | 
						|
=head1  xCATi::NotifHandler
 | 
						|
=head2    Package Description
 | 
						|
  This mondule caches the notification table and tracks the changes of it.
 | 
						|
  It also handles the event notification when xCAT database changes.
 | 
						|
=cut
 | 
						|
#-------------------------------------------------------------------------------
 | 
						|
 | 
						|
 | 
						|
#--------------------------------------------------------------------------------
 | 
						|
=head3  setup
 | 
						|
      It is called by xcatd to get set the pid of the parent of all this object.
 | 
						|
      Setup the signal to trap any changes in the notification table. It also
 | 
						|
      initializes the cache with the current data in the notification table.
 | 
						|
      table and store it into %notif variable.
 | 
						|
    Arguments:
 | 
						|
      pid -- the process id of the caller.
 | 
						|
      pid1 -- the process id of the dbworker.
 | 
						|
    Returns:
 | 
						|
      none
 | 
						|
=cut
 | 
						|
#-------------------------------------------------------------------------------
 | 
						|
sub setup
 | 
						|
{
 | 
						|
  $masterpid=shift;
 | 
						|
  if ($masterpid =~ /xCAT::NotifHandler/) {
 | 
						|
    $masterpid=shift;
 | 
						|
  }
 | 
						|
  $dbworkerid=shift;
 | 
						|
 | 
						|
  refreshNotification();
 | 
						|
 | 
						|
  $SIG{USR1}=\&handleNotifSignal;
 | 
						|
}
 | 
						|
 | 
						|
#--------------------------------------------------------------------------------
 | 
						|
=head3  handleNotifSignal
 | 
						|
      It is called when the signal is received. It then update the cache with the
 | 
						|
      latest data in the notification table.
 | 
						|
    Arguments:
 | 
						|
      none.
 | 
						|
    Returns:
 | 
						|
      none
 | 
						|
=cut
 | 
						|
#-------------------------------------------------------------------------------
 | 
						|
sub handleNotifSignal {
 | 
						|
   #print "handleNotifSignal pid=$$\n";
 | 
						|
   refreshNotification();
 | 
						|
   $SIG{USR1}=\&handleNotifSignal;
 | 
						|
}
 | 
						|
 | 
						|
#--------------------------------------------------------------------------------
 | 
						|
=head3  sendNotifSignal
 | 
						|
      It is called by any module that has made changes to the notification table.
 | 
						|
    Arguments:
 | 
						|
      none.
 | 
						|
    Returns:
 | 
						|
      none
 | 
						|
=cut
 | 
						|
#-------------------------------------------------------------------------------
 | 
						|
sub sendNotifSignal {
 | 
						|
  if ($masterpid) {
 | 
						|
    kill('USR1', $masterpid);
 | 
						|
  }
 | 
						|
  if ($dbworkerid) {
 | 
						|
    kill('USR1', $dbworkerid);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#--------------------------------------------------------------------------------
 | 
						|
=head3   refreshNotification
 | 
						|
      It loads the notification info from the "notification"
 | 
						|
      table and store it into %notif variable.
 | 
						|
      The format of it is:
 | 
						|
         {tablename=>{filename=>{'ops'=>['y/n','y/n','y/n'], 'disable'=>'y/n'}}}
 | 
						|
    Arguments:
 | 
						|
      none
 | 
						|
    Returns:
 | 
						|
      none
 | 
						|
=cut
 | 
						|
#-------------------------------------------------------------------------------
 | 
						|
sub refreshNotification
 | 
						|
{
 | 
						|
  #print "refreshNotification get called\n";
 | 
						|
  #flush the cache
 | 
						|
  %notif=();
 | 
						|
  my $table=xCAT::Table->new("notification", -create =>0);
 | 
						|
  if ($table) {
 | 
						|
    #get array of rows out of the notification table
 | 
						|
    my @row_array= $table->getTable;
 | 
						|
    if (@row_array) {
 | 
						|
      #store the information to the cache
 | 
						|
      foreach(@row_array) {
 | 
						|
        my $module=$_->{filename};
 | 
						|
        my $ops=$_->{tableops};
 | 
						|
        my $disable= $_->{disable};
 | 
						|
        my @tablenames=split(/,/, $_->{tables});
 | 
						|
 | 
						|
        foreach(@tablenames) {
 | 
						|
          if (!exists($notif{$_})) {
 | 
						|
            $notif{$_}={};
 | 
						|
          }
 | 
						|
 | 
						|
 | 
						|
          my $tempdisable=0;
 | 
						|
          if ($disable) {
 | 
						|
            if ($disable =~ m/^(yes|YES|Yes|Y|y|1)$/) {
 | 
						|
              $tempdisable=1;
 | 
						|
            }
 | 
						|
          }
 | 
						|
 | 
						|
          if (!$disable) {
 | 
						|
            if ($ops) {
 | 
						|
              if ($ops =~ m/a/) {
 | 
						|
                if (exists($notif{$_}->{a})) {
 | 
						|
                  my $pa=$notif{$_}->{a};
 | 
						|
                  push(@$pa, $module);
 | 
						|
                } else {
 | 
						|
                  $notif{$_}->{a}=[$module];
 | 
						|
                }
 | 
						|
              }
 | 
						|
              if ($ops =~ m/d/) {
 | 
						|
                if (exists($notif{$_}->{d})) {
 | 
						|
                  my $pa=$notif{$_}->{d};
 | 
						|
                  push(@$pa, $module);
 | 
						|
                } else {
 | 
						|
                  $notif{$_}->{d}=[$module];
 | 
						|
                }
 | 
						|
              }
 | 
						|
              if ($ops =~ m/u/) {
 | 
						|
                if (exists($notif{$_}->{u})) {
 | 
						|
                  my $pa=$notif{$_}->{u};
 | 
						|
                  push(@$pa, $module);
 | 
						|
                } else {
 | 
						|
                  $notif{$_}->{u}=[$module];
 | 
						|
                }
 | 
						|
              }
 | 
						|
            } #end if
 | 
						|
          }
 | 
						|
        } #end foreach
 | 
						|
 | 
						|
      } #end foreach(@row_array)
 | 
						|
    }#end if (@row_array)
 | 
						|
  } #end if ($table)
 | 
						|
 | 
						|
   #print Dumper(%notif);
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#--------------------------------------------------------------------------------
 | 
						|
=head3   dumpNotificationCache
 | 
						|
      It print out the content of the notification cache for debugging purpose.
 | 
						|
    Arguments:
 | 
						|
      none
 | 
						|
    Returns:
 | 
						|
      0
 | 
						|
=cut
 | 
						|
#-------------------------------------------------------------------------------
 | 
						|
sub dumpNotificationCache {
 | 
						|
  print "dump the notification cache:\n";
 | 
						|
  foreach(keys(%notif)) {
 | 
						|
    my $tmptn=$_;
 | 
						|
    print " $tmptn: \n";
 | 
						|
 | 
						|
    if (exists($notif{$_}->{a})) {
 | 
						|
      print "   a--:";
 | 
						|
      my $files=$notif{$_}->{a};
 | 
						|
      print "@$files\n";
 | 
						|
    }
 | 
						|
    if (exists($notif{$_}->{u})) {
 | 
						|
      print "   u--:";
 | 
						|
      my $files=$notif{$_}->{u};
 | 
						|
      print "@$files\n";
 | 
						|
    }
 | 
						|
    if (exists($notif{$_}->{d})) {
 | 
						|
      print "   d--:";
 | 
						|
      my $files=$notif{$_}->{d};
 | 
						|
      print "@$files\n";
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#--------------------------------------------------------------------------------
 | 
						|
=head3   needToNotify
 | 
						|
      It check if the given table has interested parties watching for its changes.
 | 
						|
    Arguments:
 | 
						|
      tablename - the name of the table to be checked.
 | 
						|
      tableop - the operation on the table. 'a' for add, 'u' for update
 | 
						|
                and 'd' for delete.
 | 
						|
    Returns:
 | 
						|
      1 - if the table has interested parties.
 | 
						|
      0 - if no parties are interested in its changes.
 | 
						|
=cut
 | 
						|
#-------------------------------------------------------------------------------
 | 
						|
sub needToNotify {
 | 
						|
 | 
						|
  #print "needToNotify pid=$$, notify=" . Dumper(%notif) . "\n";
 | 
						|
 | 
						|
  if (!%notif) {
 | 
						|
    # print "notif not defined\n";
 | 
						|
    refreshNotification();
 | 
						|
  }
 | 
						|
 | 
						|
  my $tablename=shift;
 | 
						|
  if ($tablename =~ /xCAT::NotifHandler/) {
 | 
						|
    $tablename=shift;
 | 
						|
  }
 | 
						|
  my $tableop=shift;
 | 
						|
 | 
						|
  if (%notif) {
 | 
						|
    if (exists($notif{$tablename})) {
 | 
						|
      if (exists($notif{$tablename}->{$tableop})) {
 | 
						|
        return 1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#--------------------------------------------------------------------------------
 | 
						|
=head3   notify
 | 
						|
      It notifies the registered the modules with the latest changes in
 | 
						|
      a DB table.
 | 
						|
    Arguments:
 | 
						|
      action - table action. It can be d for rows deleted, a for rows added
 | 
						|
                    or u for rows updated.
 | 
						|
      tablename - string. The name of the DB table whose data has been changed.
 | 
						|
      old_data - an array reference of the old row data that has been changed.
 | 
						|
           The first element is an array reference that contains the column names.
 | 
						|
           The rest of the elelments are also array references each contains
 | 
						|
           attribute values of a row.
 | 
						|
           It is set when the action is u or d.
 | 
						|
      new_data - a hash refernce of new row data; only changed values are present
 | 
						|
           in the hash.  It is keyed by column names.
 | 
						|
           It is set when the action is u or a.
 | 
						|
    Returns:
 | 
						|
      0
 | 
						|
    Comments:
 | 
						|
      If the curent table is watched by a perl module, the module must implement
 | 
						|
      the following routine:
 | 
						|
         processTableChanges(action, table_name, old_data, new_data)
 | 
						|
      If it is a watched by a command, the data will be passed to the command
 | 
						|
      through STDIN. The format is:
 | 
						|
         action
 | 
						|
         table_name
 | 
						|
         [old data]
 | 
						|
         col1_name,col2_name,...
 | 
						|
         col1_value,col2_value,...
 | 
						|
         ...
 | 
						|
         [new data]
 | 
						|
         col1_name,col2_name,...
 | 
						|
         col1_value,col2_value,...
 | 
						|
         ...
 | 
						|
 | 
						|
=cut
 | 
						|
#-------------------------------------------------------------------------------
 | 
						|
sub notify {
 | 
						|
  my $action=shift;
 | 
						|
  if ($action =~ /xCAT::NotifHandler/) {
 | 
						|
    $action=shift;
 | 
						|
  }
 | 
						|
  my $tablename=shift;
 | 
						|
  my $old_data=shift;
 | 
						|
  my $new_data=shift;
 | 
						|
 | 
						|
  # print "notify called: tablename=$tablename, action=$action\n";
 | 
						|
 | 
						|
  my @filenames=();
 | 
						|
  if (%notif) {
 | 
						|
    if (exists($notif{$tablename})) {
 | 
						|
      if (exists($notif{$tablename}->{$action})) {
 | 
						|
        my $pa=$notif{$tablename}->{$action};
 | 
						|
        @filenames=@$pa;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  foreach(@filenames) {
 | 
						|
    my ($modname, $path, $suffix) = fileparse($_, ".pm");
 | 
						|
     # print "modname=$modname, path=$path, suffix=$suffix\n";
 | 
						|
    if ($suffix =~ /.pm/) { #it is a perl module
 | 
						|
      my $pid;
 | 
						|
      if ($pid=xCAT::Utils->xfork()) { }
 | 
						|
      elsif (defined($pid)) {
 | 
						|
	my $fname;
 | 
						|
        if (($path eq "") || ($path eq ".\/")) {
 | 
						|
          #default path is /opt/xcat/lib/perl/xCAT_monitoring/ if there is no path specified
 | 
						|
          $fname = "$::XCATROOT/lib/perl/xCAT_monitoring/".$modname.".pm";
 | 
						|
        } else {
 | 
						|
          $fname = $_;
 | 
						|
        }
 | 
						|
        eval {require($fname)};
 | 
						|
        if ($@) {
 | 
						|
          print "The file $fname cannot be located or has compiling errors.\n";
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          ${"xCAT_monitoring::".$modname."::"}{processTableChanges}->($action, $tablename, $old_data, $new_data);
 | 
						|
        }
 | 
						|
        exit 0;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else { #it is a command
 | 
						|
      my $pid;
 | 
						|
      if ($pid=xCAT::Utils->xfork()) { }
 | 
						|
      elsif (defined($pid)) {
 | 
						|
        # print "command=$_\n";
 | 
						|
        if (open(CMD, "|$_")) {
 | 
						|
          print(CMD "$action\n");
 | 
						|
          print(CMD "$tablename\n");
 | 
						|
 | 
						|
          print(CMD  "[old data]\n");
 | 
						|
          foreach (@$old_data) {
 | 
						|
            print(CMD join(',', @$_)."\n");
 | 
						|
          }
 | 
						|
 | 
						|
          print(CMD  "[new data]\n");
 | 
						|
          if (%$new_data) {
 | 
						|
            print(CMD join(',', keys %$new_data) . "\n");
 | 
						|
            print(CMD join(',', values %$new_data) . "\n");
 | 
						|
          }
 | 
						|
          close(CMD) or print "Cannot close the command $_\n";
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          print "Command $_ cannot be found\n";
 | 
						|
        }
 | 
						|
 | 
						|
        exit 0;
 | 
						|
      } #elsif
 | 
						|
    }
 | 
						|
  }  #foreach
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 |