# @(#)57        1.27 src/43haes/lib/perl/libcl_disp.pm, hacmp.wsm, 61haes_r721, 1632A_hacmp721 8/4/16 15:57:19
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# 61haes_r721 src/43haes/lib/perl/libcl_disp.pm 1.27 
#  
# Licensed Materials - Property of IBM 
#  
# Restricted Materials of IBM 
#  
# COPYRIGHT International Business Machines Corp. 1004,2016 
# 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 
###########################################################
# Name: libcl_disp.pm
#
# A Perl library containing routines common to both cldisp
# and wsm_cldisp.cgi.
###########################################################
package libcl_disp;
# Set secure PATH for RBAC compatibility
$ENV{"PATH"} = "/usr/bin:/usr/sbin";
# "use strict" will help to keep this code cleaner
use strict;
use vars qw(@EXPORT @ISA);
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(
    snmpinfoRefresh
    nodes_in_cluster
    interfaces_on_node
    name_of_interface
    ip_of_interface
    network_of_ip_addr
    apps_on_cluster
    nodes_of_app_of_rg
    rg_of_app
    vgs_of_app_of_rg
    cc_vgs_of_app_of_rg
    filesystems_of_app_of_rg
    fast_connect_services_of_app_of_rg
    startscript_of_app
    stopscript_of_app
    service_labels_of_app_of_rg
    ip_of_service_label
    network_of_service_label
    monitors_of_app
    get_monitor_value
    state_of_local_clstrmgr
    state_of_cluster
    substate_of_cluster
    state_of_node
    state_of_interface
    state_of_network
    state_of_app_of_rg
    nodes_providing_app_of_rg
    calculateDNPPFallover
    calculateFalloverToNextPriorityNode
    calculateOfflineFallover
    initRgPrefs
    fallover_node_for_app_of_rg
    state_of_service_label
    translate
    translateCustomAttr
    probeStatic
    probeDynamic
    detaint
    $clusterName
    $HACMPBASE
    $ACTIVE
    $INACTIVE
    $UP
    $DOWN
    $UNKNOWN
    $UNSTABLE
    $STABLE
    $ERROR
    $RECONFIG
    $ONLINE
    $OFFLINE
    $ACQUIRING
    $RELEASING
    $MESSAGE_CL_HIGHEST_FREE_MEM
    $MESSAGE_CL_HIGHEST_IDLE_CPU
    $MESSAGE_CL_LOWEST_DISK_BUSY
    $MESSAGE_USER_SPECIFIED
    $MESSAGE_UNKNOWN_UNLESS_ONLINE
    $clusterName
    $overall_clstrmgr_state
    $local_clstrmgr_state
    $clusterState
    $clusterSubstate
    @apps
    $localNodeName
    $h
    $r
    @appStates
    $c
    @nodesByApp
    @providingNodesByApp
    @falloverNodesOfApps
    @startscriptsByApp
    @stopscriptsByApp
    @serviceIpLabelsOfApps
    @vgsOfApps
    @ccVgsOfApps
    @filesystemsOfApps
    @fastConnectServicesOfApps
    $s
    $t
    $rg
    @RgsByApp
    @serviceIpStates
    $c2
    $rr2
    @nodeNames
    %nodeID
    $cc2
    @interfacesByNode
    @networksOfIpAddrs
    @networksOfServiceLabels
    @interfaceStatesByNode
    @ipsByNode
    @nodeStates
    @networkStates
    @monitorsOfApps
    @cllsif
    $SNMPINFO_SOURCE
    @serviceIpsOfApps
    $startupPref
    $falloverPref
    $fallbackPref
);
# Subroutine declarations are in the same order in which they appear below
sub separate($$$$);
sub snmpinfoRefresh($);
sub snmpinfoRefreshViaSnmpinfo($);
sub snmpinfoRefreshViaWsm_cmd_exec($);
sub nodes_in_cluster();
sub interfaces_on_node($);
sub name_of_interface($$$);
sub ip_of_interface($$$);
sub network_of_ip_addr($$);
sub apps_on_cluster();
sub nodes_of_app_of_rg($$);
sub rg_of_app($);
sub vgs_of_app_of_rg($$);
sub cc_vgs_of_app_of_rg($$);
sub filesystems_of_app_of_rg($$);
sub fast_connect_services_of_app_of_rg($$);
sub startscript_of_app($);
sub stopscript_of_app($);
sub service_labels_of_app_of_rg($$);
sub ip_of_service_label($);
sub network_of_service_label($);
sub monitors_of_app($);
sub get_monitor_value($$);
sub state_of_local_clstrmgr();
sub state_of_cluster();
sub substate_of_cluster();
sub state_of_node($);
sub state_of_interface($$);
sub state_of_network($$);
sub state_of_app_of_rg($$);
sub nodes_providing_app_of_rg($$);
sub calculateDNPPFallover($);
sub calculateFalloverToNextPriorityNode();
sub calculateOfflineFallover();
sub initRgPrefs($);
sub fallover_node_for_app_of_rg($$);
sub state_of_service_label($);
sub translate($);
sub translateCustomAttr($);
sub probeStatic();
sub probeDynamic();
sub detaint($);
our $HACMPBASE="/usr/es/sbin/cluster";
# cluster services
# IF THESE ARE CHANGED YOU MUST ALSO CHANGE THEM INSIDE THE 'translate' SUBROUTINE
our $ACTIVE = 'active';
our $INACTIVE = 'inactive';
# cluster states
# IF THESE ARE CHANGED YOU MUST ALSO CHANGE THEM INSIDE THE 'translate' SUBROUTINE
our $UP = 'up';
our $DOWN = 'down';
our $UNKNOWN = 'unknown';
# cluster substates
# IF THESE ARE CHANGED YOU MUST ALSO CHANGE THEM INSIDE THE 'translate' SUBROUTINE
our $UNSTABLE = 'unstable';
our $STABLE = 'stable';
our $ERROR = 'error';
our $RECONFIG = 'reconfig';
#   $UNKNOWN (defined above)
# app states
# IF THESE ARE CHANGED YOU MUST ALSO CHANGE THEM INSIDE THE 'translate' SUBROUTINE
our $ONLINE = 'online';
our $OFFLINE = 'offline';
our $ACQUIRING = 'acquiring';
our $RELEASING = 'releasing';
#   $ERROR (defined above)
#   $UNKNOWN (defined above)
# IF THESE ARE CHANGED YOU MUST ALSO CHANGE THEM INSIDE THE 'translate' SUBROUTINE
our $JOINING = 'joining';
our $LEAVING = 'leaving';
# Messages explaining to which node an application will fallover when the
# resource group is set to use a particular dynamic node priority protocol.
# Setting these doesn't actually help much because of having to use dspmsg.
# these have been addded to cluster.cat, set 47, starting with msg 28
our $MESSAGE_CL_HIGHEST_FREE_MEM = "whichever node has the most free memory";
our $MESSAGE_CL_HIGHEST_IDLE_CPU = "whichever node has the least cpu usage";
our $MESSAGE_CL_LOWEST_DISK_BUSY = "whichever node has the least disk usage";
our $MESSAGE_USER_SPECIFIED = "placement is decided dynamically using a user-specified resource variable";
our $MESSAGE_UNKNOWN_UNLESS_ONLINE = "impossible to determine unless cluster services are active";
# The next three lines declare variables that are reused throughout the program
# Care must be taken not to clobber some of these globals, particularly vars:
# @snmpinfo, $app, $rg, $r, $c, $rr, $cc, $rr2, $cc2
our (@tmp, @tmp2, @word, $app, $rg, $h, $r, $c, $rr, $cc, $r2, $c2, $rr2, $cc2, $t, $s);
our (@scrap, @odm, $ip, $netState);
our @snmpinfo;
our @cllsif;
our @clshowsrv;
our $SNMPINFO_SOURCE;
# These declarations are arranged by how they relate to one another. Descriptions
# of these variables can be found in the locations in which they're initialized.
our $clusterName;                   our $overall_clstrmgr_state;     our $local_clstrmgr_state;
    our $clusterState;    our $clusterSubstate;
our $localNodeName;
our @apps;
    our @monitorsOfApps;
    our @nodesByApp;
    our @RgsByApp;                  our @nodesOfRg;
    our @startscriptsByApp;
    our @stopscriptsByApp;
    our @appStates;
    our @providingNodesByApp;
    our @falloverNodesOfApps;
    our @serviceIpLabelsOfApps;     our @serviceIpStates;     our @networksOfServiceLabels;
    our @serviceIpsOfApps;
    our @vgsOfApps;
    our @ccVgsOfApps;
    our @filesystemsOfApps;
    our @fastConnectServicesOfApps;
our @nodeNames;                     our @nodeStates;                our %nodeID;
    our @interfacesByNode;          our @interfaceStatesByNode;     our @networksOfIpAddrs;    our @networkStates;
    our @ipsByNode;
# These store resource group policies
our $startupPref;
our $falloverPref;
our $fallbackPref;
our @addrs_found = "";
our @names_found = "";
#####################################################
# Function:
#
#    separate
#
# Description:
#    Given FS (field separator) and token delimitors (which identify the 
#    beginning and end of the string that is NOT to be tokenized/split), 
#    the function tokenize the given input sequence. Here FS is ':' and 
#    delimitors are '[' and ']' signifying that string enclosed within 
#    '[' and ']' will not be tokenized inspite of ':' separator in between. 
#
# Arguments:
#
#    $_[0] : input array
#    $_[1] : input separator (field separator)
#    $_[2] : character indicating input delimitor start
#    $_[3] : character indicating input delimitor end
#
# Returns:
#    Input tokenized as per the rules of parsing.
#####################################################
sub separate ($$$$) {
    my (@interm_array, @op_array, @ip_array, $rec);
    my $ip_array = $_[0];
    my $ip_sep = $_[1];
    my $ip_delm_start= "\\".$_[2];
    my $ip_delm_end = "\\".$_[3];
    my $rec="";
    my $i = 0;
    my $flag = 0;
    @interm_array = split(/$ip_sep/, $ip_array);
    foreach my $elem (@interm_array) {
        use Switch;
        switch ($elem) {
            case (/$ip_delm_start/) { $flag = 1 }
            case (/$ip_delm_end/)   { $flag = 2 }
        }
        switch ($flag) {
            case 0 { $op_array[$i++] = $elem}
            case 1 { $rec = $rec.$elem.$ip_sep}
            case 2 { $op_array[$i++] = $rec.$elem; $rec=""; $flag=0;}
        }
    }
    return @op_array;
}
#####################################################
# Function:
#    snmpinfoRefresh
#
# Description:
#    Determines how to query the the MIB for the
#    needed info. If the this process is being run
#    by root it will use 'snmpinfo', but in the case
#    of wsm_cldisp the user will be 'nobody' so
#    wsm_cmd_exec will be used to query the info.
#    
# Arguments:
#    Name of  the output file to create
#####################################################
sub snmpinfoRefresh($){
    # No longer need to differentiate between RUIDs
	# as WebSMIT is now deprecated.
    return &snmpinfoRefreshViaSnmpinfo($_[0]);
}
#####################################################
# Function:
#    snmpinfoRefreshViaSnmpinfo
#
# Description:
#
# Arguments:
#    Name of  the output file to create
#####################################################
sub snmpinfoRefreshViaSnmpinfo($){
    umask 177;
    my $SNMPINFO_OUT = $_[0];
    # Try probing the MIB on each of the cluster's other nodes.
    # If nothing is returned by a node its cluster mgr must not
    # be active. We'll try successive nodes until we've found
    # or are forced to give up.
    
    # These are the keys for the info we're looking for in the MIB:
    my $CLUSTER_BRANCH  = "1.3.6.1.4.1.2.3.1.2.1.5.1";
    my $NODE_BRANCH     = "1.3.6.1.4.1.2.3.1.2.1.5.2.1.1";
    my $RG_BRANCH       = "1.3.6.1.4.1.2.3.1.2.1.5.11.1.1";
    my $RES_BRANCH      = "1.3.6.1.4.1.2.3.1.2.1.5.11.2.1";
    my $RG_STATE_BRANCH = "1.3.6.1.4.1.2.3.1.2.1.5.11.3.1";
    my $NETWORK_BRANCH  = "1.3.6.1.4.1.2.3.1.2.1.5.4.1.1";
    #===========================================================================
    # In 5.5, a new address format was added to the MIB in support of IPv6;
    # variables prefaced with "addr6". These new variables support both
    # IPv6 *and* IPv4. Unfortunately, testing revealed the need to continue
    # supporting the old format to accommodate mixed, migrated environments.
    # When a migrated node runs cldisp, and the node that is contacted below
    # has *not* been migrated yet, you can end up with an addr6-based cldisp
    # attempting to use non-addr6 MIB data!! Therefore, for as long as we have
    # current support for 5.4.1 or earlier, we need to still pull the non-addr6
    # data.
    #===========================================================================
    my $ADDRESS_BRANCH  = "1.3.6.1.4.1.2.3.1.2.1.5.3.1.1";   # Pre-5.5 format
    my $ADDRESS6_BRANCH = "1.3.6.1.4.1.2.3.1.2.1.5.13.1.1";  # New as of 5.5
    my @nodes = &nodes_in_cluster();
    my $found_active_node = 0;
    NODE: foreach my $remoteNode (@nodes){
        #
        # Try to get the cluster branch first. If this fails,
        # then there is no need to try the remainder!
        #
        system("$HACMPBASE/utilities/clrexec $remoteNode SNMPINFO -v $CLUSTER_BRANCH > $SNMPINFO_OUT");
        #
        # We now need to check to see if the remote node actually had cluster
        # services enabled. If it didn't, the $SNMPINFO_OUT file will be empty
        # and we'll have to try another node
        #
        next NODE if (! -s "$SNMPINFO_OUT");
        #
        # Get the rest of the data
        #
        system("$HACMPBASE/utilities/clrexec $remoteNode SNMPINFO -v $NODE_BRANCH >> $SNMPINFO_OUT; $HACMPBASE/utilities/clrexec $remoteNode SNMPINFO -v $ADDRESS_BRANCH >> $SNMPINFO_OUT; $HACMPBASE/utilities/clrexec $remoteNode SNMPINFO -v $ADDRESS6_BRANCH >> $SNMPINFO_OUT; $HACMPBASE/utilities/clrexec $remoteNode SNMPINFO -v $RG_BRANCH >> $SNMPINFO_OUT; $HACMPBASE/utilities/clrexec $remoteNode SNMPINFO -v $RES_BRANCH >> $SNMPINFO_OUT; $HACMPBASE/utilities/clrexec $remoteNode SNMPINFO -v $RG_STATE_BRANCH >> $SNMPINFO_OUT; $HACMPBASE/utilities/clrexec $remoteNode SNMPINFO -v $NETWORK_BRANCH >> $SNMPINFO_OUT");
        $found_active_node = 1;
        last NODE;
    }
    if (!$found_active_node){
        return $INACTIVE;
    }
    return $ACTIVE;
}
#####################################################
# Function:
#    snmpinfoRefreshViaWsm_cmd_exec
#
# Description:
#
# Arguments:
#    Name of  the output file to create
#####################################################
sub snmpinfoRefreshViaWsm_cmd_exec($){
    umask 077;
    my $SNMPINFO_OUT = $_[0];
    # Random filenames will be used for increased security
    my $random_num;
    $random_num = int(rand 999999);
    # Verification that the file doesn't already exist
    while( -e "snmpinfo.command.$random_num"){
        $random_num = int(rand 999999);
    }
    # create a tmp file to run the command
    my $SNMPINFO_SCRIPT = "/tmp/snmpinfo.command.$random_num";
    # Try probing the MIB on each of the cluster's other nodes.
    # If nothing is returned by a node its cluster mgr must not
    # be active. We'll try successive nodes until we've found
    # or are forced to give up.
    my @nodes = &nodes_in_cluster();
    my $found_active_node = 0;
    foreach my $remoteNode (@nodes){
        open (FPT, ">$SNMPINFO_SCRIPT");
        print FPT "#!/bin/ksh\n";
        print FPT "umask 111\n";
        print FPT "BINDIR=$HACMPBASE/wsm/cgi-bin\n";
        print FPT "CONFDIR=$HACMPBASE/wsm\n";
        print FPT "\n";
        print FPT ". \$CONFDIR/wsm_smit.conf > /dev/null\n";
        print FPT "export AUTHORIZED_PORT REDIRECT_TO_HTTPS REQUIRE_AUTHENTICATION\n";
        print FPT "export ACCEPTED_USERS SESSION_TIMEOUT REQUIRED_WEBSERVER_UID\n";
        print FPT "\n";
        print FPT "\$BINDIR/wsm_cmd_exec -M -n $remoteNode -e cluster > $SNMPINFO_OUT\n";
        print FPT "\$BINDIR/wsm_cmd_exec -M -n $remoteNode -e node >> $SNMPINFO_OUT\n";
        print FPT "\$BINDIR/wsm_cmd_exec -M -n $remoteNode -e address >> $SNMPINFO_OUT\n";
        print FPT "\$BINDIR/wsm_cmd_exec -M -n $remoteNode -e resmanager >> $SNMPINFO_OUT\n";
        print FPT "\$BINDIR/wsm_cmd_exec -M -n $remoteNode -e network >> $SNMPINFO_OUT\n";
        close(FPT);
        system("chmod +x $SNMPINFO_SCRIPT");
        system("$SNMPINFO_SCRIPT");
        # We now need to check to see if the remote node actually had cluster services enabled.
        # If it didn't, the $SNMPINFO_OUT file will be empty and we'll have to try another node
        if( -s "$SNMPINFO_OUT" ){
            $found_active_node = 1;
            last;
        }
        # cleanup
        system("rm -f $SNMPINFO_SCRIPT");
    }
    if(!$found_active_node){
        return $INACTIVE;
    }
    return $ACTIVE;
}
#####################################################
# Function:
#    nodes_in_cluster
#
# Description:
#    Returns a list of the names of nodes in the
#    cluster
#
# Arguments:
#    none
#####################################################
sub nodes_in_cluster(){
    my @nodes;
    my @tmp = `$HACMPBASE/utilities/clshownets`;
    $_ = $tmp[$#tmp];
    @nodes = split;
    if(!$nodes[0]){
        print STDERR `dspmsg -s 20 scripts.cat 25 "Error: No nodes in cluster or no access to cluster ODM.\n"`;
        exit 1;
    }
    foreach my $node (@nodes) { &detaint($node); }
    return @nodes;
}
#####################################################
# Function:
#    interfaces_on_nodes
#
# Description:
#    Returns a list of the interfaces on a 
#    node
#
# Arguments:
#    $_[0]: a node name
#####################################################
sub interfaces_on_node($){
    my @interfaces;
    my $node = $_[0];
    foreach my $cllsif_node (@cllsif){
	my @cnode = split (/~/, $cllsif_node);
        if( ($cnode[3] eq "diskhbmulti") ){
            if( ($cnode[5] eq $node) && ($cnode[6] ne "") ) {
                push @interfaces, $cnode[6];
            }
        } else {
            if( ($cnode[5] eq $node) && ($cnode[8] ne "") ) {
                push @interfaces, $cnode[8];
            }
        }
    }
    if(!$interfaces[0]){
        print STDERR `dspmsg scripts.cat -s 23 100 "\nWARNING: Node %s does not appear to have any network interfaces.\n" "$node"`;
    }
    return @interfaces;
}
#####################################################
# Function:
#    name_of_interface
#
# Description:
#    Returns the name (as known to PowerHA SystemMirror) of an
#    interface
#
# Arguments:
#    $_[0]: a node name
#    $_[1]: an interface name (on that node. ex: en0)
#####################################################
sub name_of_interface($$$){
	my ( $node, $interface, $labelsFound ) = @_; 
    my $found = 0;
    foreach my $intf (@cllsif){
	my @inf = split(/~/, $intf);
        if( ($inf[3] eq "diskhbmulti") ){
            if( ($inf[5] eq $node) && ($inf[6] eq $interface) ){
                return $inf[0];
            }
        } else {
            if( ($inf[5] eq $node) && ($inf[8] eq $interface) ){
		$found = 0;
		foreach my $interface_name (@$labelsFound){
			if( ($interface_name eq $inf[0]) ){
				$found = 1;
				last;
			}
		}
		if ($found != 1){
	                return $inf[0];
		}
            }
	}
    }
    print STDERR `dspmsg scripts.cat -s 23 101 "\nWARNING: No interface name found for %s on %s.\n" "$interface" "$node"`;
}
#####################################################
# Function:
#    ip_of_interface
#
# Description:
#    Returns the boot/standby IP address of an
#    interface. (In the case of a non-ip based
#    interface returns the path to the 
#    corresponding device. ex: /dev/tty0)
#
# Arguments:
#    $_[0]: a node name
#    $_[1]: an interface name (on that node. ex: en0)
#####################################################
sub ip_of_interface($$$){
    my ( $node, $interface, $ipaddrsFound ) = @_;
    my $found = 0;
    # Finds and returns the ip that corresponds to
    # both the supplied node and interface
    foreach my $intf (@cllsif){
	my @inf = split(/~/, $intf);
        if( ($inf[3] eq "diskhbmulti") ){
            if( ($inf[5] eq $node) && ($inf[6] eq $interface) ){
                return $inf[6];
            }
        } else {
            if( ($inf[3] eq "diskhb") ){
                if( ($inf[5] eq $node) && ($inf[8] eq $interface) ){
                    return $inf[6];
                }
            } else {
                if( ($inf[5] eq $node) && ($inf[8] eq $interface) ){
                    $found = 0;
                    foreach my $ip_addr (@$ipaddrsFound){
                        if( ($ip_addr eq $inf[6]) ){
                            $found = 1;
                            last;
                        }
                    }
                    if ($found != 1){
                        return $inf[6];
                    }
                }
            }
        }
    }
    print STDERR `dspmsg scripts.cat -s 23 102 "\nWARNING: No interface '%s' exists on node '%s'.\n" "$interface" "$node"`;
}
#####################################################
# Function:
#    network_of_ip_addr
#
# Description:
#    Returns the network (as it's known to PowerHA SystemMirror) to
#    which an interface belongs
#
# Arguments:
#    $_[0]: a node name
#    $_[1]: an ip address
#####################################################
sub network_of_ip_addr($$){
    my $node = $_[0];
    my $ip = $_[1];
    # We must check to make sure that both the node and interface names correspond
    # to the network which we're going to return
    foreach my $intf (@cllsif){
	my @inf = split(/~/, $intf);
        if( ($inf[5] eq $node) && ($inf[6] eq $ip) ){
                return $inf[2];
        }
    }
    print STDERR `dspmsg scripts.cat -s 23 103 "\nWARNING: No interface '%s' exists on node '%s'.\n" "$ip" "$node"`;
}
#####################################################
# Function:
#    apps_on_cluster
#
# Description:
#    Returns a list of the names of all applications
#    known to PowerHA SystemMirror
#
# Arguments:
#    none
#####################################################
sub apps_on_cluster(){
    my @apps = `$HACMPBASE/utilities/cllsserv -c | cut -f1 -d:`;
    chomp @apps;
    foreach my $app (@apps) { &detaint($app); }
    return @apps;
}
#####################################################
# Function:
#    nodes_of_app_of_rg
#
# Description:
#    Returns an array of the all nodes that are members
#    of a application's resource group
#
# Arguments:
#    $_[0]: an application name
#    $_[1]: rg name
#####################################################
sub nodes_of_app_of_rg($$){
    my $app = $_[0];
    my $rg = $_[1];
    local @_;
    local $_;
    my @ret_nodes;
    if($rg eq "NONE"){
        return @ret_nodes;
    }
    my @nodes = `$HACMPBASE/utilities/clgetgrp -g $rg -f nodes`;
    chomp @nodes;
    foreach (@nodes) {
        my @tmp = split;
        for (my $i = 0; $i <= $#tmp; $i++){
            push @ret_nodes, $tmp[$i];
        }
    }
    foreach my $ret_node (@ret_nodes) { &detaint($ret_node); }
    return @ret_nodes;
}
#####################################################
# Function:
#    rg_of_app
#
# Description:
#    Returns an array holding resource groups' name
#    to which the application belongs
#
# Arguments:
#    $_[0]: an application name
#####################################################
sub rg_of_app($){
    
    my $app = $_[0];
    $_ = `$HACMPBASE/utilities/cllsres -q "value = $app" -f group`;
    chomp $_;
    my @odm = split(/ +/,$_);
    chomp @odm;
    if ($odm[0] eq "") {
	$odm[0]="NONE";
    }
    foreach my $o (@odm) { &detaint($o); }
    return @odm;
}
#####################################################
# Function:
#    vgs_of_app_of_rg
#
# Description:
#    Returns a list of the volume groups associated
#    with an application
#
# Arguments:
#    $_[0]: an application name
#    $_[1]: rg name
#####################################################
sub vgs_of_app_of_rg($$){
    my $app = $_[0];
    my $rg = $_[1];
    local $_;
    local @_;
    
    # @odm = `odmget -q "group=$rg and name=VOLUME_GROUP" HACMPresource`;
    if($rg eq "NONE"){
        return my @vgs;
    }
    
    $_ = `$HACMPBASE/utilities/cllsres -cg $rg | tail -1 | cut -f2 -d:`;
    chomp $_;
    my @vgs = split (/ +/, $_);
    chomp @vgs;
    foreach my $vg (@vgs) { &detaint($vg); }
    return @vgs;
}
#####################################################
# Function:
#    cc_vgs_of_app_of_rg
#
# Description:
#    Returns a list of the concurrent volume groups
#    associated with an application
#
# Arguments:
#    $_[0]: an application name
#    $_[1]: rg name
#####################################################
sub cc_vgs_of_app_of_rg($$){
    my $app = $_[0];
    my $rg = $_[1];
    local $_;
    local @_;
    
    # @odm = `odmget -q "group=$rg and name=CONCURRENT_VOLUME_GROUP" HACMPresource`;
    if($rg eq "NONE"){
        return my @ccvgs;
    }
    $_ = `$HACMPBASE/utilities/cllsres -cg $rg | tail -1 | cut -f3 -d:`;
    chomp $_;
    my @ccvgs = split (/ +/, $_);
    chomp @ccvgs;
    foreach my $ccvg (@ccvgs) { &detaint($ccvg); }
    return @ccvgs;
}
#####################################################
# Function:
#    filesystems_of_app_of_rg
#
# Description:
#    Returns a list of the filesystems associated
#    with an application
#
# Arguments:
#    $_[0]: an application name
#    $_[1]: rg name
#####################################################
sub filesystems_of_app_of_rg($$){
    my $app = $_[0];
    my $rg = $_[1];
    local $_;
    local @_;
    # @odm = `odmget -q "group=$rg and name=FILESYSTEM" HACMPresource`;
    if($rg eq "NONE"){
        return my @fs;
    }
    $_ = `$HACMPBASE/utilities/cllsres -cg $rg | tail -1 | cut -f5 -d:`;
    chomp $_;
    my @fs = split (/ +/, $_);
    chomp @fs;
    foreach my $f (@fs) { &detaint($f); }
    return @fs;
}
#####################################################
# Function:
#    fast_connect_services_of_app_of_rg
#
# Description:
#    Returns a list of the AIX fast connect services
#    associated with a application
#
# Arguments:
#    $_[0]: an application name
#    $_[1]: rg name
#####################################################
sub fast_connect_services_of_app_of_rg($$){
    my $app = $_[0];
    my $rg = $_[1];
    local $_;
    local @_;
    
    # @odm = `odmget -q "group=$rg and name=AIX_FAST_CONNECT_SERVICES" HACMPresource`;
    if($rg eq "NONE"){
        return my @fc;
    }
    $_ = `$HACMPBASE/utilities/cllsres -cg $rg | tail -1 | cut -f11 -d:`;
    chomp $_;
    my $fc = split (/ +/, $_);
    chomp $fc;
    &detaint($fc);
    return $fc;
}
#####################################################
# Function:
#    startscript_of_app
#
# Description:
#    Returns the path to the starting script of an
#    application
#
# Arguments:
#    $_[0]: an application name
#####################################################
sub startscript_of_app($){
    # my @odm = `odmget -q name=$_[0] HACMPserver`;
    my $start = `$HACMPBASE/utilities/cllsserv -cn $_[0] | cut -f2 -d:`;
    chomp $start;
    &detaint($start);
    return $start;
}
#####################################################
# Function:
#    stopscript_of_app
#
# Description:
#    Returns the path to the stop script of an
#    application
#
# Arguments:
#    $_[0]: an application name
#####################################################
sub stopscript_of_app($){
    # my @odm = `odmget -q name=$_[0] HACMPserver`;
    my $stop = `$HACMPBASE/utilities/cllsserv -cn $_[0] | cut -f3 -d:`;
    chomp $stop;
    &detaint($stop);
    return $stop;
}
#####################################################
# Function:
#    service_labels_of_app_of_rg
#
# Description:
#    Returns a list of the service labels of an
#    application
#
# Arguments:
#    $_[0]: an application name
#    $_[1]: rg name
#####################################################
sub service_labels_of_app_of_rg($$){
    my $rg = $_[1];
    # my @odm = `odmget -q"group=$rg and name=SERVICE_LABEL" HACMPresource`;
    my @serviceLabels;
    my $tmpserviceLabels;
    if($rg eq "NONE"){
        $serviceLabels[0] = "NONE";
        return @serviceLabels;
    }
    $tmpserviceLabels = `$HACMPBASE/utilities/cllsres -cg $rg | tail -1 | cut -f15 -d:`;
    if (length($tmpserviceLabels) == 0) {
        $serviceLabels[0] == "NONE";
    }
    @serviceLabels = split( " ", $tmpserviceLabels);
    chomp @serviceLabels;
    foreach my $serviceLabel (@serviceLabels) { &detaint($serviceLabel); }
    return @serviceLabels;
}
#####################################################
# Function:
#    ip_of_service_label
#
# Description:
#    Returns the IP address of a service label
#
# Arguments:
#    $_[0]: a service label
#####################################################
sub ip_of_service_label($){
    foreach(@cllsif){
	my @tmp = split(/~/, $_);
        if (($tmp[1] eq "service") && ($tmp[0] eq "$_[0]")){
            return $tmp[6];
        }
    }
    print STDERR `dspmsg scripts.cat -s 23 104 "\nWARNING: No service label '%s' was found on this cluster.\n" "$_[0]"`;
}
#####################################################
# Function:
#    network_of_service_label
#
# Description:
#    Returns the network to which a particular
#    service IP label belongs
#
# Arguments:
#    $_[0]: a service label
#####################################################
sub network_of_service_label($){
    foreach(@cllsif){
	my @tmp = split(/~/, $_);
        if (($tmp[1] eq "service") && ($tmp[0] eq "$_[0]")){
            return $tmp[2];
        }
    }
    print STDERR `dspmsg scripts.cat -s 23 104 "\nWARNING: No service label '%s' was found on this cluster.\n" "$_[0]"`;
}
#####################################################
# Function:
#    monitors_of_app
#
# Description:
#    Returns a list of the application monitors
#    configured for an application
#
# Arguments:
#    $_[0]: an application name
#####################################################
sub monitors_of_app($){
    my $app = $_[0];
    local $_;
    local @_;
    # @odm = `odmget -q "name=RESOURCE_TO_MONITOR and value=$app" HACMPmonitor`;
    $_ = `$HACMPBASE/utilities/cllsserv -csm -n $app | cut -f4 -d:`;
    chomp $_;
    my @monitors = split (/ +/, $_);
    chomp @monitors;
    foreach my $monitor (@monitors) { &detaint($monitor); }
    return @monitors;
}
#####################################################
# Function:
#    get_monitor_value
#
# Description:
#    Returns the value stored in the HACMPmonitor odm
#    for a particular monitor and attribute
#
# Arguments:
#    $_[0]: a monitor name
#    $_[1]: index
#####################################################
sub get_monitor_value($$){
    my $monitor = $_[0];
    my $index = $_[1];
    local $_;
    local @_;
    
    # @odm = `odmget -q "monitor=$monitor and name=$attribute" HACMPmonitor`;
    my $mval = `$HACMPBASE/utilities/cllsappmon -c $monitor | tail -1 | cut -f $index -d:`;
    chomp $mval;
    &detaint($mval);
    return $mval;
}
#####################################################
# Function:
#    state_of_local_clstrmgr
#
# Description:
#    Returns "active" if cluster services are active
#    and "inactive" if not.
#
# Arguments:
#    none
#####################################################
# Possible return values:
# "active"
# "inactive"
sub state_of_local_clstrmgr(){
    @clshowsrv = `LC_ALL=C $HACMPBASE/utilities/clshowsrv clstrmgrES`;
    foreach my $line (@clshowsrv){
        $line =~ s/^\s+//;  # strip leading whitespace
        chomp $line;
        my @tmp = split (/ +/, $line);
        if(($tmp[0] eq "clstrmgrES") && ($tmp[3] eq "active")){
            return $ACTIVE;
        }
    }
    return $INACTIVE;
}
#####################################################
# Function:
#    state_of_cluster
#
# Description:
#    Returns the state of the cluster
#
# Arguments:
#    none
#####################################################
# Possible return values:
# "up"
# "down"
# "unknown"
sub state_of_cluster(){
    # $SNMPINFO_SOURCE is generated by snmpinfoRefresh which should be called
    # shortly before this script is executed.
    foreach my $info (@snmpinfo){
        my @entry = split (/ +/, $info);
        if ($entry[0] eq "clusterState.0"){
            if ($entry[2] == 2){
                return $UP;
            }
            elsif ($entry[2] == 4){
                return $DOWN;
            }
            elsif ($entry[2] == 8){
                return $UNKNOWN;
            }
            # This shouldn't happen
            print STDERR `dspmsg scripts.cat -s 23 105 "\nWARNING: Unable to determine cluster status.\n"`;
            return;
        }
    }
}
#####################################################
# Function:
#    substate_of_cluster
#
# Description:
#    Returns the substate of the cluster
#
# Arguments:
#    none
#####################################################
# Possible return values:
# "unknown"
# "unstable"
# "stable"
# "error"
# "reconfig"
sub substate_of_cluster(){
    my @tmp;
    foreach(@snmpinfo){
        @tmp = split;
        if ($tmp[0] eq "clusterSubState.0"){
            if ($tmp[2] == 8){
                return $UNKNOWN;
            }
            elsif ($tmp[2] == 16){
                return $UNSTABLE;
            }
            elsif ($tmp[2] == 32){
                return $STABLE;
            }
            elsif ($tmp[2] == 64){
                return $ERROR;
            }
            elsif ($tmp[2] == 128){
                return $RECONFIG;
            }
            print STDERR `dspmsg scripts.cat -s 23 105 "\nWARNING: Unable to determine cluster substate.\n"`;
            return;
        }
    }
}
#####################################################
# Function:
#    state_of_node
#
# Description:
#    Returns the state of a node. ("up", "down",
#    "joining", "leaving")
#
# Arguments:
#    $_[0]: node name
#####################################################
# Possible return values:
# "up"
# "down"
# "joining"
# "leaving"
sub state_of_node($){
    my @nodeNames;
    my @nodeStates;
    # Here we'll extract the node names and store them
    # in the array @nodeNames in the order in which we
    # found them.
    foreach(@snmpinfo){
        my @tmp = split /\s|\./;
        if ($tmp[0] eq "nodeName"){
            $tmp[3] =~ s/\"//g;
            push @nodeNames, $tmp[3];
            $nodeID{$tmp[3]} = $tmp[1];
        }
        # This will keep us from searching through the
        # entire @snmpinfo array after we've found
        # what we're looking for.
        elsif($tmp[0] =~ /addr/){
            last;
        }
    }
    foreach(@snmpinfo){
        my @tmp = split /\s|\./;
        if ($tmp[0] =~ /nodeState/){
            push @nodeStates, $tmp[3];
        }
        # This will keep us from searching through the
        # entire @snmpinfo array after we've found
        # what we're looking for.
        elsif($tmp[0] =~ /addr/){
            last;
        }
    }
    for(my $r = 0; $nodeNames[$r]; $r++){
        if ($_[0] eq $nodeNames[$r]){
            if($nodeStates[$r] == 2){
                return $UP;
            }
            elsif($nodeStates[$r] == 4){
                return $DOWN;
            }
            elsif($nodeStates[$r] == 32){
                return $JOINING;
            }
            elsif($nodeStates[$r] == 64){
                return $LEAVING;
            }
            print STDERR `dspmsg scripts.cat -s 23 106 "\nWARNING:'%s' was found but a problem occurred while attempting to fetch state.\n" "$_[0]"`;
            return;
        }
    }
}
#####################################################
# Function:
#    state_of_interface
#
# Description:
#    Returns the state of an interface. (up/down)
#
# Arguments:
#    $_[0]: a node name
#    $_[1]: an interface name (on that node. ex: en0)
#####################################################
# Possible return values:
# "up"
# "down"
# "unknown"
sub state_of_interface($$){
    my $node = $_[0];
    my $interface = $_[1];
    my $inet_type = 1;
    if($ip = &ip_of_interface($_[0],$_[1],\@addrs_found) ){
        # This is a work-around for non-ip interfaces. It probes
        # the MIB's ip-like value assigned to the interface so
        # that it can be used in-place of a real IP address.
	push @addrs_found, $ip;
        $_ = $ip;
        s/[0-9,a-f]//g;
        if($_ ne "..." && $_ !~ /\:*/){
            #non-ip interface
            my $interfaceName = &name_of_interface($node, $interface, \@names_found);
	    push @names_found, $interfaceName;
	    foreach(@snmpinfo){
                my @tmp = split /\s|\./;
                if($tmp[0] eq "addr6Label"){
                        if($tmp[10]){
                                $tmp[10] =~ s/\"//g;
                                if($tmp[10] eq $interfaceName){
                                        $ip = "$tmp[4].$tmp[5].$tmp[6].$tmp[7]";
                                        last;
                                }
                        }
                }
                if($tmp[0] eq "addrLabel"){
                        if($tmp[7]){
                                $tmp[7] =~ s/\"//g;
                                if($tmp[7] eq $interfaceName){
                                        $ip = "$tmp[2].$tmp[3].$tmp[4].$tmp[5]";
                                        last;
                                }
                        }
                }
            }
        }
        else {
            # ip interface.
            # In the absence of perl APIs for converting textual IP address
            # to in_addr/in6_addr, obtain the corresponding data structural
            # elements from the MIB. The below logic fetches these from the 
            # instance qualifier of the 'addrLabel' MIB variable.
            # For reference on addr6Group table entries, refer
            # 'src/43haes/usr/sbin/cluster/clsmuxpd/hacmp.my'.
            my $interfaceName = &name_of_interface($node, $interface, \@names_found);
	    push @names_found, $interfaceName;
            foreach (@snmpinfo) {
                my @interface_name = split /=/;
                my @tmp = split /\s|\./;
		my @tmp_interface_name = split(/\"/,$interface_name[1]);
                if(($tmp[0] eq "addr6Label") && ($tmp_interface_name[1] eq $interfaceName)){
                    $ip="";
                    $inet_type = $tmp[2];
                    # next is octet count...skip it.
                    $ip = $tmp[4]; # this is the first octet of the ip address
                    for(my $count = 1; $count < $tmp[3]; $count++) {
                        $ip = "$ip.$tmp[$count + 4]";
                    }
                    last;
                }
            }
        }
        my $using_addr6 = 1;
        my @addrStates = grep(/addr6State/, @snmpinfo);
        if ($inet_type == 1  &&  ! @addrStates) {
            $using_addr6 = 0;
            @addrStates = grep(/addrState/, @snmpinfo);
        }
        foreach (@addrStates){
            my @tmp = split /\s|\./;
            # enum of inet_type is defined in 
            # 'src/43haes/usr/sbin/cluster/clsmuxpd/hacmp.my'.
            use Switch;
            switch ($inet_type) {
            case (1) { #IPv4
                    if ($using_addr6) {  # Using 5.5+ MIB data
                        if ($ip eq "$tmp[4].$tmp[5].$tmp[6].$tmp[7]") {
                            return $UP      if ($tmp[$#tmp] == 2);
                            return $DOWN    if ($tmp[$#tmp] == 4);
                            return $UNKNOWN;
                        }
                    } else {             # Using pre-5.5 MIB data
                        if ($ip eq "$tmp[2].$tmp[3].$tmp[4].$tmp[5]") {
                            return $UP      if ($tmp[$#tmp] == 2);
                            return $DOWN    if ($tmp[$#tmp] == 4);
                            return $UNKNOWN;
                        }
                    }
                }
            case (2) { #IPv6
                    my $tmp_ip="";
                    $tmp_ip = $tmp[4]; #first octet of ip address
                    for(my $count = 1; $count < $tmp[3]; $count++) {
                        $tmp_ip = "$tmp_ip.$tmp[4+$count]";
                    }
                    if ($ip eq $tmp_ip) {
                        return $UP      if ($tmp[$#tmp] == 2);
                        return $DOWN    if ($tmp[$#tmp] == 4);
                        return $UNKNOWN;
                    }
                }
            }
        }
        print STDERR `dspmsg scripts.cat -s 23 107 "\nWARNING: Unable to locate interface '%s' on node '%s'.\n" "$interface" "$node"`;
    }
    print STDERR `dspmsg scripts.cat -s 23 108 "\nWARNING: An unknown problem occurred while executing the ip_of_interface subroutine.\n"`;
}
#####################################################
# Function:
#    state_of_network
#
# Description:
#    Returns the state of a network.
#    (up/down/joining/leaving)
#
# Arguments:
#    $_[0]: the node name
#    $_[1]: the name of a network
#####################################################
# Possible return values:
# "up"
# "down"
# "joining"
# "leaving"
# "unknown"
sub state_of_network($$){
    local $_;
    my @snmpinfo = @snmpinfo; # Changes will only be for this dynamic scope
    my @tmp;
    my $netID = 9999;
    # First we need to find the netID of the network
    # While our @snmpinfo stack is still popping elements ...
    while($_ = shift @snmpinfo){
        @tmp = split /\s|\./;
        if( ($tmp[0] eq "netName") && ($tmp[4] eq "\"$_[1]\"") ){
            $netID = $tmp[2];
            last;
        }
    }
    if($netID == 9999){
        print STDERR `dspmsg scripts.cat -s 23 109 "\nERROR: Unable to find the network '%s' in the Management Information Base.\n" "$_[0]"`;
        return;
    }
    # Now, using the netID, we can find the state
    while($_ = shift @snmpinfo){
        @tmp = split /\s|\./;
        if( ($tmp[0] eq "netState") && ($tmp[1] == $nodeID{$_[0]}) && ($tmp[2] == $netID) ){
            $netState = $tmp[4];
            last;
        }
    }
    # Interprets and reports the state
    if($netState == 2){
        return $UP;
    }
    elsif($netState == 4){
        return $DOWN;
    }
    elsif($netState == 32){
        return $JOINING;
    }
    elsif($netState == 64){
        return $LEAVING;
    }
    else{
        return $UNKNOWN;
    }
}
#####################################################
# Function:
#
#    state_of_app_of_rg
#
# Description:
#
#    Returns the state of an application.
#
# Arguments:
#
#    $_[0]: an application name
#    $_[1]: rg name
#####################################################
# Possible return values:
# "$INACTIVE" (the application server is configured but is not a member of a RG)
# "$ONLINE"
# "$OFFLINE"
# "$ACQUIRING"
# "$RELEASING"
# "$ERROR"
# "$UNKNOWN"
sub state_of_app_of_rg($$){
    my $app = $_[0];
    my $rg = $_[1];
    my @resGroupNodeStates;
    my $resGroupID;
    if($rg eq "NONE"){
        return $INACTIVE;
    }
    # Uses the resource group name to parse the resource group ID from snmpinfo
    my @tmpSnmpinfo = `cat $SNMPINFO_SOURCE | fgrep resGroupName`;
    foreach(@tmpSnmpinfo){
        my @tmp = split /\s|\./;
        $tmp[3] =~ s/\"//g;
        if($tmp[3] eq $rg){
            $resGroupID = $tmp[1];
            last;
        }
    }
    # Builds a list of the states of all the nodes in the resource group we're
    # examining.
    @tmpSnmpinfo = `cat $SNMPINFO_SOURCE | grep resGroupNodeState`;
    foreach(@tmpSnmpinfo){
        my @tmp = split /\s|\./;
        if(($tmp[0] eq "resGroupNodeState") && ($tmp[1] == $resGroupID)){
            push @resGroupNodeStates, $tmp[4];
        }
    }
    foreach my $resGroupNodeState (@resGroupNodeStates) { &detaint($resGroupNodeState); }
    # This shouldn't happen, but if it does something obviously went wrong.
    if(!@resGroupNodeStates){
        print STDERR `dspmsg scripts.cat -s 23 110 "\nWARNING: Could not determine the state of application '%s'.\n" "$_[0]"`;
        return;
    }
    # Here we look at the state of the application on each of the nodes that are
    # capable of providing it and determine the app's state accordingly.
    my $nodes_acquiring = 0;
    my $nodes_releasing = 0;
    my $nodes_offline = 0;
    my $nodes_error = 0;
    my $nodes_unknown = 0;
    foreach(@resGroupNodeStates){
        if($_ == 2){
            # if its online on any node, the global state is online
            return $ONLINE;
        }
        elsif($_ == 4){
            $nodes_offline++;
        }
        elsif($_ == 8){
            $nodes_unknown++;
        }
        elsif($_ == 16){
            $nodes_acquiring++;
        }
        elsif($_ == 32){
            $nodes_releasing++;
        }
        elsif($_ == 64){
            # if its in error state on any node, the global state is error
            return $ERROR;
        }
    }
    if($nodes_acquiring){
        return $ACQUIRING;
    }
    # If they're all OFFLINE / RELEASING / ERROR 
    elsif($nodes_offline == ($#resGroupNodeStates + 1)){
        return $OFFLINE;
    }
    elsif($nodes_error == ($#resGroupNodeStates + 1)){
        return $ERROR;
    }
    elsif($nodes_releasing == ($#resGroupNodeStates + 1)){
        return $RELEASING;
    }
    # If they're any other combination of states
    return $UNKNOWN;
}
#####################################################
# Function:
#
#    nodes_providing_app_of_rg
#
# Description:
#
#    Returns a list of the nodes currently providing
#    an application
#
# Arguments:
#
#    $_[0]: an application name
#    $_[1]: rg name
#####################################################
sub nodes_providing_app_of_rg($$){
    my $app = $_[0];
    my $rg = $_[1];
    my @nodes;
    my @nodesWithID;
    my @resGroupNodesID;
    my $resGroupID;
    my @resGroupNodeStates;
    my @results;
    my @tmpSnmpinfo = `cat $SNMPINFO_SOURCE | grep -e nodeName -e resGroupName -e resGroupNodeState`;
    my @tmp;
    if($rg eq "NONE"){
        $results[0] = "NONE";
        return @results;
    }
    # Parses snmpinfo's output for ...
    foreach my $line (@tmpSnmpinfo){
        @tmp = split /\s|\./, $line;
        # ... the names of the nodes in the cluster ...
        # (Although we already have a subroutine that does this
        # we do it again here because we will also be gathering
        # the states of the nodes from snmpinfo and we need to
        # insure that our nodes and node-states match.)
        if("$tmp[0]" eq "nodeName"){
            $tmp[3] =~ s/\"//g;
	    push @nodesWithID, "$tmp[3].$tmp[1]";
        }
        # ... the ID by which snmpinfo refers to app's RG ...
        elsif("$tmp[0]" eq "resGroupName"){
            $tmp[3] =~ s/\"//g;
            if($tmp[3] eq $rg){
                $resGroupID = $tmp[1];
            }
        }
        # ... and the states of each node (This IF will never be
        # followed until after all the resGroupIDs have been found
        # since states are listed later in snmpinfo's output.)
        elsif( ("$tmp[0]" eq "resGroupNodeState") && ($tmp[1] == $resGroupID) ){
            push @resGroupNodeStates, $tmp[4];
	    push @resGroupNodesID, $tmp[2];
        }
    }
    # We should always find the nodes' states. If not, something's gone wrong.
    if(!@resGroupNodeStates){
        print STDERR `dspmsg scripts.cat -s 23 111 "\nWARNING: Could not determine the state of application %s.\n" "$app"`;
        return;
    }
    # Keep only nodes that have this RG
    for(my $id = 0; $resGroupNodesID[$id]; $id++){
       for(my $node_id = 0; $nodesWithID[$node_id]; $node_id++) {
          @tmp = split /\s|\./, $nodesWithID[$node_id];
    	  if ($tmp[1] == $resGroupNodesID[$id]) {
		push @nodes, $tmp[0];
	}
      }
    }
    # Returns a list of all nodes that are both in the app's RG and
    # have a state of "online".
    for(my $r = 0; $resGroupNodeStates[$r]; $r++){
        if($resGroupNodeStates[$r] == 2){
            push  @results, $nodes[$r];
        }
    }
    if(!$results[0]){
        $results[0] = "NONE";
    }
    return @results;
}
# ------------------------------------------------------------------------
#   The next four subroutines are only called by fallover_node_for_app_of_rg
# ------------------------------------------------------------------------
#####################################################
# Function:
#
#    calculateDNPPFallover
#
# Description:
#
#    Returns an explanation of which node will be
#    selected for fallover by the dynamic node
#    priority policy
#
# Arguments: 
#
#    resource group name
#####################################################
sub calculateDNPPFallover($){
    my $rg = $_[0];
    local @_; # This keeps the global from being clobbered by local changes
    local $_; # This keeps the global from being clobbered by local changes
    # my @tmp = `odmget -q "group=$rg and name=NODE_PRIORITY_POLICY" HACMPresource`;
    
    my @currNode = &nodes_providing_app_of_rg($app,$rg);
    my $availableNodes = 0;
    # Counts up how many cluster nodes are $UP
    if(scalar(@currNode) > 0){
        foreach(@nodesOfRg){
            if( ( &state_of_node($_) eq $UP) && ($_ ne $currNode[0]) ){
                $availableNodes++;
            }
        }
    }
    
    # We only need to report the dnpp message if there are two or
    # more other nodes available to fallover to.
    if($availableNodes > 1){
        my $dnp = `$HACMPBASE/utilities/cllsres -cg $rg | tail -1 | cut -f23 -d:`;
        chomp $dnp;
        if($dnp eq "cl_highest_free_mem"){
            return $MESSAGE_CL_HIGHEST_FREE_MEM;
        }
        elsif($dnp eq "cl_highest_idle_cpu"){
            return $MESSAGE_CL_HIGHEST_IDLE_CPU;
        }
        elsif($dnp eq "cl_lowest_disk_busy"){
            return $MESSAGE_CL_LOWEST_DISK_BUSY;
        }
        else{
            return $MESSAGE_USER_SPECIFIED;
        }
        # This should never happen
        print STDERR `dspmsg scripts.cat -s 23 112 "\nERROR: Unable to determine the dynamic node priority policy for group '%s'.\n" "$rg"`;
    }
    # If there is only one other node available to fallover to,
    # we can just have calculateFalloverToNextPriorityNode() do
    # our work for us.
    elsif ($availableNodes == 1){
        return calculateFalloverToNextPriorityNode();
    }
    else{
        return "NONE";
    }
}
#####################################################
# Function:
#
#    calculateFalloverToNextPriorityNode
#
# Description:
#
#    Returns the highest-priority available node.
#    (The one to which a standard rotating or
#    cascading RG will fallover.)
#
# Arguments:
#    none
#####################################################
sub calculateFalloverToNextPriorityNode(){
    local $_; # This keeps the global from being clobbered by local changes
    # nodes_providing_app_of_rg will normally return a single node or a list of nodes (in
    # the case of a concurrent RG) but in this case we've already screened out the
    # concurrent RGs so we can just assume that the array @currNode will only have
    # a single element.
    my @currNode = &nodes_providing_app_of_rg($app,$rg);
    
    # Checks each of the nodes in the RG from highest to lowest precedence.
    # The RG will move to the first available node, so it's returned.
    if(scalar(@currNode) > 0){
        foreach(@nodesOfRg){
            if( ( &state_of_node($_) eq $UP) && ($_ ne $currNode[0]) ){
                return "$_";
            }
        }
    }
    return "NONE";
}
#####################################################
# Function:
#
#    calculateOfflineFallover
#
# Description:
#
#    Returns the second node on the list of nodes
#    that are configured to provide the application
#    or "NONE" if there isn't one. This subroutine is
#    used only for clusters that are offline.    
#
# Arguments:
#
#    none
#####################################################
sub calculateOfflineFallover(){
    if($nodesOfRg[1]){
        return "$nodesOfRg[1]";
    }
    else{
        return "NONE";
    }
}
#####################################################
# Function:
#
#    initRgPrefs
#
# Description:
#
#    Initializes $startupPref, $falloverPref,
#    and $fallbackPref
#
# Arguments:
#
#    $_[0]: Resource group name
#####################################################
sub initRgPrefs($){
    local $rg = $_[0];
    # my $tmp = `odmget -q group=$rg HACMPgroup | grep startup_pref`;
    $startupPref = `$HACMPBASE/utilities/clgetgrp -cg $rg | tail -1 | cut -f5 -d:`;
    &detaint($startupPref);
    chomp $startupPref;
    
    # $tmp = `odmget -q group=$rg HACMPgroup | grep fallover_pref`;
    $falloverPref = `$HACMPBASE/utilities/clgetgrp -cg $rg | tail -1 | cut -f6 -d:`;
    &detaint($falloverPref);
    chomp $falloverPref;
    
    # $tmp = `odmget -q group=$rg HACMPgroup | grep fallback_pref`;
    $fallbackPref = `$HACMPBASE/utilities/clgetgrp -cg $rg | tail -1 | cut -f7 -d:`;
    &detaint($fallbackPref);
    chomp $fallbackPref;
}
#####################################################
# Function:
#
#    fallover_node_for_app_of_rg
#
# Description:
#
#    Returns the node to which an application will
#    fallover if the node on which it is currently
#    running fails. (Or if the resource group is
#    offline it returns the node that the app would
#    fallover to if it can be determined.)
#
# Arguments:
#
#    $_[0]: an application name
#    $_[1]: rg name
#####################################################
sub fallover_node_for_app_of_rg($$){
    $app = $_[0];
    $rg = $_[1];
    local $_;  # keeps the global from being clobbered by local changes
    local @_;  # keeps the global from being clobbered by local changes
    if($rg eq "NONE"){
        return;
    }
    # nodes_of_app_of_rg effectively gives us a list of the nodes in the same RG
    @nodesOfRg = &nodes_of_app_of_rg($app,$rg);
    
    # First we'll parse HACMPgroup for the custom config info
    # POSSIBLE VALUES:
    # startupPref = "OFAN"  Online on first available node
    # startupPref = "OHN"  Online on home node only
    # startupPref = "OAAN"  Online on all available nodes
    # startupPref = "OUDP"  Online Using Distribution Policy
    # falloverPref = "FNPN"  Fallover to next priority node in the list
    # falloverPref = "FUDNP"  Fallover Using Dynamic Node Priority
    # falloverPref = "BO"  Bring offline (on error node only)
    # -- We don't actually care about fallback settings but here they are:
    # fallbackPref = "NFB"  Never fall back
    # fallbackPref = "FBHPN"  Fallback To Higher Priority Node In The List
    
    initRgPrefs($rg);
    
    # If the app is online
    if( $appStates[$h][$r] eq $ONLINE ){
        if ($startupPref eq "OAAN"){
            print STDERR `dspmsg scripts.cat -s 23 113 "\nApplication '%s' belongs to a resource group which is configured to run on all its nodes simultaneously. No fallover will occur." "$app"`;
        }
        elsif($falloverPref eq "BO"){
            return "NONE";
        }
        elsif($falloverPref eq "FNPN"){
            return &calculateFalloverToNextPriorityNode;
        }
        elsif($falloverPref eq "FUDNP"){
            return &calculateDNPPFallover($rg);
        }
    }
    # If cluster services are down
    elsif (&state_of_cluster() eq $DOWN){   #else if cluster is inactive
        if ($startupPref eq "OAAN"){
            print STDERR `dspmsg scripts.cat -s 23 113 "\nApplication '%s' belongs to a resource group which is configured to run on all its nodes simultaneously. No fallover will occur." "$app"`;
        }
        elsif($falloverPref eq "BO"){
            return "NONE";
        }
        elsif($falloverPref eq "FUDNP"){
            return &calculateDNPPFallover($rg);
        }
        elsif($startupPref eq "OHN" && $falloverPref eq "FNPN"){
            return &calculateOfflineFallover;
        }
        else{
            return $MESSAGE_UNKNOWN_UNLESS_ONLINE;
        }
    }
    else{
        return "NONE";
    }
}
#####################################################
# Function:
#
#    state_of_service_label
#
# Description:
#
#    Returns the state of a service label ("online"
#    or "offline/unknown").
#
# Arguments:
#
#    $_[0]: a service label
#####################################################
# Possible return values:
# "online"
# "offline/unknown"
sub state_of_service_label($){
    my $serviceLabel = $_[0];
    my $ip;
    my $tmpIP;
    my $tmp_label;
    my $state;
    local $_;
    local @_;
    my @snmpinfo = `cat $SNMPINFO_SOURCE | grep -e addr6Label -e addr6Role -e addr6State`;
    # Parses snmpinfo's output for ...
    foreach my $ent (@snmpinfo){
        my @entry = split (/\s|\./, $ent);
        # ... the ip of the service label
        if($entry[0] eq "addr6Label"){
            use Switch;
            # refer 'src/43haes/usr/sbin/cluster/clsmuxpd/hacmp.my' for 
            # description on addr6Group table indexing.
            switch($entry[2]) {
                case (1) {$tmp_label = $entry[10]} #IPv4
                case (2) {$tmp_label = $entry[22]} #IPv6
            }
            $tmp_label =~ s/\"//g;
            if($tmp_label eq $serviceLabel){
                # Cat stuff together to form the ip we're looking for
                $ip = $entry[4];
                for(my $count = 1; $count < $entry[3]; $count++) {
                    $ip = "$ip.$entry[$count + 4]";
                }
            }
        }
        # ... the role of the service label in order to ensure that addr6Label is
        # really a service label
        elsif($entry[0] eq "addr6Role"){
            my $addr_type; 
            $tmpIP = $entry[4];
            for(my $count = 1; $count < $entry[3]; $count++) {
                $tmpIP = "$tmpIP.$entry[$count + 4]";
            }
            switch($entry[2]) {
                case (1)    {$addr_type = $entry[10]}
                case (2)    {$addr_type = $entry[22]}
            }
            # If we've found the line containing the role of our IP ...
            if($ip && ($tmpIP eq $ip) ){
                # see /usr/es/sbin/cluster/hacmp.my for description of these values
                if( !( ($addr_type == 16)||($addr_type == 32)||($addr_type == 128)||($addr_type == 144)) ){
                    print STDERR `dspmsg scripts.cat -s 23 114 "\nERROR: %s is not a service label.\n" "$serviceLabel"`;
                  return;
                }
            }
        }
        # ... 
        elsif( $entry[0] eq "addr6State" ){
            $tmpIP = $entry[4];
            for(my $count = 1; $count < $entry[3]; $count++) {
                $tmpIP = "$tmpIP.$entry[$count + 4]";
            }
            # If we've found the line containing the state of our IP (on a
            # particular node
            my $state;
            if($ip && ($tmpIP eq $ip) ){
                switch($entry[2]) {
                    case (1)    {$state = $entry[10]}
                    case (2)    {$state = $entry[22]}
                }
                if($state == 2){
                    return $ONLINE;
                }
            }
        }
    }
    return "offline/unknown";
}
#####################################################
# Function:
#
#    translate
#
# Description:
#
#    Calls a predefined 'dspmsg' command based on the
#    input it's passed. This is a workaround to make
#    values stored in the ODM or returned by other
#    functions in this program translate correctly
#    into other languages.
#
# Arguments:
#
#    $_[0]: any word or phrase (should be in english)
#####################################################
sub translate($){
    if($_[0] eq "") {
        return;
    }
    my $translation;
    if($_[0] eq "custom"){
        $translation = `dspmsg scripts.cat -s 23 115 "custom"`;
    }
    elsif($_[0] eq "up"){
        $translation = `dspmsg scripts.cat -s 23 116 "up"`;
    }
    elsif($_[0] eq "down"){
        $translation = `dspmsg scripts.cat -s 23 117 "down"`;
    }
    elsif($_[0] eq "unknown"){
        $translation = `dspmsg scripts.cat -s 23 118 "unknown"`;
    }
    elsif($_[0] eq "joining"){
        $translation = `dspmsg scripts.cat -s 23 119 "joining"`;
    }
    elsif($_[0] eq "leaving"){
        $translation = `dspmsg scripts.cat -s 23 120 "leaving"`;
    }
    elsif($_[0] eq "unstable"){
        $translation = `dspmsg scripts.cat -s 23 121 "unstable"`;
    }
    elsif($_[0] eq "stable"){
        $translation = `dspmsg scripts.cat -s 23 122 "stable"`;
    }
    elsif($_[0] eq "error"){
        $translation = `dspmsg scripts.cat -s 23 123 "error"`;
    }
    elsif($_[0] eq "reconfig"){
        $translation = `dspmsg scripts.cat -s 23 124 "reconfig"`;
    }
    elsif($_[0] eq "active"){
        $translation = `dspmsg scripts.cat -s 23 125 "active"`;
    }
    elsif($_[0] eq "inactive"){
        $translation = `dspmsg scripts.cat -s 23 126 "inactive"`;
    }
    elsif($_[0] eq "online"){
        $translation = `dspmsg scripts.cat -s 23 127 "online"`;
    }
    elsif($_[0] eq "offline"){
        $translation = `dspmsg scripts.cat -s 23 128 "offline"`;
    }
    elsif($_[0] eq "offline/unknown"){
        $translation = `dspmsg scripts.cat -s 23 129 "offline/unknown"`;
    }
    elsif($_[0] eq "acquiring"){
        $translation = `dspmsg scripts.cat -s 23 130 "acquiring"`;
    }
    elsif($_[0] eq "releasing"){
        $translation = `dspmsg scripts.cat -s 23 131 "releasing"`;
    }
    elsif($_[0] eq "process"){
        $translation = `dspmsg scripts.cat -s 23 132 "process"`;
    }
    elsif($_[0] eq "notify"){
        $translation = `dspmsg scripts.cat -s 23 133 "notify"`;
    }
    elsif($_[0] eq "fallover"){
        $translation = `dspmsg scripts.cat -s 23 134 "fallover"`;
    }
# The following elsifs must be identical to the decalartions of $UP/$DOWN/$UNKNOWN/etc
# in are changed in wsm_cldisp.cgi.perl
    elsif($_[0] eq $UP){
        $translation = ''.`dspmsg scripts.cat -s 23 116 "up"`.'';
    }
    elsif($_[0] eq $DOWN){
        $translation = ''.`dspmsg scripts.cat -s 23 117 "down"`.'';
    }
    elsif($_[0] eq $UNKNOWN){
        $translation = ''.`dspmsg scripts.cat -s 23 118 "unknown"`.'';
    }
    elsif($_[0] eq $UNSTABLE ){
        $translation = ''.`dspmsg scripts.cat -s 23 121 "unstable"`.'';
    }
    elsif($_[0] eq $STABLE ){
        $translation = ''.`dspmsg scripts.cat -s 23 122 "stable"`.'';
    }
    elsif($_[0] eq $ERROR ){
        $translation = ''.`dspmsg scripts.cat -s 23 123 "error"`.'';
    }
    elsif($_[0] eq $RECONFIG ){
        $translation = ''.`dspmsg scripts.cat -s 23 124 "reconfig"`.'';
    }
    elsif($_[0] eq $ACTIVE ){
        $translation = ''.`dspmsg scripts.cat -s 23 125 "active"`.'';
    }
    elsif($_[0] eq $INACTIVE ){
        $translation = ''.`dspmsg scripts.cat -s 23 126 "inactive"`.'';
    }
    elsif($_[0] eq $ONLINE ){
        $translation = ''.`dspmsg scripts.cat -s 23 127 "online"`.'';
    }
    elsif($_[0] eq $OFFLINE ){
        $translation = ''.`dspmsg scripts.cat -s 23 128 "offline"`.'';
    }
    elsif($_[0] eq $ACQUIRING ){
        $translation = ''.`dspmsg scripts.cat -s 23 130 "acquiring"`.'';
    }
    elsif($_[0] eq $RELEASING ){
        $translation = ''.`dspmsg scripts.cat -s 23 131 "releasing"`.'';
    }
    # If it's not in the table we'll just print the english word.
    else{
        #NOTE: This appears on the screen! In the user's output!! Bad!
        #print STDERR `dspmsg scripts.cat -s 23 135 "\nWARNING: translate() was called on the word '%s' which is not in translation table.\n" "$_[0]"`;
        $translation = $_[0];
    }
    &detaint($translation);
    return $translation;
}
#####################################################
# Function:
#
#    translateCustomAttr
#
# Description:
#
#    Returns the english/other language equivalent
#    of a resource group's startup/fallover/
#    fallback settings.
#
# Arguments:
#
#    $_[0]: a startup/fallover/fallback setting
#           acronym
#####################################################
sub translateCustomAttr($){
    my $translation;
    if($_[0] eq "OFAN"){
        $translation = `dspmsg scripts.cat -s 23 140 "on first available node"`;
    }
    elsif($_[0] eq "OHN"){
        $translation = `dspmsg scripts.cat -s 23 141 "on home node only"`;
    }
    elsif($_[0] eq "OUDP"){
        $translation = `dspmsg scripts.cat -s 23 142 "using distribution policy"`;
    }
    elsif($_[0] eq "OAAN"){
        $translation = `dspmsg scripts.cat -s 23 143 "on all available nodes"`;
    }
    elsif($_[0] eq "FNPN"){
        $translation = `dspmsg scripts.cat -s 23 144 "to next priority node in the list"`;
    }
    elsif($_[0] eq "FUDNP"){
        $translation = `dspmsg scripts.cat -s 23 145 "using dynamic node priority to determine target"`;
    }
    elsif($_[0] eq "BO"){
        $translation = `dspmsg scripts.cat -s 23 146 "bring offline on error node"`;
    }
    elsif($_[0] eq "NFB"){
        $translation = `dspmsg scripts.cat -s 23 147 "never"`;
    }
    elsif($_[0] eq "FBHPN"){
        $translation = `dspmsg scripts.cat -s 23 148 "if higher priority node becomes available"`;
    }
    &detaint($translation);
    return $translation;
}
#####################################################
# Function:
#
#    probeStatic
#
# Description:
#
#    Most of the static info about the cluster config
#    is gathered here
#
# Arguments:
#
#    none
#####################################################
sub probeStatic(){
    # Gets the name of our cluster.
    $clusterName = `$HACMPBASE/utilities/cllsclstr -c | tail -1 | cut -f2 -d:`;
    chomp $clusterName;
    &detaint($clusterName);
    $localNodeName = `$HACMPBASE/utilities/get_local_nodename`;
    chomp $localNodeName;
    &detaint($localNodeName);
    # @nodeNames stores the names of each of the nodes in the cluster
    @nodeNames = &nodes_in_cluster;
    chomp @nodeNames;
    # Creation of @interfacesByNode-- a 2D array of the interfaces on each
    # host. The rows of the arrays are in the same order as @nodeNames.
    @scrap = @nodeNames;
    my @addrsFound;
    $r = 0;
    while ($_ = shift @scrap){
        my @tmp = &interfaces_on_node($_);
        for($c = 0; $c <= $#tmp; $c++){
            $interfacesByNode[$r][$c] = $tmp[$c];
            &detaint($interfacesByNode[$r][$c]);
        }
        $r++;
    } # /while
    # Creation of @ipsByNode--a 2D array of the ip addresses of each of the
    # interfaces on each node. This array is in the same layout as
    # @interfacesByNode.
    for($r = 0; $interfacesByNode[$r][0]; $r++){
        for($c = 0; $interfacesByNode[$r][$c]; $c++){
            $ipsByNode[$r][$c] = &ip_of_interface($nodeNames[$r], $interfacesByNode[$r][$c], \@addrsFound);
            &detaint($ipsByNode[$r][$c]);
            push @addrsFound, $ipsByNode[$r][$c];
        }
    }
    # Creation of @networksOfIpAddrs--a 2D array of the networks to which
    # each IP address or device (non-IP network) belongs. This array is in the same layout as
    # @interfacesByNode.
    for($r = 0; $interfacesByNode[$r][0]; $r++){
        for($c = 0; $interfacesByNode[$r][$c]; $c++){
            $networksOfIpAddrs[$r][$c] = &network_of_ip_addr($nodeNames[$r], $ipsByNode[$r][$c]);
            &detaint($networksOfIpAddrs[$r][$c]);
        }
    }
    # Initializes @apps, a list of all the cluster's apps
    @apps = &apps_on_cluster();
    # Initializes @RgsByApp---a 2D array of the applications and the resource groups to which application
    # belongs. It's order is the same as @apps.
    for($r = 0; $apps[$r]; $r++){
        @scrap = &rg_of_app($apps[$r]);
        for($c = 0; $scrap[$c]; $c++) {
            $RgsByApp[$r][$c] = $scrap[$c];
            &detaint($RgsByApp[$r][$c]);
        }
    }
    # Creation of @nodesByApp--a 3D array of the nodes configured to provide each
    # application. The application corresponds to height, rg to rows and nodes to 
    # column of the array @apps.
    @scrap = @apps;
    $h = 0;
    while ($_ = shift @scrap){
        for($r = 0; $RgsByApp[$h][$r]; $r++) {
            @tmp = &nodes_of_app_of_rg($_,$RgsByApp[$h][$r]);
            for($c = 0; $c <= $#tmp; $c++){
                $nodesByApp[$h][$r][$c] = $tmp[$c];
                &detaint($nodesByApp[$h][$r][$c]);
            }
        }
        $h++;
    } # /while
    # Initializes @startscriptsByApp
    @scrap = @apps;
    $r = 0;
    while ($_ = shift @scrap){
        $startscriptsByApp[$r] = &startscript_of_app($_);
        &detaint($startscriptsByApp[$r]);
        $r++;
    }
    # Initializes @stopscriptsByApp
    @scrap = @apps;
    $r = 0;
    while ($_ = shift @scrap){
        $stopscriptsByApp[$r] = &stopscript_of_app($_);
        &detaint($stopscriptsByApp[$r]);
        $r++;
    }
    # Initializes @serviceIpLabelsOfApps, a 3D array whose height is in same 
    # order as @apps, rows in the order of rgs(to which the app belongs)
    # and columns contain the service ip labels.
    @scrap = @apps;
    $h = 0;
    while ($_ = shift @scrap){
        for($r = 0; $RgsByApp[$h][$r]; $r++) {
            @tmp = &service_labels_of_app_of_rg($_, $RgsByApp[$h][$r]);
            for($c = 0; $tmp[$c]; $c++){
                $serviceIpLabelsOfApps[$h][$r][$c] = $tmp[$c];
                &detaint($serviceIpLabelsOfApps[$h][$r][$c]);
            }
        }
        $h++;
    } # /while
    # Initializes @serviceIpsOfApps, a 3D array whose entries correspond to the entries of
    # @serviceIpLabelsOfApps.
    for($h = 0; $h <= $#serviceIpLabelsOfApps; $h++) {
        for($r = 0; $serviceIpLabelsOfApps[$h][$r][0]; $r++){
            for($c = 0; $serviceIpLabelsOfApps[$h][$r][$c]; $c++){
                if($serviceIpLabelsOfApps[$h][$r][$c] ne "NONE"){
                    $serviceIpsOfApps[$h][$r][$c] = &ip_of_service_label($serviceIpLabelsOfApps[$h][$r][$c]);
                    &detaint($serviceIpsOfApps[$h][$r][$c]);
                }
            }
        }
    } # /for
    # Initializes @networksOfServiceLabels
    for($h = 0; $h <= $#serviceIpLabelsOfApps; $h++) {
        for($r = 0; $serviceIpLabelsOfApps[$h][$r][0] ; $r++){
            for($c = 0; $serviceIpLabelsOfApps[$h][$r][$c]; $c++){
                if($serviceIpLabelsOfApps[$h][$r][$c] ne "NONE"){
                    $networksOfServiceLabels[$h][$r][$c] = &network_of_service_label($serviceIpLabelsOfApps[$h][$r][$c]);
                    &detaint($networksOfServiceLabels[$h][$r][$c]);
                }
            }
        }
    } # /for
    # Initializes @vgsOfApps, a 3D array of the shared volume groups
    # associated with a particular app corresponding to each rg, it belongs to.
    for($h = 0; $apps[$h]; $h++){
        for($r = 0; $RgsByApp[$h][$r]; $r++) {
            my @tmp = &vgs_of_app_of_rg($apps[$r],$RgsByApp[$h][$r]);
            for($c = 0; $tmp[$c]; $c++){
                $vgsOfApps[$h][$r][$c] = $tmp[$c];
                &detaint($vgsOfApps[$h][$r][$c]);
            }
        }
    }
    # Initializes @ccVgsOfApps, a 3D array of the concurrent shared volume
    # groups associated with a particular app corresponding to each rg, it belongs to.
    for($h = 0; $apps[$h]; $h++){
        for($r = 0; $RgsByApp[$h][$r]; $r++) {
            my @tmp = &cc_vgs_of_app_of_rg($apps[$r],$RgsByApp[$h][$r]);
            for($c = 0; $tmp[$c]; $c++){
                $ccVgsOfApps[$h][$r][$c] = $tmp[$c];
                &detaint($ccVgsOfApps[$h][$r][$c]);
            }
        }
    }
    # Initializes @filesystemsOfApps, a 3D array of the filesystems 
    # associated with a particular app corresponding to each rg, it belongs to.
    for($h = 0; $apps[$h]; $h++){
        for($r = 0; $r < $RgsByApp[$h][$r]; $r++) {
            my @tmp = &filesystems_of_app_of_rg($apps[$r],$RgsByApp[$h][$r]);
            for($c = 0; $tmp[$c]; $c++){
                $filesystemsOfApps[$h][$r][$c] = $tmp[$c];
                &detaint($filesystemsOfApps[$h][$r][$c]);
            }
        }
    }
    # Initializes @fastConnectServicesOfApps, a 3D array of the fast
    # connect services associated with each rg to which the app belongs. 
    for($h = 0; $apps[$h]; $h++){
        for($r = 0; $RgsByApp[$h][$r]; $r++) {
            my @tmp = &fast_connect_services_of_app_of_rg($apps[$r],$RgsByApp[$h][$r]);
            for($c = 0; $tmp[$c]; $c++){
                $fastConnectServicesOfApps[$h][$r][$c] = $tmp[$c];
                &detaint($fastConnectServicesOfApps[$h][$r][$c]);
            }
        }
    }
    # Initializes @monitorsOfApps, a 2D array of the application monitors
    # configured to monitor a particular application
    for($r = 0; $apps[$r]; $r++){
        my @tmp = &monitors_of_app($apps[$r]);
        for($c = 0; $tmp[$c]; $c++){
            $monitorsOfApps[$r][$c] = $tmp[$c];
            &detaint($monitorsOfApps[$r][$c]);
        }
    }
}
#####################################################
# Function:
#
#    probeDynamic
#
# Description:
#
#    Probes the cluster for information on it's
#    current state.
#
# Arguments:
#
#    none
#####################################################
sub probeDynamic(){
    # Here we probe the MIB of potentially every node in
    # the cluster looking for a node where cluster
    # services are active. The MIB info is extracted
    # and stored in the file $SNMPINFO_SOURCE. If an active
    # cluster node was found, snmpinfoRefresh returns a 1
    # indicating we should go ahead an extract the cluster's
    # current state from the $SNMPINFO_SOURCE file.
    $overall_clstrmgr_state = &snmpinfoRefresh("$SNMPINFO_SOURCE");
    if($overall_clstrmgr_state eq $ACTIVE){
        if( !(-e "$SNMPINFO_SOURCE") ){
            print STDERR `dspmsg scripts.cat -s 23 149 "\nERROR: SNMP information file %s not found.\n" "$SNMPINFO_SOURCE"`;
        }
        @snmpinfo = `cat $SNMPINFO_SOURCE`;
        chomp @snmpinfo;
        $clusterState = &state_of_cluster;
        $clusterSubstate = &substate_of_cluster;
        # Initializes @nodeStates, an array of the states of the nodes in @nodeNames
        # Initializes @interfaceStatesByNode, a 2D array of the states of the interfaces in @interfacesByNode
        # Initializes @networkStates, a 2D array of the states of the networks in @networksOfIpAddrs
        for($r = 0; $nodeNames[$r]; $r++){
            $nodeStates[$r] = &state_of_node($nodeNames[$r]);
            for($c = 0; $interfacesByNode[$r][$c]; $c++){
                $interfaceStatesByNode[$r][$c] = &state_of_interface($nodeNames[$r], $interfacesByNode[$r][$c]);
                $networkStates[$r][$c] = &state_of_network($nodeNames[$r], $networksOfIpAddrs[$r][$c]);
            }
        } # /for
        for($h = 0; $apps[$h]; $h++){
            for($r = 0; $RgsByApp[$h][$r]; $r++) {
                $appStates[$h][$r] = &state_of_app_of_rg($apps[$h],$RgsByApp[$h][$r]);
                @tmp = &nodes_of_app_of_rg($apps[$h], $RgsByApp[$h][$r]);
                for($c = 0; $c <= $#tmp; $c++){
                    $nodesByApp[$h][$r][$c] = $tmp[$c];
                }
            } # /for
            for($r = 0; $RgsByApp[$h][$r]; $r++) {
                $appStates[$h][$r] = &state_of_app_of_rg($apps[$h],$RgsByApp[$h][$r]);
                @tmp = &nodes_providing_app_of_rg($apps[$h], $RgsByApp[$h][$r]);
                for($c = 0; $c <= $#tmp; $c++){
                    $providingNodesByApp[$h][$r][$c] = $tmp[$c];
                }
            } # /for
            # Now we'll calculate the fallover nodes for the applications. We must make sure not
            # to call fallover_node_for_app_of_rg on an app that runs concurrently across the nodes
            # of it's RG because it will cause an error. We'll store the fallover nodes in 
            # @falloverNodesOfApps, which is in the same order as @apps.
            # my $x = `odmget -q group=$RgsByApp[$r] HACMPgroup | grep startup_pref`;
            if($RgsByApp[$h][0] ne "NONE"){
                for($r = 0; $RgsByApp[$h][$r]; $r++) {
                    my $startupPref = `$HACMPBASE/utilities/clgetgrp -cg $RgsByApp[$h][$r] | tail -1 | cut -f5 -d:`;
                    chomp $startupPref;
                    # If it's not a concurrent rg ...
                    if( $startupPref ne "OAAN" ){
                        $falloverNodesOfApps[$h][$r] = &fallover_node_for_app_of_rg($apps[$h],$RgsByApp[$h][$r]);
                    }
                }
            }
            for(my $h2 = 0; $h2 <= $#serviceIpLabelsOfApps; $h2++) {
                for($r = 0; $serviceIpLabelsOfApps[$h][$r][0] ; $r++){
                    for($c = 0; $serviceIpLabelsOfApps[$h][$r][$c]; $c++){
                        $serviceIpStates[$h2][$r][$c] = &state_of_service_label($serviceIpLabelsOfApps[$h2][$r][$c]);
                    }
                }
            }
        } # /for
        return 0;
    }
    return 1; # No probe done because cluster services are not active
}
#####################################################
# Function:
#
#    detaint
#
# Description:
#
#    Transparent regex check to appease RBAC
#
# Arguments:
#
#    $_[0]: variable to be detained
#####################################################
sub detaint($) {
    $_[0] =~ /(.*)/;
    $_[0] = $1;
}
#Since this is a Perl module, '1;' must be the last statement
1;