# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2001 
# 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 
package MC_cli_sd_utils;
# sccsid = "@(#)58   1.6   src/rsct/rmc/cli/pm/MC_cli_sd_utils.pm.perl, rmccli, rsct_rady, rady2035a 11/12/15 16:30:02"
######################################################################
#                                                                    #
# Package: MC_cli_sd_utils.pm                                        #
#                                                                    #
# Description:                                                       #
#   This package contains utility/common subroutines for the PERL    #
#   Cluster Resource Monitoring and Control (RMC) CLI commands       #
#   that are specific to dealing with the structured data (sd) type. #
#   An SD is one of the basic cluster data types defined in ct.h.    #
#                                                                    #
# Subroutines Available:                                             #
#   get_sd_element_defs - function to get the structured data        #
#     definition for the specified class, name (attribute or         #
#     action name) and usage. Specifically the elements definition   #
#     that make up the SD. Uses the mc_qdef_sd_bp and builds a hash  #
#     indexable by either element name or index depending on input   #
#     argument to this function. Which SDs are returned is also      #
#     controled by the usage paramater.                              #
#                                                                    #
#   get_sd_element_names - returns a list of SD element names in     #
#     element index order.                                           #
#                                                                    #
#   qdef_sd - convenience function to call the Perl to C extension   #
#     for mc_qdef_sd_bp and return the response.                     #
#                                                                    #
#   format_sd_element_defs - formats the data returned in            #
#     the response of the mc_qdef_sd_bp into a hash of structured    #
#     data elements definitions indexable by the element name or     #
#     index depending on input parameter.                            #
#     If the number of names the qdef_sd was passed is greater than  #
#     1 you should use format_sd_attr_defs.                          #
#                                                                    #
#   format_sd_attr_defs - formats the data returned in               #
#     the response of the mc_qdef_sd_bp into a hash of attribute/    #
#     action names, that point to a hash of structured               #
#     data elements definitions indexable by the element name.       #
#     This differs from format_sd_element_defs in that the highest   #
#     level hash is a hash of attribute names that contains the      #
#     hash of SD elements for that attribute.                        #
#                                                                    #
#   build_cmd_arg_sd - builds the command argument SD in value_t     #
#     format. The format the Perl to C extensions expect based on    #
#     what the RMC C API expects. Verifies that each SD element in   #
#     the HoSDEleDefs has a value then builds in element index order #
#     the value_t representation of the SD.                          #
#                                                                    #
# Examples:                                                          #
#   ($rc $session) = init_session();                                 #
#   $rc = term_session($session);                                    #
#                                                                    #
#--------------------------------------------------------------------#
# Inputs:                                                            #
#   /opt/rsct/msgmaps/mccli.mccli.map - message mapping         #
#                                                                    #
# Outputs:                                                           #
#   stdout - common informational messages that get displayed.       #
#   stderr - common error messages that get displayed.               #
#                                                                    #
# External References:                                               #
#   Commands: ctdspmsg                                               #
#                                                                    #
# Tab Settings:                                                      #
#   4 and tabs should be expanded to spaces before saving this file. #
#   in vi:  (:set ts=4  and   :%!expand -4)                          #
#                                                                    #
# Change Activity:                                                   #
#   010720 SAB 75111: Initial design & write.                        #
#   020116 JAC 78199: Change build_cmd_arg_sd to handle no args.     #
######################################################################


use Exporter ();
@ISA = qw(Exporter);
@EXPORT_OK = qw(
    get_sd_element_defs
    get_sd_element_names
    qdef_sd
    format_sd_element_defs
    format_sd_attr_defs
    build_cmd_arg_sd
);

use lib "/opt/rsct/pm";
use locale;

##use CT::CT qw(
##    :ct_data_type_t
##);

use autouse CT_cli_input_utils => qw(
    convert_input_value
);

##use CT::MC;                             # import defaults 
##use CT::MC qw(
##    :mc_qdef_opts_t
##    :mc_sd_usage_t
##    qdef_sd_bp
##    free_response
##);

##use CT::MCerr;                          # RMC return/error codes
##use CT::RM;                             # RM  return/error codes

use MC_cli_utils qw(
    printCIMsg
    printCEMsg
);
use MC_cli_rc qw(:return_codes);


#--------------------------------------------------------------------#
# Global Variables                                                   #
#--------------------------------------------------------------------#
$MSGCAT = "mccli.cat";                 # msg catalogue for this cmd
$MSGSET = "mccli";                     # common message set     

$CTDIR = "/opt/rsct";             # RSCT root directory
$CTBINDIR = "$CTDIR/bin";              # Cluster Bin directory path
$LSMSG = "$CTBINDIR/ctdspmsg";         # list / display message rtn
$ENV{'MSGMAPPATH'} = "$CTDIR/msgmaps"; # msg maps used by $LSMSG


#--------------------------------------------------------------------#
# Exported Subroutines (with @EXPORT_OK, -> on demand).              #
#--------------------------------------------------------------------#

#--------------------------------------------------------------------#
# Common message handling (error, informational) routines:           #
#--------------------------------------------------------------------#

#--------------------------------------------------------------------#
# get_sd_element_defs - function to get this resource's structured   #
#   data element definitions using mc_qdef_sd_bp and build a hash    #
#   indexable by the element name with the element name, data type,  #
#   index, and value stored as data.                                 #
#                                                                    #
# Parameters:                                                        #
#   $session          input   RMC session handle.                    #
#   $resource         input   Resource class name.                   #
#   $name             input   An action name or attribute name or    #
#                             empty string. Doing a qdef on an       #
#                             action requires an action name or      #
#                             more than one may be returned. Doing   #
#                             a qdef_sd on a command argument usage  #
#                             does not require a name.               #
#   $sd_usage         input   mc_sd_usage_t as defined in CT::MC.pm  #
#                             MC_SD_USAGE_PATTR_RSRC_CLASS, ...      #
#                             MC_SD_USAGE_RESET_ARG                  #
#   $hash_by_name     input   1 - True - Level 1 of hash should be   #
#                             the SD element name.                   #
#                             0 - False - Level 1 of hash should be  #
#                             the SD element index.                  #
#   %$rHoSDEleDef     in/out  Reference to a hash of an SD           #
#                             definition.                            #
#                             key = element_name                     #
#                             data = hash                            #
#                               sd_dtype = value                     #
#                               sd_index = value                     #
#                               sd_element_name = value              #
#                               sd_value = (undefined a place holder #
#                                 value entered via command line)    #
#                                                                    #
# Return:                                                            #
#   $rc                       return code.                           #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub get_sd_element_defs
{
my ($session, $resource, $name , $sd_usage, $hash_by_name, 
    $rHoSDEleDefs) = @_;

my $rc = 0;

my @names = ();
if (defined $name && $name ne "") {
    push @names, $name;
}

# Need the right response data structure 
my $response = CT::MC::qdef_sd_rsp_t->new;

# Let the subroutine make the call to the extension, and
# handle all the assorted potential errors.
# Get the command arguments SD definition for the online command
$rc = qdef_sd($session, $resource, \@names, $sd_usage, $response);
($rc == 0) || return($rc);

format_sd_element_defs($resource, $response, $rHoSDEleDefs,
    $hash_by_name);

if ($response->array_count > 0) {
    $rc = CT::MC::free_response($response);
    ($rc == 0) || return $rc;
}

return $rc;
}   # end get_sd_element_defs


#--------------------------------------------------------------------#
# get_sd_element_names - Return a list of the SD element names       #
#   in element index order.                                          #
#                                                                    #
# Parameters:                                                        #
#   %$rHoSDDef        input   Reference to a hash of 1 SD elements   #
#                             definition.                            #
#                             key = action_id                        #
#                             data = hash                            #
#                                key = element_name                  #
#                                data = hash                         #
#                                   sd_dtype = value                 #
#                                   sd_index = value                 #
#                                   sd_element_name = value          #
#                                                                    #
# Return:                                                            #
#   @sd_element_names         Returns an array of the SD element     #
#                             names in element index order.          #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub get_sd_element_names 
{
my ($rHoSDDef) = @_;

my @sd_element_names;
my %index_to_names = ();
my @element_indexes = ();
my ($element_name, $element_index);

# Prepare to create the list of element names in element index order
# When you build up an SD for RMC the elements must be in
# the correct order, no guarantee that their won't be spaces
# also don't know if mc_qdef_sd_mc provides elements in order.
my @element_names = keys %$rHoSDDef;

# If only 1 element in SD - no need to sort it.
if (scalar @element_names <= 1) {
    return (@element_names);
}

foreach $element_name (@element_names) {
    $element_index = $$rHoSDDef{$element_name}{sd_index};
    push @element_indexes, $element_index;
    $index_to_names{$element_index} = $element_name;
}

# No guarantee the element indexes are in sorted order so
# need to sort them.
sub numerically { $a <=> $b; }
my @sorted_element_indexes = sort numerically @element_indexes;

# Create the list of element names in element index order
foreach $element_index (@sorted_element_indexes) {
    push @sd_element_names, $index_to_names{$element_index};
}

return (@sd_element_names);
}   # end get_sd_element_names


#--------------------------------------------------------------------#
# qdef_sd - function to call the CT::MC::qdef_sd_bp extension and    #
#   handle possible errors.                                          #
#                                                                    #
# Parameters:                                                        #
#   $session          input   RMC session handle.                    #
#   $resource         input   Resource class name.                   #
#   $names            input   Reference to array of attribute or     #
#                             action names or empty array.           #
#   $sd_usage         input   mc_sd_usage_t as defined in CT::MC.pm  #
#                             MC_SD_USAGE_PATTR_RSRC_CLASS, ...      #
#                             MC_SD_USAGE_RESET_ARG                  #
#   $response         in/out  qdef_sd response.                      #
#                                                                    #
# Return                                                             #
#   $rc                       return code.                           #
#                                                                    #
# Global References:                                                 #
#   $main::Trace      input   TRUE - trace turned on.                #
#--------------------------------------------------------------------#
sub qdef_sd
{
my ($session, $resource, $names, $sd_usage, $response) = @_;

my $rc = 0;
my $names_count = scalar(@$names);

my $error = CT::MC::errnum_t->new;

my $options = MC_QDEF_OPTS_NODSCRP;        

$main::Trace && print STDERR "Calling CT::MC::qdef_sd_bp\n";

$rc = CT::MC::qdef_sd_bp($session, $response, $error,
                         $options, $resource, $sd_usage,
                         $names, $names_count);

$main::Trace && print STDERR "Return  CT::MC::qdef_sd_bp\n";

$rc = error_check("mc_qdef_sd_bp", $resource, $response, 
    $rc, $error);

return($rc);
}   # end qdef_sd


#--------------------------------------------------------------------#
# format_sd_element_defs - formats the structured data elements into #
#   a complex hash of SD element definitions (%rHoSDEleDefs).        #
#   The level 1 hash                                                 #
#     key = element_name data = hash                                 #
#     or                                                             #
#     key = element_index data = hash                                #
#   Typically when processing the SD input def we want the key       #
#   to be the element_name.  But when processing the SD response def #
#   where we want to display the response SD, having the key as      #
#   the SD element index makes more sense.                           #
#   The level 2 hash                                                 #
#     key = (sd_name, sd_dtype, sd_index, sd_value)                  #
#     the values from mc_qdef_sd_bp of those fields                  #
#   Example:                                                         #
#     x $rHoSDEleDefs                                                #
#     0  'Int32'                                                     #
#     1  HASH(0x2045ddd0)                                            #
#        'sd_dtype' => 2                                             #
#        'sd_index' => 0                                             #
#        'sd_name' => 'Int32'                                        #
#        'sd_value' => undefined                                     #
#     or                                                             #
#     0  0                                                           #
#     1  HASH(0x2045ddd0)                                            #
#        'sd_dtype' => 2                                             #
#        'sd_index' => 0                                             #
#        'sd_name' => 'Int32'                                        #
#        'sd_value' => undefined                                     #
#                                                                    #
# Parameters:                                                        #
#   $resource         input   Resource Name                          #
#   $response         input   Response data structure.               #
#   %$rHoSDEleDefs    in/out  Reference to the hash of SD element    #
#                             definitions.                           #
#   $hash_by_name     input   TRUE - hash by element_name.           #
#                             FALSE - hash by element_index.         #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub format_sd_element_defs
{
my($resource, $response, $rHoSDEleDefs, $hash_by_name) = @_;

my %elements = ();
my ($element_name, $element_index, $element_cnt);

my $response_cnt = $response->array_count;
# Should only be one response, should only be dealing with 
#   1 SD at a time here. 
# TODO: We should display an internal message error if 
# $response_cnt > 1.  If it is then format_sd_attr_defs is
# what should probably have been called - since qdef_sd   
# was probably passed 1 or more names.
for (my $r = 0; $r < $response_cnt; $r++) {
    # The action id is in $response->id($r) if you later 
    # want to save it. For now only dealing with 1 action
    $element_cnt = $response->element_count($r);  
    for (my $e = 0; $e < $element_cnt; $e++) {
        %elements = ();
        $element_name = $response->element_name($r, $e);
        $element_index = $response->element_index($r, $e);
        $elements{sd_name} =  $element_name;
        $elements{sd_index} = $element_index;
        $elements{sd_dtype} = $response->element_data_type($r, $e);
        # $elements{sd_value} = No value hear yet - this is a place 
        # holder, it will be added in build_cmd_arg_sd
        if ($hash_by_name) {
            $$rHoSDEleDefs{$element_name} = { %elements };
        }
        else {
            $$rHoSDEleDefs{$element_index} = { %elements };
        }
    } # end for each element
}   # end for each response

return;
}   # end of format_sd_element_defs


#--------------------------------------------------------------------#
# format_sd_attr_defs - formats the structured data elements into a  #
#   complex hash of SD element definitions (%rHoSDAttrDefs).         #
#   Each SD attribute is an entry in the hash.                       #
#      Which points to hash of its SD elements                       #
#         Each SD element contains a hash containing values for      #
#         the: sd_dtype, sd_index, sd_name, sd_value.                #
#                                                                    #
#   x $rHoSDAttrDefs                                                 #
#   0  HASH(0x2004da68)                                              #
#      'SD' => HASH(0x206c67b0)                                      #
#         'String' => HASH(0x206f1a00)                               #
#            'sd_dtype' => 8                                         #
#            'sd_index' => 0                                         #
#            'sd_name' => 'String'                                   #
#            'sd_value' => 'String'                                  #
#                                                                    #
#   Use the following syntax to access the definitions of a          #
#   particular SD element.                                           #
#     $$rHoSDAttrDefs{$attr_name}{$sd_element_name}                  #
#   The order of the SD element names in @rLoSDAttrDefs should       #
#   correspond to the index of the element in the SD.                #
#   TODO: Check to make sure that's true...                          #
#   for each $sd_name @$rLoSDAttrDefs {                              #
#     $$rHoSDAttrDefs{$sd_name}{sd_dtype} - SD element data type.    #
#     $$rHoSDAttrDefs{$sd_name}{sd_index} - SD element index.        #
#     $$rHoSDAttrDefs{$sd_name}{sd_value} - SD element value.        #
#   }                                                                #
#                                                                    #
# Parameters:                                                        #
#   $resource         input   Resource Name                          #
#   $response         input   Response data structure.               #
#   %$rHoSDAttrDefs   in/out  Reference to the hash of SD attributes #
#                             (elements) definitions.                #
#                             {sd_dtype} and {sd_value}.             #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub format_sd_attr_defs
{
my($resource, $response, $rHoSDAttrDefs) = @_;

my($attr_name, $attr_id, $r);

my %elements = ();
$response_cnt = $response->array_count;
for ($r = 0; $r < $response_cnt; $r++) {
    $attr_name = $response->program_name($r);
    $$rHoSDAttrDefs{$attr_name} = (); 
    $element_cnt = $response->element_count($r);  
    for ($e = 0; $e < $element_cnt; $e++) {
        %elements = ();
        $elements{sd_name}  = $response->element_name($r, $e);
        $elements{sd_index} = $response->element_index($r, $e);
        $elements{sd_dtype} = $response->element_data_type($r, $e);
        $elements{sd_value} = $response->element_name($r, $e);
        $rHoSDAttrDefs->{$attr_name}->{$elements{sd_name}} =
            { %elements };

    } # end for each element
}   # end for each response

return;
}   # end of format_sd_attr_defs


#--------------------------------------------------------------------#
# build_cmd_arg_sd - builds the command argument SD in value_t       #
#   format. The format the Perl to C extensions expect based on      #
#   what the RMC C API expects. Verifies that each SD element in     #
#   the HoSDEleDefs has a value then builds in element index order   #
#   the value_t representation of the SD.                            #
#   SD is represented in Perl as a complex structure. A list of      #
#   hashes. Where each hash represents one SD element, and it        #
#   contains 2 hashes representing the type and value.               #
#                                                                    #
# Parameters:                                                        #
#   $resource         input   Resource name.                         #
#   $rNameValue       input   Reference to array of name,value pairs.#
#   $rHoSDDef         input   Reference to hash of command SD        #
#                             argument element definitions for this  #
#                             command.                               #
#                                                                    #
# Return:                                                            #
#   $rc                       return code.                           #
#   @LoSD                     Structured data complex hash.          #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub build_cmd_arg_sd
{
my ($resource, $rNameValue, $rHoSDDef) = @_;

my ($new_element, $data_type);
my ($element_name, $element_value);
my $badrc = 0;
my $rc = 0;

foreach $new_element (@{$rNameValue->[1]}) {
    $element_name = $new_element->[0];

    # first check to make sure this is a valid sd element name
    if (!defined($$rHoSDDef{$element_name})) {
        printCEMsg("EMsgMCcliInvalidCmdArg", $element_name,
            $resource, '"' . $rNameValue->[0] . '"');
        $badrc = MC_CLI_USER_ERROR;
        next;                   # continue validating rest of input
    }

    # Get the elements value
    $data_type = $rHoSDDef->{$element_name}{sd_dtype};
    # An SD cannot contain an SD
    if ($data_type == CT_SD_PTR || $data_type == CT_SD_PTR_ARRAY) {
        my $dtype_str = data_type_to_string($data_type);
        printCEMsg("EMsgMCcliInvalidArgDataType", $resource,
            $element_name, $dtype_str);
        $rc = MC_CLI_USER_ERROR;
    }
    ($rc, $element_value) = convert_input_value($data_type,
        $new_element->[1]);
    if ($rc != 0) {
        printCEMsg("EMsgMCcliBadArgValue", $element_name,
             $new_element->[1]);
        if ($badrc == 0) {$badrc = $rc; }
        next;
    }
    $rHoSDDef->{$element_name}{sd_value} = $element_value;
}

# Get the list of all the input element names in element index
# order. No guarantee that we got them from RMC in correct order
# but we have to make sure we give them to RMC in the correct order.
# Also even if RMC gave them in the correct order we stored them
# in a hash and a hash does not guarantee any order.
my @required_element_names = get_sd_element_names($rHoSDDef);

# Make sure that all of the required elements are being
# defined.
foreach $element_name (@required_element_names) {
    if (!defined($$rHoSDDef{$element_name}{sd_value})) {
        printCEMsg("EMsgMCcliMissingReqArg", $element_name,
            $resource, $main::PROGNAME);
        $badrc = MC_CLI_USER_ERROR;
    }
}

# Create the structured data structure (an array of hashes
# with the type and value - with one hash representing each
# element in the SD. Make sure the order is correct.)
my @LoSD = ();                  # Create the array

# by-pass creating @LoSD if no args specified
if ($#{$rNameValue->[1]} >=0) {

   foreach $element_name (@required_element_names) {
       my %element = ();
       $element{type} = $$rHoSDDef{$element_name}{sd_dtype};
       $element{value} = $$rHoSDDef{$element_name}{sd_value};
       push @LoSD, { %element };
   }
}

return($badrc, @LoSD);
}   # end build_cmd_arg_sd


#--------------------------------------------------------------------#
# End Exported Subroutines (with @EXPORT_OK, -> on demand).          #
#--------------------------------------------------------------------#

#--------------------------------------------------------------------#
# error_check - checks the return code from the RMC function and     #
#   the error response return code.  If an error is detected         #
#   appropriate error messages will be displayed.                    #
#                                                                    #
# Parameters:                                                        #
#   $rmc_function     in      Name of the rmc function that was      #
#                             called and whose error code we are     #
#                             checking.                              #
#   $rmc_class        in      The rmc resource class name.           #
#   $response         in      RMC response.                          #
#   $rmc_rc           in      The rmc function return code.          #
#   $error            in      The error response.                    #
#                                                                    #
# Return:                                                            #
#   $rc                       return code.                           #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub error_check
{
my ($rmc_function, $rmc_class, $response, $rmc_rc, $error) = @_;

my $rc = 0;
my $err_rc = $error->errnum();

if ($rmc_rc != 0) {
    if ($rmc_function eq "mc_qdef_sd_bp") {
        printCEMsg("EMsgMCcliQdefSDError", $rmc_class);
    }
    my $rmc_rc_hex = sprintf "0x%8.8lx", $rmc_rc;
    printCEMsg("EMsgMCcliMCFunctionFailure", $rmc_function, 
        $rmc_rc, $rmc_rc_hex);
    $rc = MC_CLI_RMC_ERROR;
    return $rc;
}

# Check the errnum in each of the RMC responses
for (my $r = 0; $r < $response->array_count; $r++) {
    if ($r > 0) {
        $response->error($error, $r);
        $err_rc = $error->errnum();
    }
    if ($err_rc != 0) {
        if ($err_rc == CT::MCerr::RMC_ECLASSNOTDEFINED) {
            printCEMsg("EMsgMCcliClassNotDef", $rmc_class);
            $rc = MC_CLI_USER_ERROR;
        }
        elsif ($err_rc == CT::MCerr::RMC_EBADRSRCHANDLE || 
               $err_rc == CT::RM::RM_EINVRESHANDLE ||
               $err_rc == CT::RM::RM_EINVALIDHANDLE) {
            printCEMsg("EMsgMCcliInvalidRsrcHandle", $rmc_class);
            $rc = MC_CLI_USER_ERROR;
        }
        elsif ($err_rc == CT::MCerr::RMC_EACCESS) {
            print STDERR $error->error_msg;
            $rc = MC_CLI_USER_ERROR;
        }
        elsif ($err_rc == RMC_EBADACTIONNAM) {
            print STDERR $error->error_msg;
            $rc = MC_CLI_USER_ERROR;
        }
        # It is possible for Resource Class to have no Cmd Args
        # defined - this is not an error.
        elsif ($err_rc == RMC_ESDNOTDEFINED) {
            $rc = 0;
        }
        elsif ($err_rc >= 0x60000 && $err_rc <= 0x6ffff) {
            # Selection string errors are in this range
            printCEMsg("EMsgMCcliSelectStrError");
            print STDERR $error->error_msg;
            $rc = MC_CLI_USER_ERROR;
        }
        else {
            if ($rmc_function eq "mc_qdef_sd_bp") {
                printCEMsg("EMsgMCcliQdefSDError", $rmc_class);
            }
            my $err_rc_hex = sprintf "0x%8.8lx", $err_rc;
            printCEMsg("EMsgMCcliMCFunctionFailure", $rmc_function,
                $err_rc, $err_rc_hex);
            print STDERR $error->error_msg;
            $rc = MC_CLI_RMC_ERROR;
        }
    }   # end if
}   # end for

return $rc;
}   # end error_check


#--------------------------------------------------------------------#
# End Non Exported Subroutines - only used within this pm.           #
#--------------------------------------------------------------------#

#--------------------------------------------------------------------#
# End File.                                                          #
#--------------------------------------------------------------------#