#!/bin/sh
#===============================================================================
# scst, (c) 2010 Navixia SA
#-------------------------------------------------------------------------------
# $Id$
#-------------------------------------------------------------------------------
# This resource agent will configure an iSCSI target and export a single LUN
# via that target. The iSCSI implementation being used is SCST, and the RA
# requires the SYSFS management interface to be available (not compatible
# with the PROCFS management interface).
#
# OCF instance parameters
#	OCF_RESKEY_iqn
#	OCF_RESKEY_path
#	OCF_RESKEY_allowed_portals
#-------------------------------------------------------------------------------

#===============================================================================
# Initialization
#-------------------------------------------------------------------------------
. ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs
export LANG=C LANGUAGE=C LC_ALL=C

#===============================================================================
# Configuration
#-------------------------------------------------------------------------------
SCST_base="/sys/kernel/scst_tgt"

#===============================================================================
# Meta-data
#-------------------------------------------------------------------------------
meta_data() {
	cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="scst">
<version>0.1</version>

<longdesc lang="en">
Configures an iSCSI target and export a single LUN via that target. The iSCSI
implementation being used is SCST, and the RA requires the SYSFS management
interface to be available (not compatible with the PROCFS management interface).
</longdesc>

<shortdesc lang="en">Exports a device via an SCST iSCSI Target/Lun pair</shortdesc>

<parameters>

<parameter name="iqn" required="1" unique="1">
<longdesc lang="en">
The iSCSI Qualified Name (IQN) of the target that will be configured.
</longdesc>
<shortdesc lang="en">iSCSI target IQN</shortdesc>
<content type="string" />
</parameter>

<parameter name="path" required="1" unique="1">
<longdesc lang="en">
The path to the block device exposed.
</longdesc>
<shortdesc lang="en">Block device path</shortdesc>
<content type="string" />
</parameter>

<parameter name="chap_username" required="1" unique="1">
<longdesc lang="en">
IncomingUser iSCSI CHAP username.
</longdesc>
<shortdesc lang="en">IncomingUser iSCSI CHAP username</shortdesc>
<content type="string" />
</parameter>

<parameter name="chap_password" required="1" unique="1">
<longdesc lang="en">
IncomingUser iSCSI CHAP password.
</longdesc>
<shortdesc lang="en">IncomingUser iSCSI CHAP password</shortdesc>
<content type="string" />
</parameter>

<parameter name="allowed_portals" required="0" unique="0">
<longdesc lang="en">
iSCSI network portal addresses that the initiators will be
able to use (ex: "10.0.0.1"). By default, SCST will allow
connections to all the listening IP addresses. NOTE, the format
"IP:port" is not supported.
</longdesc>
<shortdesc lang="en">iSCSI allowed portal addresses</shortdesc>
<content type="string" default="" />
</parameter>

</parameters>

<actions>
<action name="start"		timeout="10" />
<action name="stop"		timeout="60" />
<action name="monitor"		timeout="10" interval="10" depth="0" />
<action name="meta-data"	timeout="5" />
<action name="validate-all"	timeout="10" />
</actions>
</resource-agent>
END
}

#===============================================================================
# usage
#-------------------------------------------------------------------------------
scst_usage() {
	cat <<END
usage: $0 {start|stop|monitor|validate-all|meta-data}

Expects to have a fully populated OCF RA-compliant environment set.
END
}

#===============================================================================
# start
#-------------------------------------------------------------------------------
scst_start() {
	scst_monitor
	if [ $? = $OCF_SUCCESS ]; then
		return $OCF_SUCCESS
	fi

	# Build SCST device name from path
	devname=`basename ${OCF_RESKEY_path}`

	# Configure the backend device
	ocf_log debug "configuring fileio device ${devname} (path=${OCF_RESKEY_path})"
	echo "add_device ${devname} filename=${OCF_RESKEY_path}; nv_cache=1" >${SCST_base}/handlers/vdisk_fileio/mgmt
	if [ $? -ne 0 ]; then
		ocf_log err "FAILED to configure backend device ${devname}"
		return $OCF_ERR_GENERIC
	fi

	# Create the target
	ocf_log debug "creating target ${OCF_RESKEY_iqn}"
	echo "add_target ${OCF_RESKEY_iqn}" >${SCST_base}/targets/iscsi/mgmt
	if [ $? -ne 0 ]; then
		ocf_log err "FAILED to create target ${OCF_RESKEY_iqn}"
		return $OCF_ERR_GENERIC
	fi

	# Add allowed portals
	for portal in ${OCF_RESKEY_allowed_portals}; do
		ocf_log debug "adding allowed portal $portal to target ${OCF_RESKEY_iqn}"
		echo "add_target_attribute ${OCF_RESKEY_iqn} allowed_portal ${portal}" >${SCST_base}/targets/iscsi/mgmt
		if [ $? -ne 0 ]; then
			ocf_log debug "ERROR !!!"
			ocf_log err "FAILED to add portal ${portal} to target ${OCF_RESKEY_iqn}"
			return $OCF_ERR_GENERIC
		fi
	done

	# Configure target authentication
	ocf_log debug "adding CHAP credentials (${OCF_RESKEY_chap_username},${OCF_RESKEY_chap_password}) to target ${OCF_RESKEY_iqn}"
	echo "add_target_attribute ${OCF_RESKEY_iqn} IncomingUser ${OCF_RESKEY_chap_username} ${OCF_RESKEY_chap_password}" >${SCST_base}/targets/iscsi/mgmt
	if [ $? -ne 0 ]; then
		ocf_log err "FAILED to add CHAP credentials (${OCF_RESKEY_chap_username},${OCF_RESKEY_chap_password}) to target ${OCF_RESKEY_iqn}"
		return $OCF_ERR_GENERIC
	fi

	# Add backend device to target
	ocf_log debug "adding device ${devname} as LUN 0 to target ${OCF_RESKEY_iqn}"
	echo "add ${devname} 0" >${SCST_base}/targets/iscsi/${OCF_RESKEY_iqn}/luns/mgmt
	if [ $? -ne 0 ]; then
		ocf_log err "FAILED to add device ${devname} as LUN 0 to target ${OCF_RESKEY_iqn}"
		return $OCF_ERR_GENERIC
	fi

	# Finally, enable the target
	ocf_log debug "enabling target ${OCF_RESKEY_iqn}"
	echo 1 >${SCST_base}/targets/iscsi/${OCF_RESKEY_iqn}/enabled
	if [ $? -ne 0 ]; then
		ocf_log err "FAILED to enable target ${OCF_RESKEY_iqn}"
		return $OCF_ERR_GENERIC
	fi

	return $OCF_SUCCESS
}

#===============================================================================
# stop
#-------------------------------------------------------------------------------
scst_stop() {
	scst_monitor
	if [ $? != $OCF_SUCCESS ]; then
		return $OCF_SUCCESS
	fi

	# Build SCST device name from path
	devname=`basename ${OCF_RESKEY_path}`

	# Disable the target
	ocf_log debug "disabling target ${OCF_RESKEY_iqn}"
	echo 0 >${SCST_base}/targets/iscsi/${OCF_RESKEY_iqn}/enabled
	if [ $? -ne 0 ]; then
		ocf_log err "FAILED to disable target ${OCF_RESKEY_iqn}"
		return $OCF_ERR_GENERIC
	fi

        # Close all the existing sessions
	for s in `ls -1 ${SCST_base}/targets/iscsi/${OCF_RESKEY_iqn}/sessions` ; do
		ocf_log debug "disabling session $s on target ${OCF_RESKEY_iqn}"
		echo 1 >${SCST_base}/targets/iscsi/${OCF_RESKEY_iqn}/sessions/$s/force_close
		if [ $? -ne 0 ]; then
			ocf_log err "FAILED to disable session $s on target ${OCF_RESKEY_iqn}"
			return $OCF_ERR_GENERIC
		fi
	done

	# Wait a bit
	#sleep 5

	# Remove LUN from target
	ocf_log debug "removing LUN 0 from target ${OCF_RESKEY_iqn}"
	echo "del 0" >${SCST_base}/targets/iscsi/${OCF_RESKEY_iqn}/luns/mgmt
	if [ $? -ne 0 ]; then
		ocf_log err "FAILED to remove LUN 0 from target ${OCF_RESKEY_iqn}"
		return $OCF_ERR_GENERIC
	fi

	# Wait a bit
	sleep 3

	ocf_log debug "removing target ${OCF_RESKEY_iqn}"
    	 echo "del_target ${OCF_RESKEY_iqn}" >${SCST_base}/targets/iscsi/mgmt
	if [ $? -ne 0 ]; then
		ocf_log err "FAILED to remove target ${OCF_RESKEY_iqn}"
		return $OCF_ERR_GENERIC
	fi

	ocf_log debug "removing device ${devname}"
	echo "del_device ${devname}" >${SCST_base}/handlers/vdisk_fileio/mgmt
	if [ $? -ne 0 ]; then
		ocf_log err "FAILED to remove device ${devname}"
		return $OCF_ERR_GENERIC
	fi

	return $OCF_SUCCESS
}

#===============================================================================
# monitor
#-------------------------------------------------------------------------------
scst_monitor() {
	# Build SCST device name from path
	devname=`basename ${OCF_RESKEY_path}`

	# Check if underlying device is configured
	ocf_log debug "checking existence of ${SCST_base}/handlers/vdisk_fileio/${devname}"
	[ -e "${SCST_base}/handlers/vdisk_fileio/${devname}" ]
	if [ $? -ne 0 ]; then
		ocf_log debug "Device ${devname} not configured"
		return $OCF_NOT_RUNNING
	fi

	# Check if target iqn exists
	ocf_log debug "checking existence of ${SCST_base}/targets/iscsi/${OCF_RESKEY_iqn}"
	[ -d "${SCST_base}/targets/iscsi/${OCF_RESKEY_iqn}" ]
	if [ $? -ne 0 ]; then
		ocf_log debug "Target ${OCF_RESKEY_iqn} not found"
		return $OCF_NOT_RUNNING
	fi

	# Check if device is mapped to LUN
	ocf_log debug "checking existence of ${SCST_base}/targets/iscsi/${OCF_RESKEY_iqn}/luns/0"
	[ -e "${SCST_base}/targets/iscsi/${OCF_RESKEY_iqn}/luns/0" ]
	if [ $? -ne 0 ]; then
		ocf_log debug "LUN is not configured for target ${OCF_RESKEY_iqn}"
		return $OCF_NOT_RUNNING
	fi

	ocf_log debug "Monitor success"
	return $OCF_SUCCESS
}

#===============================================================================
# validate
#-------------------------------------------------------------------------------
scst_validate() {
	# Do we have all required variables?
	for var in iqn path chap_username chap_password; do
		param="OCF_RESKEY_${var}"
		if [ -z "${!param}" ]; then
			ocf_log error "Missing resource parameter \"$var\"!"
			return $OCF_ERR_CONFIGURED
		fi
	done

	# Is the required kernel functionality available?
	[ -d ${SCST_base}/targets/iscsi ]
	if [ $? -ne 0 ]; then
		ocf_log err "${SCST_base}/targets/iscsi does not exist or is not a directory -- check if required modules are loaded."
		return $OCF_ERR_INSTALLED
	fi
	return $OCF_SUCCESS
}

#===============================================================================
# main
#-------------------------------------------------------------------------------
case $1 in
  meta-data)
	meta_data
	exit $OCF_SUCCESS
	;;
  usage|help)
	scst_usage
	exit $OCF_SUCCESS
	;;
esac

# Everything except usage and meta-data must pass the validate test
scst_validate || exit $?

case $__OCF_ACTION in
start)		scst_start;;
stop)		scst_stop;;
monitor)	scst_monitor;;
reload)		ocf_log err "Reloading..."
	        scst_start
		;;
validate-all)	;;
*)		scst_usage
		exit $OCF_ERR_UNIMPLEMENTED
		;;
esac
rc=$?
ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : $rc"
exit $rc
