#!/bin/bash

set -e
#set -x

# Make a CSV file of VD (Megacli Virtual Disk) and the PD (Megacli Physical Drives) they use
# and "guess" the matching device name (by using a counter).
megacli_vd_list () {
	VD_LIST=$(mktemp)
	F_PDINFO=$(mktemp)
	megacli -LdPdInfo -a0 -NoLog | grep -E '^Virtual Drive:|^Slot Number:' >${F_PDINFO}

	echo "vd,pd,drive" >${VD_LIST}
	COUNT=-1
	while read line ; do
		# If the line contains : (ie: semicolumn) then it can be parsed
		if echo ${line} | grep -q -E "^Virtual Drive:" ; then
			VD_NUM=$(echo ${line} | sed "s/^Virtual Drive: //" | cut -d' ' -f1)
			COUNT=$((${COUNT} + 1))
		fi
		if echo ${line} | grep -q -E "^Slot Number:" ; then
			SLOT=$(echo "${line}" | sed "s/Slot Number: //")
			case ${COUNT} in
			0) D=sda ;;
			1) D=sdb ;;
			2) D=sdc ;;
			3) D=sdd ;;
			4) D=sde ;;
			5) D=sdf ;;
			6) D=sdg ;;
			7) D=sdh ;;
			8) D=sdi ;;
			9) D=sdj ;;
			10) D=sdk ;;
			11) D=sdl ;;
			12) D=sdm ;;
			13) D=sdn ;;
			14) D=sdo ;;
			15) D=sdp ;;
			16) D=sdq ;;
			17) D=sdr ;;
			18) D=sds ;;
			19) D=sdt ;;
			20) D=sdu ;;
			21) D=sdv ;;
			22) D=sdw ;;
			23) D=sdx ;;
			24) D=sdy ;;
			25) D=sdz ;;
			esac
			echo ${VD_NUM},${SLOT},${D} >>${VD_LIST}
		fi
	done <${F_PDINFO}
	rm -f ${F_PDINFO}
}
# Make a CSV file of the PD (Megacli Physical Disks) and their properties
megacli_pd_list () {
	PD_LIST=$(mktemp)

	ENCLOSURE=$(megacli -EncInfo -aALL -NoLog | awk '/Device ID/ {print $4}')

	echo "Slot,Model,Serial,Size,State,Spun" >${PD_LIST}
	for SLOT_NUM in $(megacli -pdlist -a0 -NoLog | grep "Slot Number:" | cut -d':' -f2 | awk '{print $1}') ; do
		SMT_OUT=$(mktemp)
		smartctl -a -d megaraid,${SLOT_NUM} /dev/sda >${SMT_OUT} || true
		MODEL=$(cat ${SMT_OUT} | grep "^Device Model" | sed 's/^Device Model:     //')
		SERIAL=$(cat ${SMT_OUT} | grep "^Serial Number" | sed 's/^Serial Number:    //')
		SIZE=$(cat ${SMT_OUT} | grep "^User Capacity:" | cut -d'[' -f2 | cut -d']' -f1)
		rm -f ${SMT_OUT}
		FULL_STATE=$(megacli -PDInfo -PhysDrv [${ENCLOSURE}:${SLOT_NUM}] -aALL -NoLog | grep -E '^Firmware state: ' | sed 's/Firmware state: //')
		STATE=$(echo ${FULL_STATE} | cut -d, -f1)
		SPUN=$(echo ${FULL_STATE} | cut -d, -f2 | awk '{$1=$1;print}')
		echo "\"${SLOT_NUM}\",\"${MODEL}\",\"${SERIAL}\",\"${SIZE}\",\"${STATE}\",\"${SPUN}\"" >>${PD_LIST}
	done
}


show_disk_status () {
	if [ ""$(lshw -class storage -json 2>/dev/null | jq -r '.[]["id"]' | head -n1) = "raid" ] ; then
		# Make a VD list CSV with output: vd,pd,drive
		megacli_vd_list
		# Make PD list CSV with output: Slot,Model,Serial,Size,State,Spun
		megacli_pd_list
		# LEFT JOIN both with a single q command
		(echo "PD,VD,Drive,Model,Serial,Size,State,Spun";q -H -d, "SELECT myfiles.Slot,pdvd.vd,pdvd.Drive,myfiles.Model,myfiles.Serial,myfiles.Size,myfiles.State,myfiles.Spun FROM ${PD_LIST} myfiles LEFT JOIN ${VD_LIST} pdvd ON (myfiles.Slot = pdvd.pd) ORDER BY pdvd.vd")
		# rm both temp CSV files
		rm ${VD_LIST}
		rm ${PD_LIST}
	else
#		echo "No raid card"
		echo "devname,Logical_ID,Size,Model,Serial"
		for i in $(ls -v /dev/disk/by-path/ | grep -v part | grep -v usb) ; do
			dsize=""
			dmodel=""
			dserial=""
			LOGICAL_ID=$(echo $i | cut -d: -f3)
			DISK_NAME=$(basename $(realpath /dev/disk/by-path/$i))

			MY_SMARTCTL_TMP=$(mktemp -t oci-hdd-maint-smartctl.XXXXXX)
			if [ ""$(lsscsi -g | grep /dev/${DISK_NAME} | awk '{print $3}') = "HPE" ] ; then
				DEVID=$(lsscsi | grep /dev/${DISK_NAME} | cut -d] -f1 | cut -d: -f4)
				SMARTCTL_MINUS_D_OPT="-d cciss,${DEVID}"
			else
				SMARTCTL_MINUS_D_OPT=""
			fi
			smartctl ${SMARTCTL_MINUS_D_OPT} -a /dev/${DISK_NAME} >${MY_SMARTCTL_TMP} || true

			dmodel=$(cat ${MY_SMARTCTL_TMP} | grep "Device Model" | sed 's/Device Model:     //')
			if [ -z "${dmodel}" ] ; then
				dmodel=$(cat ${MY_SMARTCTL_TMP} | grep "Product:" | sed 's/Product:              //')
			fi
			dserial=$(cat ${MY_SMARTCTL_TMP} | grep "Serial Number" | sed 's/Serial Number:    //')
			if [ -z "${dserial}" ] ; then
				dserial=$(cat ${MY_SMARTCTL_TMP} | grep "Serial number:" | sed 's/Serial number:        //')
			fi
			dsize=$(cat ${MY_SMARTCTL_TMP} | grep "User Capacity" | cut -d'[' -f2 | cut -d']' -f1 | sed 's#,#.#')
			echo ${DISK_NAME},${LOGICAL_ID},$dsize,$dmodel,$dserial
			rm ${MY_SMARTCTL_TMP}
		done
	fi
}

replace_disk () {
	OLD_DISK=$1
	NEW_DISK=$2
	OLD_UUID=$(cat /etc/fstab | grep /srv/node/${OLD_DISK} | awk '{print $1}' | sed -e 's/[#]*UUID=//')
	echo "===> Formating /dev/${NEW_DISK} with UUID ${OLD_UUID}"
	mkfs.xfs -f -m uuid=${OLD_UUID} /dev/${NEW_DISK}
	echo "===> Uncommenting from fstab"
	sed -i "s/[#]*UUID=${OLD_UUID}/UUID=${OLD_UUID}/" /etc/fstab
	echo "===> Mounting /srv/node/${OLD_DISK}"
	mount /srv/node/${OLD_DISK}
	echo "===> Changing disk's owner"
	chown swift:swift /srv/node/${OLD_DISK}
}

usage () {
	echo "                     -l : list devices (columns output)"
	echo "                   -csv : list devices (csv output)"
	echo "                -s <ID> : setup RAID device"
	echo "            -y <ID/sdX> : prepare disk for removal for HDD in slot <ID> or /dev/sdX"
	echo "                          (ie: megacli or hdparm commands)"
	echo "                -u <ID> : stop blinking LED for HDD in slot <ID>"
	echo "-r <hold-hdd> <new-hdd> : format and mount <new-hdd> to replace <old-hdd>"
	exit 1
}

if [ "${1}" = "-csv" ] ; then
	show_disk_status
	exit 0
else
	case ${1} in
	"-s")
		if [ -z "${2}" ] ; then
			usage
		fi
		if [ ""$(lshw -class storage -json 2>/dev/null | jq -r '.[]["id"]' | head -n1) = "raid" ] ; then
			echo "Setting up RAID for device ${2}..."
			megacli -DiscardPreservedCache -Lall -a0
			megacli -CfgLdAdd -r0[32:${2}] WB RA Direct -a0
		else
			echo "Didn't detect hardware RAID"
			exit 1
		fi
		;;
	"-l")
		$0 -csv | csvlook --blanks -I
		PRODUCT_NAME=$(dmidecode -s system-product-name)
		case "${PRODUCT_NAME}" in
		"PowerEdge R720xd"|"PowerEdge R740xd")
			echo "HDD (PD) disposition:"
			echo "Col1,Col2,Col3,Col4
0,3,6,9
1,4,7,10
2,5,8,11" | csvlook --blanks -I
		;;
		"CL2800 Gen10")
			echo "HDD disposition:"
			echo "Col1,Col2,Col3,Col4
sda,sdb,sdc,sdd
sdg,sdh,sdi,sdj
sdk,sdl,sdm,sdn" | csvlook --blanks -I
			echo "Sometimes, with inverted cabling:"
			echo "Col1,Col2,Col3,Col4
sdg,sdh,sdi,sdj
sdk,sdl,sdm,sdn
sda,sdb,sdc,sdd" | csvlook --blanks -I
		;;
		*)
		;;
		esac
		exit 0
		;;
	"-r")
		if [ -z "${2}" ] ; then
			usage
		fi
		if [ -z "${3}" ] ; then
			usage
		fi
		replace_disk ${2} ${3}
		;;
	"-y")
		if [ ""$(lshw -class storage -json 2>/dev/null | jq -r '.[]["id"]' | head -n1) = "raid" ] ; then
			ENCLOSURE=$(megacli -EncInfo -aALL -NoLog | awk '/Device ID/ {print $4}')
			case ${2} in
			0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63)
				echo "Powering off the drive in slot ${2}"
				megacli -PDOffline -PhysDrv [${ENCLOSURE}:${2}] -aall
				echo "Prepare the drive in slot ${2} for removal"
				megacli -PDPrpRmv -PhysDrv [${ENCLOSURE}:${2}] -aall
				echo "Making the LED of HDD ${2} blink."
				megacli -PdLocate -start -PhysDrv [${ENCLOSURE}:${2}] -aall
				exit 0
			;;
			*)
				echo "HDD slot not recognized: exiting."
				exit 1
			;;
			esac
		else
			case ${2} in
			sda|sdb|sdc|sdd|sde|sdf|sdg|sdh|sdi|sdj|sdk|sdl|sdm|sdn|sdo|sdp|sdq|sdr|sds|sdt|sdu|sdv|sdw|sdx|sdy|sdz)
				echo "Force ${2} to immediately enter the low power consumption standby mode, usually causing it to spin down."
				hdparm -y /dev/${2} || true
				VENDOR=$(dmidecode -s system-manufacturer)
				if [ "${VENDOR}" = "Dell Inc." ] && [ $(racadm get storage.Controller.1 | grep -i NonRAID | cut -d= -f2 | cut -d'#' -f1) = NonRAID.Integrated.1-1 ] ; then
					case ${2} in
					sda) ID="Disk.Bay.0:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdb) ID="Disk.Bay.1:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdc) ID="Disk.Bay.2:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdd) ID="Disk.Bay.3:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sde) ID="Disk.Bay.4:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdf) ID="Disk.Bay.5:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdg) ID="Disk.Bay.6:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdh) ID="Disk.Bay.7:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdi) ID="Disk.Bay.8:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdj) ID="Disk.Bay.9:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdk) ID="Disk.Bay.10:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdl) ID="Disk.Bay.11:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdm) ID="Disk.Bay.12:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdn) ID="Disk.Bay.13:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdo) ID="Disk.Bay.14:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdp) ID="Disk.Bay.15:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdq) ID="Disk.Bay.16:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdr) ID="Disk.Bay.17:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sds) ID="Disk.Bay.18:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdt) ID="Disk.Bay.19:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdu) ID="Disk.Bay.20:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdv) ID="Disk.Bay.21:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdw) ID="Disk.Bay.22:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdx) ID="Disk.Bay.23:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdy) ID="Disk.Bay.24:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					sdz) ID="Disk.Bay.25:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
					*) exit 0;;
					esac
					racadm storage blink:${ID}
				fi
				exit 0
			;;
			*)
				echo "HDD name not recognized: exiting."
				exit 1
			;;
			esac
		fi
		;;
	"-u")
		if [ ""$(lshw -class storage -json 2>/dev/null | jq -r '.[]["id"]' | head -n1) = "raid" ] ; then
			ENCLOSURE=$(megacli -EncInfo -aALL -NoLog | awk '/Device ID/ {print $4}')
			case ${2} in
			0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63)
				echo "Stop the blinking of the LED of HDD ${2}."
				megacli -PdLocate -stop -PhysDrv [${ENCLOSURE}:${2}] -aall
				exit 0
			;;
			*)
				echo "HDD slot not recognized: exiting."
				exit 1
			;;
			esac
		else
			VENDOR=$(dmidecode -s system-manufacturer)
			if [ "${VENDOR}" = "Dell Inc." ] && [ $(racadm get storage.Controller.1 | grep -i NonRAID | cut -d= -f2 | cut -d'#' -f1) = NonRAID.Integrated.1-1 ] ; then
				case ${2} in
				sda) ID="Disk.Bay.0:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdb) ID="Disk.Bay.1:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdc) ID="Disk.Bay.2:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdd) ID="Disk.Bay.3:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sde) ID="Disk.Bay.4:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdf) ID="Disk.Bay.5:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdg) ID="Disk.Bay.6:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdh) ID="Disk.Bay.7:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdi) ID="Disk.Bay.8:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdj) ID="Disk.Bay.9:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdk) ID="Disk.Bay.10:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdl) ID="Disk.Bay.11:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdm) ID="Disk.Bay.12:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdn) ID="Disk.Bay.13:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdo) ID="Disk.Bay.14:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdp) ID="Disk.Bay.15:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdq) ID="Disk.Bay.16:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdr) ID="Disk.Bay.17:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sds) ID="Disk.Bay.18:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdt) ID="Disk.Bay.19:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdu) ID="Disk.Bay.20:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdv) ID="Disk.Bay.21:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdw) ID="Disk.Bay.22:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdx) ID="Disk.Bay.23:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdy) ID="Disk.Bay.24:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				sdz) ID="Disk.Bay.25:Enclosure.Internal.0-1:NonRAID.Integrated.1-1" ;;
				*) exit 0;;
				esac
				racadm storage unblink:${ID}
				exit 0
			else
				echo "This is not possible for non-raid config."
				exit 1
			fi
		fi
		;;
	*)
		usage
		;;
	esac
fi
