
######################################################################
#"@(#) A.01.00  Package PR cleanup Script"
#
# (C) Copyright 2008-2016 Hewlett-Packard Development Company, L.P.
# @(#) Serviceguard Package Persistent reservation cleanup script.
# @(#) Product Name    : HP Serviceguard
# @(#) Product Version : A.12.10.00
# @(#) Patch Name      : 
#
# *** Note: This file MUST NOT be edited. *****
#
# Any changes made to it will be overwritten when you upgrade to the
# next release of HP Serviceguard.
#
# Changing this file may lead to unrecoverable package startup
# and/or shutdown problems.
#
#
# SYNOPSIS
#    pr_cleanup lun [-v] [-k key] physical_volume_path ... | -f target_devices_file
#    pr_cleanup [-v] [-k key] volume_group ... | -f target_devices_file
#
#    "lun"   When physial volume paths are specified, this option is
#            mandatory. When used with -f target_devices_file each
#            entry in the file is treated as a physical volume.
#    "-f"    If this option is specified then the DSFs are contained
#            in a file whose full path name follows immediately.
#    -k      Key - The key value to use in clear operation. It should
#            be the node's PR key.
#    "-v"    Verbose output
#
#
# DESCRIPTION:
# This script is called to remove any existing persistent reservations
# from a Volume group or a set of LUNs.  The PR registrations were
# assumed to be put there by a package running on current or another
# node in the cluster where the package did not shutdown gracefully.
#
# For each specified LUN A PR_clear action will be performed.  In order
# to do this the script must first register a PR key with the LUN.
# The script will first attempt to use a REGISTER PR operation to
# register with the node.  If that fails then it will attempt to use
# a REGISTER_AND_IGNORE operation. The latter operation will over ride
# any previously registered PR key values that the initiator is
# registered with. While REGISTER_AND_IGNORE is a more robust operation,
# it will ignore any existing reservations made by the initiators of
# the node and since not all devices support it, we first attempt a
# register. If both operations fail then we assume either the device
# doesn't support PR, log a message and exit from the script.
# If the "lun" option is specified then the script assumes that DSFs
# are for LUN devices not volume groups.
# Finally a -f indicates that the names of the LUN devices or volume
# groups are contained in the file following the -f option.
#
# EXAMPLE:
# Clear all the PR reservations in the LUNs listed in the file.
#
#       pr_cleanup -k abc12 lun -f /etc/list_of_disks
#
# Clear all the PR reservations in the volume group listed on the
# command line.
#
#       pr_cleanup -k abcde vg01
#
###############################################################################
USE_VG=1
FILENAME=""
GETFILE=0
DEVLIST=""
PV_NAME=""
VERBOSE=0
GETKEY=0
KEY=""
LVMADM="/usr/sbin/lvmadm -l"
PV_NAMES=""
UNAME="$(uname)"
DM_PATH=""
MPATH_LIST="multipath -ll"
DM_DETAILS="dmsetup info"

test -f /etc/redhat-release && \
if /bin/grep "Maipo" /etc/redhat-release >/dev/null 2>&1
then
    OS_Version="RH7"
fi

DEVICE_MAPPER_PATTERN="-e s/_part[0-9]+$// -e s/-part[0-9]+$// -e s/p[0-9]+$//"

function Usage
{
    echo "Usage : pr_cleanup lun [-v] [-k key] physical_volume_path ... | -f target_devices_file"
    echo "Usage : pr_cleanup [-v] [-k key] volume_group... | -f target_devices_file"
    echo "\"lun\"   When physical volume paths are specified, this option is"
    echo "        mandatory."
    echo "\"-v\"    If used, specifies verbose output"
    echo "\"-k\"    Key - The key value to use in the clear operation. It should"
    echo "        be the node's PR key."
    echo "\"-f\"    If this option is specified then the physical_volume_path/volume groups"
    echo "        are contained in a file whose full path name follows immediately."
    echo "\"physical_volume_path\" multiple physical volumes can be specified on"
    echo "the command line, if -f target_devices_file is not used."
    echo "\"volume_group\" multiple volume groups can be specified on the command"
    echo "line, if -f target_devices_file is not used."
}

function clear_keys {
    DSF=$1
    DEV_NAME=$2
    sg_persist --out -C --param-rk=$KEY $DSF > /dev/null 2>&1
    RET=$?
    if [ $RET -ne 0 ]
    then
        sg_persist --out -G --param-sark=$KEY $DSF > /dev/null 2>&1
        RET=$?
        if [ $RET -ne 0 ]
        then
            sg_persist --out -I --param-sark=$KEY $DSF > /dev/null 2>&1
            RET=$?
        fi
        if [ $RET -eq 0 ]
        then
            sg_persist --out -C --param-rk=$KEY $DSF > /dev/null 2>&1
            RET=$?
            if [ $RET -eq 0 ]
            then
                echo "Successfully cleared PR keys for $DEV_NAME"
                return $RET
            else
                echo "Unable to clear PR keys for $DEV_NAME"
                return $RET
            fi
        else
            echo $DEV_NAME" does not support PR operation"
            return $RET
        fi
    else
        echo "Successfully cleared PR keys for $DEV_NAME"
        return $RET
    fi
}

#######################################################################
# Converts disk to a suitable format for the sg_persist command
#######################################################################
function get_character_disk_format
{
    if [ "$UNAME" != "Linux" ]
    then
        PV_NAME=`echo $1 | sed -e 's/^\/dev\/dsk\//\/dev\/rdsk\//' -e 's/^\/dev\/disk\//\/dev\/rdisk\//' \
                  -e 's/^\/dev\/cdisk\//\/dev\/rcdisk\//'`
    else
       PV=$1
       PV_tmp=`readlink $PV`
       if [ $? -eq 0 ]
       then
           PV="/dev/"$PV_tmp
       fi
        PV_NAME=`echo $PV | sed 's/[0-9]*//g'`
    fi
}

#######################################################################
# Get all PV names and copy to an array. 
#######################################################################
function get_all_pv_names
{
   if [ "$UNAME" != "Linux" ]
   then
       PV_tmp=`echo $1|sed 's:/:\\\/:g'`
       PV_NAMES=`$LVMADM | awk '/'$PV_tmp'$/ {for( getline; $1 != "VG" && $0 != ""  ;getline )print $NF}'`
   else
       PV_NAMES=`vgdisplay -v $1 2> /dev/null | fgrep "PV Name" | awk '{print $NF}'`
    fi
}


#######################################################################
# Checks if the given device is a multipath device.
# If it is a multipath device, we get one of the active path. This path
# will be passed to clear_keys function to clear the PR keys.
# this function is only for the Linux, for HP-UX it just returns false
#######################################################################
function check_if_multipath
{
   if [ "$UNAME" != "Linux" ]
   then
      return 1
   else
      typeset device=$1
      DM_PATH=""

      echo $device | grep -q -e "/dev/mapper/" -e "/dev/mpath/"
      if [ $? -eq 0 ]
      then
         if [ "$OS_Version" = "RH7" ]
         then
             dm_dev=`echo $device | sed -r -e 's/\/dev\/mapper\///' -e 's/\/dev\/mpath\///'`
             dm_dev_uuid=`$DM_DETAILS $dm_dev | grep UUID | awk -F- '{print $NF}'`
             DM_PATH="/dev/"$($MPATH_LIST $dm_dev_uuid | grep sd | grep active | sed 's/|//g' \
                                      | awk '{print $3}' | awk 'NR==1 {print}' | sed 's/[0-9]*//g')
         else
             dm_dev=`echo $device | sed -r -e 's/\/dev\/mapper\///' -e 's/\/dev\/mpath\///' \
                                      $DEVICE_MAPPER_PATTERN`
             DM_PATH="/dev/"$($MPATH_LIST $dm_dev | grep sd | grep active | sed 's/|//g' \
                                      | awk '{print $3}' | awk 'NR==1 {print}' | sed 's/[0-9]*//g')
         fi
         return 0
      else
         udev_tmp=`readlink $device`
         if [ $? -eq 0 ]
         then
            echo $udev_tmp | grep -q "dm-"
            if [ $? -eq 0 ]
            then
                if [ "$OS_Version" = "RH7" ]
                then 
                    dm_dev=$($DM_DETAILS "/dev/"$(basename `readlink $device`) | grep UUID | \
                           awk -F- '{print $NF}') > /dev/null 2>&1
                else
                    dm_dev=$($DM_DETAILS "/dev/"$(basename `readlink $device`) | grep Name | \
                           awk '{print $2}'| sed -r $DEVICE_MAPPER_PATTERN) > /dev/null 2>&1
                fi 
                DM_PATH="/dev/"$($MPATH_LIST $dm_dev | grep sd | grep active | sed 's/|//g' \
                                 | awk '{print $3}' | awk 'NR==1 {print}' | sed 's/[0-9]*//g')
                return 0
            fi
         fi
         return 1
      fi
    fi
}

##########################################
# Process the command line arguments.
##########################################
if [ "$#" = "0" ]
then
    echo "No options provided."
	Usage
	exit 1
fi

for ARG in $*
do
    # If the previous argument processed was the -F option then the
    # should be the filename
    #
    if [ $GETFILE -eq 1 ]
    then
        FILENAME=$ARG
        GETFILE=0
    else
        if [ $GETKEY -eq 1 ]
        then
            KEY=$ARG
            GETKEY=0
        else
            if [ "$ARG" = "lun" ]
            then
                USE_VG=0
            else
                if [ "$ARG" = "-f" ]
                then
                    GETFILE=1
                else
                    if [ "$ARG" = "-k" ]
                    then
                        GETKEY=1
                    else
                        if [ "$ARG" = "-v" ]
                        then
                            VERBOSE=1
                        else
                            if [ "$ARG" = "-h" ]
                            then
                                Usage
                                exit 0
                            else
                                DEVLIST=$DEVLIST" "$ARG
                            fi
                        fi
                    fi
                fi
            fi
        fi
    fi
done

if [ "$FILENAME" = "" ] && [ "$DEVLIST" = "" ]
then
    echo "Physical volume path/Volume group must be specified." 
	Usage
	exit 1
fi

##########################################################################
# If the -F option was specified lets make sure that we have a valid file.
##########################################################################
if [ "$FILENAME" = "" ]
then
    if [ $VERBOSE -eq 1 ]
    then
        echo "not using a file"
    fi
else
    if [ ! -f $FILENAME ]
    then
        echo "file "$FILENAME " is not a regular file"
        exit 1
    fi

    if [ ! -s $FILENAME ]
    then
        echo "file "$FILENAME " is empty"
        exit 1
    fi

    if [ ! -r $FILENAME ]
    then
        echo "file "$FILENAME " is not readable"
        exit 1
    fi

    if [ $VERBOSE -eq 1 ]
    then
        echo "Using file "$FILENAME 
    fi
fi

if [ $VERBOSE -eq 1 ]
then
    if [ $USE_VG -eq 1 ]
    then
        echo "Processing volume groups"
    else
        echo "Processing LUN devices"
    fi
fi

# If the caller did not provide a PR key then lets attempt to get
# the SG cluster pr_key for this node.  If that fails then just
# use a random value of 01.
#
if [ "$KEY" = "" ]
then
    HOST=$(uname -n | cut -d . -f 1)
    KEY=$(cmviewcl -f line -n $HOST 2> /dev/null | fgrep node_pr_key | cut -d = -f 2)
    if [ "$KEY" = "" ]
    then
        KEY="01"
    fi
fi

######################################################################### 
# Process the request
#########################################################################

if [ "$FILENAME" = "" ]
then
    for DEV in $DEVLIST
    do
        if [ $USE_VG -eq 1 ]
        then
                     
            get_all_pv_names $DEV
            PVNAMES=$PV_NAMES
            if [ "$PVNAMES" = "" ]
            then
                echo "Volume group $DEV does not exist."
                exit 1
            fi
            for DSF in $PVNAMES
            do
                DEV_NAME=$DSF
                check_if_multipath $DSF
                if [ $? -ne 0 ]
                then
                    get_character_disk_format $DSF
                    DSF=$PV_NAME
                else
                    DSF=$DM_PATH
                fi
                clear_keys $DSF $DEV_NAME
                RET=$?
                if [ $RET -ne 0 ];
                then
                    exit $RET
                fi
            done
        else
            if [ -c $DEV -o -b $DEV ]
            then
                DEV_NAME=$DEV
                check_if_multipath $DEV
                if [ $? -eq 0 ]
                then
                    DEV=$DM_PATH
                fi
                clear_keys $DEV $DEV_NAME
                RET=$?
                if [ $RET -ne 0 ];
                then
                    exit $RET
                fi
            else
                echo $DEV "does not exist"
            fi
        fi
    done
else
    for DEV in `cat $FILENAME`
    do
        if [ $USE_VG -eq 1 ]
        then        
            get_all_pv_names $DEV
            PVNAMES=$PV_NAMES
            if [ "$PVNAMES" = "" ]
            then
                echo "Volume group $DEV does not exist."
                exit 1
            fi
            for DSF in $PVNAMES
            do
              DEV_NAME=$DSF
              check_if_multipath $DSF
              if [ $? -ne 0 ]
              then
                  get_character_disk_format $DSF
                  DSF=$PV_NAME
              else
                  DSF=$DM_PATH
              fi
              clear_keys $DSF $DEV_NAME
              RET=$?
              if [ $RET -ne 0 ];
              then
                  exit $RET
              fi
            done
        else
            if [ -c $DEV -o -b $DEV ]
            then
                DEV_NAME=$DEV
                check_if_multipath $DEV
                if [ $? -eq 0 ]
                then
                    DEV=$DM_PATH
                fi
                clear_keys $DEV $DEV_NAME
                RET=$?
                if [ $RET -ne 0 ];
                then
                    exit $RET
                fi
           else
               echo "device "$DEV "does not exist"
           fi
        fi
    done
fi
