#!/bin/sh
#
#    Copyright (c) 2013 Brocade Communications Systems, Inc.
#    All rights reserved.
#
#    Description:
#		This script is a different flavor of preinstall checking.
#		Essentially it is intended to make easier the
#		Compatibility checking of newly introduced features
#		which are introduced in new releases but also
#		back ported to Older maint/patch release versions as well
#
#		The current preinstall checking design is to always run the 
#		preinstall of the higher of the two versions.
#		This does not lend itself to checking whether
#		the new firmware being downloaded supports a given feature,
#		except through a complex specific version checking.
#
#		This scheme requires such new features to be identified by
#		a tag in preinstall script that helps identify if the new release
#		being downloaded supports the given feature or not.
#
#		Enhancements to this script can then be done to perform
#		feature compatibility checking of such features,
#		and blocking downloads where appropriate.
#
#		If is important that each feature compatibility check
#		- Appends the corresponding error message to BOTH  ERROR/STATUS FILES
#		  STATUS_FILE and BNA_STATUS_FILE
#		- Increments the return code count (or alternately set it to non zero)
#		- return STS_ERR (this is not required but recommended for consistency)
#		This will permit a one pass checking of all features
#		And a cumulative message reflecting all corrective actions.
##############################################################################
 
PATH=/bin:/usr/bin:/sbin:/usr/sbin 
STS_OK=0
STS_ERR=255
rc=$STS_OK
STATUS_FILE=/tmp/fwdl_err.txt
BNA_STATUS_FILE=/tmp/bna_fwdl_err.txt

PREINST_FILE=$1		# This is the preinst file of the new version being downloaded.
					# It indicates if it supports these new features:
					# - ZENTRON 4 CP, 8548 Rev 31 CPU
					# - nonDFE

#
# Common variables
#
NULL=/dev/null
if [ "$DEBUG" = 1 ]; then
	ECHO='echo -e'
else
	ECHO=:
fi

# cpid
#
# Retrieve the current Control Processor ID
#
cpid() {
	sed -n -e 's/^Control.\+No: \([[:digit:]]\{1,\}\)$/\1/gp'
}

#
# swbd
#
# Retrieve the current system platform name, of the form "SWBDn", where n
# is cardinal number, assuming a sin/hinv input stream.
#
swbd() {
	sed -n -e 's/^.\+\(SWBD[[:digit:]]\{1,\}\).\+$/\1/gp'
}

# Determine the system platform identifier.
SWBD=`sin | swbd 2> ${NULL}`
CPID=`sin | cpid 2> ${NULL}`

otherhost(){
	case ${SWBD##SWBD} in
	'62')
	    printf 127.1.1.$((8 - CPID % 2))
	    ;;
	'77')
	    printf 127.1.1.$((6 - CPID % 2))
	    ;;
	'141')
		printf 127.1.$((17 - CPID % 2)).$((17 - CPID % 2))
		;;
	'142')
		printf 127.1.$((15 - CPID % 2)).$((15 - CPID % 2))
		;;
	*)
	    printf 10.0.0.$((6 - CPID % 2))
	    ;;
	esac
}

HASHOW="$(/fabos/bin/hashow)"
hashow_cmd=$(ls /fabos/cliexec/hashow)
if [ "$hashow_cmd" != "/fabos/cliexec/hashow" ]; then
       	hashow_cmd="/fabos/bin/hashow"
fi

othercp(){
        that=${HASHOW#*Remote CP \(Slot}
        that="${that%%,*}"
        printf $(($that - 5))
}

ha_role() {
 	case "$($hashow_cmd | ( read a; echo $a ))" in
	    *Local*Active*)
	    printf "ACTIVE"
 	    ;;
	    *Local*Standby*)
	    printf "STANDBY"
 	    ;;
	    *"Not supported"*)
	    printf "ACTIVE"
 	    ;;
	    *)
	    printf "STANDBY"
	    ;;
	esac
}

if [ $(ha_role) == "ACTIVE" ]; then
    ACTIVECP=1
fi

ha_state() {
    sync=`/fabos/cliexec/hashow | sed -n -e 's/^.\+\(State sync\).\+$/\1/gp'`
    if [ "$sync" != "State sync" ]; then
	    printf "NOSYNC"
    else
	    printf "SYNC"
    fi
}

##############################################################################
#       Zentron 4 compatibility checking
# Zentron 4 CPs (i.e 8548 Rev (SVR) 31 CPU based CPs)
# MUST NOT be permitted to be upgraded or downgraded to firmware versions that
# Do not have the enhancement to support this new CPU.
# Any accidental firmwaredownload (on Z4 CP blade) to such firmware versions
# Will Render the CP Blade into a Brick - that will need to be RMA'd to fix.
#
# Hence the need for this fw compatibility check and blocking of older fw
# on the newer (Z4 CP) platforms.
#
# The enhanced firmware to support the new hardware is identified by the
# "CP31_ZENTRON4_SUPPORTED" string in the preinstall scripti (preinst.sh.in).
#
# This Checks if CP is new - 8548E rev (SVR) 31 CPU
# If so then it block downgrade to prior releases 
# that do not support the new CPU
#
##############################################################################
Z4_CP_ERR_MSG="The new firmware is not compatible with 
            the 8548 Rev 3.1 CPU based CP blade in this system."
check_new_8548_CP_fw_compatibility()
{
	# Do not do Z4 compatibility check for Odin.
	# Odin was always shipped with version 31 cpu.
	if  [ ${SWBD##SWBD} -eq 133 ]; then
		return $STS_OK
	fi

    chipset=`cat /proc/cpuinfo | awk '/chipset/ {print $3}'`
    if [ -z $chipset ] || [ $chipset != 8548E ]; then
        # This is not a 8548E CP return OK
        return $STS_OK
    fi

    revision=`cat /proc/cpuinfo | awk '/SVR/ {print $3}' | awk  -F 'x' '{print $2}'`
    let revision=$revision%100

    #revision=31 #uncomment to test with current 2.0 CP faked to new
    new_cp_supported=`grep -c "CP31_ZENTRON4_SUPPORTED" $PREINST_FILE`
    result=`expr $revision '>=' "31" 2>/dev/null`
    if [ $result == 1  ]; then
        # Current CP is new - 8548E CP Rev (SVR) 31
        # check if the firmware being downloaded supports new CP 
        if [ $new_cp_supported -eq 0 ]; then
            # New 8548E Rev (SVR) 31 CP But Firmware is older version.
            # Cannot downgrade to fw version that does not support this CP
            let rc=$rc+1
            echo -e $rc:	$Z4_CP_ERR_MSG >> $STATUS_FILE
            echo -e $rc:	$Z4_CP_ERR_MSG >> $BNA_STATUS_FILE
            return $STS_ERR
        else
            # New 8548E Rev (SVR) 31 CP and new firmware is compatible.
            return $STS_OK
        fi
    fi

    #
    # Current CP is Old i.e. 8548E but less than Rev (SVR) 31
    # Next check the redundant CP for the new rev (SVR) 31 CPU
    # if redundant CP is new then block downgrades to 
    # firmware versions that do not support the new CP
    #
    if [ $(ha_state) == "SYNC" ]; then
        revision=`/usr/bin/rsh -n $(otherhost) ROLE_ID=root LOGIN_ID=root CURRENT_AD=0 /bin/cat /proc/cpuinfo | /bin/awk '/SVR/ {print $3}' | awk  -F 'x' '{print $2}'`

        # if revision is not defined then return STS_OK
        if [ -z $revision ]; then
            return $STS_OK
        fi

        let revision=$revision%100
        #revision=31 #uncomment to test with current 2.0 StbyCP faked to new
        result=`expr $revision '>=' "31" 2>/dev/null`
        if [ $result == 0  ]; then
            # Other CP is also old - so no compatibility checking necessary.
            return $STS_OK
        fi
    else
        # This is a single CP system or non-redundant chassis system.
        # And the CP is old -  8548E but not Rev 31
        return $STS_OK
    fi

    # Here Current CP is Old but the Other CP is new 
    # Check if the preinstall of the new firmware being downloaded
    # Supports the new hardware

    if [ $new_cp_supported -eq 0 ]; then
        # Cannot downgrade to fw version that does not support this CP
        let rc=$rc+1
        echo -e $rc:	$Z4_CP_ERR_MSG >> $STATUS_FILE
        echo -e $rc:	$Z4_CP_ERR_MSG >> $BNA_STATUS_FILE
        return $STS_ERR
    fi

    return $STS_OK
}



correcthost() {
    if [ $ACTIVECP ] ; then
    "$@"
    else
    /usr/bin/rsh -n $(otherhost) ROLE_ID=root LOGIN_ID=root CURRENT_AD=0 "$@"
    fi
}


##############################################################################
#
# Non-DFE (Decision Feedback Equalization) Feature compatibility checks.
# If Non-DFE is enabled on any of the ports and the new firmware does not
# support this feature then firmwaredownload to that version must be blocked
# with appropriate message to disable the feature before moving to that version
#
##############################################################################

NON_DFE_ENABLED_ERR_MSG="Non-DFE is enabled and the new firmware does not support it.
\n   Please run portcfgnondfe --disable [slot/]port for all ports where it is enabled, before downloading the new firmware.
\n   Use porcfgshow to find all ports where Non-DFE is enabled."
BNA_NON_DFE_ENABLED_ERR_MSG="Non-DFE is enabled and the new firmware does not support it.
\n  Please disable Non DFE on the port using WebTools -> Port Admin -> Actions -> Non DFE.
\n  Find the list of Non DFE Enabled Ports in Port Admin table."

check_non_dfe()
{
    #
    # do not use the correcthost primitive here.
    # The preinst file is on this host and not on correcthost
    # correcthost will not work when running fwdl from standby.
    #
    non_dfe_supported=`grep -c "NON_DFE_SUPPORTED" $PREINST_FILE`
    if [ $non_dfe_supported -ne 0 ]; then
        # The new release supports this feature so ok to proceed.
        return $STS_OK
    fi

    # DFE is not supported by the new firmware.
    # Check if DFE is enabled before permitting download to a version
    # That does not support DFE and block download if enabled

    non_dfe_enabled=`correcthost /fabos/cliexec/portcfgshow | grep Non-DFE | grep -c ON`
    if [ $non_dfe_enabled -ne 0 ]; then
        # Let each failing check
        # Append its own Detailed corrective message to BOTH files:
        # - STATUS_FILE and BNA_STATUS_FILE 
        # increment return code
        let rc=$rc+1
        echo -e $rc:	$NON_DFE_ENABLED_ERR_MSG >> $STATUS_FILE
        echo -e $rc:	$BNA_NON_DFE_ENABLED_ERR_MSG >> $BNA_STATUS_FILE
        return $STS_ERR
    fi

    return $STS_OK
}




##############################################################################
#
# QSFP on ICLs Feature compatibility checks.
# Downgrade to a release that does not support this feature is not allowed
# if some of the ICL ports are connected with new QSFP which support upto 2km. 
#
##############################################################################


glb_ls_id=-1
glb_vf_id=255

#
# both Active and Standby have the information of
# VF and logical switches configured.
#
# VF case: on Standby CP, in order to login to each logical switch, 
# we need to set CHASSIS_ROLEID FABOS_SWITCHNO CURRENT_VF in rsh.
#
correcthost_to_curr_vfid() {
	if [ $ACTIVECP ] ; then
		# for_all_context already did "context_switch --switch vfid"
		"$@"
	else
		if [ $VF_ENABLED -eq $STS_OK ]; then
			# VF disabled
			/usr/bin/rsh -n $(otherhost) ROLE_ID=root LOGIN_ID=root CURRENT_AD=0 "$@"
		else
			# VF enabled, for_all_context changed glb_ls_id and glb_vf_id to each logical switch's
			/usr/bin/rsh -n $(otherhost) ROLE_ID=root LOGIN_ID=root CHASSIS_ROLEID=0 FABOS_SWITCHNO=$glb_ls_id CURRENT_VF=$glb_vf_id CURRENT_AD=0 "$@"
		fi
	fi
}


ICL_QSFP_FWDL_CHECK="Downgrade is not allowed as some of the ICL ports are connected with new QSFP which support upto 2km. Please remove the QSFP(s) flagged and connect the old QSFP before proceeding with the downgrade."

check_qsfpp_icl()
{
    #
    # do not use the correcthost primitive here.
    # The preinst file is on this host and not on correcthost
    # correcthost will not work when running fwdl from standby.
    #
    qsfpp_icl_supported=`grep -c "QSFPP_ICL_SUPPORTED" $PREINST_FILE`
    if [ $qsfpp_icl_supported -ne 0 ]; then
        # The new release supports this feature so ok to proceed.
        return $STS_OK
    fi

    # QSFP_ICL is not supported by the new firmware.
    # Check if any QSFPs on ICLs. If so then block the downgrade.

	icl_fwdl_ret=`correcthost_to_curr_vfid /fabos/bin/icl_qsfp_fwdl_check 2>/dev/null | grep -E "2KM QSFP"`
	if [ $? -eq 0 ]; then
		let rc=$rc+1
		echo -e $rc:	$icl_fwdl_ret >> $STATUS_FILE
		echo -e $ICL_QSFP_FWDL_CHECK  >> $STATUS_FILE
		echo -e $rc:	$icl_fwdl_ret >> $BNA_STATUS_FILE
		echo -e $ICL_QSFP_FWDL_CHECK  >> $BNA_STATUS_FILE
		return $STS_ERR;
	fi
    return $STS_OK
}



BUF_OP_MODE_CHECK_FAILED="Downgrade is not allowed because BufOpMode is enabled on CORE blade(s).  Please disable bufopmode using the CLI \"bufopmode --resetall\" \n"
check_buf_op_mode()
{
	buf_op_mode_supported=`grep -c "BUF_OP_MODE_SUPPORTED" $PREINST_FILE`
	if [ $buf_op_mode_supported -ne 0 ]; then
		# The new release supports this feature so ok to proceed.
		return $STS_OK
	fi

	# special check for v7.2.1d which also supports BUF_OP_MODE
	# but did not include the BUF_OP_MODE_SUPPORTED tag.
	buf_op_mode_supported=`grep -c "BUFOPMODE_ENABLED" $PREINST_FILE`
	if [ $buf_op_mode_supported -ne 0 ]; then
		# The new release supports this feature so ok to proceed.
		return $STS_OK
	fi

	# BUF_OP_MODE is not supported by the new firmware.
	# Check if BUF_OP_MODE is enabled on any slots. If so then block the downgrade.
	icl_slots=`correcthost_to_curr_vfid /fabos/bin/slotshow -m | grep "CORE" | awk {'print $1'}`
	if [ "$icl_slots" == "" ]; then
		return $STS_OK
	fi

	for slot in $icl_slots; do
		correcthost_to_curr_vfid /fabos/sbin/bufopmode --show $slot | grep "On" > /dev/null
		if [ $? -eq 0 ]; then
			let rc=$rc+1
			echo -e $rc:    $BUF_OP_MODE_CHECK_FAILED >> $STATUS_FILE
			echo -e $rc:    $BUF_OP_MODE_CHECK_FAILED >> $BNA_STATUS_FILE
			return $STS_ERR
		fi
	done

    return $STS_OK
}


UNSUPPORTEDPLATFORMMSG="Cannot download the requested firmware because the firmware does not support this platform. Please enter another firmware path."
check_q20_supported()
{
	q20_supported=`grep -c "Q20_SWBD134_SUPPORTED" $PREINST_FILE`
	if [ $q20_supported -ne 0 ]; then
		# The new release supports Q20 platform so ok to proceed.
		return $STS_OK;
	fi

	# Q20 is not supported by the new firmware.
	# Check if the platform is Q20. If so then block the downgrade.
	q20_platform=`correcthost_to_curr_vfid /fabos/cliexec/chassisshow | grep -c "SLKWRM0000391"`
	if [ ${SWBD##SWBD} == '134' ]; then		# Check if Qilin first
		if [ $q20_platform -ne 0 ]; then	# Then check if Qilin-24/Qilin-20 
			# We are downloading to Q20. And the new release does not support it.
			# So return error to block downgrade.
			let rc=$rc+1
			echo -e $rc:    $UNSUPPORTEDPLATFORMMSG >> $STATUS_FILE
			echo -e $rc:    $UNSUPPORTEDPLATFORMMSG >> $BNA_STATUS_FILE
			return $STS_ERR;
		fi
	fi

	# For all other platforms it is good to go.
	return $STS_OK;
}


NONDFE_FD_MSG="Cannot download the requested firmware because it does not support portcfgnondfe --force_disable and one or more ports are configured with --force_disable option. Please use portcfgshow to identify these ports and reconfigure them with --enable or --disable option and then retry firmwaredownload."
check_nondfeforcedisable_supported()
{
	nondfe_forcedisable_supported=`grep -c "NONDFE_FORCE_DISABLE_SUPPORTED" $PREINST_FILE`
	if [ $nondfe_forcedisable_supported -ne 0 ]; then
		# The new release supports portcfgnondfe --forcedisable
		# No need to check further for blocking fwdl due to this feature.
		return $STS_OK;
	fi

	# portcfgnondfe --forcedisable is not supported by the new firmware being downloaded
	# Check if any ports are configured for nondfe force_disable. If so then block the downgrade.
	nondfe_fd=`correcthost_to_curr_vfid /fabos/cliexec/portcfgshow | grep "8G Non-DFE" | grep -c FD`
	if [ $nondfe_fd -ne 0 ]; then
		# one or more ports have nondfe configured with --force_disable option.
		# So return error to block downgrade.
		let rc=$rc+1
		echo -e $rc:    $NONDFE_FD_MSG >> $STATUS_FILE
		echo -e $rc:    $NONDFE_FD_MSG >> $BNA_STATUS_FILE
		return $STS_ERR;
	fi

	# New firmware does not support portcfgnondfe --forcedisable
	# But none of the ports are configured for --forcedisable. so ok to proceed.
	return $STS_OK;
}

#
SSH_PERMIT_USER_ENV_NOTSUPPORTED="PermitUserEnvironment is not supported"

check_sshd_permituser_env()
{
		ssh_permit_user_env_notsupported=`grep -c "SSH_PERMIT_USER_ENV_NOTSUPPORTED" $PREINST_FILE`
				if [ $ssh_permit_user_env_notsupported -ne 0 ]; then
				# The new release supports this this so ok to proceed.  return $STS_OK
						return $STS_OK
				else
						if [ $ACTIVECP ]; then
								isActiveCp=1
						else
								isActiveCp=0
						fi
						permituser_config=`/bin/cat /etc/sshd_config | /bin/grep "PermitUserEnvironment" | /bin/awk '{ print $2 }' `
						if [ $permituser_config == "no" ]; then
								if [ "$isActiveCp" -eq 1 ]; then
										/bin/sed '/PermitUserEnvironment/ s/no/yes/' /etc/sshd_config > /tmp/sshd_config.temp
										/bin/cp /tmp/sshd_config.temp /etc/sshd_config
										/bin/cp /tmp/sshd_config.temp /mnt/etc/sshd_config
										/bin/rm -rf /tmp/sshd_config.temp
								else
										/bin/sed '/PermitUserEnvironment/ s/no/yes/' /etc/sshd_config > /tmp/sshd_config.temp
										/bin/cp /tmp/sshd_config.temp /etc/sshd_config
										/bin/cp /tmp/sshd_config.temp //mnt/etc/sshd_config
										/bin/rm -rf /tmp/sshd_config.temp
										/bin/rcp /etc/sshd_config root@$(otherhost):/etc/sshd_config
										/bin/rcp /etc/sshd_config root@$(otherhost):/mnt/etc/sshd_config
								fi
						correcthost_to_curr_vfid /fabos/cliexec/config save /etc/sshd_config
						fi
				fi
}

# main()
#
echo > $STATUS_FILE      # Clear the error file
echo > $BNA_STATUS_FILE  # Clear the BNA error file
echo "Please address the following before downloading the specified firmware:" >> $STATUS_FILE

#Copy preinst file of image to be downloaded in /tmp/ directory. Later needed by preinst.sh.in for RFM compatibility checking.
cp $PREINST_FILE /tmp/new_preinst

check_new_8548_CP_fw_compatibility
check_non_dfe
check_qsfpp_icl
check_buf_op_mode
check_q20_supported
check_nondfeforcedisable_supported
check_sshd_permituser_env
exit $rc
