#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# bos720 src/bos/usr/lib/nim/methods/swts.sh 1.15 
#  
# Licensed Materials - Property of IBM 
#  
# COPYRIGHT International Business Machines Corp. 2006,2011 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# @(#)88    1.15  src/bos/usr/lib/nim/methods/swts.sh, cmdnim, bos720  7/14/11  19:58:16
#
#   COMPONENT_NAME: CMDNIM
#
#   FUNCTIONS: ./usr/lib/nim/methods/swts.sh
#
#   ORIGINS: 27
#


NIMPATH=/usr/lpp/bos.sysmgt/nim
NIM_METHODS="${NIMPATH}/methods"
export NIMPATH NIM_METHODS
. ${NIM_METHODS}/c_sh_lib
. ${NIM_METHODS}/libcosi

# --------------------------- module globals
REQUIRED_ATTRS="pull_request"
OPTIONAL_ATTRS="verbose"
IS_CLIENT=""
IS_MASTER=""
allow_boot=""
cosi=""
time=""
ARGUMENT=""

# --------------------------------------------------------------------------- #
# NAME:         usage
#
# FUNCTION:     Print usage message
#
# PARAMETERS:   None.
#
# RETURNS:      0 - successful function execution
# --------------------------------------------------------------------------- #
function usage {

	/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_SWTS_USAGE} \
		'%1$s [-c Image] [-n | -t Time] [-v] [ThinServer]\n' "${PROGNAME}"

	return 0
}

# --------------------------------------------------------------------------- #
# NAME:         ck_attr
#
# FUNCTION:     Check for valid attribute.
#
# PARAMETERS:   None.
#
# RETURNS:      0 - valid attribute
#               1 - invalid attribute
# --------------------------------------------------------------------------- #
function ck_attr {

	# Check the following global flag:
	# ARGUMENT
	# source
	# location

	typeset tserver=""

	ck_user || return 1

        if [[ $(/usr/bin/ps -ef -o args | /usr/bin/grep "nim \\-o swts" >/dev/null 2>&1; echo $?) -eq 0 ]]; then
		/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_PLEASE_EXECUTE_CMD} \
			'Please execute the command from %1$s.\n' "${PROGNAME}"
		return 1
        fi

	if [[ $client_initiated = "yes" ]]; then
		time="now"
		thinservers=$ARGUMENT
		cosi=`/usr/sbin/lsnim -a select $ARGUMENT | /usr/bin/awk '(NR==2) {print $3}'`
                if [[ $(obj_exist "master" $cosi; echo $?) -ne 0 ]]; then
			/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_TS_NOT_SET} \
                        	'%1$s: Thin server \"%2$s\" is not set up to switch common image.\n' "${PROGNAME}" "${thinservers}"
                        return 1
                fi
	elif [[ $IS_MASTER = "yes" ]]; then
		if [[ -z $cosi ]]; then
			/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_COSI_REQUIRED} \
                        	'%1$s: A common image is required.\n' "${PROGNAME}"
			usage
			return 1
		else
			if [[ $(obj_exist "master" $cosi; echo $?) -ne 0 ]]; then
				/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_CMD_NOT_EXIST} \
                                	'%1$s: \"%2$s\" does not exist.\n' "${PROGNAME}" "${cosi}"
				return 1
			fi
		fi

		if [[ -n $ARGUMENT ]]; then
			# Ensure the clients are diskless/dataless
			for tserver in $ARGUMENT 
			do
				if [[ $(obj_exist "master" $tserver; echo $?) -ne 0 ]]; then
					/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_CMD_NOT_EXIST} \
						'%1$s: \"%2$s\" does not exist.\n' "${PROGNAME}" "${tserver}"
					return 1
				else
				        machine_type=`get_attr_value "master" $tserver "type"`
					if [[ $machine_type != "diskless" ]] && 
					   [[ $machine_type != "dataless" ]]; then
						/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_ONLY_SWITCH_TS} \
							'%1$s: Only thin server can be switched!\n' "${PROGNAME}"
						return 1
					fi
				fi
        	        done

			thinservers=$ARGUMENT
		else   
			return 1
		fi

		if [[ -n $time ]] && [[ -n $allow_boot ]]; then
			usage
			return 1
		elif [[ -z $time && -z $allow_boot ]]; then
			time="now"
		elif [[ -z $time && -n $allow_boot ]]; then
			time="client"
		fi

		ck_cron || return 1

	elif [[ $IS_CLIENT = "yes" ]]; then
		if [[ -n $cosi ]]; then
			/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_NO_COSI_ARG} \
                        	'%1$s: Common image argument is not allowed when executed on a thin server.\n' "${PROGNAME}"
			usage
			return 1
		fi
		
		if [[ -n $ARGUMENT ]]; then
			/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_NO_THINSERVER_ARG} \
                        	'%1$s: Thin server argument is not allowed when executed on a thin server.\n' "${PROGNAME}"
			usage
			return 1
		fi

		if [[ -n $time ]]; then
			/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_TIME_ARG_NOT_ALLOWED} \
                        	'%1$s: Time argument is not allowed when executed on a thin server.\n' "${PROGNAME}"
			usage
			return 1
		fi

		cosi=`/usr/sbin/nimclient -l -a select $NIM_NAME | /usr/bin/awk '(NR==2) {print $3}'`
                if [[ $(obj_exist "client" $cosi; echo $?) -ne 0 ]]; then
			/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_TS_NOT_SET} \
                        	'%1$s: Thin server \"%2$s\" is not set up to switch common image.\n' "${PROGNAME}" "${NIM_NAME}"
                        return 1
                fi

		time=now
		thinservers=$NIM_NAME
	fi

        return 0
}

# --------------------------------------------------------------------------- #
# NAME:         switch_ts
#
# FUNCTION:     Switch thinserver to new common image.
#
# PARAMETERS:   None.
#
# RETURNS:      0 - successful
#               1 - unsuccessful
# --------------------------------------------------------------------------- #
function switch_ts {
	trap "" 2 11 15
	typeset -i count=0
	typeset -i rc=0

	while getopts :T:c:t: option
	do
		case $option in
			T)	tserver="${OPTARG}";;
			c)	cosi="${OPTARG}";;
			t)	time="${OPTARG}";;
		esac
	done

	if [[ $client_initiated = "yes" ]]; then
		switch $tserver $cosi
		rc=$?
	elif [[ $IS_MASTER = "yes" ]]; then
		for ts in $tserver
		do
			if [[ $time = "now" ]]; then
				switch $ts $cosi
				rc=$?
			elif [[ "${time}" = "client" ]]; then
				/usr/lpp/bos.sysmgt/nim/methods/m_chattr -a select=$cosi $ts >/dev/null 2>&1
				if [[ $? -ne 0 ]]; then
					/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_UNABLE_SET_COSI} \
                        			'%1$s: Unable to set common image %2$s to thinserver %3$s.\n' \
						"${PROGNAME}" "${cosi}" "${ts}"
					return 1
				fi
			else
				# Add crontab entry
				echo "/usr/sbin/swts -c $cosi $ts" | /usr/bin/at -t $time > /dev/null 2>&1
				if [[ $? -ne 0 ]]; then
					/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_UNABLE_ADD_CRON} \
                        			'%1$s: Unable to add cron entry for %2$s.\n' "${PROGNAME}" "${ts}"
					return 1
				fi
			fi
		done
	elif [[ $IS_CLIENT = "yes" ]]; then
		[[ -s /tmp/__NIM_shutdown ]] && rm_file /tmp/__NIM_shutdown > /dev/null 2>&1
		/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_ROOT_SYNC_DD_CLIENTS_INST} \
                        '\n Installing software in the root directories of any diskless or dataless\n\ clients served by this SPOT.  This may take several minutes ...\n\n'
		/usr/sbin/nimclient -Fo change -a control=$NIM_NAME
		/usr/sbin/nimclient -o swts &
		count=0
                rc=1
                while [[ $count -lt 80 ]]; do
                        sleep 1
                        ((count=count+1))
                        if [[ -s /tmp/__NIM_shutdown ]]; then
                		/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_DONE} 'done\n'
                                rc=0
				break
                        fi
                done
		[[ $rc -eq 1 ]] && /usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_FAILED} 'failed\n'
	fi

	return $rc
}

# --------------------------------------------------------------------------- #
# NAME:         switch
#
# FUNCTION:     Perform actual switch.
#
# PARAMETERS:   None.
#
# RETURNS:      0 - successful
#               1 - unsuccessful
# --------------------------------------------------------------------------- #
function switch {
	trap "" 2 11 15
	typeset client=$1
	typeset cosi=$2

	typeset switch_dump="no"
	typeset backup_res=""
	typeset common_image=""
	typeset machine_type=""
	typeset resource=""
	typeset server=""
	typeset type=""
	typeset dump=""

        common_image=`get_attr_value "master" $client "spot"`
        if [[ -z $common_image ]]; then
		/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_UNABLE_SWITCH} \
                        '%1$s: Unable to switch thinserver %2$s because a common image is not allocated.\n' \
			"${PROGNAME}" "${client}"
                return 1
        fi

	res_loc=`get_attr_value "master" $cosi "location"`
	inst_root_sz=`LANG=C /usr/bin/du -sm "${res_loc}/lpp/bos/inst_root" | /usr/bin/awk '{print $1}'`
	root_res=`get_attr_value "master" "${client}" "root"`
	root_loc=`get_attr_value "master" "${root_res}" "location"`
	fs_info	=`LANG=C /usr/bin/df -m ${root_loc} | /usr/bin/awk '(NR==2){print $3":"$7}'`
	fs_sz=${fs_info%%:*}
	root_loc=${fs_info##*:}
	/usr/bin/dspmsg -s ${MSG_SET} cmdnim.cat ${MSG_SPACE_FS} 'Checking %s space requirement...' $root_loc

	if (( $inst_root_sz > $fs_sz )); then
		let inst_root_sz="$inst_root_sz - $fs_sz + 10"
		/usr/sbin/chfs -a size=+${inst_root_sz}M $root_loc
		if [[ $? -ne 0 ]]; then
			return 1
		fi
	fi

	machine_type=`get_attr_value "master" $client "type"`
	/usr/sbin/lsnim -c resources $client | /usr/bin/grep -v "^boot " | /usr/bin/awk '{print $1":"$2}'> /tmp/swts_$$.txt

	# Set the bootlist on the diskless/dataless to boot off the net
	for res_type in `/usr/bin/cat /tmp/swts_$$.txt`
	do
		resource=`echo $res_type | /usr/bin/cut -d: -f1`
		type=`echo $res_type | /usr/bin/cut -d: -f2`
		if [[ "${type}" != "spot" ]]; then
			#POC dump
			if [[ "${type}" == "dump" ]]; then
				dump_resource=$resource
				new_dump=dump_${cosi}
				dumpServer=`get_attr_value "master" $cosi "server"`

				# If the client's spot server is different than the one it will switch to, 
				# then create the new dump resource on the server to match the spot
				if [[ $dumpServer != `get_attr_value "master" $common_image "server"` ]]; then
					# The SPOT location is in the form <directory>/<spotname>/usr
					# This means dumpLocation will need to be at the <directory> location.
					# If not, the dump will not create due to NFS restriction for the SPOT location.
					dumpLocation=`get_attr_value "master" $cosi "location"`
					dumpLocation=`dirname $dumpLocation`
					dumpLocation=`dirname $dumpLocation`
					switch_dump="yes"
					if [[ $(obj_exist "master" $new_dump; echo $?) -ne 0 ]]; then
						define_res dump "$new_dump" $dumpServer $dumpLocation/$new_dump || switch_dump="no"
					fi
				fi
			fi
		fi

		if [[ "${type}" = "root" ]] || [[ "${type}" = "tmp" ]] || [[ "${type}" = "home" ]]; then
			backup_res="$backup_res $resource"
		fi
	done

	backup_client $client $backup_res

	# Define a clone NIM object of the client
	client_clone="${client}_CLONE_$$"
	net_settings=`get_attr_value "master" $client "net_setting1"`
	comments=`get_attr_value "master" $client "comments"`

	/usr/sbin/nim -o define -t $machine_type \
		-a if1="find_net ${client} 0" -a cable_type1=N/A \
		-a netboot_kernel=mp ${net_settings:+-a net_settings1="$net_settings"} \
		${comments:+-a comments="$comments"} ${client_clone}

	if [[ $(obj_exist "master" $client_clone; echo $?) -ne 0 ]]; then
		/usr/bin/dspmsg -s ${ERR_SET} cmdnim.cat ${ERR_CANT_DEFINE_OBJ} \
			'%1$s: Unable to define %2$s object.\n' "${PROGNAME}" $client_clone
		undo
	fi

	# Allocate the new spot against the clone to add the entry to /etc/exports
	/usr/sbin/nim -o allocate -a spot=$cosi $client_clone
	if [[ $? -ne 0 ]]; then
		/usr/bin/dspmsg -s ${ERR_SET} cmdnim.cat ${ERR_CANNOT_ALLOC} \
			'0042-207 %s: Unable to allocate the %s resource to %s.' \
				"${PROGNAME}" $cosi $client_clone
			undo
	fi

	/usr/lpp/bos.sysmgt/nim/methods/m_chattr -a spot=$cosi         $client       || undo
	/usr/lpp/bos.sysmgt/nim/methods/m_chattr -a spot=$common_image $client_clone || undo

	client_object_id=`/usr/sbin/lsnim -Fl $client | /usr/bin/egrep "^[   ]+id[ ]+=" | /usr/bin/awk '{print $3}'`
	common_image_location=`get_attr_value "master" $common_image "location"`
	cosi_location=`get_attr_value "master" $cosi "location"`
	if [[ -n $client_object_id ]] && [[ -n $common_image_location ]] && [[ -n $cosi_location ]]; then
		echo "nim_attr: value=$cosi_location" | \
			/usr/bin/odmchange -o nim_attr -q "id=$client_object_id and value=$common_image_location"
	fi

	# Remove the boot attribute from the original client.
	# This will allow the dump resource to be deallocated from the original client and
	# to re-initialize the original client with the new resources.
	/usr/lpp/bos.sysmgt/nim/methods/m_chattr -a boot="" $client > /dev/null 2>&1

	# POC, to be remove when the dump resource is not tie to the SPOT resource
	if [[ $switch_dump = "yes" ]]; then
		# ignore failure because of dump is not crucial for booting thin server
		/usr/sbin/nim -o deallocate -a dump=$dump_resource $client > /dev/null 2>&1
		/usr/sbin/nim -o allocate   -a dump=$new_dump      $client > /dev/null 2>&1
	fi

	/usr/lpp/bos.sysmgt/nim/methods/m_chattr -a control=master $client > /dev/null 2>&1
	# Initialize the clone client.
	if [[ $machine_type = "diskless" ]]; then
		/usr/sbin/nim -o dkls_init $client
	elif [[ $machine_type = "dataless" ]]; then
		/usr/sbin/nim -o dtls_init $client
	fi

	if [[ $? -ne 0 ]]; then
		# Error would come from dkls_init/dtls_init
		undo
	fi

	if [[ `get_attr_value "master" "$client" "Mstate"` = "currently running" ]]; then
		if [[ `get_attr_value "master" "$client" "control"` != "master" ]]; then
			/usr/sbin/nim -Fo change -a control=master $client > /dev/null 2>&1
			if [[ $? -ne 0 ]]; then
				# 0042-001 %s: processing error encountered on "%s":
				#   %s"
				/usr/bin/dspmsg -s ${ERR_SET} cmdnim.cat ${ERR_METHOD} \
					'0042-001 %s: processing error encountered on \"%s\":\n%s' \
					"${PROGNAME}" $client "nim -Fo change -a control=master $client"
				undo
			fi
		fi
		/usr/sbin/nim -o reboot $client
		if [[ $? -eq 0 ]]; then
			server=`get_attr_value "master" "$client" "if" | /usr/bin/awk '{print $2}'`
			while [[ $(/etc/ping -c1 -w1 $server > /dev/null 2>&1; echo $?) -eq 0 ]]; do
				sleep 1
			done
		fi
	fi

	restore_client $r_file		# r_file return from backup_client
	/usr/lpp/bos.sysmgt/nim/methods/m_chattr -a select= $client >/dev/null 2>&1

	uninitialize_ts $client_clone
	rm_nim_obj "$client_clone"

	rm_file $r_file
	rm_file /tmp/swts_$$.txt

	return 0
}

# --------------------------------------------------------------------------- #
# NAME:         undo
#
# FUNCTION:     Perform undo operation.
#
# PARAMETERS:   None.
#
# RETURNS:      0 - successful
#               1 - unsuccessful
# --------------------------------------------------------------------------- #
function undo {

	if [[ $(obj_exist "master" $client_clone; echo $?) -ne 0 ]]; then
		uninitialize_ts $client_clone
		rm_nim_obj "$client_clone"
	fi

	rm_file $r_file
	rm_file /tmp/swts_$$.txt

	return 1
}

# ----------------------------------- swts ---------------------------------- #
#
# NAME: swts
#
# FUNCTION:	 /usr/sbin/swts command 
#
# NOTES:
#
# RETURNS: (int)
#	0	= SUCCESS
#	1	= FAILURE
#
# --------------------------------------------------------------------------- #

trap cleanup 0
trap undo 1
trap 'err_signal' 2 11 15

# NIM initialization
nim_init

# set parameters from command line

while getopts :a:c:nt:v x
do
	case ${x} in
		a)      # Command initated by nimclient.
			# validate the attr ass
                        parse_attr_ass "${OPTARG}"

                        # include the assignment for use in this environment
                        eval ${variable}=\"${value}\"

                        if [[ $variable = "pull_request" ]]; then
                                client_initiated=yes
                        fi

			if [[ $variable = "verbose" ]]; then
				set -x
	                        for i in $(typeset +f)
        	                do
                	                typeset -ft $i
                        	done
			fi
			;;

		c)	# Specify to commit
			cosi=${OPTARG}
			;;

		n)	# Specify to remove
			allow_boot=true
			;;

		t)	# Specify the source for creating the common image.
			time=${OPTARG}
			;;

		v)	# verbose mode (for debugging)
			set -x
			for i in $(typeset +f)
			do
				typeset -ft $i
			done
			;;

		\?)	# unknown option
			usage
			exit 1
			;;
	esac
done

shift $((OPTIND - 1))

ARGUMENT=$*
ck_nim_env_ts  || exit 1
ck_attr	       || exit 1

switch_ts -c $cosi -t "${time}" -T "${thinservers}"
rc=$?

exit $rc

# end of script