#!/bin/ksh
#
# ident   "@(#)confccdssa.sh 1.19   01/03/28 SMI"
#

# Define msg. file name and location
TEXTDOMAIN=confccdssa;  export TEXTDOMAIN
TEXTDOMAINDIR=/opt/SUNWcluster/locale;  export TEXTDOMAINDIR

# definitions for ckstr replacement - leave them global because they might
# be grepped for in code to see what to do next
no=`gettext "no"`
yes=`gettext "yes"`

# 
# Internationalized ckyorn/ ckstr with yes/no answers
# It returns the valid answer received, either "yes" or "no".
#
yorn ()
{
	typeset question="$*"
	typeset reply="" 
 	typeset wrong=`gettext "Please answer yes or no."`
 
 	while true; do
 		printf "${question}" 1>&2
 		read reply
 		case $reply in
 			$yes)
 			     break
 			     ;; 
 			$no)
 			     break
 			     ;; 
 			*)
 			     printf "${wrong}\n" 1>&2
 			     ;;
 		esac
 	done
 	
	print "${reply}"

}

################################################################################
#
# lookup a value in ccd static file
#
################################################################################

function enccdmatch
{
        ${mybin}/ccdmatch $* ${ccdfile} || \
                (log_error "$pre.4703" "ccdmatch $* ${ccdfile} failed" 1>&2; return 1)
}
################################################################################
#
# function Check_for_previous_sharedCCD
#
################################################################################
function Check_for_previous_sharedCCD
{
typeset vxdgtmp="/var/opt/SUNWcluster/run/vxdg.$$"
typeset i_dg="sc_dg"
typeset ccdvol="ccdvol"
typeset -i retval=0
typeset -i rstatus
typeset lmsg="unknown"

# Does sc_dg already exist? Couple of options:
#	1. it could exist
#	2. it could exist and is imported by the other host
#	3. it could exist but have failed components
#	4. does not exit
#
# We will attempt to find out which. Here are some of the return
# codes from vxdg that might help:
#
#       vxdg import <dg>
#       $? = 12 (disk group exists and is imported by me)
#       $? = 20 with message:
# ERROR: Disk group lh1: import failed: Disk is in use by another host
#       means that this dg exists but is imported by someone else.
#       $? = 20 with message:
# ERROR: Disk group lh3: import failed: No valid disk found containing dg
#       means that this dg does not exist.
#
# So...
#       if i get a status of 12 on the import its ok because its ok
#       	if i already have the dg imported.
#       if i get a 20 then tell the user that the dg is imported by
#       	someone else.
#	if i get a 20 and the second message then the dg does not
#		exist and I can create it.
#
# Lastly, if the dg does already exist, delete the ccdvol, if it
# exists. I will recreate that later in the script.
# NOTE: I only use the -f option to force import the dg if it
# has failed components. This option will not force the import if
# its imported by another host so this should be pretty safe.
	#run vxdg in C locale so it will output in english to file, because we're grepping
	# for text in this file below
        LC_ALL=C /usr/sbin/vxdg -f import ${i_dg} > ${vxdgtmp} 2>&1
        rstatus=$?
        if (( rstatus == 20 )); then
                # we ran vxdg in C locale so this message will still be printed in english;
                # don't localize
                if /bin/egrep 'Disk is in use by another host' ${vxdgtmp} \
                        > /dev/null 2>&1
                then
                        lmsg=`gettext "Error: The disk group %s already exists and\n\
is imported by another host. %s should be\n\
rerun on that node."`
			printf "${lmsg}\n" "${i_dg}" "$0"
                        /bin/rm -rf ${vxdgtmp}
                        exit 1
                # we ran vxdg in C locale so this message will still be printed in english;
                # don't localize
                elif /bin/egrep 'No valid disk found containing disk group' \
                        ${vxdgtmp} >/dev/null 2>&1
                then
                        lmsg=`gettext "The disk group %s does not exist.\n\
Will continue with the %s setup."`
			printf "${lmsg}\n" "${i_dg}" "${i_dg}"
                        /bin/rm -rf ${vxdgtmp}
		else
			lmsg=`gettext "Unknown vxdg import error. Attempt\n\
to import the %s disk group\n\
manually to resolve the problem."`
			printf "${lmsg}\n" "${i_dg}" 
			exit 1
                fi
        elif (( rstatus == 0 || rstatus == 12 )); then
# a status of 0 means we were able to import the dg,
# a status of 12 means we already had it imported.
# in either of these cases lets check to see of ccdvol exists.
# if so remove it since we will just rebuild it later.
		lmsg=`gettext "The disk group %s was found and has been imported\n\
on this node. This disk group will be used for the\n\
shared ccd. The %s volume will be recreated to\n\
make certain that no previous shared ccd database\n\
exists."`
		printf "${lmsg}\n" "${i_dg}" "${ccdvol}"
		prompt=`gettext "Do you want to continue (yes/no) ? "`
		resp=$(yorn "${prompt}")
		if [[ "$resp" = "${no}" ]]; then
			/usr/sbin/vxdg deport ${i_dg}
			exit 0
		fi

		/usr/sbin/vxinfo ${ccdvol} > /dev/null 2>&1
		if (( $? == 0 )); then
			/usr/sbin/vxedit -rf rm ${ccdvol} >/dev/null 2>&1
		fi
		retval=1

        else
                lmsg=`gettext "Error: An undetermined error was encountered while\
attempting\nto import disk group %s. Repair\
this before proceeding."`
		printf "${lmsg}\n" "${i_dg}" 
                /bin/rm -rf ${vxdgtmp}
                exit 1
        fi
        /bin/rm -rf ${vxdgtmp}

	return ${retval}
}

################################################################################
#
# function build_devset mode
#
# This function will build the 'devset' list that will be used when
# parsing up the devices. If the devset being built is for an
# ssa then the format: SSA:disk
#
# If the devset being built is for a disk then the format is:
# DISK:disk:serialno
#
# Input:  
#	passed in: mode
#		   'ssa' or 'disk' - tells which format to create
#	global:     ssa_array for ssa disks (set in function get_devices)
#		    disks_array and id_array for disks (set in
#		    function get_devices).
#
# Output: 
#	devset
#        

function build_devset
{
typeset mode
typeset devset
typeset i

mode=$1
devset=""
if [[ $mode == ssa ]]; then
 devices=${ssa_array[*]}
 for i in $devices; do
  devset="${devset} SSA:${i}"
 done
elif [[ $mode == disk ]]; then
 let i=0
 while (( i < ${#disks_array[*]} )); do
	devset="${devset} DISK:${disks_array[i]}:${id_array[i]}"
	(( i = i + 1 ))
 done 
fi

print "${devset}"
} 
################################################################################
#
# function construct_ssa_list <c#>.<WWN>...
#
# This function takes an argument list consisting of <c#>:<WWN>... The c# is the
# controller number on which a particular SSA is present and the WWN is the
# world-wide number of the corresponding SSA. The purpose of this function is to
# break up the input list into two different lists - one corresponding to the
# controller number list - ctlr_list - and the other corresponding to the WWN
# list - ssa_list. The controller number and the WWN have corresponding
# positions on the two output lists. This correspondence is relied upon by the
# calling function.
 
function construct_ssa_list
{
  ctlr_list=""
  ssa_list=""
 
  for item in $*; do
    ctlr=${item%%:*}
    ssa=${item##*:}
    ctlr_list="${ctlr_list} ${ctlr}"
    ssa_list="${ssa_list} ${ssa}"
  done
}

###############################################################################
#
# function get_devices [diskdevice]
#
# Get the list of disks attached to the system. Remove the root device 
# of the system from the final list. 
#
# Input:
#	diskdevice - extra device to remove from the final list.
#	This is used for the second list of available devices. By
#	providing as input here the disk device that was selected
#	for device 1, it can be eliminated here since the user
#	cannot select that disk again.
#
# Output:
#	devset - the list of all ssa disks or jbods.
 
function get_devices
{
  typeset -i j
  typeset rootdev
  set -A disks_array
  set -A id_array

  rootdev=$(${FINDDEVICES} rootdev | tr '\012' '')
  ssadevlist=$(${FINDDEVICES} ssa ${ssafile} | tr '\012' ' ')
  if (( $# == 1 )); then
	diskdevlist=$(${FINDDEVICES} disks | /bin/grep -v ${rootdev} | \
	 /bin/grep -v ${1} | tr '\012' ' ')
  else
  	diskdevlist=$(${FINDDEVICES} disks | /bin/grep -v ${rootdev} | \
	 tr '\012' ' ')
  fi
  construct_ssa_list ${ssadevlist}
  set -A ctlr_array ${ctlr_list}
  set -A ssa_array ${ssa_list}

  let j=0 
  for diskgroup in ${diskdevlist}; do
	disk=${diskgroup%%:*}
	diskid=${diskgroup##*:}

	#run in C locale so it will output in english, because we're grepping
	# for text from output below
	err1=$(LC_ALL=C /usr/sbin/vxdisk list ${disk} \
		| /bin/grep errno | /bin/awk '{print $1}')
	if [[ "$err1" = "errno:" ]] ; then
  		disks_array[j]=${disk}
  		id_array[j]=${diskid}
		let j=j+1
	fi
  done

  rootc_array=${rootdev%%t*}
 
  set -A ssaset $(build_devset ssa ) 
  set -A diskset $(build_devset disk)

  devset="${ssaset[*]} ${diskset[*]}"

}

###############################################################################
#
# function select_dev devset
#
# Select a device from among the set of
# devices in devset. The selected device 
# comes in one of three flavors - <WWN>,
# <WWN>.<disk serial id>, or <disk address>:<disk serial id>
 
function select_dev
{
  typeset devset
  typeset qdev
  typeset wwn
  typeset -i done
  typeset lmsg="unknown"

  deviceno=$1; shift 
  devset=`echo $*`

  if [[ -z "$devset" ]]; then
	lmsg=`gettext "\n=================================\n\
WARNING: No Storage Device found. Please check configuration\n\
================================="`
	printf "${lmsg}\n\n"  
	exit 1
  fi 
  lmsg=`gettext "\nSelect devices from list.\n\
Type the number corresponding to the desired selection.\n\
For example: 1<CR>"`
  printf "${lmsg}\n\n" 

  let done=0 
  PS3="Device ${deviceno}: "
  while (( done == 0 )); do
  select qdev in ${devset}; do
    if [[ -n "${qdev}" ]]; then
      
      if [[ "${qdev}" = SSA:* ]]; then
        wwn=${qdev##SSA:}
        select_qdisk_from_ssa ${wwn} 
	if [[ -z ${quorumdiskdev} ]]; then
		continue
	fi
	lmsg=`gettext "\nDisk %s with serial id %s\n\
in SSA %s has been selected as device %s."`
	printf "${lmsg}\n\n" "${quorumdiskdev%%:*}" "${quorumdiskdev##*:}" "${wwn}" "${deviceno}"
        ccddev=${quorumdiskdev%%*:}
	let done=1
      else
        qdev=${qdev##DISK:}
	lmsg=`gettext "\nDisk %s with serial id %s has been selected\n\
as device %s."`
	printf "${lmsg}\n\n" "${qdev%%:*}" "${qdev##*:}" "${deviceno}"
        ccddev=${qdev}
	let done=1
      fi
      break
    else
	lmsg=`gettext "\nInvalid Selection\n"`
	printf "${lmsg}\n" 
        continue
    fi
  done
  done # while
}

################################################################################
#
# function select_qdisk_from_ssa ssa
#
# This function provides the option of selecting a disk from the
# specified ssa. The ssa to be used is passed in as a parameter.
# If the root devices of these nodes reside in
# this ssa exclude the corresponding disks from the selection
# process.
 
function select_qdisk_from_ssa
{
  typeset ssa
  typeset item
  typeset td
  typeset diskdevaddr
  typeset disksid
  typeset ctlr_node
  typeset ssadisks
  typeset lmsg="unknown"

  ssa=$1
  disk_list=""
 
  ctlr_node=$(get_ctlr_for_ssa ${ssa})

  if [[ ${ccdpath1} != 0 ]]; then
	ssadisks=$(${FINDDEVICES} disks ${ctlr_node} | \
	  /bin/grep -v ${ccdpath1} | tr '\012' ' ')
  else 
  	ssadisks=$(${FINDDEVICES} disks ${ctlr_node} | tr '\012' ' ')
  fi
 
  let j=0
  for diskgroup in ${ssadisks}; do
        disk=${diskgroup%%:*}

	#run in C locale so it will output in english to file, because we're grepping
	# for text in this file below
        err1=$(LC_ALL=C /usr/sbin/vxdisk list ${disk} \
                | /bin/grep errno | /bin/awk '{print $1}')
        if [[ "$err1" = "errno:" ]] ; then
                disk_list="${disk_list} ${disk}"
                let j=j+1
        fi
  done

 
  # set of disks in the ssa is now in the variable disk_list. First step is to
  # remove the controller number from this list to facilitate easy elimination
  # of the root devices of the two nodes from this list.
 
  disk_list=$(print ${disk_list} | sed -e 's/c[0-9]\{1,\}//g')
 
  # eliminate node's root disk from this list
 
  if [[ "${ctlr_node}" = ${rootc_array} ]]; then
    td=${rootdev##c[0-9]*([0-9])}
    disk_list=$(print ${disk_list} | sed -e "s/${td}//g")
  fi
 
  # eliminate the "s2" suffix from each item in the remaining list
 
  disk_list=$(print ${disk_list} | sed -e 's/s2//g')

  if [[ -z $disk_list ]]; then
lmsg=`gettext "\n=================================================\n\
WARNING: All disks on this SSA (ctlr: %s)\n\
are either already in disk groups, have already\n\
been selected as one of the devices for the shared\n\
ccd or otherwise unavailable.\n\
================================================="`
	printf "${lmsg}\n\n" "${ctlr_node}" 
	quorumdiskdev=""
	return
  fi
 
  PS3='Disk: '
  select quorumdiskdev in ${disk_list}; do
    if [[ -n "${quorumdiskdev}" ]]; then
      diskdevaddr=${ctlr_node}${quorumdiskdev}s2
      disksid=$(${PDBSSA} inquiry ${diskdevaddr})
      quorumdiskdev=${diskdevaddr}:${disksid}
      break
    else
	lmsg=`gettext "\nInvalid Selection\n"`
	printf "${lmsg}\n" 
    fi
  done
}

###############################################################################
#
# function get_ctlr_for_ssa wwn
#
# Get the controller number through which the SSA with the specified wwn is
# accessible on the given node.

function get_ctlr_for_ssa
{
  typeset ssa
  typeset ii
  typeset ctlrnum

  ssa=$1
  set -A ssa_array_on_node ${ssa_array[*]}
  set -A ctlr_array_on_node ${ctlr_array[*]}

  let ii=0
  while (( $ii < ${#ssa_array_on_node[*]} )); do
    if [[ ${ssa_array_on_node[ii]} = ${ssa} ]]; then
      ctlrnum=${ctlr_array_on_node[ii]}
      break
    fi
    let ii=ii+1
  done

  print ${ctlrnum}
}

################################################################################
# This function performs the following:
#
#	get all the shared devices to select which device will be used to
#	to store the shared CCD. This is only valid for a 2-node configured
#	cluster.
#
################################################################################

function Select_sharedCCD
{ 
	typeset lmsg="unknown"

	DONE=0
	CCDDEVICE1="0"
	CCDDEVICE2="0"
	CCDSSA[0]="Combo1"
	CCDSSA[1]="Combo2"

	lmsg=`gettext "\nOn a 2-node configured cluster you may select two disks\n\
that are shared between the 2 nodes to store the CCD\n\
database in case of a single node failure.\n\n\
Please, select the disks you want to use from the following list: "`
	printf "${lmsg}\n" 
	get_devices
	ccdpath1="0"
	ccdpath2="0"
	while [[ $ccdpath1 = $ccdpath2 ]]; do
		select_dev 1 "${devset}"
		ccdpath1=${ccddev%%:*}
		get_devices ${ccdpath1}
		select_dev 2 "${devset}"
		ccdpath2=${ccddev%%:*}
		if [[ $ccdpath1 == $ccdpath2 ]];then
			lmsg=`gettext "\nError: device 1 and 2 must be different!"`
			printf "${lmsg}\n\n" 
		fi
	done

# a device *must* be selected prior to getting here. select_dev
# will not allow nothing to be enterned.
#
	CCDDEVICE1=${ccdpath1%s*}
	CCDDEVICE2=${ccdpath2%s*}

}


##############################################################################

# look up a value in the configuration file
function enmatch
{
	typeset lmsg="unknown"

	cdbfile=${cdbpath}/$CLUSTNAME.cdb
	cdbmatch $* ${cdbfile}
	if [ $? -ne 0 ]; then
		lmsg=$(gettext "cdbmatch %s %s failed")
		printf "${lmsg}\n" $* ${cdbfile} 1>&2
		return 1
	fi
	return 0
}

##############################################################################

function check_cluster_name
{
  typeset default_name
  typeset lmsg="unknown"

  default_name=$(cat ${cdbpath}/default_clustername)
  if [[ "${default_name}" != "${CLUSTNAME}" ]]; then
	lmsg=`gettext "Invalid cluster name: %s\n\
Configured cluster is: %s"`
	printf "${lmsg}\n" "${CLUSTNAME}" "${default_name}"
    exit 2
  fi
}

###############################################################################


#
# initialize
#

typeset -i dg_exists
typeset lmsg="unknown"

mybin=/opt/SUNWcluster/bin
myetc=/etc/opt/SUNWcluster
myvar=/var/opt/SUNWcluster
cdbpath=/etc/opt/SUNWcluster/conf

PDBSSA=${mybin}/scssa
FINDDEVICES=${mybin}/finddevices
ccdfile=${myetc}/conf/ccd.database.init
devssa=$(enccdmatch ccd.ccddevice.ssa)

if [[ $# -lt 1 ]]; then
	lmsg=`gettext "Usage: %s <clustname>"`
	printf "${lmsg}\n" "$0"
	exit 2
fi
CLUSTNAME=$1
check_cluster_name

# Cluster Application Bit Assignment for cluster.pdbapps vector in cdb file.
CVM=3
VxVM=4
SDS=5

# Bit 5 in pdbapps vector designates DiskSuite as
# configured volume manager
pdbapps=$(enmatch cluster.pdbapps)
if [ $? -ne 0 ]; then
	exit 1
fi
vm=$(${mybin}/appmatch ${pdbapps} ${SDS})
if [[ "${vm}" = "1" ]]; then
	lmsg=$(gettext \
	    "This command is not supported in DiskSuite configurations.")
	printf "${lmsg}\n"
	exit 0
fi

if [[ "$devssa" = "none" ]]; then
	lmsg=`gettext "\nNo shared CCD volume is specified"`
	printf "${lmsg}\n\n" 
	prompt=`gettext "Do you want to configure a shared CCD volume (yes/no) ? "`
	ccdshare="0"
	ccdshare=$(yorn "${prompt}")
	if [[ "$ccdshare" = "${no}" ]]; then
		exit 0
	fi
        lmsg=`gettext \
"        Please run the following command on BOTH nodes\n\
        of the cluster : \'scconf <clustername> -S ccdvol\'\n\
        then re-run this command \'confccdssa\' on a SINGLE node"`
	printf "${lmsg}\n" 
	exit 0
fi

#
# Since there may already be a version of the sc_dg that
# exists from a previous sc2.X load, we need to make sure
# that the ccd file there is removed. Best way to do that
# is to newfs the filesystem.
Check_for_previous_sharedCCD
dg_exists=$?

#
# First of all select 2 disk devices for
# the CCD volume
#

if (( dg_exists == 0 )); then
	Select_sharedCCD

#
# if no devices are selected exit
#

	if [[ $CCDDEVICE1 = "0" || $CCDDEVICE2 = "0" ]]; then
		exit 1;
	fi

#
# vxsetup the two selected disks
#

	/etc/vx/bin/vxdisksetup -i ${CCDDEVICE1}
	stat=$?
	if [[ $stat != 0 ]]; then
		exit 1
	fi
	/etc/vx/bin/vxdisksetup -i ${CCDDEVICE2}
	stat=$?
	if [[ $stat != 0 ]]; then
		exit 1
	fi


#
# setup the disk group
#
#	"sc_dg" = disk group name for CCD disk group

	y=${CCDDEVICE1%d*}
	Y=${y#c*t}
	Z=${CCDDEVICE1#c*d}

	b=${CCDDEVICE2%d*}
	B=${b#c*t}
	C=${CCDDEVICE2#c*d}

	/usr/sbin/vxdg init sc_dg ${CCDSSA[0]}@$Y.$Z=${CCDDEVICE1} ${CCDSSA[1]}@$B.$C=${CCDDEVICE2}
	stat=$?
	if [[ $stat != 0 ]]; then
		exit 1
	fi
fi
#
# create volume and mirror
#
#	size = Size of volume in 512b segments
#		eg. size for a 10MB volume would be 20000
#
#	"ccdvol" = Name for CCD volume

/usr/sbin/vxassist -g sc_dg  -U fsgen make ccdvol 20000
stat=$?
if [[ $stat != 0 ]]; then
	exit 1
fi
/usr/sbin/vxassist -g sc_dg  -U fsgen mirror ccdvol
stat=$?
if [[ $stat != 0 ]]; then
	exit 1
fi
 

#
# create file-system on volume raw device (ccdvol)
#

/usr/sbin/newfs /dev/vx/rdsk/sc_dg/ccdvol
stat=$?
if [[ $stat != 0 ]]; then
	exit 1
fi

/usr/sbin/vxdg deport sc_dg
stat=$?
if [[ $stat != 0 ]]; then
	exit 1;
fi

exit 0


