#!/bin/sh

set -e
set -x

# Use flock to avoid launching the agent twice at once
LOCKDIR=/var/run/lock
LOCKFILE=${LOCKDIR}/$(basename ${0})
if [ "${1}" = "--nolock" ] ; then
	shift
else
	echo "Claming lock for $0 in ${LOCKFILE}"
	if ! flock -w 120 -x oci-agent-lock-file $0 --nolock $@ ; then
		exit 1
	fi
	exit 0
fi

# We need the server to report to, otherwise this agent is useless
if [ -r /etc/oci/pxe-server-ip ] ; then
	PXE_SERVER_IP=$(cat /etc/oci/pxe-server-ip)
else
	echo "No /etc/oci/pxe-server-ip: refusing to start."
	exit 1
fi

# Check if the OCI server has port 80 open before doing anything
if netcat -z ${PXE_SERVER_IP} 80 ; then
	echo -n ""
else
	echo "Port 80 is not open on host ${PXE_SERVER_IP}: refusing to start."
	exit 1
fi

###################################
### START OF HARDWARD DISCOVERY ###
###################################
### Get all disks:
TMPFILE=$(mktemp -t openstack-cluster-installer-agent.XXXXXX)
lsblk -b -l -d -J --exclude 2,7,11 -o +TRAN,MODEL  >${TMPFILE}
TMPFILE2=$(mktemp -t openstack-cluster-installer-agent.XXXXXX)
if [ "$(cat ${TMPFILE} | wc -l)" = "0" ] ; then
	# No disk present
	echo '{
   "blockdevices": [
   ]
,
' >${TMPFILE2}
else
	# Remove all "model":"VIRTUAL-DISK", as they are potentially
	# remotely mounted iSCSI disks.
	TMPFILE3=$(mktemp -t openstack-cluster-installer-agent.XXXXXX)
	echo '{
   "blockdevices": [' >$TMPFILE3
#	cat $TMPFILE | jq '.["blockdevices"][] | select((.tran != "iscsi") or (.model!="VIRTUAL-DISK"))' | sed 's/}/},/' >>$TMPFILE3
	cat $TMPFILE | jq '.["blockdevices"][] | select(.tran != "iscsi")' | sed 's/}/},/' >>$TMPFILE3
	NMU_LINES=$(( $(cat ${TMPFILE3} | wc -l) - 1))
	head -n ${NMU_LINES} ${TMPFILE3} > ${TMPFILE}
	echo '}
   ]
}' >>$TMPFILE

	# Add a coma at the end to continue the list
	NMU_LINES=$(( $(cat ${TMPFILE} | wc -l) - 1))
	head -n ${NMU_LINES} ${TMPFILE} > ${TMPFILE2}
	sed -i ${NMU_LINES}'s/$/,/' ${TMPFILE2}
fi

# Do we have hardware RAID (to configure with megacli) ?
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}')
	PHYSDEVS=""
	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 | awk '{print $1}' | sed s/,//g)
		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}')
		HDD_SPEC='{
	"slot": "'${SLOT_NUM}'",
	"model": "'${MODEL}'",
	"serial": "'${SERIAL}'",
	"size": "'${SIZE}'",
	"state": "'${STATE}'"
}'
		if [ -n "${PHYSDEVS}" ] ; then
			PHYSDEVS="${PHYSDEVS},"
		fi
		PHYSDEVS="${PHYSDEVS}${HDD_SPEC}"
	done
	echo '"phys-blockdevices": [
	'${PHYSDEVS}'
   ],' >>${TMPFILE2}
fi

### Get all interface names
echo '   "interfaces": [' >>${TMPFILE2}
for i in $(ip link show | grep -v '^ ' | grep -v 'lo:' | cut -d' ' -f2 | cut -d: -f1 | grep -v vlan | grep -v -E 'bond|.*@.*|qvb.*|qvo.*|vxlan.*|tap.*|qbr.*|qr.*|sg-.*|ha-.*|ovs.*|qg-.*|sg-.*|fg-.*|ovs-system|br-ex|br-int|br-tun|vlan.*|usb.*') ; do
	MAC_ADDR=$(ip link show dev $i | grep 'link/ether' | awk '{print $2}')
	LSHW_SPEED=$(lshw -class network -json 2>/dev/null | jq '.[] | select(.serial|test("'${MAC_ADDR}'")) | .capacity' | head -n 1)
	if [ -n ${LSHW_SPEED} ] ; then
		ETH_SPEED=$(( $(lshw -class network -json 2>/dev/null | jq '.[] | select(.serial|test("'${MAC_ADDR}'")) | .capacity' | head -n 1) / 1000000))
	else
		# If we can't find a speed, let's report it as 100 MBits/s
		ETH_SPEED=100
	fi

        # Find the neighbors using lldpcli
        if lldpcli -f json show neighbors | jq -r '.[]["interface"][] | keys[]' | grep -q $i ; then
                TMPFILE_LLDP=$(mktemp -t openstack-cluster-installer-agent.XXXXXX)
                lldpcli -f json show neighbors | jq -r '.[]["interface"][] | "\(.'${i}')"' | grep -v null >${TMPFILE_LLDP}
                SWITCHPORT_NAME=$(cat ${TMPFILE_LLDP} | jq -r '.["port"]["id"]["value"]')
                SWITCH_HOSTNAME=$(cat ${TMPFILE_LLDP} | jq -r '.["chassis"] | keys[]')
                rm -f ${TMPFILE_LLDP}
        else
                SWITCHPORT_NAME='unknown'
                SWITCH_HOSTNAME='unknown'
        fi
        echo '      {"name": "'${i}'", "macaddr": "'${MAC_ADDR}'", "max_speed": "'${ETH_SPEED}'", "switchport_name": "'${SWITCHPORT_NAME}'", "switch_hostname": "'${SWITCH_HOSTNAME}'" },' >>${TMPFILE2}
done
NMU_LINES=$(cat ${TMPFILE2} | wc -l)
sed -i ${NMU_LINES}'s/,$//' ${TMPFILE2}
# End the section
echo '   ],' >>${TMPFILE2}

### Chassis serial number, product name, BIOS version, IPMI version, IPMI ip
SYSTEM_MANUFACTURER=$(dmidecode -s system-manufacturer)
if [ "${SYSTEM_MANUFACTURER}" = "Supermicro" ] ; then
	# Supermicro is stupid, dmidecode -s system-serial-number
	# will always return 1234567890
	SYSTEM_SERIAL=$(dmidecode -s baseboard-serial-number)
else
	SYSTEM_SERIAL=$(dmidecode -s system-serial-number)
fi
PRODUCT_NAME=$(dmidecode -t 1 | grep "Product Name" | sed -e "s/[ \t]*Product Name:[ ]*//"  -e "s/,//g")
BIOS_VERSION=$(dmidecode -s bios-version)
IPMI_FIRMWARE_VERSION=$(ipmitool mc info | grep "Firmware Revision" | awk '{print $4}')
IPMI_DETECTED_IP=$(ipmitool lan print 1 | grep -E "IP Address[ ]*:" | sed 's/IP Address[ \t:]*//')

# Find out if we're on a Dell iDRAC system
LIFECYCLE_VERSION=0.0.0
# Due to some bugs on these old hardware, "ipmitool sdr elist mcloc" can simply hang,
# so we avoid doing it and just double-guess the iDRAC model based on the product-name.
if [ "${PRODUCT_NAME}" = "PowerEdge R610" ] || [ "${PRODUCT_NAME}" = "PowerEdge R410" ] ; then
	IPMI_TYPE="iDRAC6"
else
	IPMI_TYPE=$(ipmitool sdr elist mcloc | awk '{print $1}')
fi
case "${IPMI_TYPE}" in
"iDRAC6")
	if [ -x /usr/bin/racadm ] ; then
		LIFECYCLE_VERSION=$(racadm getversion | grep "USC Version" | cut -d= -f2 | awk '{print $1}')
	fi
;;
# TODO: add support for iDRAC7, currently I don't know its output...
"iDRAC8"|"iDRAC9")
	if [ -x /usr/bin/racadm ] ; then
		LIFECYCLE_VERSION=$(racadm getversion | grep "Lifecycle Controller Version" | cut -d= -f2 | awk '{print $1}')
	fi
;;
"GPX")
	LIFECYCLE_VERSION=0.0.0
;;
*)
	LIFECYCLE_VERSION=0.0.0
;;
esac

# Check if we need to upgrade the BIOS and IPMI
UPGRADE_CONFIG=/etc/oci/oci-firmware-upgrade-config.json
if [ -r ${UPGRADE_CONFIG} ] ; then
	# Spaces in product names are annoying, so we convert them to underscore
	PRODUCT_LIST=$(cat ${UPGRADE_CONFIG} | jq -r '. | keys[]' | sed 's/ /_/g' | tr '\n' ' ')
	for PRODUCT_NAME_TARGET in ${PRODUCT_LIST} ; do
		# If the current server type matches one of the product names listed in oci-firmware-upgrade-config.json
		if [ "${PRODUCT_NAME_TARGET}" = "$(echo ${PRODUCT_NAME} | sed 's/ /_/g')" ] ; then
			# Check if we have an upgrade for the BIOS
			BIOS_TARGET_VERSION=$(cat ${UPGRADE_CONFIG} | jq -r '.["'"${PRODUCT_NAME}"'"]["BIOS"]["version"]')
			if [ "${BIOS_TARGET_VERSION}" != "null" ] && dpkg --compare-versions "${BIOS_VERSION}" lt "${BIOS_TARGET_VERSION}" ; then
				SCRIPT_NAME=$(cat ${UPGRADE_CONFIG} | jq -r '.["'"${PRODUCT_NAME}"'"]["BIOS"]["script"]')
				if [ -x "${SCRIPT_NAME}" ] ; then
					BIOS_UPGRADE_SCRIPT=${SCRIPT_NAME}
					BIOS_UPGRADE_VERSION=${BIOS_TARGET_VERSION}
				fi
			fi
			# Check if we have an upgrade for IPMI
			IPMI_TARGET_VERSION=$(cat ${UPGRADE_CONFIG} | jq -r '.["'"${PRODUCT_NAME}"'"]["IPMI"]["version"]')
			if [ "${IPMI_TARGET_VERSION}" != "null" ] && dpkg --compare-versions "${IPMI_FIRMWARE_VERSION}" lt "${IPMI_TARGET_VERSION}" ; then
				SCRIPT_NAME=$(cat ${UPGRADE_CONFIG} | jq -r '.["'"${PRODUCT_NAME}"'"]["IPMI"]["script"]')
				if [ -x "${SCRIPT_NAME}" ] ; then
					IPMI_UPGRADE_SCRIPT=${SCRIPT_NAME}
					IPMI_UPGRADE_VERSION=${IPMI_TARGET_VERSION}
				fi
			fi
			LIFECYCLE_TARGET_VERSION=$(cat ${UPGRADE_CONFIG} | jq -r '.["'"${PRODUCT_NAME}"'"]["Lifecycle"]["version"]')
			if [ "${LIFECYCLE_TARGET_VERSION}" != "null" ] && [ "${LIFECYCLE_VERSION}" != "0.0.0" ] && dpkg --compare-versions "${LIFECYCLE_VERSION}" lt "${LIFECYCLE_TARGET_VERSION}" ; then
				SCRIPT_NAME=$(cat ${UPGRADE_CONFIG} | jq -r '.["'"${PRODUCT_NAME}"'"]["Lifecycle"]["script"]')
				if [ -x "${SCRIPT_NAME}" ] ; then
					LIFECYCLE_UPGRADE_SCRIPT=${SCRIPT_NAME}
					LIFECYCLE_UPGRADE_VERSION=${LIFECYCLE_TARGET_VERSION}
				fi
			fi
		fi
	done
fi

if [ -n "${LIFECYCLE_UPGRADE_VERSION}" ] ; then
	LIFECYCLE_VERSION="${LIFECYCLE_VERSION}->${LIFECYCLE_UPGRADE_VERSION}"
else
	if [ -n "${BIOS_UPGRADE_VERSION}" ] ; then
		BIOS_VERSION="${BIOS_VERSION}->${BIOS_UPGRADE_VERSION}"
	else
		if [ -n "${IPMI_UPGRADE_VERSION}" ] ; then
			IPMI_FIRMWARE_VERSION="${IPMI_FIRMWARE_VERSION}->${IPMI_UPGRADE_VERSION}"
		fi
	fi
fi



echo '   "machine": [' >>${TMPFILE2}
echo '      {"serial": "'${SYSTEM_SERIAL}'", "productname": "'${PRODUCT_NAME}'", "bios_version": "'${BIOS_VERSION}'", "dell_lifecycle_version": "'${LIFECYCLE_VERSION}'", "ipmi_firmware_version": "'${IPMI_FIRMWARE_VERSION}'", "ipmi_detected_ip": "'${IPMI_DETECTED_IP}'", "ladvd_report": "none"}' >>${TMPFILE2}
echo '   ],' >>${TMPFILE2}


### Get total amount of RAM
TOTAL_RAM=$(dmidecode -t memory |  awk '/Size: [0-9]/ {tmp=$2 ; if($3 == "GB") { tmp = $2 * 1024 }; t+=tmp;} END { print t}')
RAM_TYPE=$(dmidecode -t 17 | grep Type | head -n 1 | awk '{print $2}')
RAM_SPEED=$(dmidecode -t 17 | grep Speed | head -n 1 | awk '{print $2}')
RAM_MANUFACTURER=$(dmidecode -t 17 | grep Manufacturer | head -n 1 | awk '{print $2}')
echo '   "memory": [' >>${TMPFILE2}
	echo '      {"size": "'${TOTAL_RAM}'", "type": "'${RAM_TYPE}'", "speed": "'${RAM_SPEED}'", "manufacturer": "'${RAM_MANUFACTURER}'" }' >>${TMPFILE2}
echo '   ]' >>${TMPFILE2}
echo '}' >>${TMPFILE2}

curl -vX POST --header "Content-Type: application/json" -d @${TMPFILE2} http://${PXE_SERVER_IP}/oci/report.php

rm ${TMPFILE}
rm ${TMPFILE2}

#############################################
### Process BIOS and IPMI firmware update ###
#############################################
if [ -x "${LIFECYCLE_UPGRADE_SCRIPT}" ] ; then
	${LIFECYCLE_UPGRADE_SCRIPT}
	exit 0
fi
if [ -x "${BIOS_UPGRADE_SCRIPT}" ] ; then
	${BIOS_UPGRADE_SCRIPT}
	exit 0
fi
if [ -x "${IPMI_UPGRADE_SCRIPT}" ] ; then
	${IPMI_UPGRADE_SCRIPT}
	exit 0
fi
