#!/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 $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); } return 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; }