xcat-core/xCAT-rmc/scripts/perl/mkrmcresources

577 lines
17 KiB
Plaintext
Raw Normal View History

#!/usr/bin/env perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
#-------------------------------------------------------------------------------
=head1 mkrmcresources
=head2 mkrmcresources is used to predefine RMC conditions, responses, associations,
sensors (and can be extended to support any RSCT resource
class).
To use the command, create perl modules in a directory. Each resource
should have its own perl module (so that it is easy to update a
resource without interfering with other resources),
and should be named <Resource Name>.pm.
After the resource perl modules are installed, they will be created
by the next execution of the this command.
This command should be called by the post install scripts
of packaging files, script run after install or from the command line.
=cut
#-------------------------------------------------------------------------------
BEGIN
{
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl/xCAT_monitoring/rmc";
use Getopt::Long;
use NodeUtils;
$Getopt::Long::ignorecase = 0; #Checks case in GetOptions
Getopt::Long::Configure("bundling"); #allows short command line options to be grouped (e.g. -av)
#--------------------------------------------------------------------------------
=head3 queryResources
Queries all resources of a given class or classes. Places
results into a global hash for each resource class.
Arguments: a list of RSCT resource classes
Globals: %::EXISTS::{$resource}
=cut
#--------------------------------------------------------------------------------
sub queryResources
{
my @resources = @_;
my $where = "";
foreach my $res (@resources)
{
if ($res eq "IBM.Association")
{
#special case: run lscondresp because Associations do not have names
#cant run lsrsrc because Assoctation also does not store names of resources (just handles)
my @condresp = NodeUtils->runcmd("LANG=C /usr/bin/lscondresp");
my $class = $res;
$class =~ s/^IBM\.//;
splice @condresp, 0,
2; #delete first two lines -- they are just comments
foreach my $line (@condresp)
{
my ($condition, $response, $node, $state) = split ' ', $line;
$condition = &removeQuotes($condition);
$response = &removeQuotes($response);
my $key = "${condition}:_:${response}";
my $ActiveFlag = 0; #assume offline
if ($state =~ m/Active/)
{
$ActiveFlag = 1;
}
#currently does not checked for locked
# This \%{typeglob} syntax auto-vivifies
# the hash table for us, and gives us a reference.
my $ref = \%{$::EXISTS::{$class}};
$ref->{$key} = {ActiveFlag => $ActiveFlag,};
}
}
else
{
$where .= " -s ${res}::::'*p0x0020'";
}
}
my $output = NodeUtils->runrmccmd("lsrsrc-api", "-i -m -n -D ':|:'", $where);
foreach my $line (@$output)
{
my @array = split(/:\|:/, $line);
my $class = shift @array; #the -m flag puts the class name in front
$class =~ s/^IBM\.//;
my %attrs = @array;
# This \%{typeglob} syntax auto-vivifies
# the hash table for us, and gives us a reference.
my $ref = \%{$::EXISTS::{$class}};
my $key = $attrs{'Name'};
$ref->{$key} = {%attrs}; #sets the EXISTS array with the info
}
}
#--------------------------------------------------------------------------------
=head3 traverseDirectories
Calls readFiles on each sub-directory of the given path.
Creates a global array with all target resource classes.
Arguments: A directory
Globals: @::DIRECTORIES (will hold all resource classes)
=cut
#--------------------------------------------------------------------------------
sub traverseDirectories
{
my ($dir) = @_;
my ($dir_fh, $file);
opendir($dir_fh, $dir)
or die "Can not open directory $dir\n";
while ($file = readdir($dir_fh))
{
if ($file ne '.' and $file ne '..')
{
my $subdir = "$dir/$file";
if (-d $subdir)
{ #only look at directories
&readFiles($subdir);
push @::DIRECTORIES, $file; #file=just the filename
}
}
}
closedir($dir_fh)
or die "Can not close directory $dir\n";
}
#--------------------------------------------------------------------------------
=head3 readFiles
Calls require on all .pm files in a given directory
Arguments: A directory
=cut
#--------------------------------------------------------------------------------
sub readFiles
{
my ($dir) = @_;
my ($dir_fh, $file);
opendir($dir_fh, $dir)
or die "Can not open directory $dir\n";
while ($file = readdir($dir_fh))
{
if ($file ne '.' and $file ne '..')
{
$file = "$dir/$file";
if ($file =~ m/\.pm$/)
{
#its a perl module
require $file;
}
}
}
closedir($dir_fh)
or die "Can not close directory $dir\n";
}
#--------------------------------------------------------------------------------
=head3 compareResources
Compares existing resources to those requiring definition.
Globals: uses %::EXISTS and %::RES and makes %::CHANGE and %::CREATE
=cut
#--------------------------------------------------------------------------------
sub compareResources
{
foreach my $class (@::DIRECTORIES)
{ #this has all subdirectory names
$class =~ s/^IBM\.//; #the IBM prefix is not used in the hash name
local *exi = $::EXISTS::{$class}; #defined on system
local *res = $::RES::{$class}; #defined in file
foreach my $resource (keys %res)
{
if (defined $exi{$resource})
{ #exists on the system
if (defined $res{$resource}{'Locked'}
&& $res{$resource}{'Locked'} == 1)
{
#only change the resource if it is supposed to be locked
foreach my $attr (keys %{$res{$resource}})
{
if ($exi{$resource}{$attr} ne $res{$resource}{$attr})
{
if (!($class eq "Association" && $attr eq "Locked"))
{ # association locked attrs are not stored
# something has changed
if ($::VERBOSE)
{
print "Differs: Class=$class\tExists=$exi{$resource}{$attr}\tDefined=$res{$resource}{$attr}\n";
}
$::CHANGE::{$class}{$resource} = $res{$resource};
last;
}
}
}
}
}
else
{ #resource is not defined on the system
$::CREATE::{$class}{$resource} = $res{$resource};
}
}
}
}
#--------------------------------------------------------------------------------
=head3 removeQuotes
removes starting and ending quotes that are in the output of lsrsrc
Arguments: string
Returns: string with no leading or trailing quotes
=cut
#--------------------------------------------------------------------------------
sub removeQuotes
{
my ($string) = @_;
$string =~ s/^\"|^\'//;
$string =~ s/\"$|\'$//;
return $string;
}
#--------------------------------------------------------------------------------
=head3 createResources
Calls mkrsrc-api on all resources in the %::CREATE hash
Globals: %::CREATE
=cut
#--------------------------------------------------------------------------------
sub createResources
{
my $string;
my $counter = 0;
my @assoc_cmds;
my $sensorflg = 0;
foreach my $class (@::DIRECTORIES)
{ #all the class names
local *cre = $::CREATE::{$class};
if ($class eq "Sensor")
{
$sensorflg = 1;
}
else
{
$sensorflg = 0;
}
foreach my $resource (keys %cre)
{
if ($class eq "Association")
{ #special case
my ($cond, $resp) = split ":_:", $resource;
if ($cre{$resource}{'ActiveFlag'} == 1)
{
push @assoc_cmds, "/usr/bin/startcondresp $cond $resp";
if ($cre{$resource}{'Locked'} == 1)
{
push @assoc_cmds, "/usr/bin/startcondresp -L $cond $resp";
}
}
else
{ #not active
push @assoc_cmds, "/usr/bin/mkcondresp $cond $resp";
#no need to lock stopped associations
}
}
else
{
$string .= " IBM.${class}::";
foreach my $attr (keys %{$cre{$resource}})
{
my $value = $cre{$resource}{$attr};
$string .= "${attr}::" . NodeUtils->quote($value) . "::";
}
if (($sensorflg == 1) && ($::INSTALL))
{
# make the Sensor with no userid check
$string .= "::Options::1";
}
#
# Only build up to 10 resources at a pass
# to avoid command line limit
#
$counter = $counter + 1;
if ($counter > 10)
{
if ($string =~ m/\w+/)
{
#my $cmd = "/usr/bin/mkrsrc-api $string";
#print "running $cmd\n";
#system($cmd);
NodeUtils->runrmccmd("mkrsrc-api", "", $string);
$string = "";
$counter = 0;
}
}
}
}
}
if ($string =~ m/\w+/) # for any remaining resources
{
#my $cmd = "/usr/bin/mkrsrc-api $string";
#print "running $cmd\n";
#system($cmd);
NodeUtils->runrmccmd("mkrsrc-api", "", $string);
}
foreach my $cmd (@assoc_cmds)
{
#need to make associations after conds and resps have been made
NodeUtils->runcmd("$cmd");
}
}
#--------------------------------------------------------------------------------
=head3 changeResources
Calls chrsrc-api on all resources in the %::CHANGE hash
Globals: %::CHANGE
=cut
#--------------------------------------------------------------------------------
sub changeResources
{
my $string;
my $ustring; #unlock
my @unlock; #unlock each class
my $where; #unlock each class
foreach my $class (@::DIRECTORIES)
{ #all the class names
local *cha = $::CHANGE::{$class};
foreach my $resource (keys %cha)
{
if ($class eq "Association")
{ #special case
#code here is identical to createResource
my ($cond, $resp) = split ":_:", $resource;
if ($cre{$resource}{'ActiveFlag'} == 1)
{
NodeUtils->runcmd("/usr/bin/startcondresp $cond $resp");
if ($cre{$resource}{'Locked'} == 1)
{
NodeUtils->runcmd( "/usr/bin/startcondresp -L $cond $resp");
}
}
else
{ #not active
NodeUtils->runcmd("/usr/bin/mkcondresp $cond $resp");
#no need to lock stopped associations
}
}
else # not class association
{
$where = qq/"Name IN ('XXX')"/;
$string .= " -s IBM.${class}::${where}::";
push @unlock, $cha{$resource}{'Name'};
delete $cha{$resource}{'Name'};
foreach my $attr (keys %{$cha{$resource}})
{
my $value = $cha{$resource}{$attr};
$string .= "${attr}::" . NodeUtils->quote($value) . "::";
}
}
if (@unlock)
{
$where = qq/"Name IN ('XXX')"/;
$ustring .= " -s IBM.${class}::${where}::Locked::'0'";
}
} # foreach resource
} # foreach key
#
# although @unlock contains the resource and not the node name
# this is a hack to use runrmccmd and the node_ref must
# be provided even though we are not really dealing with nodes
# here
if ($ustring =~ m/\w+/) {
NodeUtils->runrmccmd("chrsrc-api", "", $ustring, undef, \@unlock);
}
if ($string =~ m/\w+/) {
NodeUtils->runrmccmd("chrsrc-api", "", $string, undef, \@unlock);
}
}
#--------------------------------------------------------------------------------
=head3 writeAllFiles
creates all files for the given resources classes
Arguments: a array ref of class names, basedir
=cut
#--------------------------------------------------------------------------------
sub writeAllFiles
{
my @classes = @{shift()};
my $basedir = shift;
print "classes=@classes, basedir=$basedir";
foreach my $class (@classes)
{
my $output = NodeUtils->runrmccmd("lsrsrc-api", "-i", "-s ${class}::::Name");
foreach my $line (@$output)
{
&writeFile("${class}::$line", $basedir);
}
}
}
#--------------------------------------------------------------------------------
=head3 writeFile
creates a file with the resource info in
$basedir/<class>
Arguments: class::resource_name, basedir
=cut
#--------------------------------------------------------------------------------
sub writeFile
{
my $input = shift;
my $basedir= shift;
print "input=$input, basedir=$basedir\n";
my ($class, $resourcefilename) = split "::", $input;
if (!$resourcefilename) {
print 'mkrmcresource --mkfile requires <class::resource> as input.\n';
exit 1;
}
my $resource;
push(@$resource, $resourcefilename);
if (!-e "$basedir/$class") {
`mkdir -p "$basedir/$class"`;
}
my $file = "$basedir/$class/$resourcefilename.pm";
my $where = qq/"Name IN ('XXX')"/;
my $string = " -s ${class}::${where}::*p0x0002";
my $output = NodeUtils->runrmccmd("lsrsrc-api", "-i -n -D ':|:'",
$string, undef, $resource);
$string = " -s ${class}::${where}::*p0x0008";
my $optional = NodeUtils->runrmccmd("lsrsrc-api", "-i -n -D ':|:'",
$string, undef, $resource);
#my @output = NodeUtils->runcmd("/usr/bin/lsrsrc -s $where $class");
#uses lsrsrc instead of lsrsrc-api because format is almost right (just needs a few mods)
my $fh;
open($fh, ">$file")
or die "Can not open this file for writing $file.\n";
print $fh "#!/usr/bin/perl\n\n";
$class =~ s/IBM\.//;
print $fh '$RES::' . $class . "{" . "'"
. $resourcefilename . "'"
. "} = {\n";
foreach my $line (@$output)
{
my %attrs = split /:\|:/,
$line; #can't go straight into a hash because -p creates extra fields
foreach my $attr (keys %attrs)
{
if ( $attr !~ m/ActivePeerDomain/
&& $attr !~ m/NodeNameList/
&& $attr !~ m/NodeIDs/)
{
my $value = $attrs{$attr};
if ($value =~ m/\w/ || $value =~ m/\d/)
{
# print "value = |$value|\n";
#$value = &removeQuotes($value); #quotes are not needed becaues of q()
#print "value = |$value|\n";
my $line = "\t$attr => q($value),";
print $fh "$line\n";
}
}
}
}
foreach my $line (@$optional)
{
my %attrs = split /:\|:/,
$line; #can't go straight into a hash because -p creates extra fields
foreach my $attr (keys %attrs)
{
if ( $attr !~ m/ActivePeerDomain/
&& $attr !~ m/NodeNameList/
&& $attr !~ m/NodeIDs/)
{
my $value = $attrs{$attr};
if ($value =~ m/\w/ || $value =~ m/\d/)
{
# print "value = |$value|\n";
#$value = &removeQuotes($value); #quotes are not needed becaues of q()
#print "value = |$value|\n";
my $line = "\t$attr => q($value),";
print $fh "$line\n";
}
}
}
}
print $fh "};";
print $fh "\n";
print $fh "1;";
print $fh "\n";
close($fh)
or die "cabbit close file $file\n";
}
#--------------------------------------------------------------------------------
=head3 usage
Prints the command usage statement
=cut
#--------------------------------------------------------------------------------
sub usage
{
print "Usage:
mkrmcresources [--install|--mkfile classname::rsrcname|--mkall] \
[-V|--verbose] directory\n\
mkrmcresources -h|--help\n\
directory a full path to a base directory for resurce files \
to be created or to be read from. \
-V|--verbose Verbose mode.\
-h|--help shows usage information.\
--install\
The userid in the Sensor resource will not be verified.\n";
}
#######################################################################
# main Main MAIN
#######################################################################
# get arguments
if (
!GetOptions(
'h|help' => \$::HELP,
'V|verbose' => \$::VERBOSE,
'install' => \$::INSTALL,
'mkfile=s' => \$::MKFILE,
'mkall' => \$::MKALL,
)
)
{
&usage;
exit 1;
}
if ($::HELP) { &usage; exit; }
if (NodeUtils->isHMC() && ($ENV{'DC_ENVIRONMENT'} ne 1))
{
print "mkresources is not supported on HMC.\n";
}
# any function requested
if (@ARGV < 1) {
&usage;
exit 1;
}
my $basedir = $ARGV[0];
if ($::MKFILE) {
&writeFile($::MKFILE, $basedir);
exit;
}
if ($::MKALL) {
@rsrc_classes=('IBM.Condition', 'IBM.EventResponse', 'IBM.Sensor');
&writeAllFiles(\@rsrc_classes, $basedir);
exit;
}
&traverseDirectories($basedir);
#TODO: wait for RSCT to come online
&queryResources(@::DIRECTORIES);
#compares whats defined in the files to the existing resources
&compareResources();
&createResources();
&changeResources();
END
{
}
exit 0;