# @(#)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;