#!/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.3.3 declare -i SilentMode=0 declare TicketExpiration= declare TicketStatus= declare SuperAccessStatus= declare SkipInstance=sample declare -i DoPreflight=1 declare -i PreflightAccessOK=1 declare -i PreflightEnvOK=1 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 [,] [-L ] [-si] [-v] [-n] [-D] [ []] 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 [, Set verbosity 1-5 (-v1 = quiet, -v5 = highest). -L 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) SkipInstance=$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 #============================================================================== # 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 [[ "$i" == "$SkipInstance" ]] && continue 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 [[ "$i" == "$SkipInstance" ]] && continue 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 [[ "$i" == "$SkipInstance" ]] && continue 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