#!/usr/bin/ksh
#
# Copyright 2003 Sun Microsystems, Inc. All Rights Reserved.
#
# cluster_lib.ksh
#
# The library used by the ESM Agent component to analyze and configure
# the cluster environment on a target host.
#
# Most of the operation of this library is based on the definition of the
# CLUSTER_TYPE variable that is resolved, defined and exported by the
# cluster_setEnv function.  Be sure to invoke that function prior to invoking
# any other function herein.
#
# NOTE: This script expects that the esm_lib.ksh function library is loaded.
###

# Key module definitions
#
CLUSTERALIASCONFIG=$SSCRA_CONFIG/clusteralias.cfg
TRUSTEDHOSTSCONFIG=$SSCRA_CONFIG/trustedhosts

ESMID_VER=1


#-------------------------------------------------------------------------------
# cluster_createConfigs
#
# Create all requisite configuration files for cluster support.
#
cluster_createConfigs ()
{
    _rc=0    # Return value (assume success, reset if otherwise)

    # Create the clusteralias.cfg file IFF this host is the cluster root
    #
    if [ "`cluster_isRoot`" = "yes" ] ; then
	_ClusterAlias=`el_configvar_get CLUSTER_NAME`
	_DependType=local
	_ProxyIP=`el_configvar_get CLUSTER_VIRTUAL_SERVER`
	_esmid=`cluster_getEsmId`

	/usr/bin/cat > $CLUSTERALIASCONFIG << END_HERE
ClusterAlias $_ClusterAlias
DependType $_DependType
ProxyIP $_ProxyIP
esmid_clusteralias $_esmid
END_HERE
	if [ $? -ne 0 ] ; then
	    el_log 1 "Failure to create the $CLUSTERALIASCONFIG file"
	    _rc=1
	fi
    fi

    # Create the trustedhosts file
    #
    if _members=`cluster_getMembers` ; then
	if /usr/bin/cat /dev/null > $TRUSTEDHOSTSCONFIG ; then
	    if [ -z "$_members" ] ; then
		el_log "WARNING: no cluster members detected?!?"
		el_log "WARNING: the $TRUSTEDHOSTSCONFIG file will be empty"
		# Do not reflect an error for this case??
	    else
		RESOLVENAME=$SSCRA_HOME/command/ResolveName.sh

		el_log 1 "Cluster membership: `/usr/bin/echo $_members`"

		for _member in $_members
		do
		    $RESOLVENAME -n $_member -i >> $TRUSTEDHOSTSCONFIG
		    if [ $? -ne 0 ] ; then
			el_log 1 "Failure to add member to $TRUSTEDHOSTSCONFIG"
			_rc=1
			break    # No sense in continuing...
		    fi
		done      
	    fi
	else
	    el_log "Failure to initialize the $TRUSTEDHOSTSCONFIG file"
	    _rc=1
	fi
    else
	el_log 1 "Failure to resolve the cluster membership"
	_rc=1
    fi
    
    return $_rc
}

#-------------------------------------------------------------------------------
# cluster_destroyConfigs
#
# Remove all associated cluster configuration files.
#
cluster_destroyConfigs ()
{
    _rc=0

    _doneonce=0

    for _cf in $CLUSTERALIASCONFIG $TRUSTEDHOSTSCONFIG
    do
	if [ -f $_cf ] ; then
	    if [ $_doneonce -eq 0 ] ; then
		el_log 1 "Removing cluster configuration files..."
		_doneonce=1
	    fi
	    /usr/bin/rm $_cf || _rc=1
	fi
    done

    return $_rc
}


#-------------------------------------------------------------------------------
# cluster_isMember
#
# Determine if this host is a member of a supported cluster.
#
# Unlike the related cluster_isRoot function, this function can be used without
# first setting up the cluster environment via the cluster_setEnv function.
#
# Returns 0 if the local host is part of a supported cluster, or 1 if not.
#
cluster_isMember ()
{
    [ -n "`cluster_getType`" ]
}


#-------------------------------------------------------------------------------
# cluster_isRoot
#
# Determine if this host is the "root" of the cluster (that is, the lowest
# numbered node of the cluster) based on the CLUSTER_TYPE.
#
# NOTE: you must set up the cluster environment via the cluster_setEnv function
#       before invoking this function.
#
# Outputs 'yes' or 'no' and returns 0 if resolution is successful;
# otherwise, logs error messages, outputs nothing, and returns 1
# if resolution fails.
#
cluster_isRoot ()
{
    _lltnum=''
    _lowestnum=''

    case "$CLUSTER_TYPE" in
        veritas ) _lltnum=`/sbin/lltstat -N`
                  if [ -n "$_lltnum" ] ; then
                      _lowestnum=`/usr/bin/grep . $VCS_LLTHOSTS | \
                                  /usr/bin/grep -v '^#'         | \
                                  /usr/bin/sort +0 -1           | \
                                  /usr/bin/head -1              | \
                                  /usr/bin/awk '{ print $1 }'`
                  fi
                  ;;
            sun ) _lltnum=`/usr/sbin/clinfo -n`
                  if [ -n "$_lltnum" ] ; then
                      _lowestnum=`$SUNCLUSTERBIN/scconf -p | \
                                  /usr/bin/grep 'Node ID:' | \
                                  /usr/bin/sort +1n -t:    | \
                                  /usr/bin/line            | \
                                  /usr/bin/awk '{ print $3 } '`
                  fi
                  ;;
    esac

    if [ -z "$_lltnum" ]; then
        el_log 1 "Failure to resolve cluster LLT number"
        return 1
    fi
               
    if [ -z "$_lowestnum" ]; then
        el_log 1 "Failure to resolve the lowest numbered cluster node"
        return 1
    fi
               
    if [ $_lltnum -eq $_lowestnum ]; then
        echo yes
    else
        echo no
    fi

    return 0
}


#-------------------------------------------------------------------------------
# cluster_getEsmId
#
# Output the ESM ID of the cluster based on the specified cluster name
# and globally set CLUSTER_TYPE.
#
cluster_getEsmId ()
{
    _clname=`cluster_getName`
    _clid=''

    case "$CLUSTER_TYPE" in
        veritas ) _clid=`/sbin/lltstat -nvv                                                     | \
                         /usr/bin/awk '/^                            / && NF == 3 { print $3 }' | \
	                 /usr/bin/head -1`
		  _clvnd='veritas~vcs'
                  ;;
            sun ) _clid=`$SUNCLUSTERBIN/scconf -p    | \
                         /usr/bin/grep "Cluster ID:" | \
                         /usr/bin/awk '{ print $3 }'`
		  _clvnd='sun~sc'
                  ;;
    esac

    if [ -z "$_clid" ] ; then
	el_log 1 "Failure to resolve ESM ID for cluster name '$_clname'"
	return 1
    fi

    echo "${ESMID_VER}~cluster~${_clvnd}~custom~${_clname}~${_clid}"
}


#-------------------------------------------------------------------------------
# cluster_getMembers
#
# Output the list of hostnames comprising the membership of this cluster
# based on the CLUSTER_TYPE.
#
cluster_getMembers ()
{
    case "$CLUSTER_TYPE" in
        veritas ) $VCS_HOME/bin/hastatus -summary | grep "^A" | awk '{ print $2 }'
                  ;;
            sun ) $SUNCLUSTERBIN/scstat -p | grep "Cluster node:" | awk '{ print $3 }'
                  ;;
    esac

    # Return the exit status of the above command
}


#-------------------------------------------------------------------------------
# cluster_getName
#
# Output the official cluster name based on the CLUSTER_TYPE.
#
cluster_getName ()
{
    _cname=''

    case "$CLUSTER_TYPE" in
        veritas ) _cname=`$VCS_HOME/bin/haclus -display | /usr/bin/grep ClusterName | /usr/bin/awk '{ print $2 }'`
                  ;;
            sun ) _cname=`$SUNCLUSTERBIN/scconf -p | /usr/bin/grep "Cluster name:" | /usr/bin/awk '{ print $3 }'`
                  ;;
    esac

    if [ -z "$_cname" ] ; then
        el_log 1 "Failure to resolve the cluster name"
        return  1
    fi

    echo "$_cname"
    return 0
}


#-------------------------------------------------------------------------------
# cluster_getType
#
# Output the official identifier for the cluster software installed on this host.
# If none of the supported cluster types is found, then output null.
#
# NOTE: The official identifier string that is output here MUST match the
#       corresponding value for the asset sub-type parameter to the
#       reg_target script.
#
cluster_getType ()
{
    # If a cluster software product-specific package is installed,
    # then test to ensure the cluster support is enabled.
    #
    if /usr/bin/pkginfo -q SUNWscr > /dev/null 2>&1  &&
       /usr/sbin/clinfo
    then
	echo sun

    elif /usr/bin/pkginfo -q VRTSvcs > /dev/null 2>&1  &&
       [ "`/opt/VRTSvcs/bin/hasys -state \`/usr/bin/hostname\``" = 'RUNNING' ]
    then
	echo veritas
    fi

    return 0
}


#-------------------------------------------------------------------------------
# cluster_getVirtualServers
#
# Resolves and outputs the list of virtual servers for this cluster based on
# the CLUSTER_TYPE.
#
cluster_getVirtualServers ()
{
    _vslist=''

    case "$CLUSTER_TYPE" in
        veritas ) _vslist=`$VCS_HOME/bin/hares -display -type IP | \
                           /usr/bin/grep Address                 | \
                           /usr/bin/awk '{ print $4 }'`
                  ;;
            sun ) __reslist=`$SUNCLUSTERBIN/scstat -g                                | \
	                   /usr/bin/egrep '(LogicalHostname|SharedAddress) online' | \
	                   /usr/bin/awk '{ print $2 }'`
                  
                  # resolve resource names into hostnames
                  for __res in $__reslist; do
                      __hnamelist=`${SUNCLUSTERBIN}/scrgadm -pvv -j ${__res} | \
                                   /usr/bin/grep "HostnameList) Res property value:" | \
                                   cut -f4 -d:`
                      _vslist=`echo $_vslist $__hnamelist`
                  done 
                  ;;
    esac

    if [ -z "$_vslist" ] ; then
        return 1
    fi

    echo $_vslist
    return 0
}


#-------------------------------------------------------------------------------
# cluster_postinstall
#
# Perform all cluster-related postinstallation activities.
#
cluster_postinstall () {

    # If cluster support is disabled, or if this host is found
    # to NOT be in a cluster, then immediately return.
    #
    el_isEsmTrue `el_configvar_get ENABLE_CLUSTER`
    if [ $? -ne 0 ] ; then
	el_slog 1 "No cluster postinstall performed: cluster support is not enabled."
	return 0
    fi

    # If registering this host with the Management Station is disabled,
    # then immediately return.
    #
    el_isEsmTrue `el_configvar_get ENABLE_SCANNABLE_ASSET`
    if [ $? -ne 0 ] ; then
	el_slog 1 "No cluster postinstall performed: scannable asset is not enabled."
	return 0
    fi

    # If this host is found to NOT be in a cluster, then immediately return.
    #
    cluster_setEnv
    
    if [ -z "$CLUSTER_TYPE" ] ; then
	el_slog 1 "No cluster postinstall performed: host is not in a supported cluster."
	return 0
    fi

    # This host is part of an enabled, supported cluster, so create
    # the requisite configuration files and register the cluster with
    # the Management Station (IFF this host is the root).
    #
    cluster_createConfigs ||
	el_assert "Failure to create all cluster configuration files"

    if [ "`cluster_isRoot`" = "yes" ] ; then
	cluster_register ||
	    el_assert "Failure to register the cluster"
    fi

    return 0
}


#-------------------------------------------------------------------------------
# cluster_preremove
#
# Perform all cluster-related preremove activities.
#
cluster_preremove () {

    # Detect and setup the cluster environment, then
    # return immediately if this host is not in a cluster.
    #
    cluster_setEnv
    if [ -z "$CLUSTER_TYPE" ] ; then
	return 0
    fi

    # Unregister the cluster with the Management Station IFF:
    #  - Cluster support is enabled, AND
    #  - This is the cluster root node, AND
    #  - This host has been registered as a scannable asset.
    #
    if el_isEsmTrue `el_configvar_get ENABLE_CLUSTER` &&
       el_isEsmTrue `cluster_isRoot`                  &&
       el_isEsmTrue `el_configvar_get ENABLE_SCANNABLE_ASSET`
    then
	cluster_unregister
    fi

    # Delete all cluster-related configuration files,
    # but just log any errors that might be encountered.
    #
    cluster_destroyConfigs ||
	el_log 1 "WARNING: unable to remove all cluster configuration files"

    return 0
}


# --------------------------------------------------------------------
# cluster_register
#
# Register the cluster with the Management Station.
#
# NOTE: This function should ONLY be invoked if the localhost
#       is the root for the cluster!
#
# The cluster environment is expected to be setup by now.
#
cluster_register () 
{
    el_log 1 "Registering the cluster with the Management Station..."

    _regerrors=/usr/bin/true

    _ms=`el_configvar_get SERVICE_LOCATION`       &&
    _mp=`el_configvar_get IH_SERVICE_PORT`        &&
    _cn=`el_configvar_get CLUSTER_NAME`           &&
    _vs=`el_configvar_get CLUSTER_VIRTUAL_SERVER` &&
    _arpport=`el_configvar_get ARP_HTTPS_PORT`    &&
    _regerrors=/usr/bin/false

    if $_regerrors ; then
	el_log 1 "Failure to acquire all registration-related configuration values"
	return 1
    fi

    # Register the Virtual Server as an ARP, then register the appropriate
    # targets, but do not return failure if the Management Station is not
    # currently available.
    #
    if $ESM_BIN/reg_arp "${_ms}:$_mp" "$_vs" $_arpport ; then
	
	$ESM_BIN/reg_target "${_ms}:$_mp" "$_cn" cluster "$CLUSTER_TYPE" "$_vs"
	if [ $? -ne 0 ] ; then
	    el_log 1 "WARNING: unable to register the cluster target with the Management Station"
	    el_log 1 "  You will need to manually register the cluster target."
	fi
    else
	el_log 1 "WARNING: unable to register the Agent Station component with the Management Station"
	el_log 1 "  You will need to manually register both the Agent Station component and the cluster target."
    fi

    return 0
}


#-------------------------------------------------------------------------------
# cluster_setEnv
#
# Detect and setup the cluster environment.
#
# A key aspect of this function is to define and export the CLUSTER_TYPE variable
# that contains the official identifier for the cluster type; if a supported
# cluster type is not found, this variable is set to null.
#
cluster_setEnv ()
{
    CLUSTER_TYPE=`cluster_getType` ; export CLUSTER_TYPE

    case "$CLUSTER_TYPE" in
        veritas ) if [ -z "$VCS_HOME" ] ;  then
                      # Shouldn't VCS_HOME be extracted from the VRTSvcs package params??
                      VCS_HOME=/opt/VRTSvcs ; export VCS_HOME
                  fi
                  VCS_LLTHOSTS=/etc/llthosts ; export VCS_LLTHOSTS
                  ;;
            sun ) SUNCLUSTERROOT=/usr/cluster       ; export SUNCLUSTERROOT
                  SUNCLUSTERBIN=$SUNCLUSTERROOT/bin ; export SUNCLUSTERBIN
                  ;;
    esac

    return 0
}


# --------------------------------------------------------------------
# cluster_unregister
#
# Unregister the cluster with the Management Station.
#
# NOTE: The cluster environment is expected to be setup by now.
#
cluster_unregister ()
{
    el_log 1 "Unregistering the cluster with the Management Station..."

    # First unregister the target (ie, the cluster name),
    # then the ARP for the cluster virtual server.
    #
    # Ignore registration errors (for now)
    #
    _ms=`el_configvar_get SERVICE_LOCATION`       &&
    _mp=`el_configvar_get IH_SERVICE_PORT`        &&
    _cn=`el_configvar_get CLUSTER_NAME`           &&
    _vs=`el_configvar_get CLUSTER_VIRTUAL_SERVER`

    el_isEsmTrue "$ESM_PERSIST"
    if [ $? -ne 0 ] ; then
	el_slog 1 "Calling unreg_target now for cluster -- do not persist the ESM DB"
	_targetunregcmd="$ESM_BIN/unreg_target ${_ms}:$_mp $_cn cluster $CLUSTER_TYPE"
	$_targetunregcmd
	if [ $? -ne 0 ] ; then
	    el_log 1 "WARNING: Unable to unregister the target with the Management Station at this time"
	    if [ "$ESM_CONTEXT" != 'uninstall' ] ; then
	    	el_log 1 "Please manually unregister the target when convenient using the following command:"
	    	el_log 1 "  $_targetunregcmd"
	    	el_log 1 "Note that you must be the super-user (root) to execute the above command."
	    fi
	fi
    fi

    $ESM_BIN/unreg_arp "${_ms}:$_mp" "$_vs"
	
    # Return final exit status
}
