#!/bin/bash # Usage: block-npiv [add npiv | remove dev] dir=$(dirname "$0") . "$dir/block-common.sh" #set -x #command=$1 # Look for the NPIV vport with the WWPN # $1 contains the WWPN (assumes it does not contain a leading "0x") find_vhost() { unset vhost # look in upstream locations for fchost in /sys/class/fc_vports/* ; do if test -e $fchost/port_name ; then wwpn=`cat $fchost/port_name | sed -e s/^0x//` if test $wwpn = $1 ; then # Note: makes the assumption the vport will always have an scsi_host child vhost=`ls -d $fchost/device/host*` vhost=`basename $vhost` return fi fi done # look in vendor-specific locations # Emulex - just looks like another scsi_host - so look at fc_hosts... for fchost in /sys/class/fc_host/* ; do if test -e $fchost/port_name ; then wwpn=`cat $fchost/port_name | sed -e s/^0x//` if test $wwpn = $1 ; then # Note: makes the assumption the vport will always have an scsi_host child vhost=`basename $fchost` return fi fi done } # Create a NPIV vport on the fabric w/ FABRICNM, with WWPN,WWNN # $1 contains FABRICNM # $2 contains the VPORT WWPN # $3 contains the VPORT WWNN # (assumes no name contains a leading "0x") create_vport() { # find a base adapter with npiv support that is on the right fabric # Look via upstream interfaces for fchost in /sys/class/fc_host/* ; do if test -e $fchost/vport_create ; then # is the link up, w/ NPIV support ? pstate=`cat $fchost/port_state` ptype=`cat $fchost/port_type | cut -c 1-5` fname=`cat $fchost/fabric_name | sed -e s/^0x//` if [ $pstate = "Online" -a $ptype = "NPort" -a $fname = $1 ] ; then vmax=`cat $fchost/max_npiv_vports` vinuse=`cat $fchost/npiv_vports_inuse` avail=`expr $vmax - $vinuse` if [ $avail -gt 0 ] ; then # create the vport echo $2":"$3 > $fchost/vport_create if [ $? -eq 0 ] ; then return 0 fi # failed - so we'll just look for the next adapter fi fi fi done # Look in vendor-specific locations # Emulex: interfaces mirror upstream, but are under adapter scsi_host for shost in /sys/class/scsi_host/* ; do if [ -e $shost/vport_create ] ; then fchost=`ls -d $shost/device/fc_host*` # is the link up, w/ NPIV support ? pstate=`cat $fchost/port_state` ptype=`cat $fchost/port_type | cut -c 1-5` fname=`cat $fchost/fabric_name | sed -e s/^0x//` if [ $pstate = "Online" -a $ptype = "NPort" -a $fname = $1 ] ; then vmax=`cat $shost/max_npiv_vports` vinuse=`cat $shost/npiv_vports_inuse` avail=`expr $vmax - $vinuse` if [ $avail -gt 0 ] ; then # create the vport echo $2":"$3 > $shost/vport_create if [ $? -eq 0 ] ; then return 0 fi # failed - so we'll just look for the next adapter fi fi fi done return 1 } # Look for the LUN on the indicated scsi_host (which is an NPIV vport) # $1 is the scsi_host name (normalized to simply the hostX name) # $2 is the WWPN of the tgt port the lun is on # Note: this implies we don't support a multipath'd lun, or we # are explicitly identifying a "path" # $3 is the LUN number of the scsi device find_sdev() { unset dev hostno=${1/*host/} for sdev in /sys/class/scsi_device/${hostno}:*:$3 ; do if test -e $sdev/device/../fc_trans*/port_name ; then tgtwwpn=`cat $sdev/device/../fc_trans*/port_name | sed -e s/^0x//` if test $tgtwwpn = $2 ; then if test -e $sdev/device/block* ; then dev=`readlink $sdev/device/block*` dev=${dev##*/} return fi fi fi done } # Look for the NPIV vhost based on a scsi "sdX" name # $1 is the "sdX" name find_vhost_from_dev() { unset vhost hostno=`readlink /sys/block/$1/device` hostno=${hostno##*/} hostno=${hostno%%:*} if test -z "$hostno" ; then return; fi vhost="host"$hostno } # We're about to terminate a vhost based on a scsi device # Flush all nodes on that vhost as they are about to go away # $1 is the vhost flush_nodes_on_vhost() { if test ! -x /sbin/blockdev ; then return; fi hostno=${1/*host/} for sdev in /sys/class/scsi_device/${hostno}:* ; do if test -e $sdev/device/block* ; then dev=`readlink $sdev/device/block*` dev=${dev##*/} dev="/dev/"$dev if test -n "$dev"; then blockdev --flushbufs $dev fi fi done } # Terminate a NPIV vhost # $1 is vhost delete_vhost() { # use upstream interface for vport in /sys/class/fc_vports/* ; do if test -e $vport/device/$1 ; then if test -e $vport/vport_delete ; then echo "1" > $vport/vport_delete if test $? -ne 0 ; then exit 6; fi sleep 4 return fi fi done # use vendor specific interface # Emulex if test -e /sys/class/fc_host/$1/device/../scsi_host*/lpfc_drvr_version ; then shost=`ls -1d /sys/class/fc_host/$1/device/../scsi_host* | sed s/.*scsi_host://` vportwwpn=`cat /sys/class/fc_host/$1/port_name | sed s/^0x//` vportwwnn=`cat /sys/class/fc_host/$1/node_name | sed s/^0x//` echo "$vportwwpn:$vportwwnn" > /sys/class/scsi_host/$shost/vport_delete if test $? -ne 0 ; then exit 6; fi sleep 4 return fi # Qlogic if test -e /sys/class/fc_host/$1/device/../scsi_host*/driver_version ; then shost=`ls -1d /sys/class/fc_host/$1/device/../scsi_host* | sed s/.*scsi_host://` vportwwpn=`cat /sys/class/fc_host/$1/port_name | sed s/^0x//` vportwwnn=`cat /sys/class/fc_host/$1/node_name | sed s/^0x//` echo "$vportwwpn:$vportwwnn" > /sys/class/scsi_host/$shost/vport_delete if test $? -ne 0 ; then exit 6; fi sleep 4 return fi exit 6 } case "$command" in add) # Params is one big arg, with fields separated by hyphens: # FABRIC-VPWWPN-VPWWNN-TGTWWPN-LUN# # arg 2 - Fabric Name # arg 3 - VPORT's WWPN # arg 4 - VPORT's WWNN # arg 5 - Target's WWPN # arg 6 - LUN # on Target # no wwn contains a leading 0x - it is a 16 character hex value # You may want to optionally pick a specific adapter ? par=`xenstore-read $XENBUS_PATH/params` || true #par=$2 NPIVARGS=$par; LUN=${NPIVARGS##*-*-*-*-}; NPIVARGS=${NPIVARGS%-*} if test $LUN = $NPIVARGS ; then exit 1; fi TGTWWPN=${NPIVARGS##*-*-*-}; NPIVARGS=${NPIVARGS%-*} if test $TGTWWPN = $NPIVARGS ; then exit 1; fi VPORTWWNN=${NPIVARGS##*-*-}; NPIVARGS=${NPIVARGS%-*} if test $VPORTWWNN = $NPIVARGS ; then exit 1; fi VPORTWWPN=${NPIVARGS##*-}; NPIVARGS=${NPIVARGS%-*} if test $VPORTWWPN = $NPIVARGS ; then exit 1; fi FABRICNM=$NPIVARGS # Ensure we compare everything using lower-case hex characters TGTWWPN=`echo $TGTWWPN | tr A-Z a-z` VPORTWWPN=`echo $VPORTWWPN | tr A-Z a-z` VPORTWWNN=`echo $VPORTWWNN | tr A-Z a-z` FABRICNM=`echo $FABRICNM | tr A-Z a-z` find_vhost $VPORTWWPN if test -z "$vhost" ; then create_vport $FABRICNM $VPORTWWPN $VPORTWWNN if [ $? -ne 0 ] ; then exit 2; fi sleep 8 find_vhost $VPORTWWPN if test -z "$vhost" ; then exit 3; fi fi find_sdev $vhost $TGTWWPN $LUN if test -z "$dev"; then echo "- - -" > /sys/class/scsi_host/$vhost/scan sleep 2 find_sdev $vhost $TGTWWPN $LUN fi if test ! -z "$dev"; then xenstore-write $XENBUS_PATH/node /dev/$dev write_dev /dev/$dev exit 0 fi exit 4 ;; remove) node=`xenstore-read $XENBUS_PATH/node` || true #node=$2 dev=$node; dev=${dev#/dev/} # this is really screwy. the first delete of a lun will # terminate the entire vport (all luns) find_vhost_from_dev $dev if test -z "$vhost" ; then exit 5; fi flush_nodes_on_vhost $vhost delete_vhost $vhost exit 0 ;; esac