- #!/bin/bash
- #==============================================================================
- # Copyright and license info is available in the LICENSE file included with
- # the Server Deployment Package (SDP), and also available online:
- # https://swarm.workshop.perforce.com/projects/perforce-software-sdp/view/main/LICENSE
- #------------------------------------------------------------------------------
-
- #==============================================================================
- # Declarations and Environment
-
- # Allow override of P4U_HOME, which is set only when testing P4U scripts.
- export P4U_HOME=${P4U_HOME:-/p4/common/bin}
- export P4U_LIB=${P4U_LIB:-/p4/common/lib}
- export P4U_ENV=$P4U_LIB/p4u_env.sh
- export P4U_LOG=off
- export VERBOSITY=${VERBOSITY:-3}
-
- # Environment isolation. For stability and security reasons, prepend
- # PATH to include dirs where known-good scripts exist.
- # known/tested PATH and, by implication, executables on the PATH.
- export PATH=$P4U_HOME:$PATH:~/bin:.
- export P4CONFIG=${P4CONFIG:-.p4config}
-
- [[ -r "$P4U_ENV" ]] || {
- echo -e "\nError: Cannot load environment from: $P4U_ENV\n\n"
- exit 1
- }
-
- declare BASH_LIBS=$P4U_ENV
- BASH_LIBS+=" $P4U_LIB/libcore.sh"
- BASH_LIBS+=" $P4U_LIB/libp4u.sh"
-
- for bash_lib in $BASH_LIBS; do
- source $bash_lib ||\
- { echo -e "\nFATAL: Failed to load bash lib [$bash_lib]. Aborting.\n"; exit 1; }
- done
-
- declare Version=1.4.0
- declare -i SilentMode=0
- declare TicketExpiration=
- declare TicketStatus=
- declare SuperAccessStatus=
- declare SkipInstanceList=sample
- declare NewInstanceList=
- declare -i DoPreflight=1
- declare -i PreflightAccessOK=1
- declare -i PreflightEnvOK=1
- declare -i Skipped=0
- declare HTCfgFile=/p4/common/config/HelixTopology.cfg
- declare SDPEnvFile=/p4/common/bin/p4_vars
- declare InstanceEnvFile=
- declare InstanceList=$(grep '^INSTANCE' "$HTCfgFile" | cut -d '|' -f 2)
- declare Program=Unset
- declare ProgramPath=
- declare ProgramArgs=
- export P4USER=$(echo $(source $SDPEnvFile hms; echo $P4USER))
- export P4TICKETS=/p4/hms/.p4tickets
- export P4TRUST=/p4/hms/.p4trust
- export P4ENVIRO=/dev/null/.p4enviro
- unset P4CONFIG
-
- export VERBOSITY=3
-
- #==============================================================================
- # Local Functions
-
- #------------------------------------------------------------------------------
- # Function: terminate
- function terminate
- {
- # Disable signal trapping.
- trap - EXIT SIGINT SIGTERM
-
- vvmsg "$THISSCRIPT: EXITCODE: $OverallReturnStatus"
-
- # Stop logging.
- [[ "${P4U_LOG}" == off ]] || stoplog
-
- # Don't litter.
- cleanTrash
-
- # With the trap removed, exit.
- exit $OverallReturnStatus
- }
-
- #------------------------------------------------------------------------------
- # Function: usage (required function)
- #
- # Input:
- # $1 - style, either -h (for short form) or -man (for man-page like format).
- # The default is -h.
- #
- # $2 - error message (optional). Specify this if usage() is called due to
- # user error, in which case the given message displayed first, followed by the
- # standard usage message (short or long depending on $1). If displaying an
- # errror, usually $1 should be -h so that the longer usage message doesn't
- # obsure the error message.
- #
- # Sample Usage:
- # usage
- # usage -h
- # usage -man
- # usage -h "Incorrect command line usage."
- #------------------------------------------------------------------------------
- function usage
- {
- declare style=${1:--h}
- declare errorMessage=${2:-Unset}
-
- if [[ $errorMessage != Unset ]]; then
- echo -e "\n\nUsage Error:\n\n$errorMessage\n\n"
- fi
-
- echo "USAGE for $THISSCRIPT v$Version:
-
- $THISSCRIPT [[-i <instance1>[,<instance2>,...]] [-sk <instance1>[,<instance2>,...]]] [-L <log>] [-si] [-v<n>] [-n] [-D] [<program> [<program arguments ...>]]
-
- or
-
- $THISSCRIPT [-h|-man|-V]
- "
- if [[ $style == -man ]]; then
- echo -e "
- DESCRIPTION:
- This script runs a preflight check, or a command you specify, against
- all Helix instances defined in the Helix Topology configuration file.
-
- If no arguments are supplied, verifies that all instances can be
- centrally managed with 'p4' commands from the current host. It does
- this by verifying that a Perforce command can execute with super user
- access and has a long-term ticket for each instance. The $P4TICKETS
- and $P4TRUST files are effectively verified with the preflight
- check.
-
- OPTIONS:
- -i <instance1>[,<instance2>,...]
- Specify a comma-delimited list of instances to operate on.
-
- By default, all instances defined in the Helix Topology configuration
- file are operated on.
-
- -sk <instance1>[,<instance2>,...]
- Specify a comma-delimited list of instances to ignore for processing purposes.
-
- By default, the instance named 'sample' is skipped, the equivalent of specifying
- '-sk sample'. Specify '-i none' to to process all instances including the 'sample'
- instance (if defined).
-
- -v<n> Set verbosity 1-5 (-v1 = quiet, -v5 = highest).
-
- -L <log>
- Specify the path to a log file, or the special value 'off' to disable
- logging. By default, output (stdout and stderr) are not captured.
-
- When -L is used, this script is self-logging. That is, output displayed
- on the screen is simultaneously captured in the log file. When -L is used,
- do not run this script with redirection operators like '> log' or '2>&1',
- and do not use 'tee.'
-
- -si Operate silently. All output (stdout and stderr) is redirected to the log
- only; no output appears on the terminal. This cannot be used with
- '-L off'.
-
- This is useful when running from cron, as it prevents automatic
- email from being sent by cron directly, as it does when a script called
- from cron generates any output.
-
- -n No-Op. Prints commands instead of running them.
-
- -D Set extreme debugging verbosity.
-
- HELP OPTIONS:
- -h Display short help message
- -man Display man-style help message
- -V Dispay version info for this script and its libraries.
-
- FILES:
- Helix Topology Config file:
- $HTCfgFile
-
- Cental tickets file:
- $P4TICKETS
-
- Cental trust file:
- $P4TRUST
-
- EXAMPLES:
- Example 1: Preflight Chcek
-
- With no arguments, do a quick preflight check to ensure that
- all configured instances can be managed, and that shell envionment
- files for all instances exist:
-
- $THISSCRIPT
-
- Example 2: Shell Envionment Check
-
- Check the envionment for instance 1:
-
- $THISSCRIPT -i 1 p4 set
-
- WARNING: This script overrides environment settings for
- P4PORT, P4TICKETS, P4TRUST, and P4ENVIRO, so settings for
- these values may not match what is defined by setting the
- SDP environment in the usual way, i.e.:
-
- source $SDPEnvFile N # where N is the instance
-
- Example 3: Server Version Check for all instances
-
- $THISSCRIPT p4 -ztag -F %serverVersion% info -s
-
- Example 4: Server Version Check for all instances, with
- higher verbosity ('-v4') to display each instance as the command is run:
-
- $THISSCRIPT p4 -v4 -ztag -F %serverVersion% info -s
-
- "
- fi
-
- exit 1
- }
-
- #==============================================================================
- # Command Line Processing
-
- declare -i shiftArgs=0
-
- set +u
- while [[ $# -gt 0 ]]; do
- case $1 in
- (-i) InstanceList="${2//,/ }"; shiftArgs=1;;
- (-sk) SkipInstanceList="${2/,/ }"; shiftArgs=1;;
- (-h) usage -h;;
- (-man) usage -man;;
- (-V) show_versions; exit 1;;
- (-v1) export VERBOSITY=1;;
- (-v2) export VERBOSITY=2;;
- (-v3) export VERBOSITY=3;;
- (-v4) export VERBOSITY=4;;
- (-v5) export VERBOSITY=5;;
- (-L) export P4U_LOG=$2; shiftArgs=1;;
- (-si) SilentMode=1;;
- (-n) export NO_OP=1;;
- (-D) set -x;; # Debug; use 'set -x' mode.
- (-*) usage -h "Unknown arg ($1).";;
- (*)
- Program=$1
- shift
- ProgramArgs=$@
- break
- ;;
- esac
-
- # Shift (modify $#) the appropriate number of times.
- shift; while [[ $shiftArgs -gt 0 ]]; do
- [[ $# -eq 0 ]] && usage -h "Incorrect number of arguments."
- shiftArgs=$shiftArgs-1
- shift
- done
- done
- set -u
-
- #==============================================================================
- # Command Line Verification
-
- [[ $SilentMode -eq 1 && $P4U_LOG == off ]] && \
- usage -h "Cannot use '-si' with '-L off'."
-
- [[ "$Program" == Unset ]] || DoPreflight=0
-
- # Determine the list of instances to process, accounting for any instances
- # skipped.
- if [[ $SkipInstanceList != "none" ]]; then
- NewInstanceList=
- for i in $InstanceList; do
- Skipped=0
- for s in $SkipInstanceList; do
- if [[ "$s" == "$i" ]]; then
- Skipped=1
- break
- fi
- done
-
- # If the instance isn't in the skip list, add it to the list of instances.
- [[ $Skipped -eq 1 ]] || NewInstanceList+=" $i"
- done
- InstanceList="$(echo $NewInstanceList)"
- vmsg "Instance List: $InstanceList (excluding $SkipInstanceList)"
- fi
-
- #==============================================================================
- # Main Program
-
- trap terminate EXIT SIGINT SIGTERM
-
- declare -i OverallReturnStatus=0
- declare -i ExitCode=0
-
- if [[ "${P4U_LOG}" != off ]]; then
- touch ${P4U_LOG} || bail "Couldn't touch log file [${P4U_LOG}]."
-
- # Redirect stdout and stderr to a log file.
- if [[ $SilentMode -eq 0 ]]; then
- exec > >(tee ${P4U_LOG})
- exec 2>&1
- else
- exec >${P4U_LOG}
- exec 2>&1
- fi
-
- initlog
- fi
-
- if [[ $DoPreflight -eq 1 ]]; then
- msg "${H1}\nPreflight Check for instances:\n$(echo $InstanceList)\n"
-
- msg "Accesss Preflight Check\n${H2}"
-
- printf "%-12s %-24s %-5s %-s\n" "Instance" "P4PORT" "Super" "Ticket Status"
- printf "%-12s %-24s %-5s %-s\n" "------------" "------------------------" "-----" "-----------------------------------------"
- for i in $InstanceList; do
- host=$(grep "COMPONENT|$i|p4d-mc|" "$HTCfgFile"|cut -d '|' -f 5)
- port=$(grep "COMPONENT|$i|p4d-mc|" "$HTCfgFile"|cut -d '|' -f 8)
-
- if [[ $port == "ssl:"* ]]; then
- export P4PORT="ssl:$host:${port#ssl:}"
- else
- export P4PORT="$host:$port"
- fi
-
- TicketExpiration=$(p4 -ztag -F %TicketExpiration% -p $P4PORT -u $P4USER login -s 2>/dev/null)
- if [[ -n "$TicketExpiration" && "$TicketExpiration" -gt "$((60*60*24*31))" ]]; then
- TicketStatus="OK with long-term ticket."
- elif [[ -n "$TicketExpiration" && "$TicketExpiration" -gt "60" ]]; then
- TicketStatus="OK with short-term ticket ($TicketExpiration seconds)."
- else
- TicketStatus="ERROR, ticket not available for $P4USER on $P4PORT."
- PreflightAccessOK=0
- fi
-
- if [[ "$TicketStatus" == "OK"* ]]; then
- ProtectCheck=$(p4 -ztag -F %Protections0% -u $P4USER -p $P4PORT protect -o 2>/dev/null)
- if [[ -n "$ProtectCheck" ]]; then
- SuperAccessStatus="OK"
- else
- SuperAccessStatus="Error"
- PreflightAccessOK=0
- fi
- else
- SuperAccessStatus="Unknown"
- PreflightAccessOK=0
- fi
-
- printf "%-12s %-24s %-5s %-s\n" "$i" "$P4PORT" "$SuperAccessStatus" "$TicketStatus"
-
- done
-
- if [[ "$PreflightAccessOK" -eq 1 ]]; then
- msg "\nVerified: Access OK. All instances can be accessed as super from this host."
- else
- errmsg "Preflight Access check failed. See results above."
- OverallReturnStatus=1
- fi
-
- msg "Environment Preflight Check\n${H2}"
-
- for i in $InstanceList; do
- InstanceEnvFile=/p4/common/config/p4_${i}.vars
-
- if [[ ! -r "$InstanceEnvFile" ]]; then
- errmsg "Missing instance-specific environment file: $InstanceEnvFile"
- PreflightEnvOK=0
- fi
- done
-
- if [[ "$PreflightEnvOK" -eq 1 ]]; then
- msg "\nVerified: Environment is OK, all instances have environment files."
- else
- errmsg "Preflight Environment check failed. See missing files listed above."
- OverallReturnStatus=1
- fi
- fi
-
- if [[ "$Program" != Unset ]]; then
- if [[ $Program == /* || $Program == \.* ]]; then
- # Non-path depedent, absolute or relative path specified.
- ProgramPath=$Program
- else
- # Path-dependent path specified.
- ProgramPath=$(which $Program)
- fi
-
- [[ -z "$ProgramPath" ]] && \
- bail "The specified program [$Program] cannot be found. Aborting.\n"
-
- [[ ! -r "$ProgramPath" ]] && \
- bail "The specified program [$Program] cannot be found. Aborting.\n"
-
- [[ ! -x "$ProgramPath" ]] && \
- bail "The specified program [$Program] is not executable. Aborting.\n"
-
- for i in $InstanceList; do
- vmsg "For instance $i calling $Program $ProgramArgs"
- InstanceEnvFile=/p4/common/config/p4_${i}.vars
-
- if [[ -r "$InstanceEnvFile" ]]; then
- if [[ $NO_OP -eq 0 ]]; then
- source "$SDPEnvFile" "$i"
-
- export P4ENVIRO=/dev/null/.p4enviro
- export P4CONFIG=FileThatDoesNotExist
- export P4TICKETS=/p4/hms/.p4tickets
- export P4TRUST=/p4/hms/.p4trust
-
- host=$(grep "COMPONENT|$i|p4d-mc|" "$HTCfgFile"|cut -d '|' -f 5)
- port=$(grep "COMPONENT|$i|p4d-mc|" "$HTCfgFile"|cut -d '|' -f 8)
-
- if [[ $port == "ssl:"* ]]; then
- export P4PORT="ssl:$host:${port#ssl:}"
- else
- export P4PORT="$host:$port"
- fi
-
- $Program $ProgramArgs
- ExitCode=$?
-
- if [[ $ExitCode -ne 0 ]]; then
- warnmsg "Non-zero exit code returned: $ExitCode"
- OverallReturnStatus=1
- fi
- else
- msg "NO_OP: For instance $i, would run: $Program $ProgramArgs"
- fi
- else
- errmsg "Missing instance-specific environment file: $InstanceEnvFile"
- OverallReturnStatus=1
- fi
- done
- fi
-
- if [[ $OverallReturnStatus -eq 0 ]]; then
- msg "${H}\nAll processing completed successfully.\n"
- else
- msg "${H}\nProcessing completed, but with errors. Scan above output carefully.\n"
- fi
-
- msg "That took $(($SECONDS/3600)) hours $(($SECONDS%3600/60)) minutes $(($SECONDS%60)) seconds.\n"
-
- # See the terminate() function, which is really where this script exits.
- exit $OverallReturnStatus
-