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

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 "  --vstorage-attr ID      print Vstorage attributes 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
				;;
			"--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" ]; then
		echo "Error: Option '--vstorage-attr' requires '-x,--xml'" 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 get_lun_info {
	local ctl_port="$1"
	local lun_id="$2"
	local max_lun_id=`$TGTADM -C $ctl_port --mode target --op show 2>/dev/null | grep "LUN:" | awk '{ print $2 }' | sort -nr | head -n 1`
	[ -z "$max_lun_id" ] && return
	max_lun_id=$[ $max_lun_id + 1]
	$TGTADM -C $ctl_port --mode target --op show | tr '\n' '_' | sed "s/Target.*\(LUN: 0.*\)Account information:.*/\1 LUN: ${max_lun_id=}/" | sed "s/.*\(LUN: ${lun_id}.*\)LUN: $[$lun_id + 1].*/\1/" | tr '_' '\n'
}

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`
	if [ -n "$ctl_port" ] ; then
		online=`get_lun_info $ctl_port $lun_id | grep "Online:" | tr '\n' ' ' | awk '{ print $2 }'`
	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
		$TGTADM -C $ctl_port --mode target --op show | grep "Initiator: " | \
			awk '{ printf " Initiator:   %s (%s)\n", $2,$4}'
	fi
}

function print_initiators_xml {
	local target="$1"
	ctl_port=`pcs_iscsi_get_ctl_port $target`
	if [ -n "$ctl_port" ] ; then
	    echo -e "<initiators>"
		$TGTADM -C $ctl_port --mode target --op show | grep "Initiator: " | \
			awk '{ printf "<initiator>\n<iqn>%s</iqn>\n<host>%s</host>\n</initiator>\n", $2,$4}'
	    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 lun_id="$4"
	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
	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 "$XML_MODE" ] ; then
		echo "<?xml version=\"1.0\"?>"
		echo "<targets>"
		print_target_xml $ISCSI_ROOT/$target $target $is_local $PRINT_ATTR
		echo "</targets>"
	else

	    echo "Target $target:"
	    echo " Portals:     `get_portals $target`"
	    echo " Status:      $status"
	    echo " Registered:  $is_local"
	    echo " Host:        $host"
	    
	    [ "$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=""
		fi
		if [ -z "$XML_MODE" ] ; then
			print_target $ISCSI_ROOT/$target $target $is_local
		else
			print_target_xml $ISCSI_ROOT/$target $target $is_local
		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
}

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
