#!/bin/bash -e
#
# Open-iSCSI Xen block device hotplug script
#
# Author Roger Pau Monné <roger.pau@citrix.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation; version 2.1 only. with the special
# exception on linking described in file LICENSE.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# Usage:
#
# Target should be specified using the following syntax:
#
# script=block-iscsi,vdev=xvda,target=iqn=<iqn>,portal=<portal IP>
#
# Portal address must be in IP format.
#

dir=$(dirname "$0")
. "$dir/block-common.sh"

remove_label()
{
    echo $1 | sed "s/^\("$2"\)//"
}

check_tools()
{
    if ! command -v iscsiadm > /dev/null 2>&1; then
        fatal "Unable to find iscsiadm tool"
    fi
    if [ "$multipath" = "y" ] && ! command -v multipath > /dev/null 2>&1; then
        fatal "Unable to find multipath"
    fi
}

# Sets the following global variables based on the params field passed in as
# a parameter: iqn, portal, auth_method, user, multipath, password
parse_target()
{
    # set multipath default value
    multipath="n"
    for param in $(echo "$1" | tr "," "\n")
    do
        case $param in
        iqn=*)
            iqn=$(remove_label $param "iqn=")
            ;;
        portal=*)
            portal=$(remove_label $param "portal=")
            ;;
        multipath=*)
            multipath=$(remove_label $param "multipath=")
            ;;
        esac
    done
    if [ -z "$iqn" ] || [ -z "$portal" ]; then
        fatal "iqn= and portal= are required parameters"
    fi
    if [ "$multipath" != "y" ] && [ "$multipath" != "n" ]; then
        fatal "multipath valid values are y and n, $multipath not a valid value"
    fi
}

# Sets $dev to point to the device associated with the value in iqn
find_device()
{
    count=0
    while [ ! -e /dev/disk/by-path/*"$iqn"-lun-0 ]; do
        sleep 0.1
        count=`expr $count + 1`
        if [ count = 100 ]; then
            # 10s timeout while waiting for iSCSI disk to settle
            fatal "timeout waiting for iSCSI disk to settle"
        fi
    done
    sddev=$(readlink -f /dev/disk/by-path/*"$iqn"-lun-0 || true)
    if [ ! -b "$sddev" ]; then
        fatal "Unable to find attached device path"
    fi
    if [ "$multipath" = "y" ]; then
        mdev=$(multipath -ll "$sddev" | head -1 | awk '{ print $1}')
        if [ ! -b /dev/mapper/"$mdev" ]; then
            fatal "Unable to find attached device multipath"
        fi
        dev="/dev/mapper/$mdev"
    else
        dev="$sddev"
    fi
}

# Attaches the target $iqn in $portal and sets $dev to point to the
# multipath device
attach()
{
    do_or_die iscsiadm -m node --targetname "$iqn" -p "$portal" --login > /dev/null
    find_device
}

# Discovers targets in $portal and checks that $iqn is one of those targets
# Also sets the auth parameters to attach the device
prepare()
{
    # Check if target is already opened
    iscsiadm -m session 2>&1 | grep -q "$iqn" && fatal "Device already opened"
    # Discover portal targets
    iscsiadm -m discovery -t st -p $portal 2>&1 | grep -q "$iqn" || \
        fatal "No matching target iqn found"
}

# Attaches the device and writes xenstore backend entries to connect
# the device
add()
{
    attach
    write_dev $dev
}

# Disconnects the device
remove()
{
    find_device
    do_or_die iscsiadm -m node --targetname "$iqn" -p "$portal" --logout > /dev/null
}

command=$1
target=$(xenstore-read $XENBUS_PATH/params || true)
if [ -z "$target" ]; then
    fatal "No information about the target"
fi

parse_target "$target"

check_tools || exit 1

case $command in
add)
    prepare
    add
    ;;
remove)
    remove
    ;;
*)
    exit 1
    ;;
esac
