1#!/bin/bash -e
2#
3# Open-iSCSI Xen block device hotplug script
4#
5# Author Roger Pau Monné <roger.pau@citrix.com>
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU Lesser General Public License as published
9# by the Free Software Foundation; version 2.1 only. with the special
10# exception on linking described in file LICENSE.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU Lesser General Public License for more details.
16#
17# Usage:
18#
19# Target should be specified using the following syntax:
20#
21# script=block-iscsi,vdev=xvda,target=iqn=<iqn>,portal=<portal IP>
22#
23# Portal address must be in IP format.
24#
25
26dir=$(dirname "$0")
27. "$dir/block-common.sh"
28
29remove_label()
30{
31    echo $1 | sed "s/^\("$2"\)//"
32}
33
34check_tools()
35{
36    if ! command -v iscsiadm > /dev/null 2>&1; then
37        fatal "Unable to find iscsiadm tool"
38    fi
39    if [ "$multipath" = "y" ] && ! command -v multipath > /dev/null 2>&1; then
40        fatal "Unable to find multipath"
41    fi
42}
43
44# Sets the following global variables based on the params field passed in as
45# a parameter: iqn, portal, auth_method, user, multipath, password
46parse_target()
47{
48    # set multipath default value
49    multipath="n"
50    for param in $(echo "$1" | tr "," "\n")
51    do
52        case $param in
53        iqn=*)
54            iqn=$(remove_label $param "iqn=")
55            ;;
56        portal=*)
57            portal=$(remove_label $param "portal=")
58            ;;
59        multipath=*)
60            multipath=$(remove_label $param "multipath=")
61            ;;
62        esac
63    done
64    if [ -z "$iqn" ] || [ -z "$portal" ]; then
65        fatal "iqn= and portal= are required parameters"
66    fi
67    if [ "$multipath" != "y" ] && [ "$multipath" != "n" ]; then
68        fatal "multipath valid values are y and n, $multipath not a valid value"
69    fi
70}
71
72# Sets $dev to point to the device associated with the value in iqn
73find_device()
74{
75    count=0
76    while [ ! -e /dev/disk/by-path/*"$iqn"-lun-0 ]; do
77        sleep 1
78        count=`expr $count + 1`
79        if [ count = 100 ]; then
80            # 10s timeout while waiting for iSCSI disk to settle
81            fatal "timeout waiting for iSCSI disk to settle"
82        fi
83    done
84    sddev=$(readlink -f /dev/disk/by-path/*"$iqn"-lun-0 || true)
85    if [ ! -b "$sddev" ]; then
86        fatal "Unable to find attached device path"
87    fi
88    if [ "$multipath" = "y" ]; then
89        mdev=$(multipath -ll "$sddev" | head -1 | awk '{ print $1}')
90        if [ ! -b /dev/mapper/"$mdev" ]; then
91            fatal "Unable to find attached device multipath"
92        fi
93        dev="/dev/mapper/$mdev"
94    else
95        dev="$sddev"
96    fi
97}
98
99# Attaches the target $iqn in $portal and sets $dev to point to the
100# multipath device
101attach()
102{
103    do_or_die iscsiadm -m node --targetname "$iqn" -p "$portal" --login > /dev/null
104    find_device
105}
106
107# Discovers targets in $portal and checks that $iqn is one of those targets
108# Also sets the auth parameters to attach the device
109prepare()
110{
111    # Check if target is already opened
112    iscsiadm -m session 2>&1 | grep -q "$iqn" && fatal "Device already opened"
113    # Discover portal targets
114    iscsiadm -m discovery -t st -p $portal 2>&1 | grep -q "$iqn" || \
115        fatal "No matching target iqn found"
116}
117
118# Attaches the device and writes xenstore backend entries to connect
119# the device
120add()
121{
122    attach
123    write_dev $dev
124}
125
126# Disconnects the device
127remove()
128{
129    find_device
130    do_or_die iscsiadm -m node --targetname "$iqn" -p "$portal" --logout > /dev/null
131}
132
133command=$1
134target=$(xenstore-read $XENBUS_PATH/params || true)
135if [ -z "$target" ]; then
136    fatal "No information about the target"
137fi
138
139parse_target "$target"
140
141check_tools || exit 1
142
143case $command in
144add)
145    prepare
146    add
147    ;;
148remove)
149    remove
150    ;;
151*)
152    exit 1
153    ;;
154esac
155