#!/bin/bash
PSTORAGE_ISCSI_LIB=/usr/libexec/vstorage-iscsi/vstorage_functions
ROOT=""
IQN=""
FORCE=""
SHOW_ALL=""
IQN=""
ARGS="${@}"
HOST_ID=""
HIDE_HEADER=""
XML_MODE=""
HIDE_LUNS=""
PRINT_ATTR=""
JSON_STAT=""

function print_usage {
	exec 1>&2
	echo "List existing iSCSI targets."
	echo "Usage:"
	echo " $CTL_TOOL list [--all] [-t,--target IQN] [--vstorage-attr ID]"
	echo "Options:"
	echo "  -a,--all                show all targets on ISCSI_ROOT."
	echo "  -t,--target IQN         show information only for specified target."
	echo "  -H, --no-header         suppress displaying the header row. Usable for scripts."
	echo "  -L, --no-luns           suppress displaying the luns information."
	echo "  -X, --xml               get output in XML."
	echo "  -J, --json-stat         get latency stat in JSON."
	echo "  --vstorage-attr ID      print Vstorage attributes(-X) or stats(-J) for LUN (optional)."
	echo " "
	exit 1
}

function parse_args {

	while [ "${#}" -gt 0 ]; do
		case "${1}" in
			"-t"|"--target")
				[ -z "${2}" ] && print_usage
				IQN="${2}"
				shift
				shift
				;;
			"-a"|"--all")
				SHOW_ALL="yes"
				shift
				;;
			"-r"|"--root")
				[ -z "${2}" ] && print_usage
				ROOT="${2}"
				shift
				shift
				;;
			"-H"|"--no-header")
				HIDE_HEADER="1"
				shift
				;;
			"-L"|"--no-luns")
				HIDE_LUNS="1"
				shift
				;;
			"-X"|"--xml")
				XML_MODE="1"
				SHOW_ALL="1"
				shift
				;;
			"-J"|"--json-stat")
				JSON_STAT="1"
				shift
				;;
			"--vstorage-attr")
				[ -z "${2}" ] && print_usage
				PRINT_ATTR="${2}"
				shift
				shift
				;;
			"-h"|"--help")
				print_usage
				;;
			*)
				echo "Unknown option '${1}'." 1>&2
				print_usage
				;;
			esac
		done

	if [ -n "$PRINT_ATTR" -a -z "$XML_MODE" ] && [ -n "$PRINT_ATTR" -a -z "$JSON_STAT" ] ; then
		echo "Error: Option '--vstorage-attr' requires '-x,--xml' or '-J,--json-stat'" 1>&2
		exit 1
	fi
}

function get_ploop_size {
	local path="$1"
	$PLOOP info -s "$path/DiskDescriptor.xml" 2>/dev/null | grep "^size:" | awk '{ print $2 }'
	return $?	
}

function print_xml {
	echo -e "$2" | awk -F '!' -v parent="$1" \
		'{ if (NR == 1) for(i=1;i<=NF;++i) { tag[i]=$i } else { print "<"parent">" ; for(k=1;k<=NF;++k) { print "<"tag[k]">" $k "</"tag[k]">" } ; print "</"parent">" }}'
}

function print_lun {
	local target="$1"
	local lun="$2"
	local lun_id=`expr "$(basename $lun)" : "^lun\([0-9]*\)" `
	local online=""
	local size=`get_ploop_size "$lun"`
	if [ -n "$size" ] ; then
		size="$[$size * 512 / 1048576]M"
	else
		size="unknown size"
	fi

	used=`ls --block-size=m -l -n "$lun/ploop" 2>/dev/null | awk '{ print $5 }'`
	[ -z "$used" ] && used="Unknown"

	ctl_port=`pcs_iscsi_get_ctl_port $target`
	online=""
	if [ -n "$ctl_port" ] ; then
		online=`${SCSI_TGT}_is_lun_online target=$target lun_id=$lun_id ctl_port=$ctl_port`
	fi

	[ -z "$online" ] && online="No"

	if [ -z "$XML_MODE" ]; then
		echo "${lun_id}!$size!$used!$online" | awk -F '!' '{ printf " LUN: %3s, Size: %6s, Used: %6s, Online: %s\n", $1,$2,$3,$4}'
	else
		print_xml "lun" "id!size!used!online!attr\n${lun_id}!$size!$used!$online!$lun_attr"
	fi
}

function print_luns {
	local target="$1"
	local luns="$2"
	for l in $luns; do
		(print_lun $target $l > "${ISCSI_TMP}/.print_lun.${target}.${l##*/}") &
	done
	wait
	for l in $luns; do
		cat "${ISCSI_TMP}/.print_lun.${target}.${l##*/}"
	done
}

function print_initiators {
	local target="$1"
	ctl_port=`pcs_iscsi_get_ctl_port $target`
	if [ -n "$ctl_port" ] ; then
		${SCSI_TGT}_show_initiators target=$target ctl_port=$ctl_port | \
			awk '{ printf " Initiator:   %s (%s)\n", $1,$2 }'
	fi
}

function print_initiators_xml {
	local target="$1"
	ctl_port=`pcs_iscsi_get_ctl_port $target`
	if [ -n "$ctl_port" ] ; then
		echo -e "<initiators>"
		${SCSI_TGT}_show_initiators target=$target ctl_port=$ctl_port | \
			awk '{ printf "<initiator>\n<iqn>%s</iqn>\n<host>%s</host>\n</initiator>\n", $1,$2}'
		echo -e "</initiators>"
	fi
}

function get_remote_status {
	local target="$1"
	local owner="$2"
	if [ -n "$owner" -a "$owner" != "$TARGET_NO_OWNER" ] ; then
		echo "remote"
	else
		echo "unregistered"
	fi
}

function get_portals_xml {
	local target="$1"
	if [ -f "$ISCSI_ROOT/$target/control/address" ] ; then
		echo "<portals>"
		for p in `cat "$ISCSI_ROOT/$target/control/address"`; do
			echo "<portal>$(echo $p | cut -d '/' -f1)</portal>"
		done
		echo "</portals>"
	else
		echo "</portals>"
	fi
}

function get_portals {
	local target="$1"
	local portals=""
	if [ -f "$ISCSI_ROOT/$target/control/address" ] ; then
		for p in `cat "$ISCSI_ROOT/$target/control/address"`; do
			[ -n "$portals" ] && portals="$portals,"
			portals="$portals`echo $p | cut -d '/' -f1`"
		done
	else
		portals="None"
	fi
	echo "$portals"
}

function print_lun_attrs {
	local target="$1"
	local lun_id="$2"
	attrs=$(vstorage -q get-attr "$ISCSI_ROOT/$target/lun$lun_id" -X 2>/dev/null)
	if [ $? -ne 0 ]; then
		echo "Can't get attributes for $ISCSI_ROOT/$target/lun$lun_id" 1>&2
		exit 1
	fi
	echo "<target>"
	echo "<iqn>$target</iqn>"
	echo "<lun><id>$lun_id</id>$attrs</lun>"
	echo "</target>"
}

function print_target_xml {
	local path="$1"
	local target="$2"
	local is_local="$3"
	local max_kbps="$4"
	local max_iops="$5"
	local lun_id="$6"
	local status=""

	if [ -n "$lun_id" ]; then
		print_lun_attrs $target $lun_id
		return
	fi

	local host=`cat $ISCSI_ROOT/$target/control/host`
	echo "<target>"
	echo "<iqn>$target</iqn>"
	get_portals_xml $target
	
	if [ "x$is_local" = "xyes" ] ; then
		status=`pcs_iscsi_get_status "$target"`
	else
		status=`get_remote_status $target $host`
		[ "$status" = "unregistered" ] && host=""
	fi
	echo "<host>$host</host><status>$status</status>"

	if [ -n "$HIDE_LUNS" ] ; then
		luns=`ls -d "$ISCSI_ROOT/$target/"lun[0-9]* 2>/dev/null | wc -l`
		echo "<luns>$luns</luns>"
	else
		luns=`ls -d "$ISCSI_ROOT/$target/"lun[0-9]* 2>/dev/null`
		if [ -n "$luns" ] ; then
			print_luns $target "$luns"
		fi
	fi

	[ "$status" = "running" ] && print_initiators_xml $target

	print_limits_xml "$max_kbps" "$max_iops"
}

function print_limits_xml {
	local max_kbps="$1"
	local max_iops="$2"

	echo "<limits>"
	echo "<max_kbps>$max_kbps</max_kbps>"
	echo "<max_iops>$max_iops</max_iops>"
	echo "</limits>"
	echo "</target>"
}

function print_target_info {
	local target="$1"
	local is_local="no"
	if [ ! -d  "$ISCSI_ROOT/$target" ] ; then
		echo "Target $target doesn't exist on $ISCSI_ROOT" 1>&2
		exit 1
	fi

	local host=`cat $ISCSI_ROOT/$target/control/host`
	pcs_iscsi_is_registered_local $target $HOST_ID >/dev/null 2>&1
	if [ $? -eq 0 ] ; then
		is_local="yes"
		status=`pcs_iscsi_get_status "$target"`
	else
		status=`get_remote_status $target $host`
		[ "$status" = "unregistered" ] && host="None"
	fi

	if [ -n "$PRINT_ATTR" ] ; then
		if [ ! -d "$ISCSI_ROOT/$target/lun$PRINT_ATTR" ]; then
			echo "Can't find LUN $PRINT_ATTR for $target" 1>&2
			exit 1
		fi
	fi

	if [ -n "$JSON_STAT" ] ; then
		print_target_stat_json $target $PRINT_ATTR
		return
	fi

	local kbps_limit=$(pcs_iscsi_get_limit "$target" "max_kbps")
	local iops_limit=$(pcs_iscsi_get_limit "$target" "max_iops")
	if [ -n "$XML_MODE" ] ; then
		echo "<?xml version=\"1.0\"?>"
		echo "<targets>"
		print_target_xml $ISCSI_ROOT/$target $target $is_local $kbps_limit $iops_limit $PRINT_ATTR
		echo "</targets>"
	else
		echo "Target $target:"
		echo " Portals:     `get_portals $target`"
		echo " Status:      $status"
		echo " Registered:  $is_local"
		echo " Host:        $host"
	    echo " kbps limit:      $kbps_limit"
	    echo " IOPS limit:      $iops_limit"
	    
		[ "$status" = "running" ] && print_initiators "$target"
	    
	    if [ -z "$HIDE_LUNS" ] ; then
            	luns=`ls -d "$ISCSI_ROOT/$target/"lun[0-9]* 2>/dev/null`
            	if [ -n "$luns" ] ; then
					print_luns $target "$luns"
            	fi
	    fi
	fi
}

function print_target {
	local path="$1"
	local target="$2"
	local is_local="$3"
	local luns=`ls -d "$ISCSI_ROOT/$target/"lun[0-9]* 2>/dev/null | wc -l`
	local host=`cat $ISCSI_ROOT/$target/control/host`
	if [ "x$is_local" = "xyes" ] ; then
		status=`pcs_iscsi_get_status "$target"`
	else
		status=`get_remote_status $target $host`
		[ "$status" = "unregistered" ] && host="None"
	fi
	local portals=`get_portals $target`
	echo "$target!$status!$luns!$host!$portals"
}

function get_targets {
        ls $ISCSI_ROOT | grep "^iqn*" 2>/dev/null | while read target
        do
                [ ! -d  "$ISCSI_ROOT/$target/control" ] && continue
                pcs_iscsi_is_registered_local $target $HOST_ID >/dev/null 2>&1
		if [ $? -eq 0 ] ; then
			is_local="yes"
		else
			[ -z "$SHOW_ALL" ] && continue
			is_local="no"
		fi
		if [ -z "$XML_MODE" ] ; then
			print_target $ISCSI_ROOT/$target $target $is_local
		else
			local kbps_limit=$(pcs_iscsi_get_limit "$target" "max_kbps")
			local iops_limit=$(pcs_iscsi_get_limit "$target" "max_iops")
			print_target_xml $ISCSI_ROOT/$target $target $is_local $kbps_limit $iops_limit
		fi
        done
}

function get_format {
	echo -e "$1" | awk -F '!' \
        'BEGIN { numf=5 } { for (i=1; i<=numf; i++) { if(length($i) >= m[i]) m[i]=length($i)} } END { p=""; s=""; for(i=1;i<=numf; i++) { p=p",$"i; s=s"%-"m[i]"s " } ; print "{printf \""s"\\n\""p"}" }'
}


function list_targets {

	if [ -n "$XML_MODE" ]; then
		echo "<?xml version=\"1.0\"?>"
		echo "<targets>"
		get_targets
		echo "</targets>"
		exit 0
	fi

	targets=""
	[ -z "$HIDE_HEADER" ] && targets="IQN!STATUS!LUNs!HOST!PORTAL(s)\n"
	targets="$targets$(get_targets)"
	fmt=`get_format "$hdr$targets"`
	IFS=$'\n'
	for t in $targets; do
		echo -e $t | awk -F '!' "$fmt"
	done
}

function print_target_stat_json {
	local target="$1"
	local lun_id="$2"
	local ctl_port=`pcs_iscsi_get_ctl_port $target`

	if [ -n "$lun_id" ]; then
		${SCSI_TGT}_show_lun_stats target=$target lun_id=$lun_id crl_port=$crl_port
	else
		local luns=`ls -d "$ISCSI_ROOT/$target/"lun[0-9]* 2>/dev/null`
		echo -n "{\"luns_info\" : ["
		first_item=1
		for lun in $luns; do
			if [ $first_item != 1 ] ; then
				echo -n ", "
			fi
			first_item=0

			local lun_id=`expr "$(basename $lun)" : "^lun\([0-9]*\)" `
			${SCSI_TGT}_show_lun_stats target=$target lun_id=$lun_id crl_port=$crl_port
		done
		echo -n "]}"
	fi
}

if [ ! -x $PSTORAGE_ISCSI_LIB ] ; then
	echo "Unable find executable $PSTORAGE_ISCSI_LIB" 1>&2
	exit 1
fi

source $PSTORAGE_ISCSI_LIB
[ -f $ISCSI_ETC/config ] && source $ISCSI_ETC/config

parse_args $ARGS

ISCSI_ROOT=`pcs_iscsi_get_root "${ROOT}"`
pcs_iscsi_check_root

HOST_ID=`get_host_id`
[ $? -ne 0 ] && exit 2

if [ -n "$IQN" ] ; then
	pcs_iscsi_lock_exec print_target_info $IQN
	exit 0
fi

list_targets
exit 0
