#!/bin/bash

set -e
#set -x

LOCKDIR=/var/run/lock
LOCKFILE=${LOCKDIR}/$(basename ${0})
if [ "${1}" = "--nolock" ] ; then
        shift
else
        logger -t "udev" "===> Claming lock for $0 in ${LOCKFILE}"
        if ! flock -w 120 -x ${LOCKFILE} $0 --nolock $@ ; then
                exit 1
        fi
        exit 0
fi

logger -t "udev" "===> DEVPATH=${DEVPATH} ID_BUS=${ID_BUS}"

# Script param (sent as env var):
# ID_BUS (example: scsi, ata)
# DEVPATH (example: /devices/pci0000:80/0000:80:08.2/0000:81:00.0/ata8/host7/target7:0:0/7:0:0:0/block/sdc)
#
# In fact, we see the variables as per:
# udevadm test -a -p  $(udevadm info -q path -n /dev/sdc)

CHASSIS_MANUFACTURER=$(dmidecode -s system-manufacturer)
CHASSIS_MODEL=$(dmidecode -s system-product-name)
CACHE_FILE=/run/oci-hdd-cache

case "${CHASSIS_MANUFACTURER}" in
"LinuxKVM"|"OpenStack Foundation")
	case "${CHASSIS_MODEL}" in
	"qemu-oci"|"OpenStack Nova")
		if [ -n "$ID_BUS" ] && [ "$ID_BUS" = "scsi" ]; then
			card=$(echo $DEVPATH | cut -d/ -f5)
			target=$(echo $DEVPATH | cut -d/ -f8)
			if [ "$card" = "virtio0" ] || [ "$card" = "virtio1" ] ; then
				case "${target}" in
				"2:0:0:0"|"0:0:0:0") disk="sda" ;;
				"2:0:1:0"|"2:0:0:1"|"0:0:1:0") disk="sdb" ;;
				"2:0:2:0"|"2:0:0:2"|"0:0:2:0") disk="sdc" ;;
				"2:0:3:0"|"2:0:0:3"|"0:0:3:0") disk="sdd" ;;
				"2:0:4:0"|"2:0:0:4"|"0:0:4:0") disk="sde" ;;
				"2:0:5:0"|"2:0:0:5"|"0:0:5:0") disk="sdf" ;;
				"2:0:6:0"|"2:0:0:6"|"0:0:6:0") disk="sdg" ;;
				"2:0:7:0"|"2:0:0:7"|"0:0:7:0") disk="sdh" ;;
				"2:0:8:0"|"2:0:0:8"|"0:0:8:0") disk="sdi" ;;
				"2:0:9:0"|"2:0:0:9"|"0:0:9:0") disk="sdj" ;;
				*) logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1 ;;
				esac
				logger -t "udev" "Adding symlink $disk for device $card-$target"
				echo $disk
				exit 0
			else
				logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
			fi
		else
			logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
		fi
	;;
	*)
		logger -t "udev" "Qemu chassis not supported, exit."
		exit 1
	;;
	esac
;;
"Dell Inc.")
	case "${CHASSIS_MODEL}" in
	"PowerEdge R410")
		# Here we have 2 internal SSDs on CDROM link and 4 SATA HDD
		if [ -n "$ID_BUS" ] && [ "$ID_BUS" = "ata" ]; then
			card=$(echo $DEVPATH | cut -d/ -f5)
			target=$(echo $DEVPATH | cut -d/ -f8)
			if   [ "$card" = "ata1" ] && [ "$target" = "0:0:0:0" ]; then disk="sda"
			elif [ "$card" = "ata1" ] && [ "$target" = "0:1:0:0" ]; then disk="sdb"
			elif [ "$card" = "ata2" ] && [ "$target" = "1:0:0:0" ]; then disk="sdc"
			elif [ "$card" = "ata3" ] && [ "$target" = "2:0:0:0" ]; then disk="sdd"
			elif [ "$card" = "ata4" ] && [ "$target" = "3:0:0:0" ]; then disk="sde"
			elif [ "$card" = "ata5" ] && [ "$target" = "4:0:0:0" ]; then disk="sdf"
			else logger -t "udev" "Device $DEVPATH detected but not supported."; exit 1
			fi
		else    
			logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
		fi
		logger -t "udev" "Adding symlink $disk for device $card-$target"
		echo "$disk"
		exit 0
	;;
	# This should work on an LSI raid thingy, so on all Dell provided
	# we have the same ID_BUS / DEVPATH thing.
	"PowerEdge R430"|"PowerEdge R610"|"PowerEdge R620"|"PowerEdge R630"|"PowerEdge R640"|"DSS 2500"|"DSS2500"|"PowerEdge R720xd"|"PowerEdge R6525")
		if [ -n "$ID_BUS" ] && [ "$ID_BUS" = "scsi" ]; then
			target=$(echo $DEVPATH | cut -d/ -f8)
			if   [ "$target" = "0:2:0:0" ]; then disk="sda"
			elif [ "$target" = "0:2:1:0" ]; then disk="sdb"
			elif [ "$target" = "0:2:2:0" ]; then disk="sdc"
			elif [ "$target" = "0:2:3:0" ]; then disk="sdd"
			elif [ "$target" = "0:2:4:0" ]; then disk="sde"
			elif [ "$target" = "0:2:5:0" ]; then disk="sdf"
			elif [ "$target" = "0:2:6:0" ]; then disk="sdg"
			elif [ "$target" = "0:2:7:0" ]; then disk="sdh"
			elif [ "$target" = "0:2:8:0" ]; then disk="sdi"
			elif [ "$target" = "0:2:9:0" ]; then disk="sdj"
			elif [ "$target" = "0:2:10:0" ]; then disk="sdk"
			elif [ "$target" = "0:2:11:0" ]; then disk="sdl"
			elif [ "$target" = "0:2:12:0" ]; then disk="sdm"
			else logger -t "udev" "Device $target detected but not supported."; exit 1
			fi
		else
			logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
		fi
		logger -t "udev" "Adding symlink $disk for device $card-$target"
		echo "$disk"
		exit 0
	;;
	# Example DEVPATH for this type of server:
	# sda:
	# DEVPATH=/devices/pci0000:00/0000:00:1f.2/ata10/host10/target9:0:0/9:0:0:0/block/sda
	# DEVPATH=/devices/pci0000:00/0000:00:1f.2/ata10/host10/target10:0:0/10:0:0:0/block/sda
	# sdb to sde:
	# DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/host0/target0:0:0/0:0:0:0/block/sda
	# DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/host0/target0:0:1/0:0:1:0/block/sdb
	# DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/host0/target0:0:2/0:0:2:0/block/sdc
	# DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/host0/target0:0:3/0:0:3:0/block/sdd
	# or:
	# DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/host0/target0:0:0/0:2:0:0/block/sda
	# DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/host0/target0:0:1/0:2:1:0/block/sdb
	# DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/host0/target0:0:2/0:2:2:0/block/sdc
	# DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/host0/target0:0:3/0:2:3:0/block/sdd
	# Example with R440 USB:
	# DEVPATH=/devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.0/host15/target15:0:0/15:0:0:0/block/sde
	"PowerEdge R420"|"PowerEdge R440"|"DSS1510"|"DSS 1510")
		card=$(echo $DEVPATH | cut -d/ -f5)
		host=$(echo $DEVPATH | cut -d/ -f6)
		target=$(echo $DEVPATH | cut -d/ -f8)
		case "${host}" in
		"usb2"|"1-1"|"2-1"|"2-8")
			case "${target}" in
			"host11"|"host15"|"1-1.4:1.0"|"2-1.2:1.0")	PHYDISK=sda	;;
			*)
				logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
			;;
			esac
		;;
		"host3"|"host9"|"host10")
			case "${target}" in
			"3:0:0:0"|"9:0:0:0"|"10:0:0:0")
				PHYDISK=sda
			;;
			*)
				logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
			;;
			esac
		;;
		"host0")
#			echo "In host0, target is ${target}"
			case "${target}" in
			"0:0:0:0"|"0:2:0:0")	PHYDISK=sdb	;;
			"0:0:1:0"|"0:2:1:0")	PHYDISK=sdc	;;
			"0:0:2:0"|"0:2:2:0")	PHYDISK=sdd	;;
			"0:0:3:0"|"0:2:3:0")	PHYDISK=sde	;;
			*)
				logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
			;;
			esac
		;;
		*)
			logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
		;;
		esac
		echo "$PHYDISK"
	;;
	"PowerEdge R740xd")
		# Only versions without hardware RAID PERC controller
		# We expect here 2 SATA SSDs and 12 SAS HDD

		usecache=false
		# read a cache file, if exist and recent
		if [ -f $CACHE_FILE ]; then
			lastentry=$(stat -c %Y $CACHE_FILE)
			lastvalid=$(date -d "now -3 min" +%s)
			if [ $lastentry -gt $lastvalid ]; then
				usecache=true
			else
				rm $CACHE_FILE
			fi
		elif [ -e $CACHE_FILE ]; then
			rm $CACHE_FILE
		fi
		# cache is invalid
		if ! $usecache; then
			# Rebuild cache with PERC CLI
			# Output in json
			perccli /c0 show all nolog J > $CACHE_FILE
			logger -t "udev" "[Cache built]"
		else
			logger -t "udev" "[Using cache]"
		fi
		disk=""

		# Parse the output of "perccli64 /c0 show all nolog J"
		# and build a temporary cache file...

		# WWN ID in udev is hex +3 compare to WWN in perccli
		logger -t "udev" "ID_WWN: $ID_WWN"
		printf -v WWN "%X" $(($ID_WWN - 0x3))
		logger -t "udev" "WWN: $WWN"

		# Get disk json key from WWN
		disk_key=$(cat $CACHE_FILE | jq -r "[.Controllers[].\"Response Data\".\"Physical Device Information\"[]|select(..|.WWN? == \"$WWN\")|to_entries[]|select(.key|startswith(\"Drive\")).key][0]")
		logger -t "udev" "disk_key: $disk_key"

		if [ "$disk_key" == "null" ]; then
			# Let's try original WWN
			printf -v WWN "%X" $ID_WWN
			logger -t "udev" "WWN: $WWN"
			disk_key=$(cat $CACHE_FILE | jq -r "[.Controllers[].\"Response Data\".\"Physical Device Information\"[]|select(..|.WWN? == \"$WWN\")|to_entries[]|select(.key|startswith(\"Drive\")).key][0]")
			logger -t "udev" "disk_key: $disk_key"
			if [ "$disk_key" == "null" ]; then
				# Last try last
				printf -v WWN "%X" $(($ID_WWN + 0x3))
				logger -t "udev" "WWN: $WWN"
				disk_key=$(cat $CACHE_FILE | jq -r "[.Controllers[].\"Response Data\".\"Physical Device Information\"[]|select(..|.WWN? == \"$WWN\")|to_entries[]|select(.key|startswith(\"Drive\")).key][0]")
				logger -t "udev" "disk_key: $disk_key"
			fi
		fi

		PHYSDRIVE=$(echo "$disk_key" | awk 'BEGIN { FS="[/ ]" } { print $5 }')
		logger -t "udev" "PHYSDRIVE: $PHYSDRIVE"
		case "${PHYSDRIVE}" in
			# The format is:
			# ${BAY}
			# Cause we strip controller and enclusure just before
			"s0") PHYDISK="sda" ;;
			"s1") PHYDISK="sdb" ;;
			"s2") PHYDISK="sdc" ;;
			"s3") PHYDISK="sdd" ;;
			"s4") PHYDISK="sde" ;;
			"s5") PHYDISK="sdf" ;;
			"s6") PHYDISK="sdg" ;;
			"s7") PHYDISK="sdh" ;;
			"s8") PHYDISK="sdi" ;;
			"s9") PHYDISK="sdj" ;;
			"s10") PHYDISK="sdk" ;;
			"s11") PHYDISK="sdl" ;;
			"s12") PHYDISK="sdm" ;;
			"s13") PHYDISK="sdn" ;;
		esac

		if ! [ -n "$PHYDISK" ]; then
			logger -t "udev" "Device $DEVPATH detected but not found."
			exit 1
		fi

		logger -t "udev" "Adding symlink $PHYDISK for device $DEVPATH"
		echo "$PHYDISK"
		exit 0
	;;
	*)
		logger -t "udev" "Dell chassis not supported, exit."
		exit 1
	;;
	esac
;;
"HPE")
	case "${CHASSIS_MODEL}" in
	"CL2800 Gen10")
		ata=$(echo $DEVPATH | cut -d/ -f5)
#		target=$(echo $DEVPATH | awk -F '/' '{ print $8 }')
		case "$ata" in
		"ata1")	PHYDISK=sda	;;
		"ata2")	PHYDISK=sdb	;;
		"ata3")	PHYDISK=sdc	;;
		"ata4")	PHYDISK=sdd	;;
		"ata5")	PHYDISK=sde	;;
		"ata6")	PHYDISK=sdf	;;
		"ata7")	PHYDISK=sdg	;;
		"ata8")	PHYDISK=sdh	;;
		"ata9")	PHYDISK=sdi	;;
		"ata10")	PHYDISK=sdj	;;
		"ata11")	PHYDISK=sdk	;;
		"ata12")	PHYDISK=sdl	;;
		"ata13")	PHYDISK=sdm	;;
		"ata14")	PHYDISK=sdn	;;
		esac
		if [ -n "$PHYDISK" ] ; then
			echo "$PHYDISK"
			exit 0
		else
			logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
		fi
	;;
	"ProLiant DL385 Gen10 Plus")
		card=$(echo $DEVPATH | cut -d/ -f6)
		target=$(echo $DEVPATH | awk -F '/' '{ print $9 }')
		# We expect here 2 SATA SSDs and 12 SAS HDD
		if [ "${card}" = "ata7" ] && [ "${target}" = "6:0:0:0" ]; then
			disk="sda"
		elif [ "${card}" = "ata8" ] && [ "${target}" = "7:0:0:0" ]; then
			disk="sdb"
		elif   [ "${card}" = "host8" ] ; then
			usecache=false
			# read a cache file, if exist and recent
			if [ -f $CACHE_FILE ]; then
				lastentry=$(stat -c %Y $CACHE_FILE)
				lastvalid=$(date -d "now -3 min" +%s)
				if [ $lastentry -gt $lastvalid ]; then
					usecache=true
				else
					rm $CACHE_FILE
				fi
			elif [ -e $CACHE_FILE ]; then
				rm $CACHE_FILE
			fi
			# cache is recent and a file
			if $usecache; then
				# Just read from cache file...
				disk=$(grep $DEVNAME $CACHE_FILE | awk '{ print $2 }')
				if ! [ -n "$disk" ]; then
					logger -t "udev" "[Cache mode] Device $DEVPATH detected but not found."
					exit 1
				fi
			else
				disk=""

				# Parse the output of "ssacli controller slot=0 physicaldrive all show detail"
				# and build a cache file...
				TMPFILE=$(mktemp -t parse-hpe-drives.XXXXXX)
				# The grep here just helps because we later have less lines to parse.
				SLOT_NUM=$(ssacli controller all show | head -n 2 | tail -n 1 | sed -r 's/.*(Slot [0-9]+).*/\1/' | sed 's/Slot //')
				ssacli controller slot=${SLOT_NUM} physicaldrive all show detail | grep -E 'physicaldrive|Disk Name: /dev/' >$TMPFILE
				while IFS='' read -r LINE || [ -n "${LINE}" ]; do
					if echo ${LINE} | grep -q physicaldrive ; then
						PHYSDRIVE=$(echo ${LINE} | sed 's/^.*physicaldrive //')
						case "${PHYSDRIVE}" in
							# The format is:
							# ${SAS_PORT}:${BOX}:${BAY}
							# Since we don't care about ports, but where the drive is physically located
							# we use ${BOX}:${BAY}, so the physical location of the drives is deterministic
							# and we don't care the order of the ports.
							"1I:1:1"|"2I:1:1"|"3I:1:1") PHYDISK="sdc" ;;
							"1I:1:2"|"2I:1:2"|"3I:1:2") PHYDISK="sdd" ;;
							"1I:1:3"|"2I:1:3"|"3I:1:3") PHYDISK="sde" ;;
							"1I:1:4"|"2I:1:4"|"3I:1:4") PHYDISK="sdf" ;;
							"1I:2:1"|"2I:2:1"|"3I:2:1") PHYDISK="sdg" ;;
							"1I:2:2"|"2I:2:2"|"3I:2:2") PHYDISK="sdh" ;;
							"1I:2:3"|"2I:2:3"|"3I:2:3") PHYDISK="sdi" ;;
							"1I:2:4"|"2I:2:4"|"3I:2:4") PHYDISK="sdj" ;;
							"1I:3:1"|"2I:3:1"|"3I:3:1") PHYDISK="sdk" ;;
							"1I:3:2"|"2I:3:2"|"3I:3:2") PHYDISK="sdl" ;;
							"1I:3:3"|"2I:3:3"|"3I:3:3") PHYDISK="sdm" ;;
							"1I:3:4"|"2I:3:4"|"3I:3:4") PHYDISK="sdn" ;;
						esac
					elif echo ${LINE} | grep -q 'Disk Name: /dev/' ; then
						MY_DEV_NAME=$(echo ${LINE} | sed 's#^.*Disk Name: ##')
						if [ -n "${PHYDISK}" ] ; then
							echo "$MY_DEV_NAME $PHYDISK" >> $CACHE_FILE
							unset PHYDISK
						fi
						unset MY_DEV_NAME
					fi
				done <$TMPFILE
				rm -f ${TMPFILE}

				# Once it's built, just read from the cache file.
				disk=$(grep $DEVNAME $CACHE_FILE | awk '{ print $2 }')
				if ! [ -n "$disk" ]; then
					logger -t "udev" "[Normal mode] Device $DEVPATH detected but not found."
					exit 1
				fi
			fi
		else
			logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
		fi
		logger -t "udev" "Adding symlink $disk for device $DEVPATH"
		echo "$disk"
		exit 0
	;;
	*)
		logger -t "udev" "HPE chassis not supported, exit."
		exit 1
	;;
	esac
;;
"Lenovo")
	case "${CHASSIS_MODEL}" in
	"ThinkSystem SR645")
		host=$(echo $DEVPATH | cut -d/ -f6)
		target=$(echo $DEVPATH | cut -d/ -f8)
		case "${host}" in
		"host0")
			case "${target}" in
			"0:2:0:0"|"0:3:111:0")      echo "sda"      ;;
			"0:2:1:0")      echo "sdb"      ;;
			"0:2:2:0")      echo "sdc"      ;;
			"0:2:3:0")      echo "sdd"      ;;
			"0:2:4:0")      echo "sde"      ;;
			"0:2:5:0")      echo "sdf"      ;;
			"0:2:6:0")      echo "sdg"      ;;
			"0:2:7:0")      echo "sdh"      ;;
			*)
				logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
			;;
			esac
		;;
		*)
			logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
		;;
		esac
	;;
	"ThinkSystem SR665")
		host=$(echo $DEVPATH | cut -d/ -f6)
		target=$(echo $DEVPATH | cut -d/ -f8)
		case "${host}" in
		"ata1")
			echo "sda";
		;;
		"ata2")
			echo "sdb";
		;;
		"host0")
			case "${target}" in
			# Note that the first value is in case of JBOD
			"0:0:0:0"|"0:2:0:0"|"0:3:96:0")	echo "sdc"	;;
			"0:0:1:0"|"0:2:1:0"|"0:3:97:0")	echo "sdd"	;;
			"0:0:2:0"|"0:2:2:0"|"0:3:98:0")	echo "sde"	;;
			"0:0:3:0"|"0:2:3:0"|"0:3:99:0")	echo "sdf"	;;
			"0:0:4:0"|"0:2:4:0"|"0:3:100:0")	echo "sdg"	;;
			"0:0:5:0"|"0:2:5:0"|"0:3:101:0")	echo "sdh"	;;
			"0:0:6:0"|"0:2:6:0"|"0:3:102:0")	echo "sdi"	;;
			"0:0:7:0"|"0:2:7:0"|"0:3:103:0")	echo "sdj"	;;
			"0:0:8:0"|"0:2:8:0"|"0:3:104:0")	echo "sdk"	;;
			"0:0:9:0"|"0:2:9:0"|"0:3:105:0")	echo "sdl"	;;
			"0:0:10:0"|"0:2:10:0"|"0:3:106:0")	echo "sdm"	;;
			"0:0:11:0"|"0:2:11:0"|"0:3:107:0")	echo "sdn"	;;
			"0:0:12:0"|"0:2:12:0"|"0:2:24:0"|"0:3:108:0")	echo "sdo"	;;
			"0:0:13:0"|"0:2:13:0"|"0:2:25:0"|"0:3:109:0")	echo "sdp"	;;
			"0:0:14:0"|"0:2:14:0"|"0:2:26:0"|"0:3:110:0")	echo "sdq"	;;
			"0:0:15:0"|"0:2:15:0"|"0:2:27:0"|"0:3:111:0")	echo "sdr"	;;
			*)
				logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
			;;
			esac
		;;
		*)
			logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
		;;
		esac
	;;
	*)
		logger -t "udev" "Lenovo chassis not supported (yet), exit."
		exit 1
	;;
	esac
;;
"GIGABYTE")
	case "${CHASSIS_MODEL}" in
	"R182-Z93-00")
		host=$(echo $DEVPATH | cut -d/ -f6)
		target=$(echo $DEVPATH | cut -d/ -f8)
		case "${host}" in
		"host0")
			case "${target}" in
			"0:2:0:0")      echo "sda"      ;;
			"0:2:1:0")      echo "sdb"      ;;
			"0:2:2:0")      echo "sdc"      ;;
			"0:2:3:0")      echo "sdd"      ;;
			"0:2:4:0")      echo "sde"      ;;
			"0:2:5:0")      echo "sdf"      ;;
			"0:2:6:0")      echo "sdg"      ;;
			"0:2:7:0")      echo "sdh"      ;;
			*)
				logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
			;;
			esac
		;;
		*)
			logger -t "udev" "Device $DEVPATH detected but not supported"; exit 1
		;;
		esac
	;;
	*)
		logger -t "udev" "Lenovo chassis not supported (yet), exit."
		exit 1
	;;
	esac
;;
*)
	logger -t "udev" "Chassis not supported, exit."
	exit 1
;;
esac
