#!/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"