#!/usr/bin/env sh
# set -x

# Covert all upper case character to lower case characters.
upperToLower() {
  lowercase=$(echo $1 | awk 'BEGIN { getline; print $1=tolower($1) }')
}

_LOGFILE=/var/cpq/Component.log

# Return codes
SUCCESS_NO_REBOOT=0
SUCCESS_WITH_REBOOT=1
VERSION_MATCH=2
INSTALL_NOT_ATTEMPTED=3
INSTALL_FAILED=7

# Variables
SC_RESULT_FAIL=0
SC_RESULT_PASS=0
COUNT=0
discoveryFlag=0
forceFlag=0
silentFlag=0
downgradeFlag=0
rewriteFlag=0

ARGUS=$@
NARGS=$#
_VER=
_REV=
_COMP=
_DESCRIPT=
_FILENAME=
_COMPVERBOSE=

#parseCmdArgs() {
#echo "parseCmdArgs"
for arg in "$@"
do
    case $arg in
        -s|--silent)
            silentFlag=1
            #echo "silent flag enabled"
            shift
        ;;
        -f|--force)
            forceFlag=1
            #echo "force flag enabled"
            shift
        ;;
        -g|--downgrade)
            downgradeFlag=1
            echo "downgrade flag enabled"
            shift
        ;;
        -e|--rewrite)
            rewriteFlag=1
            echo "rewrite flag enabled"
            shift
        ;;
        -d|--discovery)
            discoveryFlag=1
            xmlFile="$2"
            #echo "Discovery flag enabled"
            #echo "Discovery file Path: $xmlFile"
            shift
        ;;
        --default)
          echo "Invalid argument: $1"
        ;;
    esac
done

#}

# Write information to stdout and the log file.
output_CmpLog() {
  if [ 1 -eq $silentFlag ]; then
    echo "$1" >> $_LOGFILE
  else
    echo "$1" | tee -a $_LOGFILE
  fi
}

# Write information to discovery xml file.
printXML() {
  if [ ! -x $1 ]; then
    #echo -e $1 "$2" | tee -a $xmlFile
    echo -e $1 "$2" >> $xmlFile
  else
    #echo "$2" | tee -a $xmlFile
    echo "$2" >> $xmlFile
  fi
}

file="unknown"
# Search the specified directory (i.e. $1) for a specific file or file type (i.e. $2).
directory_listing() {
  file=""
  file=$(find "$1" -type f -iname "$2" | sed -e 's/^\.\///' 2> /dev/null)  
  number_of_files=$(echo $file | awk ' { print split($0, ARRAY_OF_FILES, "[[:space:]]+") } ')
  # Verify that the file variable is not an empty string.
  if [ 0 -eq $number_of_files ]; then
    output_CmpLog "Error: Could not find any Smart Component XML file, please install at least one Smart Component package and re-run the script."
    output_CmpLog "Script Exiting."
    output_CmpLog "Exit Status: $INSTALL_NOT_ATTEMPTED"
    exit $INSTALL_NOT_ATTEMPTED
  fi
  _VER=`grep \<version $file|awk -F = '{print $2}'|awk '{print $1}'|tr -d '"'`
  _COMP=`grep \<filename\> $file|awk -F '>' '{print $2}'|awk -F '<' '{print $1}'|tr -d '"'`
  _DESCRIPT=`grep \<alt_name_xlate\ lang\=\"en\" $file|awk -F '>' '{print $2}'|awk -F '<' '{print $1}'|sed -e 's/\"//g'|tail -n 1`
  _FILENAME="${_COMP%.*}"
  
  output_CmpLog "Run with $NARGS Command Arguments(s): $ARGUS"
  output_CmpLog ""
  output_CmpLog "$_COMP - $_DESCRIPT"
  output_CmpLog "    Component Version: $_VER"
}

# Check to see if the package installed successfully.
didPackageInstall() {
   upperToLower "$1"
   if echo "$results" | grep -w -q 'error\|fail'; then
     output_CmpLog "Error: The $package failed to install!"
     output_CmpLog "Exit Status: $INSTALL_NOT_ATTEMPTED"
     exit $INSTALL_NOT_ATTEMPTED
   fi
}

getOSPlatform() {
#echo "getOSPlatform"
# Determine what type of OS platform the script is running on and Storcli packages installation.
if [[ -f "/etc/redhat-release" || -f "/etc/SuSE-release" ]]; then
  output_CmpLog "Info: You are running on an RPM based distribution!"
  RPMNAME=storcli-*.noarch.rpm
  chmod +x ${RPMNAME}
  if ! rpm -qa | grep storclio; then	
    rpm_install=$(rpm -ivh --replacepkgs --replacefiles "$RPMNAME" 2> /dev/null)    
    didPackageInstall $rpm_install
    output_CmpLog "Storcli rpm installation completed."
  else    
    rpm_install=$(rpm -Uvh --replacepkgs --replacefiles "$RPMNAME" 2> /dev/null)  
    didPackageInstall $rpm_install
    output_CmpLog "${RPMNAME} installation done."
  fi
elif [ -f "/etc/debian_version" ]; then
  output_CmpLog "Info: You are running on an Debian based distribution!"
  DEBNAME=storcli_*.deb
  chmod +x ${DEBNAME}  
  sudo dpkg -i --force-architecture ${DEBNAME}
else
  output_CmpLog "Info: You are running on a VMWare distribution!"
  #esxcli software vib list
  vibname=`find $PWD -type f -iname "vmware-esx-storcli*.vib"`  
  esxcli software vib install -v="$vibname" --no-sig-check    
fi
}

getCmpXML() {
  directory_listing "." "[cC][Pp]0*.xml"
}

# This function Check to see if the file is readable.
isFileReadable() {
  #echo "isFileReadable"
  if [ ! -r $1 ]; then
    output_CmpLog "Error: $1 file is not readable, please make the file readable and re-run the script."
    output_CmpLog "Script exiting."
    output_CmpLog "Exit Status: $INSTALL_NOT_ATTEMPTED"
    exit $INSTALL_NOT_ATTEMPTED
  fi
}

locateStorcli() {
#echo "locateStorcli"
# Locate where the storcli utility is installed.
# Once the installation directory is found, split the string containing the filenames into an array.
storcli_array=$(find / -type f \( -name "storcli64" -o -name "storclio64" -o -name "storcli" \) 2> /dev/null)
# The following for loop iterates over each file, the first executable file that is found will be used as the storcli executable file.
for file_name in $storcli_array
do
  if [ -x "$file_name" ]; then
    storcli=$file_name
  fi
done

# Verify that the variable contains an executable file.
if [ ! -x "$storcli" ]; then
  output_CmpLog "Error: Cannot find the storcli utility, please install either the storcli or storcli64 utility."
  output_CmpLog "Script Exiting."
  output_CmpLog "Exit Status: $INSTALL_NOT_ATTEMPTED"
  exit $INSTALL_NOT_ATTEMPTED
fi
}

getCtrlCnt() {
#echo "getCtrlCnt"
# Find the number of supported controllers.
controller_count=$("$storcli" show ctrlcount)
# Verify that at least one supported controller is installed in the server.
num_controllers=$(echo $controller_count | grep -o -E '[0-9]+' | tail -1)
if [ 0 -eq $num_controllers ]; then

  if [ 1 -eq $discoveryFlag ]; then
    printXML '\t' "</devices>"
    printXML "" "</hp_rom_discovery>"
    exit $INSTALL_NOT_ATTEMPTED
  fi

  output_CmpLog "Error: No supported controllers found."
  output_CmpLog "Script exiting."
  output_CmpLog "Exit Status: $INSTALL_NOT_ATTEMPTED"
  exit $INSTALL_NOT_ATTEMPTED
fi

# Total number of supported controllers found.
output_CmpLog "Number of controllers found: $num_controllers"
}

# Get the target controller's firmware version.
getControllerFirmwareVersion() {
   ctrl_firmware_version=$("$storcli" /c"$controller_id" show | grep 'FW Package Build =' | awk '{print $5;exit;}')   
   # Verify that the variable is not empty.
   if [ -z $ctrl_firmware_version ]; then
     output_CmpLog "Error: Could not obtain the current firmware version for $target"
   fi
}

# Discover the devices
deviceDiscovery() {
  if [ 1 -eq $discoveryFlag ]; then  
    output_CmpLog "Generating discovery file at $xmlFile"
    printXML "" "<hp_rom_discovery version=\"2.0.2.0\">"
    printXML '\t' "<type value=\"firmware:sd\" />"
    printXML '\t' "<alt_name value=\"Smart Array P824i-p\" />"
    printXML '\t' "<version value=\"$xml_file_firmware_version\" />"
    printXML '\t' "<takes_effect value=\"immediate\" />"
    printXML '\t' "<devices>"
  fi
  # The following while loop iterates over each supported attached controller.
  controller_id=0
  while [ "$controller_id" -le $(($num_controllers-1)) ]
  do
    output_CmpLog "The controller ID is: $controller_id"    
    ctrl_model_name=$("$storcli" /c"$controller_id" show | grep 'Product Name =' | awk '{print $4, $5, $6, $7, $8, $9}')
    target="$ctrl_model_name"
	# Get the Sub Vendor id and Device id information that is currently on the controller.
	SubVendor_Id=$("$storcli" /c"$controller_id" show | grep 'SubVendor Id =' | awk '{print $4}')
	SubDevice_Id=$("$storcli" /c"$controller_id" show | grep 'SubDevice Id =' | awk '{print $4}')
	if [[ "$SubVendor_Id" == "0x1590" && "$SubDevice_Id" == "0x22C" ]]; then
      # Get the version of firmware that is currently on the controller.
      getControllerFirmwareVersion
	  if [ "$ctrl_firmware_version" \< "$xml_file_firmware_version" ]; then
        perform_action="upgrade"     
      elif [ "$ctrl_firmware_version" \> "$xml_file_firmware_version" ]; then     
        perform_action="downgrade"     
      else     
        perform_action="re-write"	  
      fi
   
      if [ $discoveryFlag -eq 1 ]; then
	    output_CmpLog "Discovered SubVendorId: $SubVendor_Id, SubDeviceId: $SubDevice_Id"   
        printXML '\t\t' "<device id=\"$target\">"
        printXML '\t\t\t' "<device_id value=\"$target\" />"
        printXML '\t\t\t' "<controller_id value=\"$target\" />"
        printXML '\t\t\t' "<product_id value=\"022C1590\" />"
        printXML '\t\t\t' "<fw_item>"
        printXML '\t\t\t\t' "<type value=\"firmware:smartarray\" />"
        printXML '\t\t\t\t' "<firmware_id value=\"\" />"
        printXML '\t\t\t\t' "<takes_effect value=\"immediate\" />"
        printXML '\t\t\t\t' "<version value=\"$xml_file_firmware_version\" />"
        printXML '\t\t\t\t' "<active_version value=\"$ctrl_firmware_version\" />"
        printXML '\t\t\t\t' "<action value=\"$perform_action\" />"
        printXML '\t\t\t\t' "<duration value=\"6\" />"
        printXML '\t\t\t\t' "<shared value=\"no\" />"
        printXML '\t\t\t' "</fw_item>"     
        printXML '\t\t' "</device>"
      fi
	fi
    
    controller_id=$(($controller_id + 1))
  done

  if [ $discoveryFlag -eq 1 ]; then
    printXML '\t' "</devices>"
    printXML "</hp_rom_discovery>"
    output_CmpLog "============ Summary ============"    
    output_CmpLog "Smart Component Finished"
    output_CmpLog "Exit Status: $INSTALL_NOT_ATTEMPTED"
    output_CmpLog "Device discovery complete"
    output_CmpLog "Update not attempted."
    exit $INSTALL_NOT_ATTEMPTED
  fi

}

flashDevices() {
  # The following while loop iterates over each supported attached controller.
  controller_id=0
  while [ "$controller_id" -le $(($num_controllers-1)) ]
  do
    # Get the Sub Vendor id and Device id information that is currently on the controller.
	SubVendor_Id=$("$storcli" /c"$controller_id" show | grep 'SubVendor Id =' | awk '{print $4}')
	SubDevice_Id=$("$storcli" /c"$controller_id" show | grep 'SubDevice Id =' | awk '{print $4}')
	if [[ "$SubVendor_Id" == "0x1590" && "$SubDevice_Id" == "0x22C" ]]; then
      output_CmpLog "The controller ID is: $controller_id" 
      output_CmpLog "Discovered SubVendorId: $SubVendor_Id, SubDeviceId: $SubDevice_Id"	  
      ctrl_model_name=$("$storcli" /c"$controller_id" show | grep 'Product Name =' | awk '{print $4,   $5, $6, $7, $8, $9}')    
      # Get the version of firmware that is currently on the controller.
      getControllerFirmwareVersion    
      output_CmpLog "Firmware binary: $PWD/$firmware_binary"
      flash "$ctrl_model_name" "$PWD/$firmware_binary"
	fi
    
    controller_id=$(($controller_id + 1))
  done
}

# Flash the Controller.
flash() {
   target=$1
   getControllerFirmwareVersion
   NoVerChk=""
   if [ "$ctrl_firmware_version" \< "$xml_file_firmware_version" ]; then
     perform_action="upgrade"
   elif [ "$ctrl_firmware_version" \> "$xml_file_firmware_version" ]; then
     NoVerChk="noverchk"
     perform_action="downgrade"
   else
     NoVerChk="noverchk"
     perform_action="re-write"
   fi

   upgrade_or_downgrade="firmware from $ctrl_firmware_version to $xml_file_firmware_version on $target"
   rewrite="firmware from $xml_file_firmware_version to $ctrl_firmware_version on $target"
   if [ "$perform_action" == "re-write" ]; then
     if [ $forceFlag -eq 0 ]; then
       if [ $rewriteFlag -eq 0 ]; then      
         output_CmpLog "Firmware versions are matching.Installation not attempted for controller ID is: $controller_id"
         return
        fi 
     fi
     output_CmpLog "Attempting to $perform_action $rewrite"
   elif [ "$perform_action" == "downgrade" ]; then
     if [ $forceFlag -eq 0 ]; then
       if [ $downgradeFlag -eq 0 ]; then
         output_CmpLog "Firmware is at latest version. Installation not attempted for controller ID is: $controller_id"
         return
        fi
     fi
     output_CmpLog "Attempting to $perform_action $upgrade_or_downgrade"
   else
     output_CmpLog "Attempting to $perform_action $upgrade_or_downgrade"
   fi

   output_CmpLog "This could take several minutes..."
   flash_flag="failed"
   "$storcli" /c"$controller_id" set ocr = on
   flash_status=$("$storcli" /c"$controller_id" download file=$2 "$NoVerChk") 
   "$storcli" /c"$controller_id" restart
   
   for status in $flash_status
   do
     if [ "$status" == "Success" ]; then
       flash_flag="succeeded"
     fi
   done

   sleep 20
   if echo "$flash_flag" | grep -q 'succeeded'; then
     flash_status_flag="no reboot required."
     if echo "$perform_action" | grep -q 'upgrade'; then
       performed_action="upgraded"
       output_CmpLog "Successfully $performed_action $upgrade_or_downgrade, $flash_status_flag"
     elif echo "$perform_action" | grep -q 'downgrade'; then
       performed_action="downgraded"
       output_CmpLog "Successfully $performed_action $upgrade_or_downgrade, $flash_status_flag"
     else
       performed_action="re-write"
       output_CmpLog "Successfully $performed_action $rewrite, $flash_status_flag"
     fi
     output_CmpLog "========================================================================================================================================"
     SC_RESULT_PASS=1
   else
     output_CmpLog "Error: Failed to flash $target"
     output_CmpLog "========================================================================================================================================"
     SC_RESULT_FAIL=1 
   fi

}


parseXML() {
  xml_file=$file
  #echo $xml_file
  isFileReadable $PWD/$xml_file
  # Extract the name of the firmware binary file from the XML file.
  firmware_binary=$(grep -o -E "<FileName>(.*)</FileName>" "$PWD/$xml_file" | sed 's/FileName//g' | sed 's/<//g' | sed 's/>//g' | sed 's/\///g' | head -1)
  if [ -z $firmware_binary ]; then
    output_CmpLog "No firmware binary file was found for $package"
    continue
  fi
  isFileReadable $PWD/$firmware_binary
  
  # Read from the smart component's xml file and obtain the firmware version.
  xml_file_firmware_version=$(grep 'version value=' "$xml_file" | head -1 | awk '{print $2}' | sed -e 's/value="\(.*\)"/\1/')    
}

main() {
  #parseCmdArgs
  getCmpXML  
  parseXML
  getOSPlatform
  locateStorcli
  getCtrlCnt  
  if [ $discoveryFlag -eq 1 ]; then
    deviceDiscovery
  else
    flashDevices
    output_CmpLog "============ Summary ============"
    output_CmpLog "Smart Component Finished"

    if [ 1 -eq $SC_RESULT_FAIL ]; then
      output_CmpLog "Exit Status: $INSTALL_FAILED"
      exit $INSTALL_FAILED
    elif [ 1 -eq $SC_RESULT_PASS ]; then
      output_CmpLog "Exit Status: $SUCCESS_NO_REBOOT"
      exit $SUCCESS_NO_REBOOT
    else
      output_CmpLog "Update not attempted (or interrupted). All selected devices are either up-to-date or have newer versions installed or Hardware not detected"
      output_CmpLog "Exit Status: $INSTALL_NOT_ATTEMPTED"
      exit $INSTALL_NOT_ATTEMPTED
    fi
  fi

}

main
