xcat-core/perl-xCAT-2.0/xCAT/NotifHandler.pm

339 lines
9.6 KiB
Perl

#!/usr/bin/env perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT::NotifHandler;
use File::Basename qw(fileparse);
#%notif is a cache that holds the info from the "notification" table.
#the format of it is:
# {tablename=>{'a'=>[filename,...]
# 'u'=>[filename,,..]
# 'd'=>[filename,...]
# }
# }
%notif;
$masterpid;
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.
Returns:
none
=cut
#-------------------------------------------------------------------------------
sub setup
{
$masterpid=shift;
if ($masterpid =~ /xCAT::NotifHandler/) {
$masterpid=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 {
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);
}
}
#--------------------------------------------------------------------------------
=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
{
#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)
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 {
if (!defined %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=fork()) { }
elsif (defined($pid)) {
if (($path eq "") || ($path eq ".\/")) {
#default path is /usr/lib/xcat/monitoring/ if there is no path specified
require "/usr/lib/xcat/monitoring/".$modname.".pm";
} else {
require $_;
}
${"xCAT_monitoring::".$modname."::"}{processTableChanges}->($action, $tablename, $old_data, $new_data);
exit 0;
}
}
else { #it is a command
my $pid;
if ($pid=fork()) { }
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;
}