#!/bin/ksh93
# IBM_PROLOG_BEGIN_TAG
# This is an automatically generated prolog.
#
#
#
# Licensed Materials - Property of IBM
#
# (C) COPYRIGHT International Business Machines Corp. 1990,2006
# 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
# @(#)13 1.2.5.51 src/43haes/usr/sbin/cluster/events/utils/cl_disk_available.sh, hacmp.events, 51haes_r520, r520s008a 4/7/06 17:06:35
#
# COMPONENT_NAME: hacmp.events
#
# FUNCTION: Given a list of disks, make sure that they are in the
# 'available' state. If necessary, break reserves, and remove any
# ghost disks.
#
# ORIGINS: 27
#
###############################################################################
#
# Name: cl_disk_available
# Returns:
# 0 Success
# 1 Failure - Can't make it Available
# 2 Failure - Bad args
#
# Check to see if a disk is available to the system, if not try to
# make it available.
#
# Arguments: [ -s ] [ hdiskname .... ]
#
# If called with '-s' flag the script implies that it is called not from the
# event script context, so skip resource manager updates
#
# If called with a list of hdisks, make those disks available
#
# If called with no list of hdisks, then process by resource group, based on
# environment variables set by process_resources:
# RESOURCE_GROUPS - space separated list of resource groups
# HDISKS - list of disks for a resource group
# - comma separated list of disks within a resource group
# - colon separated lists of disks for each resource group
# VOLUME_GROUPS - list of owning volume group (if any) for each
# hdisk. Same pattern of comma and colon separated
# lists as for HDISKS
# e.g. -
# RESOURCE_GROUPS="curley larry moe shemp"
# HDISKS="hdisk11,hdisk22:hdisk21,hdisk31:hdisk99:hdisk101,hdisk1"
# VOLUME_GROUPS="vg01,vg01:vg02,vg02,::vg03,vg0"
#
# Environment: VERBOSE_LOGGING - "high" => set -x
#
# Questions? Comments? Expressions of Astonishment?
#
###############################################################################
#
# Name: make_disktypes_available
#
# Input: 1) owning adapter for list of disks
# 2) disk type - FSCSI or SCSIDISK
# 3) list of disks
#
# Function: This routine is passed a list of disks, all on a common
# adapter.
# * The names of any ghost disks are collected for later removal
# * Any that are not available are added to the list of disks to
# be made available later
# * The adapter and disk names are passed to a routine that will
# open the adapter and use ioctls to break any reserves
#
# Output: None
#
# Environment: VERBOSE_LOGGING, PATH, disklist, ghostlist, fscarray,
# pscarray
#
###############################################################################
function make_disktypes_available
{
[[ "$VERBOSE_LOGGING" == "high" ]] && set -x
typeset parent=$1
typeset disktype=$2
shift 2
typeset disks=$*
#
# Scan through the list of given disks, checking each one
# for possible ghost disks, which must be later removed
#
for disk in $disks # Check to see if each disk is in an
do # available state
#
# The disk is not available
#
if [[ -z $(lsdev -Cc disk -l $disk -S A -F 'status') ]]
then
# It is possible for the original disk device to become
# "Defined" while a new disk device becomes "Available".
# Go find all those new disks, which have the same parent
# and address, but different name. Add them to the global
# list of ghost disks to be removed.
case $disktype in
FSCSI)
ghostlist=${ghostlist:+"$ghostlist "}$(cl_fscsighost $disk)
;;
SCSIDISK)
ghostlist=${ghostlist:+"$ghostlist "}$(odmget -q "parent = $parent AND \
name != $disk AND \
location = $(lsdev -Cc disk -l $disk -F 'location')" CuDv | \
sed -n 's/^.*name ="\(.*\)".*/\1/p')
;;
esac
#
# Add this disk to the list of those that must be made
# available, after reserves are broken and ghosts
# removed.
#
disklist=${disklist:+"$disklist "}"MKDEV.${disk}"
fi # End if disk not available
done # End loop through all given disks
#
# Break the reserve - pass the given list of disks having the given parent
#
case $disktype in
FSCSI )
#
# Perform a LUN reset on a SCSI-3 device
#
cl_fscsilunreset $parent $disks
;;
SCSIDISK)
#
# Perform a LUN reset on a SCSI-2 device, if the device supports
# it. Otherwise, do a target reset.
#
cl_pscsilunreset $parent $disks
;;
ISCSI )
#
# Perform a LUN reset on an iSCSI device
#
cl_iscsilunreset $parent $disks
;;
SCSIRAID )
#
# Tell the SCSI RAID adapter that this node is going to be
# primary
#
if [[ -x /usr/bin/sisraidmgr ]] ; then
#
# Talk to the right ioa
#
SISIOA=$(lsdev -C -l $parent -F parent)
#
# Make this adapter primary
#
if ! /usr/bin/sisraidmgr -X -l $SISIOA -o '1'
then
#
# Log any failure, but keep going
#
cl_log 999 "$PROGNAME: sisraidmgr failed for $SISIOA" $PROGNAME $SISIOA
fi
fi
#
# Perform a LUN reset for each disk on the adapter
#
for disk in $disks ; do
cl_flutereset $disk
done
;;
esac
}
###############################################################################
#
# Name: is_disk_reserved
# Returns:
# 0 If disk does NOT have reservation against it
# 1 If disk has reservation against it
# 2 If scdiskutil failed for some other reason
#
# Check to see if a disk is reserved by a system through use of the SCIOTUR
# (Test Unit Ready) ioctl.
#
# Arguments: diskname
#
# Environment: None
#
###############################################################################
function is_disk_reserved
{
[[ "$VERBOSE_LOGGING" == "high" ]] && set -x
typeset disk
disk=$1
#
# scdiskutil retries 6 times, if necessary.
# Call it only once.
#
scdiskutil -t /dev/$disk
TUR_RC=$?
if (( $TUR_RC == 0 ))
then
return 0
elif (( $TUR_RC == 24 ))
then
# 24 is SC_RESERVATION_CONFLICT - disk has a reserve on it
return 1
else
# scdiskutil -t failed for some other reason
return 2
fi
}
###############################################################################
#
# Name: breakres
# Returns:
# 0 If reservation has been broken
# -1 If anything goes wrong
#
# Break and, if appropriate, re-establish the reservation on a disk
#
# Arguments: 1. Owning adapter - e.g., "scsi0"
# 2. disk type - e.g., "SCSIDISK"
# 3. disk name - e.g., "hdisk333"
#
# Environment: None
#
###############################################################################
function breakres
{
[[ "$VERBOSE_LOGGING" == "high" ]] && set -x
# Pick up the passed disk name
typeset parent disktype disk
parent=$1
disktype=$2
disk=$3
# Process by disk type
case $disktype in
SSA | FCPARRAY )
# Perform a target reset, using openx(hdisk, ,SC_FORCED_OPEN)
# Check to see if the disk is currently reserved
is_disk_reserved $disk
isdr_rc=$?
if (( $isdr_rc == 0 )) ; then
# If no reserve is in place,
# claim the reset worked
rsrv_rc=0
elif (( $isdr_rc == 1 )) ; then
# There is a reserve in place. Break it and establish
# our own reserve.
# Note: cl_scdiskrsrv resets AND reserves device
cl_scdiskrsrv /dev/$disk
rsrv_rc=$?
elif (( $isdr_rc == 2 )) ; then
# Test unit ready failed for unknown reason, let's
# try a reset to ready the disk
cl_scdiskreset /dev/$disk
rsrv_rc=$?
fi
if (( $rsrv_rc == 255 )) ; then
cl_log 205 "$PROGNAME: Failed reset/reserve for device: $disk." $PROGNAME $disk
fi
;;
DPO )
#
# Special processing for vpath disks, which may have a
# persistent reserve in place.
#
if [[ -x /usr/sbin/lquerypr ]] ; then
#
# If support is available to directly query the
# persistent reserve, do so.
#
[[ "$VERBOSE_LOGGING" == "high" ]] && Vflags='-v -V'
lquerypr $Vflags -h /dev/$disk
query_rc=$?
case $query_rc in
0 | 3 )
# Either this node has a persistent reserve on
# this device, or the device is already open
# (and need not be further checked)
rsrv_rc=0
;;
1 )
# This device has a persistent reserve put in
# place by another node. Pre-empt that with our
# key.
lquerypr $Vflags -p -h /dev/$disk
rsrv_rc=$?
if (( $rsrv_rc == 2 )) ; then
# pre-empt can fail if the ESS is sufficiently
# dazed and confused, or is being pounded on by
# the malicious, or generally having problems
# with its queues. Use a bigger hammer.
cl_vpathreset /dev/$disk
rsrv_rc=$?
fi
;;
* )
# For some reason, the query persistent reserve
# function isn't working. Resort to cruder
# methods.
cl_vpathreset /dev/$disk
rsrv_rc=$?
;;
esac
else
# Check to see if we can read the disk directly. If so,
# there's nothing more to do. Otherwise, attempt to break
# the reserve and re-establish the reservation.
#
cl_vpathreset /dev/$disk
rsrv_rc=$?
fi
;;
FLUTE )
#
# For Flute, check to see if the device can be read; and if not
# break the reserve
#
cl_flutereset /dev/$disk
rsrv_rc=$?
;;
ARRAY )
# 7135
is_disk_reserved $disk
isdr_rc=$?
if (( $isdr_rc == 0 )) ; then
# If there is no reserve, we should be able to read the disk
# directly and we're done.
if cl_querypv -q /dev/$disk ; then
rsrv_rc=0
else
# It was not possible to read the disk. The most likely
# cause is that a persistent reserve is held by another
# node. An SC_FORCED_OPEN will take care of that.
cl_scdiskreset /dev/$disk
rsrv_rc=$?
fi
elif (( $isdr_rc == 1 )) ; then
# There is a reserve in place. Break it and establish
# our own reserve.
# Note: cl_scdiskrsrv resets AND reserves device
cl_scdiskrsrv /dev/$disk
rsrv_rc=$?
elif (( $isdr_rc == 2 )) ; then
# Test unit ready failed for unknown reason, let's
# try a reset to ready the disk
cl_scdiskreset /dev/$disk
rsrv_rc=$?
fi
if (( $rsrv_rc == 255 )) ; then
cl_log 205 "$PROGNAME: Failed reset/reserve for device: $disk." $PROGNAME $disk
fi
;;
SCSIDISK | FSCSI | ISCSI )
# Processing for this disk type is done in the function
# 'make_disktypes_available'. This routine should not be called
# for these cases.
:
;;
* )
# Anything else, we don't have special support for
# So, try the straight forward approach. This will
# work if the disk device driver supports openx(SC_FORCED_OPEN)
# and ioctl(SCIOTUR)
is_disk_reserved $disk
isdr_rc=$?
if (( $isdr_rc == 0 )) ; then
# If no reserve is in place,
# claim the reset worked
rsrv_rc=0
elif (( $isdr_rc == 1 )) ; then
# There is a reserve in place. Break it and establish
# our own reserve.
# Note: cl_scdiskrsrv resets AND reserves device
cl_scdiskrsrv /dev/$disk
rsrv_rc=$?
elif (( $isdr_rc == 2 )) ; then
# Test unit ready failed for unknown reason, let's
# try a reset to ready the disk
cl_scdiskreset /dev/$disk
rsrv_rc=$?
fi
if (( $rsrv_rc == 255 )) ; then
cl_log 205 "$PROGNAME: Failed reset/reserve for device: $disk." $PROGNAME $disk
fi
;;
esac
return $rsrv_rc
}
###############################################################################
#
# Name: vpath_dup
#
# Function: Given a vpath, look at the underlying hdisks
# * collect any ghost disks for later removal
# * if any underlying hdisks are not 'Available', save their
# names for later processing by make_disktypes_available
#
# Arguments: vpath
#
# Environment: VERBOSE_LOGGING, PATH, fscarray, pscarray
#
###############################################################################
function vpath_dup
{
[[ "$VERBOSE_LOGGING" == "high" ]] && set -x
typeset vpath v_disklist hdiskvalue u_disk u_parent
vpath=$1
#
# For vpath there should be multiple available hdisks for each vpath.
# Pick up the disk names from ODM.
#
v_disklist=$(odmget -q "name = $vpath AND attribute = active_hdisk" CuAt |
sed -n 's/^.*value = "\(.*\)".*/\1/p')
for hdiskvalue in $v_disklist ; do
#
# For each of the hdisks underlying the vpath disk, make sure
# that there are no ghost disks. First, extract the hdisk name.
#
u_disk=${hdiskvalue%%/*}
#
# Check to see if the underlying disk is available. If not, it
# will be made so. If the underlying disk is available, do not
# add it to the array which will be passed to the 'make_disktypes_available'
# function.
#
if [[ -n $(lsdev -Cc disk -l $u_disk -S A -F 'status') ]]
then
continue
else
#
# We have determined that the underlying disk is not available.
# Vpath disks can be built on top of either parallel or fibre
# SCSI. Figure out which disk is this, and add it to the
# appropriate list of disks to be processed.
#
u_parent=$(lsdev -Cl $u_disk -F parent)
if [[ ${u_parent} == fscsi* ]] ; then
fscarray[${u_parent}]=${fscarray[${u_parent}]:+"${fscarray[${u_parent}]} "}${u_disk}
else # Assume it's a scsi* parent
pscarray[${u_parent}]=${pscarray[${u_parent}]:+"${pscarray[${u_parent}]} "}${u_disk}
fi
fi
done # all hdisks in this vpath
}
###############################################################################
#
# Name: makedev
#
# Run the mkdev command on a given disk, up to four times, to make it
# available
#
# Returns: return code from mkdev command
#
# Arguments: disk name - e.g., 'hdisk108'
#
# Environment: None
#
###############################################################################
function makedev {
typeset disk
[[ "$VERBOSE_LOGGING" == "high" ]] && set -x
disk=$1
# Try up to four times to make the disk available by running the appropriate
# config method
[[ $VERBOSE_LOGGING != high ]] &&
cl_echo 641 "$PROGNAME: Attempting to make disk device: $disk." $PROGNAME $disk
for ((i=0 ; i<4 ; i++)) ; do
if mkdev -l $disk ; then # if it worked
break # can quit now
fi
done
}
###############################################################################
#
# Name: oemghostdisks
#
# Invoke the OEM supplied method - or the specified internal built in method
# to find all the ghost disks for a given disk
#
# Returns; A list of hdisk names that are ghosts for the given disk,
# written to standard out
#
# Arguements: Method name - this is either the path name for the OEM
# supplied method, or the identifier for an
# internal build in method
#
# disk name - e.g., hdisk43
#
###############################################################################
function oemghostdisks {
[[ "$VERBOSE_LOGGING" == "high" ]] && set -x
typeset findghost disk msg
findghost=$1
disk=$2
msg=$(dspmsg scripts.cat 6581 "Custom method %s invoked for disk %s to perform %s" $findghost $disk "ghostbusting")
logger -t "HACMP for AIX" $msg
case $findghost in
SCSI2 )
loc=$(lsdev -Cc disk -l $disk -F 'location')
odmget -q "parent = $parent AND \
name != $disk AND \
location = $loc" CuDv | sed -n 's/^.*name = "\(.*\)".*/\1/p'
;;
SCSI3 )
cl_fscsighost $disk
;;
* )
$findghost $disk
;;
esac
}
###############################################################################
#
# Name: oemcheckres
#
# Invoke the OEM supplied method - or the specified internal built in method
# to check if a reserve is held on a disk (and hence must be broken)
#
# Returns: 0 If disk does NOT have reservation against it
# 1 If disk has reservation against it
#
# Arguements: Method name - this is either the path name for the OEM
# supplied method, or the identifier for an
# internal build in method
#
# disk name - e.g., hdisk43
#
###############################################################################
function oemcheckres {
[[ "$VERBOSE_LOGGING" == "high" ]] && set -x
typeset checkres disk msg
checkres=$1
disk=$2
msg=$(dspmsg scripts.cat 6581 "Custom method %s invoked for disk %s to perform %s " $checkres $disk "checkreserves")
logger -t "HACMP for AIX" $msg
case $checkres in
SCSI_TUR )
is_disk_reserved $disk
;;
* )
$checkres $disk
;;
esac
}
###############################################################################
#
# Name: oembreakres
#
# Invoke the OEM supplied methods - or the specified internal built in
# methods - to check for a reserve on a disk, and break it if its there.
#
# Returns: any return code from the method
#
# Arguements: Break Reserve Method name - this is either the path name for
# the OEM supplied method, or the identifier for an
# internal build in method
#
# Check Reserve Method Name
#
# parent - name of the adapter owning the disk - e.g., scsi3
#
# disk name - e.g., hdisk43
#
###############################################################################
function oembreakres {
[[ "$VERBOSE_LOGGING" == "high" ]] && set -x
typeset breakres checkres parent disk msg
breakres=$1
checkres=$2
parent=$3
disk=$4
msg=$(dspmsg scripts.cat 6581 "Custom method %s invoked for disk %s to perform %s" $breakres $disk "breakres")
logger -t "HACMP for AIX" $msg
oemcheckres ${checkres} $disk
ckres_rc=$?
if (( $ckres_rc != 0 )) ; then
case $breakres in
TARGET )
cl_scdiskrsrv /dev/$disk
;;
PSCSI )
oem_disktype=SCSIDISK
pscarray[${parent}]=${pscarray[${parent}]:+"${pscarray[${parent}]} "}${disk}
;;
FSCSI )
oem_disktype=FSCSI
fscarray[${parent}]=${fscarray[${parent}]:+"${fscarray[${parent}]} "}${disk}
;;
* )
$breakres $parent $disk
;;
esac
else
return $ckres_rc
fi
}
###############################################################################
#
# Name: oemmakedev
#
# Invoke the OEM supplied method - or the specified internal built in method
# to make a disk available
#
# Returns: any return code from the method
#
# Arguements: Method name - this is either the path name for the OEM
# supplied method, or the identifier for an
# internal build in method
#
# disk name - e.g., hdisk43
#
###############################################################################
function oemmakedev {
[[ "$VERBOSE_LOGGING" == "high" ]] && set -x
typeset makedev disk msg
makedev=$1
disk=$2
msg=$(dspmsg scripts.cat 6581 "Custom method %s invoked for disk %s to perform %s" $makedev $disk "makdev")
logger -t "HACMP for AIX" $msg
case $makedev in
MKDEV )
makedev $disk
;;
* )
$makedev $disk
;;
esac
}
###############################################################################
#
# Name: make_disk_available
#
# Function: Given a disk name, determine its type and state, and invoke
# the appropriate routines to
# * break any reserves
# * remove any ghost disks
# * make it 'Available'
#
# Input: disk name - e.g., hdisk43
#
# Output: None
#
###############################################################################
function make_disk_available
{
[[ "$VERBOSE_LOGGING" == "high" ]] && set -x
disk=$1
# Determine the owning adapter. This will be used to determine the
# disk type.
parent=$(lsdev -Cc disk -l $disk -F parent)
PdDvLn=$(lsdev -Cc disk -l $disk -F PdDvLn)
#
# Do not check Remote Physical Volume Client disk.
# If PdDvLn=disk/remote_disk/rpvclient there is no parent.
#
if [[ ${PdDvLn} != 'disk/remote_disk/rpvclient' ]]
then
if [[ -z "$parent" || -z "$PdDvLn" ]] ; then
cl_log 35 "$PROGNAME: Undefined disk device $disk. (May have been duplicate)." $PROGNAME $disk
# update resource manager with the status
cl_RMupdate resource_error $disk $PROGNAME
continue
fi
fi
# Determine the disk type. The mechanism used to break the reserves is
# dependent on the type.
disktype="UNKNOWN"
# First, check to see if this disk should be treated as equivalent to
# one of the known types. This check is done first, because it is more
# specific than the "known types" checking below, which is primarily
# based on owning adapter type.
OEMDISKTYPES="/etc/cluster/disktype.lst"
# The format of this file is
#
# E.g.,
# disk/fcal/HAL9000 FSCSI
#
# will cause this routine to treat such as device as 'fibre SCSI'
if [[ -r $OEMDISKTYPES && -s $OEMDISKTYPES ]] ; then
PdDvLn=$(odmget -q "name = $disk" CuDv | sed -n 's/^.*PdDvLn = "\(.*\)".*/\1/p')
if oemdisktype=$(grep -w "^${PdDvLn}" $OEMDISKTYPES | cut -f2) ; then
if [[ ${oemdisktype} == @(SCSIDISK|SSA|FCPARRAY|ARRAY|FSCSI|FLUTE) ]] ; then
disktype=$oemdisktype
fi
fi
fi
#
# Check to see if the disk has custom methods.
#
parallel='false'
makedev='MKDEV'
checkres='SCSI_TUR'
breakres=''
if [[ ${disktype} == 'UNKNOWN' ]] ; then
# Read the custom methods from the HACMPdisktype ODM class. This
# will instatiate the following variables:
# ghostdisks - routine to find ghost disks
# checkres - routine to check is a reserve is held
# breakres - routine to break reserves
# parallel - 'true' => can break reserves in parallel
# makedev - routine to make device available
eval $(odmget -q "PdDvLn = $PdDvLn" HACMPdisktype | sed -n 's/ = /=/p')
if [[ -n $breakres ]] ; then
disktype="OEM"
fi
fi
#
# Check for disk type Remote Physical Volume Client
#
if [[ ${disktype} == 'UNKNOWN' ]]
then
if [[ ${PdDvLn} == 'disk/remote_disk/rpvclient' ]]
then
disktype="RPV_CLIENT"
fi
fi
if [[ ${disktype} == 'UNKNOWN' ]] ; then
#
# Normal disk type checking, in lieu of any overrides or added
# customizations
#
case $parent in
scsi* | vscsi* )
disktype="SCSIDISK" # parallel SCSI - SCSI-2
;;
ssar )
disktype="SSA"
;;
dar* )
if [[ -n $(odmget -q "name = $disk AND PdDvLn = disk/fdar/array" CuDv) ]] ; then
# Either Dolphin or Flute. They behave differently, but have
# nearly identical ODM entries.
daclist=$(odmget -q "name = $parent AND attribute = all_controller" CuAt | grep 'value =' | cut -f2 -d'"')
if [[ -z $daclist ]] ; then
disktype="FCPARRAY"
else
# Check for IBM microcode, necessary to work with reset
# logic later
firstdac=${daclist%%,*}
if LANG=C lscfg -vl $firstdac | grep -q 'Manufacturer\.*\.IBM' ; then
disktype="FLUTE"
fi
fi
else
disktype="ARRAY"
fi
;;
dpo )
disktype="DPO" # SDD vpath disks
;;
fscsi* )
disktype="FSCSI" # fibre SCSI - SCSI-3
;;
isc* )
disktype="ISCSI" # iSCSI
;;
sisraid* )
disktype="SCSIRAID" # SCSI RAID adapters
;;
* )
# The following code has been duplicated above under parent of
# dar as it was apparent from the field that the parent in the
# case of FLUTE at least, is a dar. Since we were unable to
# determine for sure if the parent of a Dolphin is also a dar
# or possibly something else, the code has been left here as
# well to be on the safe side.
if [[ -n $(odmget -q "name = $disk AND PdDvLn = disk/fdar/array" CuDv) ]] ; then
# Either Dolphin or Flute. They behave differently, but have
# nearly identical ODM entries.
daclist=$(odmget -q "name = $parent AND attribute = all_controller" CuAt | grep 'value =' | cut -f2 -d'"')
if [[ -z $daclist ]] ; then
disktype="FCPARRAY"
else
# Check for IBM microcode, necessary to work with reset
# logic later
firstdac=${daclist%%,*}
if LANG=C lscfg -vl $firstdac | grep -q 'Manufacturer\.*\.IBM' ; then
disktype="FLUTE"
fi
fi
fi
;;
esac
fi
# Check to see if the device is available. We can tell this by
# calling lsdev with the logical name of the device (-l) and
# asking to see the devices that have a status of Available (-S A)
# we limit the output to the status field (-F "status).
if [[ -n $(lsdev -Cc disk -l $disk -S A -F 'status') ]] ; then
# Break the reserve on an "available" disk. Where supported, do so in
# a background task that will run asynchronously to this one.
case $disktype in
SSA )
#
# SSA and vpath can run in parallel
#
parallel='true'
breakres $parent $disktype $disk &
;;
DPO )
#
# Clean up any ghost disks for underlying hdisks,
# and note the vpath disk for processing later
#
vpath_dup $disk
dpolist=${dpolist:+"$dpolist "}${disk}
;;
OEM )
#
# For OEM methods, the method provider indicates whether
# parallel processing is possible.
#
if [[ $parallel == 'true' ]] ; then
oembreakres $breakres $checkres $parent $disk &
else
oembreakres $breakres $checkres $parent $disk
fi
;;
FSCSI )
#
# Collect the fibre SCSI disks by owning adapter, for
# later processing in parallel.
#
fscarray[${parent}]=${fscarray[${parent}]:+"${fscarray[${parent}]} "}${disk}
;;
ISCSI )
#
# Collect the ISCSI disks by owning adapter, for
# later processing in parallel.
#
iscarray[${parent}]=${iscarray[${parent}]:+"${iscarray[${parent}]} "}${disk}
;;
SCSIDISK )
#
# Collect the parallel SCSI disks by owning adapter, for
# later processing in parallel
#
pscarray[${parent}]=${pscarray[${parent}]:+"${pscarray[${parent}]} "}${disk}
;;
RPV_CLIENT )
#
# Do nothing if Remote Physical Volume Client disk is available.
#
;;
SCSIRAID )
#
# Collect the RAID scsi disks by owning adapter, for later
# processing by adapter
#
rscarray[${parent}]=${rscarray[${parent}]:+"${rscarray[${parent}]} "}${disk}
;;
* )
#
# Everything else has to be serial
#
breakres $parent $disktype $disk
;;
esac
else
# If the disk is not available, determine if disk is defined.
# If it is not then there is nothing we can do since we
# don't have any information on the disk.
if [[ -z $(lsdev -Cc disk -l $disk -S D -F 'status') ]]
then
# If the disk is not even defined then we can't recover
cl_log 35 "$PROGNAME: Undefined disk device $disk. (May have been duplicate)." $PROGNAME $disk
continue
fi
# At this point we know the disk is Defined
# Process by disk type
case $disktype in
SSA )
# Reconfigure the defined disk, and make it available
makedev $disk
# Check to see if it worked
if [[ -n $(lsdev -Cc disk -l $disk -S A -F 'status') ]] ; then
# Disk is now available; go break reserves
breakres $parent "SSA" $disk &
fi
;;
DPO )
#
# Clean up any ghost disks for underlying disks, and add
# this vpath device to the list to be made available and
# have reserves broken.
#
vpath_dup $disk
disklist=${disklist:+"$disklist "}"MKDEV.${disk}"
dpolist=${dpolist:+"$dpolist "}${disk}
;;
SCSIDISK )
#
# Collect the parallel SCSI disks by owning adapter, for
# later processing in parallel
#
pscarray[${parent}]=${pscarray[${parent}]:+"${pscarray[${parent}]} "}${disk}
;;
FSCSI )
#
# Collect the fibre SCSI disks by owning adapter, for
# later processing in parallel.
#
fscarray[${parent}]=${fscarray[${parent}]:+"${fscarray[${parent}]} "}${disk}
;;
ISCSI )
#
# Collect the ISCSI disks by owning adapter, for
# later processing in parallel.
#
iscarray[${parent}]=${iscarray[${parent}]:+"${iscarray[${parent}]} "}${disk}
;;
SCSIRAID )
#
# Collect the RAID scsi disks by owning adapter, for later
# processing by adapter
#
rscarray[${parent}]=${rscarray[${parent}]:+"${rscarray[${parent}]} "}${disk}
;;
ARRAY )
# It is possible for the original disk device to become
# "Defined" while the new disk device becomes "Available".
# This needs to be corrected. Get the connection address for
# the device and check to see if there is another device
# available at this address. If there is then delete it.
loc=$(lsdev -Cc disk -l $disk -F 'location')
duplist=$(odmget -q "parent = $parent AND \
name != $disk AND \
location = $loc" CuDv | \
sed -n 's/^.*name = "\(.*\)".*/\1/p')
brokeres='false'
for ghostdisk in $duplist ; do
# The reason we reserve rather than simply reset is
# because a device must be reserved by another node
# during boot of this node for the disk to be unavailable.
if [[ $brokeres == 'false' && \
-n $(lsdev -Cc disk -l $ghostdisk -S A -F 'status') ]] ; then
breakres $parent $disktype $ghostdisk
if (( $? == 0 || $? == 1 )) ; then
brokeres='true'
fi
fi
[[ $VERBOSE_LOGGING != high ]] &&
cl_echo 640 "$PROGNAME: Removing duplicate disk device: $ghostdisk." $PROGNAME $ghostdisk
rmdev -l "$ghostdisk" '-d'
done
# Reconfigure the defined disk, and make it available
makedev $disk
if [[ $brokeres == 'false' && \
-n $(lsdev -Cc disk -l $disk -S A -F 'status') ]] ; then
# If the reserve was not broken using the available
# duplicate above, break it here
breakres $parent $disktype $disk
fi
;;
FCPARRAY | FLUTE )
# FCPARRAY and Flute disks aren't supposed to get ghost disks,
# so let's just make it available, and then break any
# reservations.
makedev $disk
# Break the reserve on an "available" disk, this is a special
# case for fcparray only.
if [[ -n $(lsdev -Cc disk -l $disk -S A -F 'status') ]] ; then
breakres $parent $disktype $disk
fi
;;
OEM )
# Find the ghost disks for the given disk
duplist=$(oemghostdisks ${ghostdisks} $disk)
if (( $? != 0 )) ; then
# If something goes wrong with finding ghost disks, skip
# this disk and go on to the next
continue
fi
brokeres='false'
for ghostdisk in $duplist ; do
# Break the reserve using this available ghost disk, so
# that when we go to make the real disk available, the
# PVID can be read.
if [[ $brokeres == 'false' && \
-n $(lsdev -Cc disk -l $ghostdisk -S A -F 'status') ]] ; then
if [[ $parallel == 'true' ]] ; then
oembreakres $breakres $checkres $parent $ghostdisk &
else
oembreakres $breakres $checkres $parent $ghostdisk
if (( $? != 0 )) ; then
# If for some reason, breaking the reserve
# failed, skip this disk and go on to the
# next
continue
fi
fi
brokeres='true'
# Because we may have back grounded the process
# of breaking the reserve, the remaining steps
# of deleting the ghost disk profile and making
# the real disk available have to be queued up,
# and done when that finishes.
disklist="$disklist ${makedev}.${disk}"
fi
ghostlist=${ghostlist:+"$ghostlist "}${ghostdisk}
done
if [[ $brokeres == 'false' ]] ; then
# If we've not queued up this processing above, now try
# and make the real disk available.
oemmakedev $makedev $disk
if [[ -n $(lsdev -Cc disk -l $disk -S A -F 'status') ]] ; then
if [[ $parallel == 'true' ]] ; then
oembreakres $breakres $checkres $parent $disk &
else
oembreakres $breakres $checkres $parent $disk
fi
else
# We had a problem, exit failure
cl_log 31 "$PROGNAME: Unable to make device $disk available. Check hardware connections." $PROGNAME $disk
fi
fi
;;
RPV_CLIENT )
#
# Do not do makedev for a Remote Physical Volume Client disk.
# The makedev for a Remote Physical Volume Client disk is
# done in the predisk_available Replicated Resource Method.
#
;;
* )
# Whatever this kind of disk is, there is no special support
# for it. So, treat it similar to a generic SCSI-2 disk
# and hope for the best.
loc=$(lsdev -Cc disk -l $disk -F 'location')
# First, look for obvious duplicates
duplist=$(odmget -q "parent = $parent AND \
name != $disk AND \
location = $loc" CuDv | \
sed -n 's/^.*name = "\(.*\)".*/\1/p')
brokeres='false'
for ghostdisk in $duplist; do
# Attempt to break any outstanding reserve on this device
# because a device must be reserved by another node
# during boot of this node for the disk to be unavailable
if [[ $brokeres == 'false' && \
-n $(lsdev -Cc disk -l $ghostdisk -S A -F 'status') ]] ; then
breakres $parent $disktype $ghostdisk
if (( $? == 0 || $? == 1 )) ; then
brokeres='true'
fi
fi
ghostlist=${ghostlist:+"$ghostlist "}${ghostdisk}
done
# Reconfigure the defined disk, and make it available
makedev $disk
if [[ $brokeres == 'false' && \
-n $(lsdev -Cc disk -l $disk -S A -F 'status') ]] ; then
# If the reserve was not broken using the available
# duplicate above, break it here
breakres $parent $disktype $disk
fi
;;
esac
fi
}
###############################################################################
#
# Name: verify_disk_availability
#
# This function verifies whether the disk came up. If not, an appropriate
# error message is written.
#
# Returns:
#
# Arguments:
# disk name - e.g., hdisk43
#
###############################################################################
function verify_disk_availability
{
[[ "$VERBOSE_LOGGING" == "high" ]] && set -x
typeset disk=$1
#
# Do not do this check for a Remote Physical Volume Client
# disk. The makedev for a Remote Physical Volume Client disk
# is done in the predisk_available Replicated Resource Method.
#
PdDvLn=$(lsdev -Cc disk -l $disk -F PdDvLn)
if [[ ${PdDvLn} == 'disk/remote_disk/rpvclient' ]]
then
return 0
fi
if [[ -z $(LANG=C lsdev -Cc disk -l $disk -S A -F 'status') ]] ; then
# We were unable to bring this disk on line. Note the problem,
# and the non-functional state of the disk
cl_log 31 "$PROGNAME: Unable to make device $disk available. Check hardware connections." $PROGNAME $disk
fi
#
# Note that the resource manager is not updated with the status of the
# individual disks. This is because loss of a disk is not necessarily
# severe enough to stop the event - varyonvg may still work.
#
}
###############################################################################
#
# Main routine
#
###############################################################################
PROGNAME=${0##*/}
export PATH="$(/usr/es/sbin/cluster/utilities/cl_get_path all)"
[[ "$VERBOSE_LOGGING" == "high" ]] && set -x
[[ "$VERBOSE_LOGGING" == "high" ]] && version="1.2.5.51"
[[ "$VERBOSE_LOGGING" != "high" ]] && cl_echo 33 "Starting execution of $0 with parameters: $*\n" $0 "$*"
HA_DIR="es"
#
# This routine can be invoked from various C-SPOC flows, in which its
# inappropriate to do cl_RMupdate. Such processing is skipped if '-s' is
# specified.
#
if [[ "$1" == "-s" ]]
then
shift
JUST_DISKS=true # No resource manager updates needed
else
JUST_DISKS=false # Normal event processing
fi
ghostlist='' # Ghost disks to be deleted
disklist='' # Disks to be made available
#
# Global variables to define associative arrays for types FSCSI SCSIDISK
# ISCSI: This will be used to collect the disks by owning adapter, to handle
# in parallel. The index is the adapter name, the element is a list of
# disks on that adapter. E.g.,
#
# fscarray[fscsi0]="hdisk11 hdisk22 hdisk33"
#
typeset -A fscarray # Array for type FSCSI
typeset -A pscarray # Array for type SCSIDISK
typeset -A iscarray # Array for type ISCSI
typeset -A rscarray # Array for type SCSIRAID
#
# List of vpath devices to be processed after the underlying hdisks are
# cleaned up
#
typeset dpolist # Array for type dpo
#
# This routine can be invoked in two different fashions:
# - resource groups processed serially
# A list of disks are passed as arguments
# - resource groups are processed in parallel
# Resource groups, their disks, and owning
# volume groups are passed as environment
# variables
#
if [[ $JUST_DISKS == false && -n $JOB_TYPE && $JOB_TYPE != "GROUP" ]] ; then
#
# Resource groups processed in parallel
#
PROC_RES=true # implies JUST_DISKS is false
ON_LIST=$(print $(lsvg -L -o 2>/var/hacmp/log/lsvg.err))
#
# Get a sorted list of the volume groups, and add any that are vary'd on
# in passive mode to the list of volume groups that are on line. The
# disks for such volume groups are already locally accessable, so do not
# need to have reserves broken.
#
SORTED_VGS=$(
IFS=:, set -- $VOLUME_GROUPS ; print $* | \
tr ' ' '\n' | sort -u
)
# If a volume group is vary'd off, lsvg -L produces no output
PASSIVE_VGS=$(
# If a volume group is vary'd off, lsvg -L produces no output
LANG=C lsvg -L $SORTED_VGS 2>/dev/null | \
sed -n 's/VOLUME GROUP: *\([^ ]*\).*/\1/p'
)
ON_LIST="$ON_LIST $PASSIVE_VGS"
#
# Process by resource group
# RESOURCE_GROUPS - space separated list of resource groups
# HDISKS - list of disks for a resource group
# - comma separated list of disks within a resource group
# - colon separated lists of disks for each resource group
# VOLUME_GROUPS - list of owning volume group (if any) for each
# hdisk. Same pattern of comma and colon separated
# lists as for HDISKS
# e.g. -
# RESOURCE_GROUPS="curley larry moe shemp"
# HDISKS="hdisk11,hdisk22:hdisk21,hdisk31:hdisk99:hdisk101,hdisk1"
# VOLUME_GROUPS="vg01,vg01:vg02,vg02,::vg03,vg0"
#
_HDISKS=$HDISKS
_VOLUME_GROUPS=$VOLUME_GROUPS
for GROUPNAME in $RESOURCE_GROUPS ; do
#
# Tell the resource manager we're about to bring these disks online
#
export GROUPNAME
ALLDISKS="All_disks"
cl_RMupdate resource_acquiring $ALLDISKS $PROGNAME
#
# Extract the disk list and corresponding volume group list for
# the disks for this resource group
#
print $HDISKS | IFS=':' read LIST_OF_HDISKS_FOR_RG HDISKS
print $VOLUME_GROUPS | IFS=':' read LIST_OF_VOLUME_GROUPS_FOR_RG VOLUME_GROUPS
for disk in $(IFS=', ' set -- $LIST_OF_HDISKS_FOR_RG ; print $*)
do
#
# Extract the name of the volume group that owns this disk
#
print $LIST_OF_VOLUME_GROUPS_FOR_RG | IFS=', ' read vg LIST_OF_VOLUME_GROUPS_FOR_RG
#
# If the disk is in a volume group that is already vary'd on, or
# vary'd on in passive mode, the disks are aleady locally
# accessable, so there is nothing to do.
#
if [[ -n $vg && $ON_LIST == ?(* )$vg?( *) ]]
then
continue
#
# Otherwise, go make sure the disk is accessable and available
#
else
make_disk_available $disk
fi
done
done
#
# Restore resource group list
#
HDISKS=$_HDISKS
VOLUME_GROUPS=$_VOLUME_GROUPS
export GROUPNAME=$RESOURCE_GROUPS
else
#
# If not called from process_resources, arguments are needed on
# the command line
#
PROC_RES=false
if (( $# == 0 )) ; then # no disks passed
cl_echo 34 "$PROGNAME usage: cl_disk_available hdisk ...\n" $PROGNAME
exit 2
fi
if [[ $JUST_DISKS == true ]] ; then
#
# If not called during event processing, skip the resource manager
# update.
#
ALLDISKS="All_disks"
cl_RMupdate resource_acquiring $ALLDISKS $PROGNAME
fi
#
# Requests from serially processed resource groups
#
for disk in $*
do
make_disk_available $disk
done
fi
#
# Take care of all the fibre scsi disks, doing all those associated
# with a specific adapter at one time
#
for parent in ${!fscarray[@]} ; do
make_disktypes_available $parent FSCSI ${fscarray[$parent]}
done
#
# Take care of all the parallel scsi disks, doing all those associated
# with a specific adapter at one time
#
for parent in ${!pscarray[@]} ; do
make_disktypes_available $parent SCSIDISK ${pscarray[$parent]}
done
#
# Take care of all the iscsi disks, doing all those associated
# with a specific adapter at one time
#
for parent in ${!iscarray[@]} ; do
make_disktypes_available $parent ISCSI ${iscarray[$parent]}
done
#
# Take care of all the RAID scsi disks, doing all those associated
# with a specific adapter at one time
#
for parent in ${!rscarray[@]} ; do
make_disktypes_available $parent SCSIRAID ${rscarray[$parent]}
done
# wait to sync any background processes still breaking reserves
wait
#
# If there were ghost disk profiles queued up to be removed, do so now.
#
for ghostdisk in $ghostlist ; do
[[ $VERBOSE_LOGGING != high ]] &&
cl_echo 640 "$PROGNAME: Removing duplicate disk device: $ghostdisk." $PROGNAME $ghostdisk
rmdev -l "$ghostdisk" '-d'
done
#
# If there were disks to make available, do so now
#
for diskspec in $disklist ; do
# Limitations on ksh processing are worked around by saving in the
# elements of 'disklist' the makedev method name catenated with the
# hdisk name with a '.'. The next two statements pull the two
# apart.
makedev=${diskspec%.*}
disk=${diskspec#*.}
oemmakedev $makedev $disk
done
#
# Having finally cleaned up any reserves or ghost disks on the underlying
# hdisks, break any persistent reserve on vpath devices.
#
for vpath in $dpolist ; do
breakres DPO DPO $vpath
done
#
# Go back and check to see if the various disks came on line, and update
# the cluster manager's status as appropriate
#
if [[ $PROC_RES == true ]]; then
for GROUPNAME in $RESOURCE_GROUPS ; do
export GROUPNAME
#
# Extract the disk list and the corresponding volume group list
# for the disks in this resource group
#
print $HDISKS | IFS=':' read LIST_OF_HDISKS_FOR_RG HDISKS
print $VOLUME_GROUPS | IFS=':' read LIST_OF_VOLUME_GROUPS_FOR_RG VOLUME_GROUPS
#
# Do each of the disks in the list for this resource group
#
for disk in $(IFS=', ' set -- $LIST_OF_HDISKS_FOR_RG ; print $*)
do
#
# Extract the name of the volume group that owns this disk
#
print $LIST_OF_VOLUME_GROUPS_FOR_RG | IFS=', ' read vg LIST_OF_VOLUME_GROUPS_FOR_RG
#
# Those disks which are in a volume group which is already vary'd on
# do not need to be checked
#
if [[ -z $vg || $ON_LIST != ?(* )$vg?( *) ]] ; then
verify_disk_availability $disk
fi
done
done
else
#
# Resource groups processed serially: check all the disks passed on the command line
#
for disk in $*
do
verify_disk_availability $disk
done
fi
#
# Update the resource manager with the disks that came on line
#
ALLNOERR="All_nonerror_disks"
if [[ $PROC_RES == true ]]; then
for GROUPNAME in $RESOURCE_GROUPS ; do
cl_RMupdate resource_up $ALLNOERR $PROGNAME
done
elif [[ $JUST_DISKS == false ]] ; then
cl_RMupdate resource_up $ALLNOERR $PROGNAME
fi
exit 0