#!/usr/bin/env perl # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html package xCAT_plugin::monctrlcmds; BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; } use lib "$::XCATROOT/lib/perl"; use strict; use xCAT::NodeRange; use xCAT::Table; use xCAT::MsgUtils; use xCAT_monitoring::monitorctrl; use xCAT::Utils; use Sys::Hostname; 1; #------------------------------------------------------------------------------- =head1 xCAT_plugin:monctrlcmds =head2 Package Description xCAT monitoring control commands plugini module. This modules handles monitoring related commands. =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 { monstart => "monctrlcmds", monstop => "monctrlcmds", monls => "monctrlcmds", monadd => "monctrlcmds", monrm => "monctrlcmds", moncfg => "monctrlcmds", mondecfg => "monctrlcmds", } } #------------------------------------------------------- =head3 preprocess_request Check and setup for hierarchy =cut #------------------------------------------------------- sub preprocess_request { my $req = shift; my $callback = shift; my $command = $req->{command}->[0]; if ($req->{_xcatdest}) { return [$req]; } #exit if preprocessed my $args=$req->{arg}; my @requests=(); if (($command eq "monstart") || ($command eq "monstop") || ($command eq "moncfg") || ($command eq "mondecfg") ) { my @a_ret; #(0, $modulename, $nodestatutmon, $scope, \@nodes) if ($command eq "monstart") { @a_ret=preprocess_monstart($args, $callback); } elsif ($command eq "monstop") { @a_ret=preprocess_monstop($args, $callback); } elsif ($command eq "moncfg") { @a_ret=preprocess_moncfg($args, $callback); } else { @a_ret=preprocess_mondecfg($args, $callback); } if ($a_ret[0] != 0) { $req = {}; return; } else { my $allnodes=$a_ret[4]; print "allnodes=@$allnodes\n"; my $pname=$a_ret[1]; my $file_name="$::XCATROOT/lib/perl/xCAT_monitoring/$pname.pm"; my $module_name="xCAT_monitoring::$pname"; undef $SIG{CHLD}; #initialize and start monitoring no strict "refs"; my $mon_hierachy; if (defined(${$module_name."::"}{getNodesMonServers})) { $mon_hierachy = ${$module_name."::"}{getNodesMonServers}->($allnodes, $callback); } else { $mon_hierachy=xCAT_monitoring::monitorctrl->getNodeMonServerPair($allnodes, 1); } my @mon_servers=keys(%$mon_hierachy); my @hostinfo=xCAT::Utils->determinehostname(); #print "hostinfo=@hostinfo\n"; my $isSV=xCAT::Utils->isServiceNode(); my %iphash=(); foreach(@hostinfo) {$iphash{$_}=1;} if (!$isSV) { $iphash{'noservicenode'}=1;} foreach (@mon_servers) { #service node come in pairs, the first one is the monserver adapter that facing the mn, # the second one is facing the cn. we use the first one here my @server_pair=split(',', $_); my $sv=$server_pair[0]; my $mon_nodes=$mon_hierachy->{$_}; if ((!$mon_nodes) || (@$mon_nodes ==0)) { next; } print "sv=$sv, nodes=@$mon_nodes\n"; my $reqcopy = {%$req}; if (! $iphash{$sv}) { if ($isSV) { next; } #if the command is issued on the monserver, # only handle its children. $reqcopy->{'_xcatdest'}=$sv; my $rsp2={}; $rsp2->{data}->[0]="sending request to $sv..."; $callback->($rsp2); } push @{$reqcopy->{module}}, $a_ret[1]; push @{$reqcopy->{nodestatmon}}, $a_ret[2]; push @{$reqcopy->{scope}}, $a_ret[3]; push @{$reqcopy->{nodeinfo}}, join(',', @$mon_nodes); push @requests, $reqcopy; } } } else { my $reqcopy = {%$req}; push @requests, $reqcopy; } return \@requests; } #-------------------------------------------------------------------------------- =head3 process_request It processes the monitoring control commands. Arguments: request -- a hash table which contains the command name and the arguments. callback -- a callback pointer to return the response to. 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; #print "process_request get called\n"; my $request = shift; my $callback = shift; my $command = $request->{command}->[0]; my $args=$request->{arg}; my $doreq = shift; if ($command eq "monstart") { return monstart($request, $callback, $doreq); } elsif ($command eq "monstop") { return monstop($request, $callback, $doreq); } elsif ($command eq "monls") { return monls($request, $callback, $doreq); } elsif ($command eq "monadd") { return monadd($request, $callback, $doreq); } elsif ($command eq "monrm") { return monrm($request, $callback, $doreq); } elsif ($command eq "moncfg") { return moncfg($request, $callback, $doreq); } elsif ($command eq "mondecfg") { return mondecfg($request, $callback, $doreq); } else { my $rsp={}; $rsp->{data}->[0]= "unsupported command: $command."; $callback->($rsp); return 1; } } #-------------------------------------------------------------------------------- =head3 preprocess_monstart This function handles the syntax checking for monstart command, turn on the given monitoring plug-in to the 'monitoring' table. Arguments: callback - the pointer to the callback function. args - The format of the args is: [-h|--help|-v|--version] or name [noderange] [-r|--remote] where name is the monitoring plug-in name. For example: rmcmon. The specified plug-in will be invoked for monitoring the xCAT cluster. noderange a range of nodes to be monitored. Default is all. -r|--remote indicates that both monservers and the nodes need to be called to start the monitoring. The defaults is monservers only. Returns: (0, $modulename, $nodestatutmon, $scope, \@nodes) for success. scope is the scope of the actions. 1 means monervers only, 2 means both nodes and monservers. (1, "") for unsuccess. The error messages are returns through the callback pointer. =cut #-------------------------------------------------------------------------------- sub preprocess_monstart { my $args=shift; my $callback=shift; if (xCAT::Utils->isServiceNode()) { my $rsp={}; $rsp->{data}->[0]= "This command is not supported on a service node."; $callback->($rsp); return (1, ""); } # subroutine to display the usage sub monstart_usage { my $cb=shift; my $rsp={}; $rsp->{data}->[0]= "Usage:"; $rsp->{data}->[1]= " monstart name [noderange] [-r|--remote]"; $rsp->{data}->[2]= " monstart [-h|--help|-v|--version]"; $rsp->{data}->[3]= " name is the name of the monitoring plug-in module to be invoked."; $rsp->{data}->[4]= " Use 'monls -a' command to list all the monitoring plug-in names."; $rsp->{data}->[5]= " noderange is a range of nodes to be monitored. The default is all nodes."; $rsp->{data}->[6]= " -r|--remote indicates that both monservers and the nodes need to be called\n to start the monitoring. The default is monservers only."; $cb->($rsp); } @ARGV=(); if ($args) { @ARGV=@{$args};} my $settings; # parse the options if(!GetOptions( 'h|help' => \$::HELP, 'v|version' => \$::VERSION, 'r|remote' => \$::REMOTE,)) { &monstart_usage($callback); return (1, ""); } # display the usage if -h or --help is specified if ($::HELP) { &monstart_usage($callback); return 1; } # display the version statement if -v or --verison is specified if ($::VERSION) { my $rsp={}; $rsp->{data}->[0]= xCAT::Utils->Version(); $callback->($rsp); return (1, ""); } my $pname=""; my $scope=0; #set it to 0 instead of 1 because it will be distributed to monservers. my @nodes=(); my $nodestatmon=0; if ($::REMOTE) { $scope=2; } if (@ARGV < 1) { &monstart_usage($callback); return (1, ""); } else { #@product_names=split(/,/, $ARGV[0]); $pname=$ARGV[0]; if (@ARGV > 1) { my $noderange=$ARGV[1]; @nodes = noderange($noderange); if (nodesmissed) { my $rsp={}; $rsp->{data}->[0]= "Invalid nodes in noderange:".join(',',nodesmissed); $callback->($rsp); return (1, ""); } } my $file_name="$::XCATROOT/lib/perl/xCAT_monitoring/$pname.pm"; if (!-e $file_name) { my $rsp={}; $rsp->{data}->[0]="File $file_name does not exist."; $callback->($rsp); return (1, ""); } else { #load the module in memory eval {require($file_name)}; if ($@) { my $rsp={}; $rsp->{data}->[0]="The file $file_name has compiling errors:\n$@\n"; $callback->($rsp); return (1, ""); } } } my $table=xCAT::Table->new("monitoring", -create => 1,-autocommit => 1); if ($table) { my $found=0; my $tmp1=$table->getAllEntries("all"); if (defined($tmp1) && (@$tmp1 > 0)) { foreach(@$tmp1) { if ($pname eq $_->{name}) { $found=1; if ($_->{disable} !~ /0|NO|No|no|N|n/) { my %key_col = (name=>$pname); my %tb_cols=(disable=>"0"); $table->setAttribs(\%key_col, \%tb_cols); } if ($_->{nodestatmon} =~ /1|Yes|yes|YES|Y|y/) { $nodestatmon=1;} last; } } } if (!$found) { my $rsp={}; $rsp->{data}->[0]="$pname has not been added to the monitoring table. Please run 'monadd' command to add."; $callback->($rsp); $table->close(); return (1, ""); } $table->close(); } else { my $rsp={}; $rsp->{data}->[0]="Failed to open the monitoring table."; $callback->($rsp); return (1, ""); } return (0, $pname, $nodestatmon, $scope, \@nodes); } #-------------------------------------------------------------------------------- =head3 monstart This function calls moniutoring control to start the monitoring and node status monitoring for the given plug-in module. Arguments: request -- pointer to a hash with keys are command, module and nodestatmon. callback - the pointer to the callback function. 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 monstart { my $request=shift; my $callback=shift; my $pname=$request->{module}->[0]; my $nodestatmon=$request->{nodestatmon}->[0]; my $scope=$request->{scope}->[0]; my $nodeinfo=$request->{nodeinfo}->[0]; my @nodes=split(',', $nodeinfo); print "monstart get called: pname=$pname\nnodestatmon=$nodestatmon\nnodeinfo=$nodeinfo\nscope=$scope\n"; xCAT_monitoring::monitorctrl->startMonitoring([$pname], \@nodes, $scope, $callback); if ($nodestatmon) { xCAT_monitoring::monitorctrl->startNodeStatusMonitoring($pname, \@nodes, $scope, $callback); } return; } #-------------------------------------------------------------------------------- =head3 preprocess_monstop This function unregisters the given monitoring plug-in from the 'monitoring' table. Arguments: callback - the pointer to the callback function. args - The format of the args is: [-h|--help|-v|--version] or name [noderange] [-r|--remote] name where name is the monitoring plug-in name. For example: rmcmon. The specified plug-in will be stoped for monitoring the xCAT cluster. noderange a range of nodes. Default is all. -r|--remote indicates that both monservers and the nodes need to be called to stop the monitoring. The defaults is monservers only. Returns: (0, $modulename, $nodestatutmon, $scope, \@nodes) for success. scope is the scope of the actions. 1 means monervers only, 2 means both nodes and monservers. (1, "") for unsuccess. The error messages are returns through the callback pointer. =cut #-------------------------------------------------------------------------------- sub preprocess_monstop { my $args=shift; my $callback=shift; if (xCAT::Utils->isServiceNode()) { my $rsp={}; $rsp->{data}->[0]= "This command is not supported on a service node."; $callback->($rsp); return (1, ""); } # subroutine to display the usage sub monstop_usage { my $cb=shift; my $rsp={}; $rsp->{data}->[0]= "Usage:"; $rsp->{data}->[1]= " monstop name [noderange] [-r|--remote]"; $rsp->{data}->[2]= " monstop [-h|--help|-v|--version]"; $rsp->{data}->[3]= " name is the name of the monitoring plug-in module registered in the monitoring table."; $cb->($rsp); } @ARGV=(); if ($args) { @ARGV=@{$args};} # parse the options if(!GetOptions( 'h|help' => \$::HELP, 'r|remote' => \$::REMOTE, 'v|version' => \$::VERSION,)) { &monstop_usage($callback); return (1, ""); } # display the usage if -h or --help is specified if ($::HELP) { &monstop_usage($callback); return (1, ""); } # display the version statement if -v or --verison is specified if ($::VERSION) { my $rsp={}; $rsp->{data}->[0]= xCAT::Utils->Version();; $callback->($rsp); return (1, ""); } my $pname=""; my $scope=0; my @nodes=(); my $nodestatmon=0; if ($::REMOTE) { $scope=2;} if (@ARGV < 1) { &monstop_usage($callback); return (1, ""); } else { $pname=$ARGV[0]; if (@ARGV > 1) { my $noderange=$ARGV[1]; @nodes = noderange($noderange); if (nodesmissed) { my $rsp={}; $rsp->{data}->[0]= "Invalid nodes in noderange:".join(',',nodesmissed); $callback->($rsp); return (1, ""); } } my $file_name="$::XCATROOT/lib/perl/xCAT_monitoring/$pname.pm"; if (!-e $file_name) { my $rsp={}; $rsp->{data}->[0]="File $file_name does not exist."; $callback->($rsp); return (1, ""); } else { #load the module in memory eval {require($file_name)}; if ($@) { my $rsp={}; $rsp->{data}->[0]="The file $file_name has compiling errors:\n$@\n"; $callback->($rsp); return (1, ""); } } } my $table=xCAT::Table->new("monitoring", -create => 1,-autocommit => 1); if ($table) { my $found=0; my $tmp1=$table->getAllEntries("all"); if (defined($tmp1) && (@$tmp1 > 0)) { foreach(@$tmp1) { if ($pname eq $_->{name}) { $found=1; if ($_->{disable} =~ /0|NO|No|no|N|n/) { my %key_col = (name=>$pname); my %tb_cols=(disable=>"1"); $table->setAttribs(\%key_col, \%tb_cols); } if ($_->{nodestatmon} =~ /1|Yes|yes|YES|Y|y/) { $nodestatmon=1;} last; } } } if (!$found) { my $rsp={}; $rsp->{data}->[0]="$pname cannot be found in the monitoring table."; $callback->($rsp); $table->close(); return (1, ""); } $table->close(); } else { my $rsp={}; $rsp->{data}->[0]="Failed to open the monitoring table."; $callback->($rsp); return (1, ""); } return (0, $pname, $nodestatmon, $scope, \@nodes); } #-------------------------------------------------------------------------------- =head3 monstop This function calls moniutoring control to stop the monitoring and node status monitoring for the given plug-in module. Arguments: request -- pointer to a hash with keys are command, module and nodestatmon. callback - the pointer to the callback function. 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 monstop { my $request=shift; my $callback=shift; my $pname=$request->{module}->[0]; my $nodestatmon=$request->{nodestatmon}->[0]; my $scope=$request->{scope}->[0]; my $nodeinfo=$request->{nodeinfo}->[0]; my @nodes=split(',', $nodeinfo); print "monstop get called: pname=$pname\nnodestatmon=$nodestatmon\nnodeinfo=@nodes\nscope=$scope\n"; if ($nodestatmon) { xCAT_monitoring::monitorctrl->stopNodeStatusMonitoring($pname, \@nodes, $scope, $callback); } xCAT_monitoring::monitorctrl->stopMonitoring([$pname], \@nodes, $scope, $callback); return; } #-------------------------------------------------------------------------------- =head3 monls This function list the monitoring plug-in module names, status and description. Arguments: callback - the pointer to the callback function. args - The format of the args is: [-h|--help|-v|--version] or [name] [-a|all] [-d|--description] 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 monls { my $request = shift; my $callback = shift; my $args=$request->{arg}; my $doreq = shift; # subroutine to display the usage sub monls_usage { my $cb=shift; my $rsp={}; $rsp->{data}->[0]= "Usage:"; $rsp->{data}->[1]= " monls name [-d|--description]"; $rsp->{data}->[2]= " monls [-a|--all] [-d|--description]"; $rsp->{data}->[3]= " monls [-h|--help|-v|--version]"; $rsp->{data}->[4]= " name is the name of the monitoring plug-in module."; $cb->($rsp); } @ARGV=(); if ($args) { @ARGV=@{$args}; } # parse the options if(!GetOptions( 'h|help' => \$::HELP, 'v|version' => \$::VERSION, 'a|all' => \$::ALL, 'd|discription' => \$::DESC)) { &monls_usage($callback); return; } # display the usage if -h or --help is specified if ($::HELP) { &monls_usage($callback); return; } # display the version statement if -v or --verison is specified if ($::VERSION) { my $rsp={}; $rsp->{data}->[0]= xCAT::Utils->Version(); $callback->($rsp); return; } my $usetab=0; my %names=(); my $plugin_dir="$::XCATROOT/lib/perl/xCAT_monitoring"; if (@ARGV > 0) { $names{$ARGV[0]}=0; } else { if ($::ALL) { #get all the module names from /opt/xcat/lib/perl/XCAT_monitoring directory my @plugins=glob($plugin_dir."/*.pm"); foreach (@plugins) { /.*\/([^\/]*).pm$/; $names{$1}=0; } # remove 2 files that are not plug-ins delete($names{monitorctrl}); delete($names{montbhandler}); } else { $usetab=1; } } #get the list from the table my $table=xCAT::Table->new("monitoring", -create =>1); if ($table) { my $tmp1=$table->getAllEntries("all"); if (defined($tmp1) && (@$tmp1 > 0)) { foreach(@$tmp1) { my $pname=$_->{name}; if (($usetab) || exists($names{$pname})) { $names{$pname}=1; #find out the monitoring plugin file and module name for the product my $rsp={}; my $file_name="$::XCATROOT/lib/perl/xCAT_monitoring/$pname.pm"; my $module_name="xCAT_monitoring::$pname"; #load the module in memory eval {require($file_name)}; if ($@) { $rsp->{data}->[0]="$pname: The file $file_name cannot be located or has compiling errors."; $callback->($rsp); next; } else { no strict "refs"; if (! defined(${$module_name."::"}{start})) { next; } } my $monnode=0; my $disable=1; if ($_->{nodestatmon} =~ /1|Yes|yes|YES|Y|y/) { $monnode=1; } if ($_->{disable} =~ /0|NO|No|no|N|n/) { $disable=0; } if ($disable) { $monnode=0; } $rsp->{data}->[0]="$pname\t\t". ($disable ? "not-monitored" : "monitored") . ($monnode ? "\tnode-status-monitored" : ""); if ($::DESC) { getModuleDescription($rsp, $module_name); } $callback->($rsp); } } #foreach } $table->close(); } #now handle the ones that are not in the table foreach(keys(%names)) { my $pname=$_; if (! $names{$pname}) { my $rsp={}; #find out the monitoring plugin file and module name for the product my $file_name="$::XCATROOT/lib/perl/xCAT_monitoring/$pname.pm"; my $module_name="xCAT_monitoring::$pname"; #load the module in memory eval {require($file_name)}; if ($@) { $rsp->{data}->[0]="$pname: The file $file_name cannot be located or has compiling errors."; $callback->($rsp); next; } else { no strict "refs"; if (! defined(${$module_name."::"}{start})) { next; } } $rsp->{data}->[0]="$pname\t\tnot-monitored"; if ($::DESC) { getModuleDescription($rsp, $module_name); } $callback->($rsp); } } return; } #-------------------------------------------------------------------------------- =head3 getModuleDescription This function gets description, postscripts and other info from the the given monitoring plug_in and stored it in the given hash. Arguments: Returns: 0 for success. 1. for unsuccess. =cut #-------------------------------------------------------------------------------- sub getModuleDescription { my $rsp=shift; my $module_name=shift; no strict "refs"; #description if (defined(${$module_name."::"}{getDescription})) { $rsp->{data}->[1]=${$module_name."::"}{getDescription}->(); } else { $rsp->{data}->[1]=" No description available."; } #postscripts $rsp->{data}->[2] = " Postscripts:\n"; if (defined(${$module_name."::"}{getPostscripts})) { my $desc=${$module_name."::"}{getPostscripts}->(); my @pn=keys(%$desc); if (@pn>0) { foreach my $group (@pn) { $rsp->{data}->[2] .= " $group: " . $desc->{$group}; } } else { $rsp->{data}->[2] .= " None";} } else { $rsp->{data}->[2] .= " None";} #support node status monitoring $rsp->{data}->[3] = " Support node status monitoring:\n"; my $snodestatusmon=0; if (defined(${$module_name."::"}{supportNodeStatusMon})) { $snodestatusmon=${$module_name."::"}{supportNodeStatusMon}->(); } if ($snodestatusmon) { $rsp->{data}->[3] .= " Yes\n";} else { $rsp->{data}->[3] .= " No\n"; } return 0; } #-------------------------------------------------------------------------------- =head3 monadd This function adds the given module name into the monitoring table and sets the postsctipts in the postsctipts table. It also sets the given settings into the monsetting table. Arguments: request -- a hash table which contains the command name and the arguments. callback - the pointer to the callback function. args - The format of the args is: [-h|--help|-v|--version] or name [-n|--nodestatmon] [-s|--settings ...] where name is the monitoring plug-in name. For example: rmcmon. The specified plug-in will be registered and invoked for monitoring the xCAT cluster. -n|--nodestatmon indicates that this plug-in will be used for feeding the node liveness status to the xCAT nodelist table. If not specified, the plug-in will not be used for feeding node status to xCAT. -s|--settings settings are used by the plug-in to customize it behavor. 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 monadd { my $request = shift; my $callback = shift; my $args=$request->{arg}; my $doreq = shift; # subroutine to display the usage sub monadd_usage { my $cb=shift; my $rsp={}; $rsp->{data}->[0]= "Usage:"; $rsp->{data}->[1]= " monadd name [-n|--nodestatmon] [-s|--settings settings]"; $rsp->{data}->[2]= " monadd [-h|--help|-v|--version]"; $rsp->{data}->[3]= " name is the name of the monitoring plug-in module to be added."; $rsp->{data}->[4]= " Use 'monls -a' command to list all the monitoring plug-in names."; $rsp->{data}->[5]= " settings is used by the monitoring plug-in to customize its behavior."; $rsp->{data}->[6]= " Format: [key1=value1],[key2=value2]... "; $rsp->{data}->[7]= " Please note that the square brackets are needed. "; $rsp->{data}->[7]= " Use 'monls name -d' command to look for the possible settings for a plug-in."; $rsp->{data}->[8]= " Example: monadd xcatmon -n -s [ping-interval=10]"; $cb->($rsp); } @ARGV=(); if ($args) { @ARGV=@{$args};} my $settings; # parse the options if(!GetOptions( 'h|help' => \$::HELP, 'v|version' => \$::VERSION, 'n|nodestatmon' => \$::NODESTATMON, 's|settings=s' => \$settings)) { &monadd_usage($callback); return 1; } # display the usage if -h or --help is specified if ($::HELP) { &monadd_usage($callback); return 1; } # display the version statement if -v or --verison is specified if ($::VERSION) { my $rsp={}; $rsp->{data}->[0]= xCAT::Utils->Version(); $callback->($rsp); return 1; } #my @product_names; my $pname; my $nodestatmon=0; if (@ARGV < 1) { &monadd_usage($callback); return 1; } else { #@product_names=split(/,/, $ARGV[0]); $pname=$ARGV[0]; my $file_name="$::XCATROOT/lib/perl/xCAT_monitoring/$pname.pm"; if (!-e $file_name) { my $rsp={}; $rsp->{data}->[0]="File $file_name does not exist."; $callback->($rsp); return 1; } else { #load the module in memory eval {require($file_name)}; if ($@) { my $rsp={}; $rsp->{data}->[0]="The file $file_name has compiling errors:\n$@\n"; $callback->($rsp); return 1; } } } my $table=xCAT::Table->new("monitoring", -create =>1); if ($table) { my $tmp1=$table->getAllEntries("all"); if (defined($tmp1) && (@$tmp1 > 0)) { foreach(@$tmp1) { my $name=$_->{name}; if ($name eq $pname) { my $rsp={}; $rsp->{data}->[0]="$pname has already been added in the monitoring table."; $callback->($rsp); $table->close(); return 1; } } } my $module_name="xCAT_monitoring::$pname"; #check if the module suppors node status monitoring or not. if ($::NODESTATMON) { no strict "refs"; my $snodestatusmon=0; if (defined(${$module_name."::"}{supportNodeStatusMon})) { $snodestatusmon=${$module_name."::"}{supportNodeStatusMon}->(); } if (!$snodestatusmon) { my $rsp={}; $rsp->{data}->[0]="$pname does not support node status monitoring."; $callback->($rsp); $table->close(); return 1; } } #update the monsetting table if ($settings) { my $table1=xCAT::Table->new("monsetting", -create => 1,-autocommit => 1); my %key_col1 = (name=>$pname); #parse the settings. Setting format: key="value",key="value".... while ($settings =~ s/^\[([^\[\]\=]*)=([^\[\]]*)\](,)*//) { $key_col1{key}=$1; my %setting_hash=(); $setting_hash{value}=$2; $table1->setAttribs(\%key_col1, \%setting_hash); } $table1->close(); } #update the monitoring table my %key_col = (name=>$pname); my $nstat='N'; if ($::NODESTATMON) { $nstat='Y'; $nodestatmon=1; } my %tb_cols=(nodestatmon=>$nstat, disable=>"1"); $table->setAttribs(\%key_col, \%tb_cols); $table->close(); #updating the postscript table no strict "refs"; my $postscripts_h={}; if (defined(${$module_name."::"}{getPostscripts})) { my $postscripts_h=${$module_name."::"}{getPostscripts}->(); my @pn=keys(%$postscripts_h); if (@pn>0) { my $table2=xCAT::Table->new("postscripts", -create =>1); if (!$table2) { my $rsp={}; $rsp->{data}->[0]="Cannot open the postscripts table.\nFailed to set the postscripts for $pname."; $callback->($rsp); return 1; } foreach my $group (@pn) { my $posts=$postscripts_h->{$group}; if ($posts) { (my $ref) = $table2->getAttribs({node => $group}, 'postscripts'); if ($ref and $ref->{postscripts}) { my @old_a=split(',', $ref->{postscripts}); my @new_a=split(',', $posts); my %new_h=(); foreach my $new_tmp (@new_a) { my $found=0; foreach my $old_tmp (@old_a) { if ($old_tmp eq $new_tmp) { $found=1; last; } } if (!$found) { $new_h{$new_tmp}=1;} } if (keys(%new_h) > 0) { foreach (keys(%new_h)) { push(@old_a, $_); } my $new_post=join(',', @old_a); my %key_col2 = (node=>$group); my %tb_cols2=(postscripts=>$new_post); $table2->setAttribs(\%key_col2, \%tb_cols2); } } else { my %key_col2 = (node=>$group); my %tb_cols2=(postscripts=>$posts); $table2->setAttribs(\%key_col2, \%tb_cols2); } } } $table2->close(); } } } else { my $rsp={}; $rsp->{data}->[0]="Failed to open the monitoring table."; $callback->($rsp); return 1; } return 0; } #-------------------------------------------------------------------------------- =head3 monrm This function removes the given monitoring plug-in from the 'monitoring' table. It also removed the postscritps for the module from the 'postscritps' table. Arguments: request -- a hash table which contains the command name and the arguments. callback - the pointer to the callback function. args - The format of the args is: [-h|--help|-v|--version] or name where name is the monitoring plug-in name. For example: rmcmon. The specified plug-in will be stopped for monitoring the xCAT cluster if it is running and then removed from the monitoring table. Returns: 0 for success. 1 for unsuccess. The error messages are returns through the callback pointer. =cut #-------------------------------------------------------------------------------- sub monrm { my $request = shift; my $callback = shift; my $args=$request->{arg}; my $doreq = shift; if (xCAT::Utils->isServiceNode()) { my $rsp={}; $rsp->{data}->[0]= "This command is not supported on a service node."; $callback->($rsp); return (1, ""); } # subroutine to display the usage sub monrm_usage { my $cb=shift; my $rsp={}; $rsp->{data}->[0]= "Usage:"; $rsp->{data}->[1]= " monrm name"; $rsp->{data}->[2]= " monrm [-h|--help|-v|--version]"; $rsp->{data}->[3]= " name is the name of the monitoring plug-in module registered in the monitoring table."; $cb->($rsp); } @ARGV=(); if ($args) { @ARGV=@{$args};} # parse the options if(!GetOptions( 'h|help' => \$::HELP, 'v|version' => \$::VERSION,)) { &monrm_usage($callback); return (1, ""); } # display the usage if -h or --help is specified if ($::HELP) { &monrm_usage($callback); return (1, ""); } # display the version statement if -v or --verison is specified if ($::VERSION) { my $rsp={}; $rsp->{data}->[0]= xCAT::Utils->Version();; $callback->($rsp); return (1, ""); } my $pname; if (@ARGV < 1) { &monrm_usage($callback); return (1, ""); } else { $pname=$ARGV[0]; } my $disable=1; my $found=0; my $table=xCAT::Table->new("monitoring", -create =>1); if ($table) { my $tmp1=$table->getAllEntries("all"); if (defined($tmp1) && (@$tmp1 > 0)) { foreach(@$tmp1) { if ($pname eq $_->{name}) { if ($_->{disable} =~ /0|NO|No|no|N|n/) { $disable=0; } $found=1; } } } if (!$found) { my $rsp={}; $rsp->{data}->[0]="$pname is not in the monitoring talble."; $callback->($rsp); $table->close(); return 0; } if (!$disable) { my $rsp={}; $rsp->{data}->[0]="Please run command 'monstop $pname' to stop monitoring before running this command."; $callback->($rsp); $table->close(); return 0; } my %key_col = (name=>$pname); $table->delEntries(\%key_col); $table->close(); #remove the postscripts for the module from the postscript table no strict "refs"; my $file_name="$::XCATROOT/lib/perl/xCAT_monitoring/$pname.pm"; my $module_name="xCAT_monitoring::$pname"; if (!-e $file_name) { return 0; } else { #load the module in memory eval {require($file_name)}; if ($@) { return 0; } } my $postscripts_h={}; if (defined(${$module_name."::"}{getPostscripts})) { my $postscripts_h=${$module_name."::"}{getPostscripts}->(); my @pn=keys(%$postscripts_h); if (@pn>0) { my $table2=xCAT::Table->new("postscripts", -create =>1); if (!$table2) { my $rsp={}; $rsp->{data}->[0]="Cannot open the postscripts table.\nFailed to remove the postscripts for $pname."; $callback->($rsp); return 1; } foreach my $group (@pn) { my $posts=$postscripts_h->{$group}; if ($posts) { (my $ref) = $table2->getAttribs({node => $group}, 'postscripts'); if ($ref and $ref->{postscripts}) { my @old_a=split(',', $ref->{postscripts}); my @new_a=split(',', $posts); my %new_h=(); my @new_post_a=(); foreach my $old_tmp (@old_a) { my $found=0; foreach my $new_tmp (@new_a) { if ($old_tmp eq $new_tmp) { $found=1; last; } } if (!$found) { push(@new_post_a,$old_tmp); } } if (@new_post_a > 0) { my $new_post=join(',', @new_post_a); if ( $new_post ne $ref->{postscripts} ) { my %key_col2 = (node=>$group); my %tb_cols2=(postscripts=>$new_post); $table2->setAttribs(\%key_col2, \%tb_cols2); } } else { my %key_col2 = (node=>$group); $table2->delEntries(\%key_col2); } } } } $table2->close(); } } } else { my $rsp={}; $rsp->{data}->[0]="Cannot open monitoring table."; $callback->($rsp); return 1; } return 0; } #-------------------------------------------------------------------------------- =head3 preprocess_moncfg This function handles the syntax checking for moncfg command. Arguments: callback - the pointer to the callback function. args - The format of the args is: [-h|--help|-v|--version] or name [noderange] [-r|--remote] where name is the monitoring plug-in name. For example: rmcmon. The specified plug-in will be invoked for configuring the cluster to monitor the nodes. noderange a range of nodes to be configured for. Default is all. -r|--remote indicates that both monservers and the nodes need to configured. The defaults is monservers only. Returns: (0, $modulename, $nodestatutmon, $scope, \@nodes) for success. scope is the scope of the actions. 1 means monervers only, 2 means both nodes and monservers. (1, "") for unsuccess. The error messages are returns through the callback pointer. =cut #-------------------------------------------------------------------------------- sub preprocess_moncfg { my $args=shift; my $callback=shift; # subroutine to display the usage sub moncfg_usage { my $cb=shift; my $rsp={}; $rsp->{data}->[0]= "Usage:"; $rsp->{data}->[1]= " moncfg name [noderange] [-r|--remote]"; $rsp->{data}->[2]= " moncfg [-h|--help|-v|--version]"; $rsp->{data}->[3]= " name is the name of the monitoring plug-in module to be invoked."; $rsp->{data}->[4]= " Use 'monls -a' command to list all the monitoring plug-in names."; $rsp->{data}->[5]= " noderange is a range of nodes to be configured for. The default is all nodes."; $rsp->{data}->[6]= " -r|--remote indicates that both monservers and the nodes need to be configured.\n The default is monservers only."; $rsp->{data}->[7]= " The default is monservers only."; $cb->($rsp); } @ARGV=(); if ($args) { @ARGV=@{$args};} # parse the options if(!GetOptions( 'h|help' => \$::HELP, 'v|version' => \$::VERSION, 'r|remote' => \$::REMOTE,)) { &moncfg_usage($callback); return; } # display the usage if -h or --help is specified if ($::HELP) { &moncfg_usage($callback); return; } # display the version statement if -v or --verison is specified if ($::VERSION) { my $rsp={}; $rsp->{data}->[0]= xCAT::Utils->Version(); $callback->($rsp); return; } my $pname=""; my $scope=0; my @nodes=(); my $nodestatmon=0; if ($::REMOTE) { $scope=2;} if (@ARGV < 1) { &moncfg_usage($callback); return (1, ""); } else { $pname=$ARGV[0]; if (@ARGV > 1) { my $noderange=$ARGV[1]; @nodes = noderange($noderange); if (nodesmissed) { my $rsp={}; $rsp->{data}->[0]= "Invalid nodes in noderange:".join(',',nodesmissed); $callback->($rsp); return (1, ""); } } my $file_name="$::XCATROOT/lib/perl/xCAT_monitoring/$pname.pm"; if (!-e $file_name) { my $rsp={}; $rsp->{data}->[0]="File $file_name does not exist."; $callback->($rsp); return (1, ""); } else { #load the module in memory eval {require($file_name)}; if ($@) { my $rsp={}; $rsp->{data}->[0]="The file $file_name has compiling errors:\n$@\n"; $callback->($rsp); return (1, ""); } } } my $table=xCAT::Table->new("monitoring", -create => 1,-autocommit => 1); if ($table) { my $found=0; my $tmp1=$table->getAllEntries("all"); if (defined($tmp1) && (@$tmp1 > 0)) { foreach(@$tmp1) { if ($pname eq $_->{name}) { $found=1; if ($_->{nodestatmon} =~ /1|Yes|yes|YES|Y|y/) { $nodestatmon=1;} last; } } } if (!$found) { my $rsp={}; $rsp->{data}->[0]="$pname cannot be found in the monitoring table."; $callback->($rsp); $table->close(); return (1, ""); } $table->close(); } else { my $rsp={}; $rsp->{data}->[0]="Failed to open the monitoring table."; $callback->($rsp); return (1, ""); } return (0, $pname, $nodestatmon, $scope, \@nodes); } #-------------------------------------------------------------------------------- =head3 moncfg This function configures the cluster for the given nodes. It includes configuring and setting up the 3rd party monitoring software for monitoring the given nodes. Arguments: request -- a hash table which contains the command name and the arguments. callback -- the callback pointer for error and status displaying. It can be null. 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 moncfg { my $request=shift; my $callback=shift; my $pname=$request->{module}->[0]; my $nodestatmon=$request->{nodestatmon}->[0]; my $scope=$request->{scope}->[0]; my $nodeinfo=$request->{nodeinfo}->[0]; my @nodes=split(',', $nodeinfo); print "moncfg get called: pname=$pname\nnodestatmon=$nodestatmon\nnodeinfo=@nodes\nscope=$scope\n"; xCAT_monitoring::monitorctrl->config([$pname], \@nodes, $scope, $callback); return 0; } #-------------------------------------------------------------------------------- =head3 preprocess_mondecfg This function handles the syntax checking for mondecfg command. Arguments: callback - the pointer to the callback function. args - The format of the args is: [-h|--help|-v|--version] or name [noderange] [-r|--remote] where name is the monitoring plug-in name. For example: rmcmon. The specified plug-in will be invoked for deconfiguring the cluster to monitor the nodes. noderange a range of nodes to be deconfigured for. Default is all. -r|--remote indicates that both monservers and the nodes need to be deconfigured. The defaults is monservers only. Returns: (0, $modulename, $nodestatutmon, $scope, \@nodes) for success. scope is the scope of the actions. 1 means monervers only, 2 means both nodes and monservers. (1, "") for unsuccess. The error messages are returns through the callback pointer. =cut #-------------------------------------------------------------------------------- sub preprocess_mondecfg { my $args=shift; my $callback=shift; # subroutine to display the usage sub mondecfg_usage { my $cb=shift; my $rsp={}; $rsp->{data}->[0]= "Usage:"; $rsp->{data}->[1]= " mondecfg name [noderange] [-r|--remote]"; $rsp->{data}->[2]= " mondecfg [-h|--help|-v|--version]"; $rsp->{data}->[3]= " name is the name of the monitoring plug-in module to be invoked."; $rsp->{data}->[4]= " Use 'monls -a' command to list all the monitoring plug-in names."; $rsp->{data}->[5]= " noderange is a range of nodes to be deconfigured for."; $rsp->{data}->[6]= " The default is all nodes."; $rsp->{data}->[7]= " -r|--remote indicates that both monservers and the nodes need to be deconfigured."; $rsp->{data}->[8]= " The default is monservers only."; $cb->($rsp); } @ARGV=(); if ($args) { @ARGV=@{$args} ; } # parse the options if(!GetOptions( 'h|help' => \$::HELP, 'v|version' => \$::VERSION, 'r|remote' => \$::REMOTE,)) { &mondecfg_usage($callback); return; } # display the usage if -h or --help is specified if ($::HELP) { &mondecfg_usage($callback); return; } # display the version statement if -v or --verison is specified if ($::VERSION) { my $rsp={}; $rsp->{data}->[0]= xCAT::Utils->Version(); $callback->($rsp); return; } my $pname=""; my $scope=0; my @nodes=(); my $nodestatmon=0; if ($::REMOTE) { $scope=2;} if (@ARGV < 1) { &mondecfg_usage($callback); return (1, ""); } else { $pname=$ARGV[0]; if (@ARGV > 1) { my $noderange=$ARGV[1]; @nodes = noderange($noderange); if (nodesmissed) { my $rsp={}; $rsp->{data}->[0]= "Invalid nodes in noderange:".join(',',nodesmissed); $callback->($rsp); return (1, ""); } } my $file_name="$::XCATROOT/lib/perl/xCAT_monitoring/$pname.pm"; if (!-e $file_name) { my $rsp={}; $rsp->{data}->[0]="File $file_name does not exist."; $callback->($rsp); return (1, ""); } else { #load the module in memory eval {require($file_name)}; if ($@) { my $rsp={}; $rsp->{data}->[0]="The file $file_name has compiling errors:\n$@\n"; $callback->($rsp); return (1, ""); } } } my $table=xCAT::Table->new("monitoring", -create => 1,-autocommit => 1); if ($table) { my $found=0; my $tmp1=$table->getAllEntries("all"); if (defined($tmp1) && (@$tmp1 > 0)) { foreach(@$tmp1) { if ($pname eq $_->{name}) { $found=1; if ($_->{nodestatmon} =~ /1|Yes|yes|YES|Y|y/) { $nodestatmon=1;} last; } } } if (!$found) { my $rsp={}; $rsp->{data}->[0]="$pname cannot be found in the monitoring table."; $callback->($rsp); $table->close(); return (1, ""); } $table->close(); } else { my $rsp={}; $rsp->{data}->[0]="Failed to open the monitoring table."; $callback->($rsp); return (1, ""); } return (0, $pname, $nodestatmon, $scope, \@nodes); } #-------------------------------------------------------------------------------- =head3 mondecfg This function deconfigures the cluster for the given nodes. It includes deconfiguring and clearning up the 3rd party monitoring software for monitoring the given nodes. Arguments: names -- a pointer to an array of monitoring plug-in names. If non is specified, all the plug-ins registered in the monitoring table will be notified. p_nodes -- a pointer to an arrays of nodes to be removed from the monitoring domain. none means all. scope -- the action scope, it indicates the node type the action will take place. 0 means localhost only. 1 means monserver only, 2 means both monservers and nodes, callback -- the callback pointer for error and status displaying. It can be null. 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 mondecfg { my $request=shift; my $callback=shift; my $pname=$request->{module}->[0]; my $nodestatmon=$request->{nodestatmon}->[0]; my $scope=$request->{scope}->[0]; my $nodeinfo=$request->{nodeinfo}->[0]; my @nodes=split(',', $nodeinfo); print "mondecfg get called: pname=$pname\nnodestatmon=$nodestatmon\nnodeinfo=@nodes\nscope=$scope\n"; xCAT_monitoring::monitorctrl->deconfig([$pname], \@nodes, $scope, $callback); return 0; }