2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-05-21 19:22:05 +00:00

1552 lines
51 KiB
Bash
Executable File

#!/bin/bash
#
# Usage:
#
# There are utils for diskdiscover and configraid.
#
log_file=$1
##############################
# declare all commands
##############################
awk="awk"
sed="sed"
cut="cut"
sleep="sleep"
sort="sort"
ps="ps"
head="head"
readlink="readlink"
basename="basename"
udevadm="udevadm"
touch="touch"
tail="tail"
dmesg="dmesg"
grep="grep"
lspci="lspci"
############################################
#
# source raidcmd
#
###########################################
str_dir_name=`dirname $0`
. $str_dir_name/raidcmd
################################################################
#
# Input PCI_ID to get PCI location
#
# input: pci_id
#
# output: pci locations
#
################################################################
function get_pciloc_by_id {
local __in_pciid=$1
if echo "$__in_pciid" | $grep -sq "_"; then
__in_pciid=`echo "$__in_pciid" | $sed -e 's/_/:/'`
fi
local sysdevdir=/sys/bus/pci/devices
local pcilocs=`cd $sysdevdir 2>/dev/null && for dev in *
do
lines=$($udevadm info --query=property --path=$sysdevdir/$dev)
if echo "$lines" | $grep -i -sq -E "^PCI_ID=$__in_pciid$|^PCI_SUBSYS_ID=$__in_pciid$"; then
echo $dev
fi
done `
[ -z "$pcilocs" ] && return 1
echo "$pcilocs"
return 0
}
######################################################
#
# get pci_slot scsi device
#
# input: slocs, for example, 0:0:0:0 0:0:1:0
#
# output: <sloc>=<device_name> ...
# <sloc>=<device_name>
#
######################################################
function convert_sloc_to_sg {
local __slocs="$*"
for __sloc in $__slocs
do
if echo "$__sloc" | grep -sq "[0-9]\+:[0-9]\+:[0-9]\+:[0-9]\+"; then
__sg=`$readlink /sys/class/scsi_device/$__sloc/device/generic`
if [ "$__sg" ]; then
__sg=`$basename $__sg`
fi
elif echo "$__sloc" | grep -sq -E '^sg[0-9]+|^sd[a-z]+'; then
__sg="$__sloc"
__sloc=`convert_sg_to_sloc $__sg | $awk -F= '{print $2}'`
fi
echo "$__sloc=$__sg"
done
return 0
}
#################################################################
#
# Through PCI/SCSI device to find PCI/SCSI location
#
# input: device names,
# sg0 ...sgn
#
# output: <dev>=<sloc>... <dev>=<sloc>
#
################################################################
function convert_sg_to_sloc {
local __sgs="$*"
local __sloc=""
for __sg in $__sgs
do
if echo "$__sg" | grep -sq "^sg[0-9]\+"; then
__sloc=`$readlink /sys/class/scsi_generic/$__sg/device`
if [ "$__sloc" ]; then
__sloc=`$basename $__sloc`
fi
elif echo "$__sg" | grep -sq "^sd[a-z]\+"; then
__sloc=`$readlink /sys/block/$__sg/device`
if [ "$__sloc" ]; then
__sloc=`$basename $__sloc`
fi
elif echo "$__sg" | grep -sq "[0-9]\+:[0-9]\+:[0-9]\+:[0-9]\+"; then
__sloc="$__sg"
__sg=`convert_sloc_to_sg $__sloc | $awk -F= '{print $2}'`
fi
echo "$__sg=$__sloc"
done
return 0
}
############################################################################
#
# get devices which are qualified to be used to create raid
# it should equals to "query-raid-create" after all array had
# been deleted.
# Note: output format of this command is multilines
# <pciloc_of_ioa1>=<sloc_of_disk1>,<sloc_of_disk2>,...
# <pciloc_of_ioa2>=<sloc_of_disk1>,<sloc_of_disk2>,...
# ...
###########################################################################
function get_raid_create_devices_by_pciloc {
local lines=""
local pcilocs="$*"
[ -z "$pcilocs" ] && return 1
# reorder ipr ioa pcilocs by its Primary and Secondary state
local ioas=`get_ipr_ioas_by_pciloc $pcilocs`
pcilocs=`get_sg_pciloc $ioas | $awk -F= '{print $2}' `
lines=`cmd_show_config`
local slocs=""
local line=""
for pciloc in $pcilocs
do
# exclude:
# 1) scsi adapter(ioa);
# 2) scsi enclosure;
# 3) disk array;
slocs=`echo "$lines" \
| grep '^.*[ ]\+'$pciloc'\/[0-9]\+:[0-9]\+:[0-9]\+:[0-9]\+.*$' \
| grep -v -E "Adapter|Enclosure|Disk Array" \
| cut_sloc_from_iprconfig_line \
| $sort -V \
| $sed -e 's/ /,/g' \
| awk '{printf (NR>1)?","$0:$0}'`
if [ -n "$slocs" ]; then
line="$pciloc=$slocs"
echo "$line"
fi
done
return 0
}
########################################################################################
#
# return list of ioas on target pciloc with order of Primary --> Secondary --> Others
#
# input: pci locations
#
# output: sorted raid adapters location list
#
#########################################################################################
function get_ipr_ioas_by_pciloc {
local pcilocs="$*"
[ -z "$pcilocs" ] && return 1
# find out all ioas and its current adapter state
local ioas_all=`cmd_show_ioas | grep "^sg[0-9]\+.*Operational" | $awk '{print $1}'`
# group them into "Primary" and "Secondary" groups
local lines=`for ioa in $ioas_all
do
state=$(cmd_show_details $ioa | grep "Current Dual Adapter State" | $sed -e 's/^.* : \+\(.*\)$/\1/')
echo "$state=$ioa"
done`
local ioa_primary=`echo "$lines" | $awk -F= '($1 == "Primary") {print $2}'`
local ioa_secondary=`echo "$lines" | $awk -F= '($1 == "Secondary") {print $2}'`
local ioa_others=`echo "$lines" | $awk -F= '($1 != "Primary") && ($1 != "Secondary") {print $2}'`
ioas_all="$ioa_primary $ioa_secondary $ioa_others"
# pick up ioa on target pciloc
lines=`echo "$pcilocs" | $sed -e 's/[, ]/\n/g'`
local ioas_in=""
if [ "$lines" = "all" ]; then
ioas_in="$ioas_all"
else
ioas_in=`for ioa in $ioas_all
do
ioa_pciloc=$(get_sg_pciloc $ioa | awk -F= '{print $2}')
if echo "$lines" | grep -sq -i "^${ioa_pciloc}$"; then
echo $ioa
fi
done `
fi
[ -z "$ioas_in" ] && return 1
echo "$ioas_in"
return 0
}
###############################################
#
# use udev to determine pciloc of sg device
#
# input: disk name list
#
# output: <dev>=<sloc>...<dev>=<sloc>
#
###############################################
function get_sg_pciloc {
local sgs="$*"
[ -z "$sgs" ] && return 1
local sg=""
local pciloc=""
for item in $sgs
do
sg=`convert_sloc_to_sg $item | $awk -F= '{print $2}'`
[ -z "$sg" ] && continue
pciloc=`cmd_show_details $sg \
| grep "^PCI Address" \
| $sed -e 's/.*:[ ]\+\([0-9]\+:[0-9]\+:[0-9]\+\.[0-9]\+\).*$/\1/'`
if [ -n "$pciloc" ]; then
echo "$sg=$pciloc"
fi
done
return 0
}
######################################################
#
# cut resouce_path
#
#####################################################
function cut_sloc_from_iprconfig_line {
$sed -e 's/^.*[ ]\+\(.*\)\/\([0-9]\+:[0-9]\+:[0-9]\+:[0-9]\+\).*$/\2/g'
}
#####################################################################
#
# find descriptions and status for device
#
# input: pci locations
#
# output: descriptions and status from "iprconfig -c show-config"
#
###################################################################
function find_desc_status_sg {
local lines=""
local pciscsilocs="$*"
[ -z "$pciscsilocs" ] && return 1
lines=`cmd_show_config`
local slocs=""
local line=""
for pciscsiloc in $pciscsilocs
do
slocs=`echo "$lines" \
| $grep "${pciscsilocs}" \
| awk '{for(i=3;i<=NF;++i) printf $i "\t";printf "\n"}'`
echo $slocs
done
return 0
}
#######################################################
#
# get disk devices through pci_id
#
# input: pci id
#
#######################################################
function get_devices_by_pciid {
local pciid="$*"
[ -z "$pciid" ] && return 1
pcilocs=`get_pciloc_by_id $pciid`
if [ -z "$pcilocs" ]; then
echo "There is no PCI_SLOT_NAME for PCI_ID:$pciid."
return 1
fi
slocs_grps=`get_raid_create_devices_by_pciloc $pcilocs`
if [ -z "$slocs_grps" ]; then
echo "Could not find any disk on target pciloc ${pcilocs}!"
return 1
fi
arrage_output $pciid "$slocs_grps"
}
######################################################################
#
# get ipr raid arrays by PCI location
#
# input: pci locations
#
# output: raid arrays
#
#####################################################################
function get_ipr_arrays_by_pciloc {
local pcilocs="$*"
[ -z "$pcilocs" ] && return 1
# reorder ipr ioa pcilocs by its Primary and Secondary state
local ioas=`get_ipr_ioas_by_pciloc $pcilocs`
pcilocs=`get_sg_pciloc $ioas | $awk -F= '{print $2}' `
local lines=$(cmd_show_arrays)
local slocs=`for pciloc in $pcilocs
do
echo "$lines"
done `
[ -n "$slocs" ] && echo "$slocs"
return 0
}
#####################################################################
#
# get all af and jbod disks
#
# input: no
#
# output: all devices table
#
####################################################################
function get_all_devices_for_raid {
local showlines=`cmd_show_config`
pcilocs=`echo "$showlines" \
| $grep -v -E "Adapter|Enclosure|Disk Array" \
| $grep '^.*[ ]\+\(.*\)\/\([0-9]\+:[0-9]\+:[0-9]\+:[0-9]\+\).*$'|$sed 's/ /,/g'|cut -c 8-19`
[ -z "$pcilocs" ] && return 1
slocs_grps=`get_raid_create_devices_by_pciloc $pcilocs`
if [ -z "$slocs_grps" ]; then
echo "Could not find any disk on target pciloc ${pcilocs}!"
fi
arrage_output "null" "$slocs_grps"
}
#####################################################################
#
# output of disks and arrays
#
# input: pci_id sloc_grps
#
# output: all devices table
#
####################################################################
function arrage_output {
pciid=$1
shift
slocs_grps=$*
# find the required member disks
echo "--------------------------------------------------------------------------"
echo "PCI_ID PCI_SLOT_NAME Resource_Path Device Description Status"
echo "------ ------------- ------------- ------ ----------- ----------------"
slocs=""
for item in $slocs_grps
do
pciloc=`echo "$item" | $awk -F= '{print $1}'`
slocs_grp=`echo "$item" | $awk -F= '{print $2;}'i \
| $sed 's/,/ /g'`
if [ x$pciid == "xnull" ]; then
pciid=`get_PCI_ID $pciloc`
fi
for sloc in $slocs_grp
do
pciscsiloc="$pciloc/$sloc"
desc=`find_desc_status_sg $pciscsiloc`
disk=`convert_sloc_to_sg $sloc | $awk -F= '{print $2}'`
echo "$pciid $pciloc $sloc $disk $desc "
done
echo "-------------------------------------------------------------------"
echo "Get ipr RAID arrays by PCI_SLOT_NAME: $pciloc"
echo "-------------------------------------------------------------------"
get_ipr_arrays_by_pciloc $pciloc
done
}
###############################################################
#
# get PCI_ID through lspci
#
# input: pci location
#
# output: pci id
#
###############################################################
function get_PCI_ID {
pcislot=$1
pciid=`$lspci | $grep ${pcislot} | $awk '{print $5}'`
echo $pciid
}
###########################################
#
# Identify if sloc is exist or not
#
# input: dev sloc
#
# output: 0 exist
# 1 not
#
##########################################
function sloc_exist {
local __sloc="$1"
__sloc=`convert_sg_to_sloc $__sloc | $awk -F= '{print $2}'`
rc=1
[ -n "$__sloc" ] && [ -d /sys/class/scsi_device/$__sloc/device ] && rc=0
return $rc
}
###########################################
#
# get all raid arrays
#
# output : array1#array2..
#
##########################################
function get_all_raid_arrays {
local arrays=`cmd_show_arrays \
| $awk '{print $1}'\
| $sed -n '3,$p'`
echo $arrays
}
#################################################
#
# Identify if sloc is raid or not
#
# input : dev sloc
#
# return : 1 ------not raid
# 0 ------is raid
#
##################################################
function is_ipr_array {
local __sloc="$1"
local __sg=`convert_sloc_to_sg $__sloc | $awk -F= '{print $2}'`
[ -n "$__sg" ] && is_array $__sg
}
#################################################
#
# get all ioas
#
##################################################
function get_ipr_ioas {
get_ipr_ioas_by_pciloc all
}
#################################################
#
# cut pci location from iprconfig line
#
##################################################
function cut_pciloc_from_iprconfig_line {
$sed -e 's/^.*[ ]\+\(.*\)\/\([0-9]\+:[0-9]\+:[0-9]\+:[0-9]\+\).*$/\1/g'
}
##############################################
#
# get member disk for ipr array
#
# input : array name
#
# output : member sloc list
# "member_sloc1 member_sloc2 ...."
#
#############################################
function get_ipr_array_members {
local array="$1"
local array_sloc=`convert_sg_to_sloc $array | $awk -F= '{print $2}'`
# array does not exist, error with rc=1
[ -z "$array_sloc" ] && return 1
local lines=`cmd_show_config`
# get scsi locations of disk array members
local member_slocs=`echo "$lines" \
| $awk '
BEGIN { hit=0; members=""; }
/^.*\/'$array_sloc'[ ]+.*Disk Array.*$/ { hit=1; next; }
{
if (hit==1) {
if ($0 ~ /RAID .* Member/) {
# ignore "Remote" array members
if ($0 !~ / *Remote *$/) {
print $0
}
} else {
hit=0
}
}
}' \
| cut_sloc_from_iprconfig_line `
# array does not have any member which should not, error with rc=1
[ -z "$member_slocs" ] && return 1
echo "$member_slocs"
log_details "array[$array] has member disks[$member_slocs]" >&2
return 0
}
#####################################################################
#
# get array by disk
#
# input : array disk member name
#
# output : array sloc
#
###################################################################
function get_ipr_array_by_member {
local sg="$1"
local member_sloc=`convert_sg_to_sloc $sg | $awk -F= '{print $2}'`
# member does not exists, error with rc=1
[ -z "$member_sloc" ] && return 1
local lines=`cmd_show_config`
local array_sloc=`echo "$lines" \
| $awk '
BEGIN { hit=0; line="na"; }
/Disk Array/ { hit=1; line=$0; next; }
{
if (hit==1) {
if ($0 ~ /RAID .* Member/) {
if ($0 ~ /\/'$member_sloc'[ ]+.*/) {
print line
exit 0
}
} else {
hit=0; line="na"
}
}
}' \
| cut_sloc_from_iprconfig_line`
# disk does not belongs to any array, error with rc=1
[ -z "$array_sloc" ] && return 1
echo "$array_sloc"
log_details "array[$array_sloc] owns disk[$sg]" >&2
return 0
}
#################################################
#
# get process
# input : pids
# output : child_pids pids
#
##################################################
function getpstree {
local pids="$*"
local pslines=`$ps -e -o pid,ppid`
while true
do
local has_expand=0
local lines=`echo "$pids" | $sed -e 's/ /\n/g'`
for pid in $pids
do
local child_pids=`echo "$pslines" | $awk -v pid=$pid '$2 == pid{print $1}'`
child_pids=`for pid2 in $child_pids
do
echo "$lines" | grep -sq "^$pid2$" || echo "$pid2"
done `
if [ -n "$child_pids" ]; then
pids="$child_pids $pids"
has_expand=1
fi
done
if [ $has_expand -eq 0 ]; then
break
fi
done
echo "$pids"
}
#####################################################################
#
# delete used disks from slocs
#
# input : disk slocs
#
# output : unused disk slocs
#
###################################################################
function delete_disks_belong_array_from_slocs {
local slocs=$*
validslocs=""
usedslocs=""
for sloc in $slocs
do
line=$(get_ipr_array_by_member $sloc)
if [ $? -eq 0 ]; then
if [ "x$usedslocs" != x ]; then
usedslocs=$usedslocs" "$sloc
else
usedslocs=$sloc
fi
fi
done
if [ -n "$usedslocs" ]; then
validslocs=`echo $slocs|sed "s/$usedslocs//g"`
if [ -n "$validslocs" ]; then
echo $validslocs
fi
else
echo $slocs
fi
}
####################################################################################
#
# create raid array
#
# input : $1 minutes,
# Times to wait for the raid creation finish, minutes
#
# $2 seconds
# Interval of minitoring raid creation, status check
#
# left: member disks , iprconfig_args
#
# output :
#
################################################################################
function create_ipr_array {
local global_rc=0
local lines=""
local monCnt=$1
shift
local monInt=$1
shift
local member_sgs=`echo "$1" | $sed -e 's/,/ /g'`
shift
local stage_1_timeout=1 # unit minutes
local stage_1_last_tick_count=`$awk -v monInt=$monInt -v timeout=$stage_1_timeout 'END{print timeout*60/monInt}' </dev/null`
[ -z "$member_sgs" ] && return 1
local member_slocs=`convert_sg_to_sloc $member_sgs | $awk -F= '{print $2}' `
local member_sgs=`convert_sloc_to_sg $member_sgs | $awk -F= '{print $2}' `
[ -z "$member_sgs" ] && return 1
local iprconfig_args="$*"
# log for details
{
echo "Attempt to create ipr array with command: iprconfig -c raid-create \"$iprconfig_args\" \"$member_sgs\""
for member_sg in $member_sgs
do
echo "-------------------------------------"
cmd_show_details $member_sg | $sed -e 's/^/details['$member_sg']: >> /g'
done
echo "-------------------------------------"
for member_sg in $member_sgs
do
cmd_show_status $member_sg | $sed -e 's/^/status['$member_sg']: >> /g'
done
echo "-------------------------------------"
} | log_lines details >&2
# prepare tmp log file for iprconfig worker process
local ftmp="/tmp/log_create.log.$$"
$touch $ftmp
# launch the iprconfig worker process
cmd_raid_create "$iprconfig_args" "$member_sgs" >$ftmp 2>&1 &
local pid_create=$!
# log for debug
local line=`$ps -f -p $pid_create | $awk -v pid=$pid_create '$2==pid{print $0}' | $head -n1`
log_details "ps1: $line"
# monitor the worker process and show status
# array_stage=0, monitor array member disk states only
# array_stage=1, wait for array showes up after array member disks are in termination states
# array_stage=2, monitor array state
local array_stage=0
local array_sloc=""
# is that possible sg names of member disks were changed after issued raid-create on top of them?
local slocs="$member_slocs"
local i=$monCnt
while [ $i -gt 0 -a $monCnt -gt 0 ];
do
# Get status of all sgs which might include array itself.
local status_lines=""
local rc=0
local sloc=""
[ $array_stage -ne 1 ] && \
for sloc in $slocs
do
local status=""
local sg=`convert_sloc_to_sg $sloc | $awk -F= '{print $2}'`
if [ -n "$sg" ]; then
status=`cmd_show_status $sg `
else
status="Missing-sg_name"
fi
[ -z "$status" ] && status="no_status"
local msg="status[$sloc/$sg]=\"$status\""
log_details "array create stage[$array_stage], $msg"
# list of "termination" status for member disks and target array
# Refer to "Disk array states" section in ipr doc.
local status_exp
if [ $array_stage -eq 0 -o $array_stage -eq 1 ]; then
# 1) If a physical disk fail to be formated as IPR AF, its status will be "Format Required" and be "Failed" later.
# Should we handle this?
status_exp="Active|Failed|Offline|Missing|R/W Protected|Format Required"
# I saw phy disk in following state which not in the doc.
status_exp+="|Optimized|Non-Optimized"
# my faked status for disk
status_exp+="|Missing-sg_name"
else
# Note:
# - Array state "Missing" is not a terminate state, exclude it by adding "#" as prefix
status_exp="Optimized|Active|Non-Optimized|Degraded|#Missing|Offline|Failed|R/W Protected|Rebuilding"
fi
# if not "termination" status, we record it and wait for next cycle.
if ! echo "$status" | grep -i -sq -E "$status_exp"; then
# aggregate status messages.
[ -n "$status_lines" ] && status_lines+=","
status_lines+="$msg"
rc=1
fi
done
# if all terminated, we fall back to check staus of array itself
if [ $rc -eq 0 ]; then
if [ $array_stage -eq 2 ]; then
log_info "Create array($array_sg=$array_sloc) successfully on top of disks: $member_slocs."
break
else # end if stage == 2
if [ $array_stage -eq 0 ]; then
# since all disks were in termination states, let's move to stage 2
# to wait for array to be created and start rebuild.
#
# It's possible that such array could not be created for some critical
# error, such in-sufficient disks for required raid level.
#
# Though there was smart way to handle this, here I only use a simple way
# by waiting for a fixed time(1 minute) before return error.
log_details "array create stage[1], entered, after finished formatting member disks $slocs"
lines=`{ cmd_show_config; cmd_show_alt_config; } | $sed -e 's/^/[0-->1] >> /g'`
log_lines details "$lines"
((array_stage=1))
((stage_1_last_tick_count=i-stage_1_last_tick_count))
fi
# IPR might had problem if some disks failed to response.
# These disks will disapear in the system with sloc and sg were all gone.
# in these case, we drop these disks in the operation and abort the operation
# if all member disks were gone.
local slocs_new=""
local __tmp_item=""
for __tmp_item in $slocs
do
if sloc_exist $__tmp_item; then
[ -n "$slocs_new" ] && slocs_new+=" "
slocs_new+="$__tmp_item"
else
log_error "$i: disk $__tmp_item disapear..."
fi
done
if [ -n "$slocs_new" ]; then
slocs="$slocs_new"
else
global_rc=1
log_error "All array member disks, $member_slocs, were gone, abort!"
break
fi
# get array owner of each member sg
local array_slocs=`for __tmp_item in $slocs
do
local sg_owner_sloc=$(get_ipr_array_by_member $__tmp_item)
[ -n "$sg_owner_sloc" ] && echo "$sg_owner_sloc"
done \
| $sort -u -V `
# we only allow one unique array owns all member disks.
# or, we we need to break out?
local array_cnt=`echo "$array_slocs" | $awk 'END {print NF; exit 0;}'`
log_details "All \"$array_cnt\" array(es), \"$array_slocs\", contain target disks, \"$slocs\"." >&2
if [ $array_cnt -gt 0 ]; then
array_sloc=`echo "$array_slocs" | $awk '{print $1}'`
local array_sg=`convert_sloc_to_sg $array_sloc | $awk -F= '{print $2}'`
if [ -n "$array_sg" ]; then
log_details "array create stage[2], entered, after array show up with sg name $array_sg and sloc $array_sloc" >&2
array_stage=2
slocs="$array_sloc"
else
log_warn "$i: >> Why new array $array_sloc($array_slocs) does not have a valid sg name?" >&2
log_warn "$i: >> Let wait for a while to see if it could show up or not until tick count equals to $stage_1_last_tick_count" >&2
fi
elif [ $array_stage -eq 1 -a $i -lt $stage_1_last_tick_count ]; then
log_error "Fail to wait array($array_slocs) showes up after its member disks, $member_slocs, were finished formatting." >&2
# log for debug
lines=`{
echo "$i: Current IPR configuration";
{
cmd_show_config;
cmd_show_alt_config;
} | $sed -e 's/^/'$i': >> /g';
}`
log_lines details "$lines"
global_rc=2
break
else
# see if we need to issue the raid-create again.
# for some reason, previous raid-create might be terminated unexpectedly with disks
# actually had been formated for raid and ready to be used. So, we try to issue
# the raid-create command again to see if that would help. This usually happened when
# some failures when formtting physical disks to ipr AF. In that situation, iprconfig
# might abort without have array created after it had actually formatted those disks.
local pid_create_still_alive=0
local pid_create_cur=`getpstree $pid_create`
local pid_cur
for pid_cur in $pid_create_cur
do
if $kill -0 $pid_cur 2>/dev/null; then
pid_create_still_alive=1
break
fi
done
if [ $pid_create_still_alive -eq 0 ]; then
log_info "$i: disks $slocs had finished formatting, but array was not shown up, and iprconfig process($pid_create) had terminated."
log_details "$i: Last several raid-create output:"
lines=`$tail -n10 $ftmp | $sed -e 's/^/'$i': >> /g'`
log_lines details "$lines"
# launch the iprconfig worker process again
member_sgs=`convert_sloc_to_sg $member_slocs | $awk -F= '{print $2}'`
if [ -n "$member_sgs" ]; then
$iprconfig -c raid-create $iprconfig_args $member_sgs >$ftmp 2>&1 &
pid_create=$!
log_info "$i: Restart \"iprconfig -c raid-create $iprconfig_args $member_sgs\" with new pid $pid_create." >&2
# log for debug
local line=`$ps -f -p $pid_create | $awk -v pid=$pid_create '$2==pid{print $0}' | $head -n1`
if [ -n "$line" ]; then
log_details "ps1.2: $line" >&2
else
log_error "ps1.2: new iprconfig($pid_create) was gone" >&2
fi
else
log_warn "$i: Fail to restart iprconfig since $member_slocs could not be mapped to sg." >&2
log_warn "$i: Let try again next loop until tick count equals to $stage_1_last_tick_count" >&2
fi
else
log_warn "$i: Wait for array($array_slocs) showes up after disk format were finished on $slocs until tick count equals to $stage_1_last_tick_count" >&2
log_details "$i: Last several raid-create output:" >&2
lines=`$tail -n2 $ftmp | $sed -e 's/^/'$i': >> /g'`
log_info debug "$lines" >&2
fi
fi
fi # end else if stage == 2
fi # end if $rc eq 0
# log status for monitor
# log for debug
if [ -n "$status_lines" ]; then
log_status "Creating IPR array with last info at time \""`date "+%Y-%m-%d %H:%M:%S"`"\": $status_lines" >&2
lines=`echo "$status_lines" | $sed -e 's/,/\n/g' | $sed -e "s/^/$i: >> /g"`
log_lines details "$lines" >&2
fi
$sleep $monInt
[ $monCnt -gt 0 ] && ((i-=1))
done
# log for debug
line=`$ps -f -p $pid_create | $awk -v pid=$pid_create '$2==pid{print $0}' | $head -n1`
[ -n "$line" ] && log_details "ps2: $line"
if $kill -0 $pid_create 2>/dev/null; then
# If succ out, array status should in "rebuilding" state while iprconfig is still running
# until the rebuild had finished. But we do not need to wait for its termination since the
# block device is actually ready to use though performance might be downgrade.
# so, by default, we will just kill the "iprconfig" command(? not sure if that would affect
# the rebuild or not) and let raid controller do its job at backend.
# in order to wait for the whole rebuilding, set IPR_REBUILD_WAIT=true explicitly.
if [ "x$IPR_REBUILD_WAIT" = "xtrue" -a $i -gt 0 ]; then
log_info "Wait for ipr raid creation command to finish. pid=$pid_create" >&2
wait $pid_create
else
log_warn "Terminate ipr raid creation process enforcely. pid=$pid_create" >&2
$kill -TERM $pid_create 2>/dev/null
$sleep 1
$kill -0 $pid_create 2>/dev/null \
&& $kill -KILL $pid_create 2>/dev/null
fi
fi
# log for debug
line=`$ps -f -p $pid_create | $awk -v pid=$pid_create '$2==pid{print $0}' | $head -n1`
[ -n "$line" ] && log_details "ps3: $line" >&2
log_details "Last several raid-create output:" >&2
lines=`$tail -n10 $ftmp | sed -e 's/^/[last]: >> /g'`
log_lines details "$lines" >&2
rm -f $ftmp
# Log "failed" status to PCM/xCAT with reason and instruction for future investigation.
if [ $global_rc -ne 0 ]; then
# set node "status" to "failed" to indicate that the installation was failed.
fstatus_app=/tmp/baz.py
if [ -x "$fstatus_app" ]; then
$fstatus_app "installstatus failed" >&2
fi
# set node state with brief reason why installation failed and future instructions.
# end user can get this message by running "nodestate" command to target nodes.
log_status "Fail to create IPR array with return code \"$global_rc\"! Refer to more details in syslog by checking \"rhPreDbg\" flag..." >&2
fi
return $global_rc
}
###########################################################################################
#
# main process to handle create raid array when using pci_id or pci_slot_name
#
##########################################################################################
function handle_create_raid_array {
local striple_size=$1
local raidlevel=$2
local disknum=$3
local pcilocs=$4
local disknames=$5
if [ "x$pcilocs" != "xnull" ]; then
# Find out all member disks in target IOAs
# pciloc=sloc,sloc,sloc...
slocs_grps=`get_raid_create_devices_by_pciloc $pcilocs `
if [ -z "$slocs_grps" ]; then
log_error "Could not find any disk on target pciloc ${pcilocs}!" >&2
return 1
else
log_info "Disks in target IPR raid adapter: \"$slocs_grps\"" >&2
fi
# find the required member disks
slocs=""
validslocs=""
for item in $slocs_grps
do
pciloc=`echo "$item" | $awk -F= '{print $1}'`
slocs_grp=`echo "$item" | $awk -F= '{print $2;}'`
# order disks by its resource path
slocs_grp=`sort_devices_by_resource_path $slocs_grp`
slocs=`echo "$slocs_grp" | sed 's/,/ /g'`
if [ -n "$slocs" ]; then
break
else
log_warn "Could not find enough disk on target adapter $pciloc which only has following disks ${slocs_grp}!" >&2
fi
done
if [ -z "$slocs" ]; then
log_error "Could not find enough disk on target adapter ${pciloc}!" >&2
return 1
else
log_info "Find member disks $slocs on target IOA $pciloc" >&2
slocs=`echo "$slocs" | $sed -e 's/,/ /g'`
# if it is the first create_raid, do nothing, else:
# delete used disks belong to existed raid array
validslocs=`delete_disks_belong_array_from_slocs $slocs`
if [ -z "$validslocs" ]; then
log_error "Could not find enough unused disk on target adapter $pciloc."
return 1
else
slocs="$validslocs"
log_info "Find unused member disks $slocs on target IOA $pciloc" >&2
fi
fi
# handle disk_num
# if disknum is all, do nothing
# if disknum is a number, then it should be compared with actual_disknum
actual_disknum=`echo "$slocs"| $awk '{print NF}'`
slocs=`compare_disknum_actualnum $disknum $actual_disknum $slocs`
else
# find the slocs for disks in disknames
disklist=`echo $disknames|sed 's/#/ /g'`
slocs_grp=`convert_sg_to_sloc $disklist`
slocs=`echo "$slocs_grp"|awk -F= '{print $2}'`
fi
# check if target disks were already in a reusable array.
# if not, create one by calling block command "create_ipr_array".
# after than, check again to confirm if the target array was ready.
array_sloc=""
((__i=0))
while [ $__i -lt 2 ];
do
# get disk --> array association
# format 0:0:0:0=sg0
declare array_slocs_lines=`for sloc in $slocs
do
line=$(get_ipr_array_by_member $sloc)
echo "$sloc=$line"
done`
# extract arrays from associations
# get sg0 sg1
declare array_slocs=`echo "$array_slocs_lines" \
| $awk -F= '{print $2}' | $sort -u`
# count the number of arrays on target disks
# which might include "blank" array
declare n_arrays_lines=`echo "$array_slocs" | $awk 'END {print NR}'`
# count the number of arrays on target disks, again
# w/o "blank" array
declare n_arrays=`echo "$array_slocs" | grep -v "^ *$" | $awk 'END {print NR}'`
# get the sg names for owner arrays.
declare array_sgs=""
if [ $n_arrays -gt 0 ]; then
array_sgs=`convert_sloc_to_sg $array_slocs \
| $awk -F= '{print $2}' `
fi
# (cond_1): if ALL target disks are in ONE array, reuse it.
if [ $n_arrays_lines -eq 1 -a $n_arrays -eq 1 ]; then
if [ $__i -eq 0 ]; then
log_info "Target disks, $slocs, were already in an array, $array_slocs." >&2
else
log_info "An array, $array_slocs, had been created to include target disks, $slocs" >&2
fi
lines=`{ cmd_show_details $array_sgs;
cmd_show_status $array_sgs;
} | $sed -e 's/^/>> /g'`
log_lines details "$lines"
array_sloc="$array_slocs"
break
fi
# (cond_2): delete arrays which owe target disks first before any future operations.
# NOTE: we redo the loop after "delete" command was issued to ipr
if [ $n_arrays -gt 0 ]; then
log_info "Target disks, $slocs, were owned by multiple arrays, \"$array_slocs\"($array_sgs). Delete them first before creating a new array on top of them." >&2
delete_ipr_array enforce=1 tryCnt=360 tryInt=60 -- $array_sgs
continue
fi
# (cond_3): create an array to include all target disks
# we assume all these disks are ready to be included into an array.
if [ $__i -eq 0 ]; then
declare sgs=`convert_sloc_to_sg $slocs \
| $awk -F= '{print $2}' | $sed -e 's/ /,/g'`
# Times to wait for the raid creation finish
# The lager capacity of member disks, longer times to wait.
# :unit minutes
monTime=240
# Interval of minitoring raid creation, status check
# :unit seconds
monInt=30
monCnt=`$awk -v monInt=$monInt -v monTime=$monTime 'END{print 60*monTime/monInt}' </dev/null`
log_status "Creating RAID $raidlevel array on top of disks \""`echo "$slocs" | $sed -e 's/ /,/g'`"\"/\"$sgs\"..." >&2
# handle striple_size
if [ "x$striple_size" == "xdefault" ]; then
create_ipr_array $monCnt $monInt "$sgs" -r $raidlevel
elif [ -z $striple_size ]; then
create_ipr_array $monCnt $monInt "$sgs" -r $raidlevel
else
create_ipr_array $monCnt $monInt "$sgs" -r $raidlevel -s $striple_size
fi
fi
((__i+=1))
done
lines=`cmd_show_config`
if [ $__i -lt 2 -a -n "$array_sloc" ]; then
array_sd=`echo "$lines" \
| grep '^sd[a-z]\+[ ]\+.*\/'$array_sloc'[ ]\+.*Disk Array' \
| $awk '{print $1}'`
if [ -n "$array_sd" ]; then
log_info "array_dev=$array_sd"
return 0
else
log_error "Fail to get scsi device name for new array $array_sloc" >&2
echo "$lines" | $sed -e 's/^/>> /g' | log_lines error >&2
return 1
fi
else
log_error "Fail to create array on top of disks $slocs" >&2
echo "$lines" | $sed -e 's/^/>> /g' | log_lines error >&2
return 1
fi
}
############################################################################
#
# handle disk_num
# if disknum is all, do nothing
# if disknum is a number, then it should be compared with actual_disknum
# return <disknum> of slocs
#
##############################################################################
function compare_disknum_actualnum {
local disknum=$1
local actualnum=$2
shift;
shift;
local slocs=$*
local actualslocs
if [ "x$disknum" != "xall" ]; then
if [ $actualnum -ge $disknum ]; then
actualslocs=`echo "$slocs"| \
awk -v anum=$disknum '{ for ( i=1; i<=anum; i++ ){print $i}}'`
echo $actualslocs
else
log_error "Could not find enough disk on target adapter ${pciloc}!" >&2
fi
else
echo "$slocs"
fi
}
############################################################################
#
# Delete raid array
#
# input format : enforce=1 tryCnt=2 tryInt=60 -- arrays_name list
#
##########################################################################
function delete_ipr_array {
# default to NO if enforcely wait for rebuilding array
local enforce=0
# default to wait 2 minutes for array rebuilding to finish
local tryCnt=2
# monitor rebuilding status every 60 seconds
local tryInt=60
# parser input arguments
while [ -n "$1" ];
do
local key=`echo "$1" | $cut -s -d= -f1`
if [ "$key" = "tryCnt" ] || \
[ "$key" = "tryInt" ] || \
[ "$key" = "enforce" ]; then
local val=`echo "$1" | $cut -s -d= -f2-`
eval "$key=\"$val\""
elif [ "$1" = "--" ]; then
shift
arrays="$*"
break
fi
shift
done
# should not with empty array list, if yes, error with rc=1
if [ -z "$arrays" ]; then
log_error "No array to delete, assume success!" >&2
return 0
fi
local array_slocs=`convert_sg_to_sloc $arrays | awk -F= '{print $2}'`
# if none of arrays were valid, error with rc=1
if [ -z "$array_slocs" ]; then
log_error "Fail to map arrayes, $arrays, to scsi location before deletion!"
return 1
fi
local ioas=`get_ipr_ioas`
local i=1
while [ $i -le $tryCnt ];
do
# verify if all target arrays are still exists
local tmp_array_slocs=`for _tmp_sloc in $array_slocs
do
is_ipr_array $_tmp_sloc && echo "$_tmp_sloc"
done `
if [ "$tmp_array_slocs" != "$array_slocs" ]; then
array_slocs="$tmp_array_slocs"
log_details "Renew available target arrays which will be deleting, \"$array_slocs\"" >&2
fi
if [ -z "$array_slocs" ]; then
log_details "Round $i: All target arrays were gone!" >&2
break
fi
# find out sg devs for all deletable arrays
local array_slocs_deletable=`for ioa in $ioas
do
# [root@dn01-dat ~]# iprconfig -c query-raid-delete sg5
# Name PCI/SCSI Location Description Status
# ------ ------------------------- ------------------------- -----------------
# sdk 0003:70:00.0/0:2:0:0 RAID 0 Disk Array Optimized
# sdl 0003:70:00.0/0:2:1:0 RAID 0 Disk Array Optimized
cmd_query_raid_delete $ioa \
| grep "Disk Array" \
| cut_sloc_from_iprconfig_line
done`
if [ -z "$array_slocs_deletable" -a $enforce -eq 0 ]; then
log_details "Round $i: No deletable array, nothing to do and finish." >&2
break
fi
# log for debug
{
echo "$array_slocs_deletable" | $sed -e 's/^/Round '$i': Deletable array >> /g'
cmd_show_config | $sed -e 's/^/Round '$i': ipr info before delete >> /g'
} | log_lines details >&2
log_info "Round $i: delete_ipr_array, \"$array_slocs\"" >&2
# if requested array is a deletable array, mark it to be deleted in this round.
local array_slocs_verified=`for array_sloc in $array_slocs
do
if echo "$array_slocs_deletable" | grep -sq "^$array_sloc$"; then
echo $array_sloc
else
log_warn "Round $i: Array $array_sloc is un-deletable at present." >&2
fi
done`
if [ -n "$array_slocs_verified" ]; then
log_details "Round $i: Verified array slocs for deletion are \"$array_slocs_verified\"." >&2
else
log_info "Round $i: All remaining target arrayes are un-deletable now." >&2
if [ $enforce -eq 1 ]; then
log_info "Round $i: Wait for these un-deletable arrayes deletable with tryCnt=$tryCnt, tryInt=$tryInt." >&2
wait_for_ipr_device_status \
tryCnt=$tryCnt tryInt=$tryInt cmd="grep -sq -E 'Optimized'" \
-- \
`echo "$array_slocs" | $sed -e "s/ /,/g"`
# if wait succ, let's try to delete again
if [ $? -eq 0 ]; then
((i+=1))
continue
else
break
fi
fi
fi
# issue delete command
local dcnt=0
for array_sloc in $array_slocs_verified
do
local array=`convert_sloc_to_sg $array_sloc | awk -F= '{print $2}'`
[ -z "$array" ] && continue
log_details "Round $i: Delete $dcnt array $array=$array_sloc" >&2
cmd__raid_delete $array
((dcnt+=1))
done
# break out the loop forcely if nothing deleted.
[ $dcnt -eq 0 ] && break
# wait for next round
$sleep $tryInt
((i+=1))
done
test $i -lt $tryCnt -a -z "$array_slocs"
return $?
}
#################################################
#
# sort devices by resource path,
#
# input: dev list, "sg0,..sgn"
#
##################################################
function sort_devices_by_resource_path {
local sgs="$*"
local del=","
if [ $# -gt 1 ]; then
del=" "
else
sgs=`echo "$sgs" | $sed -e "s/,/ /g"`
fi
# [root@dn01-dat ~]# iprconfig -c show-alt-config
# Name Resource Path/Address Vendor Product ID Status
# ------ -------------------------- -------- ---------------- -----------------
# sg15 FE IBM 57D7001SISIOA Operational
# sg10 00-0C IBM HUC101212CSS600 Active
# sg11 00-0D IBM HUC101212CSS600 Active
local lines=`$iprconfig -c show-alt-config | grep "^sg"`
local item
local lines_out=`for item in $sgs
do
local sg=$(convert_sloc_to_sg $item | $awk -F= '{print $2;}')
local rp=$(echo "$lines" | $awk -vsg=$sg '$1 == sg { print $2; }')
echo "$item $sg $rp"
done \
| $sort -t' ' -k3`
# log for debug
{
echo "sort devices \"$sgs\" by IPR resource path:"
echo "$lines_out" | $sed -e 's/^/sort by rp output: >> /g'
} | log_lines details >&2
# deliver ordered result
lines_out=`echo "$lines_out" | $awk '{print $1;}' `
if [ "$del" = "," ]; then
echo "$lines_out" | $sed -e "s/ /,/g"
fi
return 0
}
##########################################################################################################################
#
# wait to check ipr device status
# usage: wait_for_ipr_device_status tryCnt=<minitutes> tryInt=<interval_seconds> cmd="check_cmd_line" -- "sg0,sg1,...,sgn"
#
#########################################################################################################################
function wait_for_ipr_device_status {
local tryCnt=1
local tryInt=10
local cmd="true"
# parser input arguments
while [ -n "$1" ];
do
local key=`echo "$1" | $cut -s -d= -f1`
if [ "$key" = "tryCnt" ] || \
[ "$key" = "tryInt" ] || \
[ "$key" = "cmd" ]; then
local val=`echo "$1" | $cut -s -d= -f2-`
eval "$key=\"$val\""
elif [ "$1" = "--" ]; then
shift
sgs="$*"
break
fi
shift
done
sgs=`echo "$sgs" | $sed -e "s/,/ /g"`
local cnt=0
local lines
while [ $cnt -lt $tryCnt -o $tryCnt -le 0 ];
do
# sleep for specific interval for next cycle
[ $cnt -gt 0 ] && $sleep $tryInt
lines=`check_ipr_device_status $sgs`
local status_lines=""
local neg=0
local sg
for sg in $sgs
do
local _sg_status=`echo "$lines" | grep "^$sg=" | $cut -d= -f2- -s`
echo "$_sg_status" | eval $cmd >&2
if [ $? -ne 0 ]; then
neg=1
status_lines="${status_lines}${status_lines:+,}status[$sg]=\"$_sg_status\""
fi
done
# break out if NO negative matching
# or, try next loop
if [ $neg -eq 0 ]; then
break
else
log_status "Wait for device status at time \""`date "+%Y-%m-%d %H:%M:%S"`"\": $status_lines, expect: \"$cmd\"."
fi
((cnt+=1))
done
test $cnt -lt $tryCnt -o $tryCnt -le 0
local rc=$?
# log for debug
if [ $rc -eq 0 ]; then
log_info "Wait for status on devices: \"$sgs\" succeed! (expected: \"$cmd\")"
else
log_warn "Wait for status on devices: \"$sgs\" failed! (expected: \"$cmd\")"
echo "$lines" | $sed -e 's/^/last device status: >> /g' | log_lines debug
fi
return $rc
}
##############################################################
#
# check status of ipr devices
# input : slocs list, for example, 0:0:0:0 0:0:1:0
# usage : check_ipr_device_status <sloc1> <sloc2> ... <slocn>
#
##############################################################
function check_ipr_device_status {
local sgs="$*"
local lines=`convert_sloc_to_sg $sgs`
local item
for item in $sgs
do
declare sg=`echo "$lines" | $awk -F= -vkey=$item '$1 == key {print $2;}'`
declare _sg_status=`$iprconfig -c status $sg`
if [ "$_sg_status" = "Rebuilding" ]; then
declare _sg_status_alt=`cmd_alt_status $sg`
[ -n "$_sg_status_alt" ] && _sg_status="$_sg_status, $_sg_status_alt"
fi
echo "$item=$_sg_status"
done
return 0
}
######################################################
#
# log lines
#
# input : details or status or error
#
######################################################
function log_lines {
local pcnt=$#
local __level=$1
shift
local cmd=log_${__level}
local hit=0
local OIFS=$IFS
local NIFS=$'\n'
IFS=$NIFS
local __msg
for __msg in $*
do
IFS=$OIFS
$cmd "$__msg"
hit=1
IFS=$NIFS
done
IFS=$OIFS
[ $hit -eq 0 -a $pcnt -le 1 ] && \
while read __msg;
do
$cmd "$__msg"
done
}
######################################################
#
# error information
#
######################################################
function log_error {
local __msg="$*"
$log_print_cmd $log_print_arg "[E]: $__msg" >&2
return 0
}
######################################################
#
# warning information
#
# input : message
#
######################################################
function log_warn {
local __msg="$*"
$log_print_cmd $log_print_arg "[W]: $__msg" >&2
return 0
}
######################################################
#
# log information
#
# input : message
#
######################################################
function log_info {
local __msg="$*"
$log_print_cmd $log_print_arg "[I]: $__msg" >&2
return 0
}
######################################################
#
# more details information
#
######################################################
function log_details {
local __msg="$*"
$log_print_cmd $log_print_arg "[I]: $__msg" >&2
return 0
}
####################################################
#
# print command status
#
###################################################
__my_log_status=
function log_status {
local __msg="$*"
$log_print_cmd $log_print_arg "[S]: $__msg" >&2
# call my_log_status hook to triger more processing for status messages.
if [ -n "$__my_log_status" ]; then
$__my_log_status "$__msg"
fi
return 0
}
####################################################
#
# print output and logfile
#
###################################################
function log_print_default {
echo $*
echo $* >> $log_file
}
####################################################
#
# handle command
#
###################################################
function set_log_print {
local cmd="$1"
if [ -z "$cmd" ]; then
return 1
fi
shift
local args="$*"
eval "log_print_cmd=\"$cmd\""
eval "log_print_arg=\"$args\""
}
# let log work
[ -z "$log_print" ] && set_log_print "log_print_default"