#!/bin/bash #set -x # # block-iscsimple - 2014-07-01-000 - Thierry Jaouen # # Based on: block-iscsi - 2009 Keith Herron # # Simple iscsi connector for Xen : # - Faster # - No multipath (I don't known what it is) # - Accept different "Lun" (see syntax below) # - login/logout only when necessary # # Note: This script does NOT depends on a block-iscsimple.conf file # located in the same directory. # This optionnal file contains an array of available iSCSI target IPs # # Syntaxe: # Only iscsi: # iscsimple:{lunX@}iqn..... # With Raid-1 support: # iscsimple:{lunX@}iqn+ # # is define in the file block-iscsimple-tab.conf # # # Examples: # iscsimple:iqn.2014-06.fr.thierry-jaouen:share-sn-0001+test_raid # # In the "block-iscsimple-tab.conf" file, you have got: # test_raid /dev/vg0/my-disk # NAME=$( basename "$0" ) dir=$(dirname "$0") . "$dir/block-common.sh" # Log which mode we are in logger -t "$NAME" "*** Beginning device ${command} ***" # Fetch the param we specify in the domu config file PARAMS=`xenstore_read "${XENBUS_PATH}/params"` # This is a RAID1 "LABEL" ? RAID1_NAME=$( echo "${PARAMS}" | sed -n -e 's/^.\++\([-_0-9a-zA-Z]\+\)$/\1/Ip' ) if [ -n "${RAID1_NAME}" ]; then # NAME+IQN TAB_FILE="$dir/$NAME-tab.conf" if [ ! -r "${TAB_FILE}" ] ; then logger -t "$NAME" "File ${TAB_FILE} required." fi # Check for mdadm if ! [ -x "$( which mdadm )" ]; then logger -t "$NAME" "No mdadm utility - install mdadm for RAID-1 support." exit 1 fi ISCSI_PARAM=$( echo "${PARAMS}" | awk -F'+' '{ print $1 }' ) DEVICE_PARAM=$( awk -F' ' '/^'"${RAID1_NAME}"'\s+/ { print $2 }' "${TAB_FILE}" | head -1 ) if [ -z "${DEVICE_PARAM}" ]; then logger -t "$NAME" "No device found in ${TAB_FILE}" fi if [ -z "${ISCSI_PARAM}" ] || [[ "${ISCSI_PARAM}" = MISSING* ]]; then # No ISCSI drive ISCSI_PARAM='' fi if [[ "${DEVICE_PARAM}" = MISSING* ]]; then # No local device DEVICE_PARAM='' fi if [ -z "${ISCSI_PARAM}" ] && [ -z "${DEVICE_PARAM}" ]; then logger -t "$NAME" "No drive for starting RAID-1" exit 1 fi else # IQN simple ISCSI_PARAM="${PARAMS}" fi if [ -n "${ISCSI_PARAM}" ]; then # Check for iscsiadm if ! [ -x "$( which iscsiadm )" ]; then logger -t "$NAME" "No iscsiadm utility - install open-iscsi." exit 1 fi if [ -r "$dir/${NAME}.conf" ]; then . "$dir/${NAME}.conf" # We define portal IP in order to support new luns which don't yet have # /var/lib/iscsi/node entrys yet, not dynamic but avoids manual discovery # for PORTAL in ${PORTALS[@]}; do logger -t "$NAME" `iscsiadm -m discovery -t st -p "${PORTAL}"` done fi # ISCSI_PARAM=IQN.... or ISCSI_PARAM=LunXXX@IQN.... # Example: iqn.2014-06.fr.thierry-jaouen:partage1-sn-1234 # Lun0@iqn.2014-06.fr.thierry-jaouen:partage1-sn-1234 # If exists, extract "Lun" number IQN_LUN=$( echo "$ISCSI_PARAM" | sed -n -e 's/^lun\([0-9]\+\)@iqn\..\+$/\1/Ip' ) if [ -n "$IQN_LUN" ]; then IQN=$( echo "$ISCSI_PARAM" | sed -n -e 's/^lun[0-9]\+@\(iqn\..\+\)$/\1/Ip' ) else IQN_LUN="0" IQN=$ISCSI_PARAM fi fi if [ -n "${RAID1_NAME}" ]; then logger -t "$NAME" "RAID1:${RAID1_NAME} IQN:${IQN} LUN:${IQN_LUN} DEVICE:${DEVICE_PARAM}" else logger -t "$NAME" "IQN:${IQN} LUN:${IQN_LUN}" fi if [ -n "${ISCSI_PARAM}" ]; then # Using the iscsi node directory we can determine the ip and port of # our iscsi target on a lun by lun basis # [ -d /var/lib/iscsi/nodes ] && NODES_DIR=/var/lib/iscsi/nodes [ -d /etc/iscsi/nodes ] && NODES_DIR=/etc/iscsi/nodes if [ -z "${NODES_DIR}" ]; then logger -t "$NAME" "Do not know where iscsi node directory is located" exit 1 fi if IPPORT=$( ls "${NODES_DIR}/${IQN}/" ) ; then IP=$( echo "${IPPORT}" | cut -d , -f 1 ) PORT=$( echo "${IPPORT}" | cut -d , -f 2 ) fi if [ -z "${IP}" ] || [ -z "${PORT}" ]; then logger -t "$NAME" "iscsi node not found. Try 'discovery' with iscsiadm" exit 1 fi logger -t "$NAME" "TARGET: ${IP}:${PORT}" fi # ----------------------------------------------------- # This is called by each command to determine which device to use # function guess_md_dev { MD_DEV='' if MD_DEV=$( readlink --canonicalize-existing "/dev/md/${RAID1_NAME}" ) ; then if [ -e "${MD_DEV}" ]; then logger -t "$NAME" "md device: ${MD_DEV}" else MD_DEV='' fi fi } function assemble_md { MD_DEV_PREEXIST=1 guess_md_dev if [ -z "${MD_DEV}" ]; then MD_DEV_PREEXIST=0 if mdadm --quiet --build "/dev/md/${RAID1_NAME}" --level=1 --raid-devices=2 $@ ; then guess_md_dev fi fi } function guess_iscsi_dev { # Now we determine which /dev/sd* device belongs to the iqn SCSI_DEV='' if SCSI_DEV=$( readlink --canonicalize-existing "/dev/disk/by-path/ip-${IP}:${PORT}-iscsi-${IQN}-lun-${IQN_LUN}" ) ; then if [ -e "${SCSI_DEV}" ]; then logger -t "$NAME" "scsi device: ${SCSI_DEV}" else SCSI_DEV='' fi fi } function add_iscsi { for i in 0 0.25 0.5 1 0 1 1 do guess_iscsi_dev if [ -n "${SCSI_DEV}" ]; then logger -t "$NAME" "adding device: ${SCSI_DEV}" # keep a list of added device... echo "${SCSI_DEV}" >> "${DEVLIST}" break fi if [ "$i" = "0" ]; then if RESULT=$( iscsiadm -m node -T "${IQN}" -p "${IP}:${PORT}" --login 2>&1 ) ; then echo "$RESULT" | logger -t "$NAME" sleep 0.25 else ERR_CODE=$? echo "$RESULT" | logger -t "$NAME" logger -t "$NAME" "Abort with iscsiadm error: $ERR_CODE" break fi else sleep $i fi done } function remove_iscsi { if [ -e "${SCSI_DEV}" ]; then logger -t "$NAME" "flushing buffers on ${SCSI_DEV}" blockdev --flushbufs "${SCSI_DEV}" # remove this device from the list... sed -i -e "\%^${SCSI_DEV}\$%d" "${DEVLIST}" if [ ! -s "${DEVLIST}" ]; then # the list is empty: we can logout logger -t "$NAME" "attempting logout of ${IQN} on ${IP}:${PORT}" iscsiadm -m node -T "${IQN}" -p "${IP}:${PORT}" --logout 2>&1 | logger -t "$NAME" rm -f "${DEVLIST}" fi fi } # ----------------------------------------------------- WORKDIR="/var/run/xen-hotplug/${NAME}/${IQN}" LOCKFILE="${WORKDIR}/lock" DEVLIST="${WORKDIR}/dev.list" [ -d "${WORKDIR}" ] || mkdir -p "${WORKDIR}" >/dev/null 2>&1 lockfile -1 -l 10 -s 0 "${LOCKFILE}" || exit 1 # TRAP -------- trap "rm -f \"${LOCKFILE}\"" 0 # ------------- case $command in add) if [ -n "${RAID1_NAME}" ]; then if [ -n "${ISCSI_PARAM}" ]; then # Not "MISSING"... # Login to the target logger -t "$NAME" "logging in to ${IQN} on ${IP}:${PORT}" add_iscsi if [ -n "${SCSI_DEV}" ]; then logger -t "$NAME" "md: assemble ..." assemble_md "missing" "--write-mostly" "${SCSI_DEV}" if [ -n "${MD_DEV}" ]; then ERR=0 if [ $MD_DEV_PREEXIST -ne 0 ] ; then logger -t "$NAME" "Device $MD_DEV already create. Continue." elif [ -n "${DEVICE_PARAM}" ] ; then # Ajouter le disk local logger -t "$NAME" "Adding ${DEVICE_PARAM} ..." if ! mdadm --quiet "${MD_DEV}" --add "${DEVICE_PARAM}" ; then ERR=$(( ERR+1 )) fi fi if [ $ERR -eq 0 ]; then logger -t "$NAME" "$command complete. Dev: ${MD_DEV}" write_dev "${MD_DEV}" exit 0 fi logger -t "$NAME" "Abort $MD_DEV" mdadm --quiet --stop "${MD_DEV}" fi remove_iscsi else logger -t "$NAME" "No device" fi else logger -t "$NAME" "md: assemble ..." assemble_md "${DEVICE_PARAM}" "missing" if [ -n "${MD_DEV}" ]; then # OK logger -t "$NAME" "$command complete. Dev: ${MD_DEV}" write_dev "${MD_DEV}" exit 0 fi fi else # Login to the target logger -t "$NAME" "logging in to ${IQN} on ${IP}:${PORT}" add_iscsi if [ -n "${SCSI_DEV}" ]; then logger -t "$NAME" "$command complete. Dev: ${SCSI_DEV}" write_dev "${SCSI_DEV}" exit 0 else logger -t "$NAME" "No device" fi fi exit 1 ;; remove) if [ -n "${RAID1_NAME}" ]; then guess_md_dev if [ ! -n "${MD_DEV}" ]; then logger -t "$NAME" "No device found for ${RAID1_NAME}" exit 1 else if mdadm --quiet --stop "${MD_DEV}" ; then if [ -n "${ISCSI_PARAM}" ]; then guess_iscsi_dev remove_iscsi fi fi fi else guess_iscsi_dev remove_iscsi fi ;; esac # ----------------------------------------------------- # EOF