#!/usr/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2005,2008 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# @(#)54   1.21.1.6   src/csm/core/cmds/mkresources.perl, csmcore, csm_rgar2h, rgar2hs001a 11/30/07 03:08:13

#######################################################################
# mkresources is a way for CSM components to
# predefine conditions, responses, associations, sensors and node
# groups (and can be extended to support any RSCT resource
# class). This will allow CSM components to easily use the advanced
# logic in predefined-condresp on all CSM endpoints (instead of just
# the management server).

# To use the infrastructure, components create perl modules in
# /opt/csm/install/resources. 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 /opt/csm/bin/mkresources
# command. This command should be called by the post install scripts
# of packaging files, script run after install or from the command line.
#######################################################################

use strict;
use locale;

BEGIN
{

    # this enables us to redirect where it looks for other CSM files during development
    $::csmroot = $ENV{'CSM_ROOT'} ? $ENV{'CSM_ROOT'} : '/opt/csm';
    $::csmpm   = "$::csmroot/pm";
    $::csmbin  = "$::csmroot/bin";
}
use lib $::csmpm;
use lib "/opt/csm/install/resources";
use Getopt::Long;
use Data::Dumper;
use CSMDefs;
require NodeUtils;
require MessageUtils;
require ServerUtils;

$::MKRESMSGCAT = 'nodecmds.cat';
$::MKRESMSGSET = "mkres";
$::MSGMAPPATH  = "$::csmroot/msgmaps";
if (defined $ENV{'CSM_VERBOSE'}) { $::VERBOSE = 1; }

$Getopt::Long::ignorecase = 0;    #Checks case in GetOptions
Getopt::Long::Configure("bundling")
  ;    #allows short command line options to be grouped (e.g. -av)

our (%res, %exi, %cre, %cha, %rem);    #predeclare so use strict is happy

#--------------------------------------------------------------------------------

=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 -U", -1);
            if ($::RUNCMD_RC) 
            {
                next;
            }
            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, $lock) = split ' ', $line;
                $condition = &removeQuotes($condition);
                $response  = &removeQuotes($response);
                my $key        = "${condition}:_:${response}";
                my $ActiveFlag = 0;                              #assume offline
                if ($state =~ m/Active/)
                {
                    $ActiveFlag = 1;
                }
                my $Locked = 0;                              #assume offline
                if ($lock =~ m/Yes/)
                {
                    $Locked = 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,Locked => $Locked,};
            }
        }
        else
        {
            $where .= " -s ${res}::::'*p0x0020'";
        }
    }
    if ($where eq "") # the case of only query IBM.Association
    {
        return;
    }
    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 MessageUtils->messageFromCat($::MKRESMSGCAT, $::MSGMAPPATH,
                                $::MKRESMSGSET, 'I', 'EMsgCanNotOpenDir', $dir);
    while ($file = readdir($dir_fh))
    {
        if ($file ne '.' and $file ne '..' and $file =~ m/^IBM\..*/ )
        {
            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 MessageUtils->messageFromCat($::MKRESMSGCAT, $::MSGMAPPATH,
                               $::MKRESMSGSET, 'I', 'EMsgCanNotCloseDir', $dir);
                               
    #remove the duplicated items in @::DIRECTORIES
    foreach my $item (@::DIRECTORIES) 
    {
        $::DIRECTORIES{$item} = 1;
    }
    @::DIRECTORIES = sort {$b cmp $a } keys (%::DIRECTORIES);
}

#--------------------------------------------------------------------------------

=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 MessageUtils->messageFromCat($::MKRESMSGCAT, $::MSGMAPPATH,
                                $::MKRESMSGSET, 'I', 'EMsgCanNotOpenDir', $dir);
    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 MessageUtils->messageFromCat($::MKRESMSGCAT, $::MSGMAPPATH,
                               $::MKRESMSGSET, 'I', 'EMsgCanNotCloseDir', $dir);
}

#--------------------------------------------------------------------------------

=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 ($::FORCE or (defined $exi{$resource}{'Locked'}
                    && $exi{$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 ($::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", -1);
        if($::VERBOSE and $::RUNCMD_RC) {
            print "execute command $cmd failed\n";
        }
    }
}

#--------------------------------------------------------------------------------

=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)
        {
            my $resname = $cha{$resource}{'Name'};
            if ($class eq "Association")
            {       #special case
                    #code here is identical to createResource
                my ($cond, $resp) = split ":_:", $resource;
                NodeUtils->runcmd("/usr/sbin/rsct/bin/rmcondresp -U $cond $resp", -1);
                NodeUtils->runcmd("/usr/sbin/rsct/bin/rmcondresp $cond $resp", -1);
                if ($cha{$resource}{'ActiveFlag'} == 1)
                {
                    NodeUtils->runcmd("/usr/bin/startcondresp $cond $resp");
                    if ($cha{$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 ('$resname')"/;
                $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 and ($class eq "Condition" or $class eq "EventResponse"))
            {
                $where = qq/"Name IN ('$resname')"/;

                $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);
    }
    if ($string =~ m/\w+/)
    {
        NodeUtils->runrmccmd("chrsrc-api", "", $string);
    }
}
#--------------------------------------------------------------------------------

=head3    pollRSCT

        Calls waitForRsct to poll and wait RSCT and RMC running
       
        Globals: %::RES
=cut

#--------------------------------------------------------------------------------
sub pollRSCT
{
    my %rms;
    my %mapRCtoRM = (
        'IBM.Condition' => q(IBM.ERRM),
        'IBM.EventResponse' => q(IBM.ERRM),
        'IBM.Association' => q(IBM.ERRM),
        'IBM.Sensor' => q(IBM.SensorRM),
        'IBM.NodeGroup' => q(IBM.DMSRM),
        'IBM.DeviceGroup' => q(IBM.DMSRM),
    );
    foreach my $class (@::DIRECTORIES)
    { 
        my $rm = $mapRCtoRM{$class};
        $rms{$rm} = 1;
    }
    my $count = 0;
    while ($count < $::TIMEOUT)
    {
        my $ready = 1;
        foreach my $rm (keys %rms)
        {
            NodeUtils->runcmd("/opt/csm/csmbin/waitForRsct -t 0 $rm 2>/dev/null", -1 );
            if ($::RUNCMD_RC) 
            {
                $ready = 0;
            }
        }
        if ($ready) 
        {
            last;
        }
        `/usr/bin/lssensor 1>/dev/null 2>/dev/null`;
        sleep(1);
        $count++;
    }
    if ($count == $::TIMEOUT)
    {
        MessageUtils->messageFromCat($::MKRESMSGCAT, $::MSGMAPPATH,
                     $::MKRESMSGSET, 'E', 'EMsgPollRSCTTimeout');
        exit 1;
    }
}
#--------------------------------------------------------------------------------

=head3    removeResources

        Calls rmrsrc-api on all resources in the %::RES hash
       
        Globals: %::RES
=cut

#--------------------------------------------------------------------------------
sub removeResources
{
    &unlock(@::DIRECTORIES);
    foreach my $class (@::DIRECTORIES)
    {    #all the class names
        $class =~ s/^IBM\.//;    #the IBM prefix is not used in the hash name
        if ($class eq "Association")
        {
            local *rem = $::RES::{$class};
            foreach my $assoc (keys %rem)
            {
                my ($cond, $resp) = split ":_:", $assoc;
                NodeUtils->runcmd("/usr/sbin/rsct/bin/rmcondresp $cond $resp", -1);
            }
        }
        else
        {
            my $where = qq/"Name IN ('XXX')"/;
            local *rem = $::RES::{$class};
            my @nodelist = keys(%rem);
            NodeUtils->runrmccmd('rmrsrc-api', '-i', "-s IBM.${class}::$where", undef, \@nodelist);
        }
    }
}

#Unlocks all the locked predefined conditions and responses
sub unlock
{
    
    foreach my $class (@_)
    {    #all the class names
        $class =~ s/^IBM\.//;    #the IBM prefix is not used in the hash name
        if ($class eq "Association")
        {
            local *rem = $::RES::{$class};
            foreach my $assoc (keys %rem)
            {
                my ($cond, $resp) = split ":_:", $assoc;
                NodeUtils->runcmd("/usr/sbin/rsct/bin/rmcondresp -U $cond $resp", -1);
            }
        }
        else
        {
            my $where = qq/"Name IN ('XXX')"/;
            my $string = "-s IBM.${class}::${where}::Locked::0";
            local *rem = $::RES::{$class};
            my @nodelist=keys(%rem);

            NodeUtils->runrmccmd('chrsrc-api', '', $string, -1, \@nodelist);
        }
    }
}

#--------------------------------------------------------------------------------

=head3    usage

        Prints the command usage statement
=cut

#--------------------------------------------------------------------------------

sub usage
{
    MessageUtils->messageFromCat($::MKRESMSGCAT, $::MSGMAPPATH, $::MKRESMSGSET,
                                 'I', 'IMsgMkresourceUsage');

}

#--------------------------------------------------------------------------------

=head3    writeAllFiles

        creates all files for the given resources classes
      
        Arguments: class names in an array
=cut

#--------------------------------------------------------------------------------
sub writeAllFiles
{
    my @classes = @_;
    foreach my $class (@classes)
    {
        if ($class eq "IBM.Association")
        {
            my @condresp = NodeUtils->runcmd("LANG=C /usr/bin/lscondresp", -1);
            if ($::RUNCMD_RC) 
            {
                next;
            }
            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);
                &writeFile("${class}::${condition}:_:${response}");
            }
        }
        else 
        {    
            my $output =
            NodeUtils->runrmccmd("lsrsrc-api", "-i", "-s ${class}::::Name");
            foreach my $line (@$output)
            {
                &writeFile("${class}::$line");
            }
        }
    }
}

#--------------------------------------------------------------------------------

=head3    writeFile

        creates a file with the resource info in 
        /opt/csm/install/resources/base/<class>

        Arguments: class::resource_name
=cut

#--------------------------------------------------------------------------------
sub writeFile
{
    my ($input) = @_;

    my ($class, $resourcename) = split "::", $input;
    if (!$resourcename)
    {
        MessageUtils->messageFromCat($::MKRESMSGCAT, $::MSGMAPPATH,
                                     $::MKRESMSGSET, 'E',
                                     'EMsgResourceMissing');
        exit 1;
    }
    my $resource;
    push(@$resource, $resourcename);
    my $resourcefilename;
    $resourcefilename = $resourcename;
    if ($class eq "IBM.Association")
    {
        $resourcefilename =~ s/://g;
    }
    if (!defined $::BASEDIR)
    {
        $::BASEDIR = "/csminstall/resources/backup";
        if (!-e "/csminstall/resources/") 
        {
            mkdir("/csminstall/resources/")
        }
    }
    if (!-e $::BASEDIR)
    {
        mkdir($::BASEDIR)
            or die MessageUtils->messageFromCat($::MKRESMSGCAT, $::MSGMAPPATH,
                $::MKRESMSGSET, 'I', 'EMsgCanNotOpenDir', $::BASEDIR);
        
    }
    if (!-e "$::BASEDIR/$class")
    {
        mkdir("$::BASEDIR/$class")
            or die MessageUtils->messageFromCat($::MKRESMSGCAT, $::MSGMAPPATH,
                $::MKRESMSGSET, 'I', 'EMsgCanNotOpenDir', "$::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 MessageUtils->messageFromCat($::MKRESMSGCAT, $::MSGMAPPATH,
                             $::MKRESMSGSET, 'I', 'EMsgCanNotOpenFile2', $file);
    print $fh "#!/usr/bin/perl\n\n";
    $class =~ s/IBM\.//;

    print $fh '$RES::' . $class . "{" . "'"
        . $resourcename . "'"
        . "} = {\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";
                }
            }
        }
    }
    if ($class eq "Association")
    {
        my ($cond, $resp) = split ':_:', $resourcename;
        my $line = NodeUtils->runcmd("LANG=C /usr/bin/lscondresp -xtU $cond $resp", -1);
        if (not $::RUNCMD_RC) 
        {
            my ($cond, $resp, $node, $state, $lock) = split ' ', $line;
            my $ActiveFlag = 0;                              #assume offline
            if ($state =~ m/Active/)
            {
                $ActiveFlag = 1;
            }
            my $Locked = 0;                              #assume offline
            if ($lock =~ m/Yes/)
            {
                $Locked = 1;
            }
            print $fh "\tActiveFlag => $ActiveFlag,\n";
            print $fh "\tLocked => $Locked,\n";
        }
    
    }
    print $fh "};";
    print $fh "\n";
    print $fh "1;";
    print $fh "\n";
    close($fh)
      or die MessageUtils->messageFromCat($::MKRESMSGCAT, $::MSGMAPPATH,
                             $::MKRESMSGSET, 'I', 'EMsgCanNotCloseFile', $file);
    MessageUtils->messageFromCat($::MKRESMSGCAT, $::MSGMAPPATH, $::MKRESMSGSET,
                                 'V', 'IMsgFileCreated', $file);
}

#--------------------------------------------------------------------------------

=head3    dump_CondRespAssoc_List

        Dump the required conditions, responses and associations,
		the dumped date will be used by 
		/opt/diagnostic/probes/ibm.csm.predefined*
      
=cut

#--------------------------------------------------------------------------------
sub dump_CondRespAssoc_List
{

	# Each condition, response, and association will have a "is required" flag
	# Possible values are:
	#    'O' - optional
	#    'L' - locked

	$Data::Dumper::Varname = "Version";
	print Dumper("1");

	my %Conditions;
	foreach my $key (keys %RES::Condition)
	{
        # Skip AllServiceableEvents which is not defined by default
        # Probemgr will check it if it is not skipped
        if ($key eq "AllServiceableEvents")
        {
            next;
        }
		$Conditions{$key}{is_required} = "O";
	}
	$Data::Dumper::Varname = "Conditions";
	print Dumper(\%Conditions);

	my %Responses;
	foreach my $key (keys %RES::EventResponse)
	{
        # Skip AuditLogServiceableEvents which is not defined by default
        # Probemgr will check it if it is not skipped
        if ($key eq "AuditLogServiceableEvents")
        {
            next;
        }
		$Responses{$key}{is_required} = "O";
	}
	$Data::Dumper::Varname = "Responses";
	print Dumper(\%Responses);

	my %CondResp;
	foreach my $origkey (keys %RES::Association)
	{
        my $key = join(' ', split(':_:', $origkey));
        # Skip this condresp which is not defined by default
        # Probemgr will check it if it is not skipped
        if ($key eq '"AllServiceableEvents" "AuditLogServiceableEvents"')
        {
            next;
        }
		$CondResp{$key}{is_required} = "O";
	}
	$Data::Dumper::Varname = "CondResp";
	print Dumper(\%CondResp);
}

#--------------------------------------------------------------------------------

=head3    dump_NodeGroup_List

        Dump the required node groups,
		the dumped date will be used by 
		/opt/diagnostic/probes/ibm.csm.predefined*
      
=cut

#--------------------------------------------------------------------------------
sub dump_NodeGroup_List
{

	# Each nodegroup will have a "is required" flag
	# Possible values are:
	#    '0' - optional
	#    'L' - locked

	my $key;

	$Data::Dumper::Varname = "Version";
	print Dumper("1");

	my %NodeGroups;
	foreach $key (keys %RES::NodeGroup)
	{
		$NodeGroups{$key}{is_required} = "O";
	}
	$Data::Dumper::Varname = "NodeGroups";
	print Dumper(\%NodeGroups);

	my %DeviceGroups;
	foreach $key (keys %RES::DeviceGroup)
	{
		$DeviceGroups{$key}{is_required} = "O";
	}
	$Data::Dumper::Varname = "DeviceGroups";
	print Dumper(\%DeviceGroups);
}

#######################################################################
#Main
#######################################################################
#
# get command line input
my @command_line = ();
@command_line = @ARGV;
my $command_line = $0 . " " . join(" ", @command_line);

# get arguments
# -z and -Z are internal flags that will be used by probemgr,
# to dump all the required conditions,responses and associations,
# nodegroups, and device groups
if (
    !GetOptions(
                'h'        => \$::HELP,
                'v'        => \$::VERBOSE,
                'V'        => \$::VERBOSE,
                'all'      => \$::ALL,
                'install'  => \$::INSTALL,
                'dir=s'    => \$::BASEDIR,
                'mkfile=s' => \$::MKFILE,
                'mkall'    => \$::MKALL,
                'r'        => \$::REMOVE,
                'f'        => \$::FORCE,
                't=s'      => \$::TIMEOUT,
                'resfile=s'=> \$::RESFILE,
                'z'        => \$::DUMP_CONDRESP,
                'Z'        => \$::DUMP_GROUP,
                
    )
  )
{
    &usage;
    exit 1;
}

if ($::HELP) { &usage; exit; }

if ((!!$::ALL + !!$::BASEDIR + !!$::RESFILE)>1)
{
    &usage;
    exit 1;
}

if ((!!$::FORCE + !!$::REMOVE + !!$::MKFILE + !!$::MKALL) > 1)
{
    &usage;
    exit 1;
}


# setup logging
#
my $logpath = "/var/log/csm";    # this commands runs on MS and nodes
                                 # on MS can get from CSMLogDir
                                 # but on nodes will need in configinfo file
my $mkresourcelog = "$logpath/mkresources.log";
if (!(-e $logpath))
{
    mkdir($logpath, 0755);
}
MessageUtils->start_logging($mkresourcelog);
MessageUtils->messageFromCat(
                             $::MKRESMSGCAT, $::MSGMAPPATH,
                             $::MKRESMSGSET, 'V',
                             'IMsgInput',    $command_line
                             );    # Log input
my $logging = 1;

# any function requested
if ($::MKFILE)
{
    &writeFile($::MKFILE);
    exit;
}
if ($::MKALL)
{
    &writeAllFiles('IBM.Condition', 'IBM.EventResponse', 'IBM.Sensor', 
                   'IBM.NodeGroup','IBM.DeviceGroup', 'IBM.Association');
    exit;
}

if (not defined $::TIMEOUT) 
{
    $::TIMEOUT = 180;
}

if ($::ALL)
{

    #go through all subdirectories of /opt/csm/install/resources
    my ($dir_fh, $file);
    my $dir = "/opt/csm/install/resources";
    opendir($dir_fh, $dir)
      or die MessageUtils->messageFromCat($::MKRESMSGCAT, $::MSGMAPPATH,
                                $::MKRESMSGSET, 'I', 'EMsgCanNotOpenDir', $dir);
    while ($file = readdir($dir_fh))
    {
        if ($file ne '.' and $file ne '..')
        {
            my $subdir = "$dir/$file";
            if (-d $subdir)
            {    #only look at directories
                &traverseDirectories($subdir);
            }
        }
    }
    closedir($dir_fh)
      or die MessageUtils->messageFromCat($::MKRESMSGCAT, $::MSGMAPPATH,
                               $::MKRESMSGSET, 'I', 'EMsgCanNotCloseDir', $dir);
}
elsif ($::RESFILE)
{
    -e $::RESFILE
        or die MessageUtils->messageFromCat("csmInstall.cat", $::MSGMAPPATH,
                "csminstall", 'I', 'EMsgDOESNT_EXIST', $::RESFILE);
    require $::RESFILE;
    for my $class (keys %::RES::)
    {
        push @::DIRECTORIES, "IBM.".$class;
    }
}
else
{
    if (!defined $::BASEDIR)
    {
        $::BASEDIR = "/opt/csm/install/resources/base";
        &traverseDirectories($::BASEDIR);
    }
    else
    {
        # mkresources should accept IBM.* directory
        # trim the last "/" and spaces
        $::BASEDIR =~ s/\/\s*$//g;
        if ($::BASEDIR =~ /^.*\/IBM\.[^\/]+$/)
        {
            $::BASEDIR =~ /^.*\/(.*)$/;
            push @::DIRECTORIES, $1;
            &readFiles($::BASEDIR); 
        }
        else
        {
            &traverseDirectories($::BASEDIR);
        }
    }
}

if ($::DUMP_CONDRESP)
{
    &dump_CondRespAssoc_List();
    exit 0;
}
if ($::DUMP_GROUP)
{
    &dump_NodeGroup_List();
    exit 0;
}

#wait for RSCT to come online
if ($::TIMEOUT != 0 ) 
{
    &pollRSCT();
}

if ($::REMOVE) 
{
    &removeResources();
    exit;
}

&queryResources(@::DIRECTORIES);

#compares whats defined in the files to the existing resources
&compareResources();

&createResources();

&changeResources();

END
{
    if ($logging)
    {
        MessageUtils->stop_logging($mkresourcelog);
    }
}
exit 0;
