verify_sdp.sh #1

  • //
  • guest/
  • robert_cowham/
  • perforce/
  • sdp/
  • Server/
  • Unix/
  • p4/
  • common/
  • bin/
  • verify_sdp.sh
  • View
  • Commits
  • Open Download .zip Download (15 KB)
#!/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
#------------------------------------------------------------------------------

# verify_sdp.sh
# Verifies current SDP environment according to current version of SDP.

#==============================================================================
# Declarations and Environment

export VS_SDP_P4CBIN=/p4/common/bin
export VS_SDP_ENV=$VS_SDP_P4CBIN/p4_vars
export SDP_INSTANCE=${SDP_INSTANCE:-UnsetSDPInstance}

export SDP_INSTANCE=${1:-$SDP_INSTANCE}
if [[ $SDP_INSTANCE == UnsetSDPInstance ]]; then
   echo "Instance parameter not supplied."
   echo "You must supply the SDP instance as a parameter to this script."
   exit 1
fi

declare -i ServerOnline=0
declare -i ErrorCount=0
declare -i CheckCount=0
declare -i ExitCode=0
declare -i ShowLog=1
declare Version=5.2.0
declare H1="=============================================================================="
declare H2="------------------------------------------------------------------------------"
export LOGFILE=Unset

#==============================================================================
# Local Functions

# Note: This script does not use SDP library files, as its purpose is to
# verify the integrity of an SDP installation.  Thus, it has its own
# self-contained versions of some functions for which similar versions
# would normally be sourced from files in /p4/common/lib, like libbcore.sh.

# Micro-functions, one-liners used to avoid external dependencies.
function msg () { if [[ "$LOGFILE" != Unset ]]; then echo -e "$*" >> "$LOGFILE"; else echo -e "$*"; fi; }
function errmsg () { msg "\nError: ${1:-Unknown Error}\n"; ErrorCount+=1; }
function bail () { errmsg "${1:-Unknown Error}"; exit "${2:-1}"; }

function run () { 
   local cmd="${1:-echo}"
   local desc="${2:-}"
   local -i showOutput="${3:-1}"
   local log=$(mktemp)
   local -i exitCode=

   [[ -n "$desc" ]] && msg "$desc"
   msg "Executing: $cmd"
   $cmd > "$log" 2>&1
   exitCode=$?

   if [[ "$showOutput" ]]; then
      echo "EXIT_CODE: $exitCode" >> "$log"
      cat "$log" >> $LOGFILE
   fi

   /bin/rm -f "$log"
   return "$exitCode"
}

#------------------------------------------------------------------------------
# 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 -man
# usage -h "Incorrect command line usage."
#
# This last example generates a usage error message followed by the short
# '-h' usage summary.
#------------------------------------------------------------------------------
function usage
{
   declare style=${1:--h}
   declare errorMessage=${2:-Unset}

   if [[ $errorMessage != Unset ]]; then
      echo -e "\n\nUsage Error:\n\n$errorMessage\n\n" >&2
   fi

   echo "USAGE for verify_sdp.sh v$Version:

verify_sdp.sh [<instance>]

   or

verify_sdp.sh -h|-man
"
   if [[ $style == -man ]]; then
      echo -e "DESCRIPTION:

	This script verifies the current SDP setup for the specified instance.

	Useful if you change anything, particularly after an SDP upgrade.

OPTIONS:
<instance>
	Specify the SDP instances.  If not specified, the SDP_INSTANCE
	environment variable is used instead.  If the instance is not
	defined by a parameter and SDP_INSTANCE is not defined,
	exits immediately with an error message.

 -online
	Online mode.  Does additional checks that require P4D to be online.

 -si	Silent mode.  Does not display the generated log file to stdout
	at the end of processing.

 -L <log>
	Specify the log file to use.  The default is /p4/N/logs/verify_sdp.log

 -D	Set extreme debugging verbosity.

HELP OPTIONS:
 -h	Display short help message
 -man	Display man-style help message

EXAMPLES:
	This script is typically called after SDP update with only the instance
	name or number as an argument, e.g.:

	verify_sdp.sh 1

LOGGING:
	This script generates a log file and also displays it to stdout at the
	end of processing.  By default, the log is:
	/p4/N/logs/verify_sdp.log.

	The exception is usage errors, which result an error being sent to
	stderr followed usage info on stdout, followed by an immediate exit.

	If the '-si' (silent) flag is used, the log is generated, but its
	contents are not displayed to stdout at the end of processing.

EXIT CODES:
	An exit code of 0 indicates no errors were encounted attempting to
	perform verifications, and that all checks verified cleanly.
"
   fi

   exit 1
}

#------------------------------------------------------------------------------
# Function: do_preflight_checks ()
#
# Sample Usage:
# do_preflght_checks || bail "Preflight checks failed. Aborting further checks."
#------------------------------------------------------------------------------
function do_preflight_checks () {

   declare toolsList="which date ls grep awk id head tail"
   declare -i preflightOK=1

   msg "$H2\nDoing preflight sanity checks."
   msg "Preflight Check 1: Ensuring these utils are in PATH: $toolsList"

   for tool in $toolsList; do
      CheckCount+=1
      [[ -z "$(which "$tool" 2>/dev/null)" ]] && \
         errmsg "Tool '$tool' not in PATH."
   done

   [[ $ErrorCount -eq 0 ]] || return 1

   msg "Verified: Essential tools are in the PATH."

   msg "Preflight Check 2: cd $VS_SDP_P4CBIN"

   CheckCount+=1
   cd "$VS_SDP_P4CBIN" >> $LOGFILE 2>&1
   if [[ "$?" -ne 0 ]]; then
      errmsg "Could not cd to: $VS_SDP_P4CBIN"
      return 1
   fi

   msg "Verified: cd works to: $VS_SDP_P4CBIN"

   msg "Preflight Check 3: Checking current user owns $VS_SDP_P4CBIN"

   # shellcheck disable=SC2012
   export VS_SDP_OWNER=$(ls -ld . | awk '{print $3}')
   export ThisUser="$(id -n -u)"

   CheckCount+=1
   if [[ "$ThisUser" == "$VS_SDP_OWNER" ]]; then
      msg "Verified: Current user [$ThisUser] owns $VS_SDP_P4CBIN"
   else
      errmsg "Current user [$ThisUser] does not own $VS_SDP_P4CBIN."
      return 1
   fi

   return 0
}


#------------------------------------------------------------------------------
# Function: check_file ($file, $errMsg)
#
# Checks for existance of a file, returns 0 if it exists, 1 otherwise.
# Allows optional custom error message describing the file, to be displayed if
# the file is missing.  Default error message is "Missing file [FILE]."
#
# Inputs:
function check_file () {
   local file=$1
   local errMsg=${2:-Missing file}
   CheckCount+=1
   msg "Checking existence of file $file"
   [[ -f $file ]] && return 0
   errmsg "$errMsg: [$file]."
   return 1
}

#------------------------------------------------------------------------------
# Function: check_configurable ($instance, $configurable, $scope, $expectedVal, $errMsg1, $errMsg2)
#
# Check that a configurable is set, and optionally check that it is set to
# an expected value.
#
# Inputs:
# $1 - SDP Instance. Required.
# $2 - Configurable name. Required.
# $3 - Configurable scope. Default is "any"
# $4 - Expected value of variable. Optional. If defined, an additional check is
#      done, checking the current value against the expected value.
# $5 - Optional error message to display if no value is defined.  See code
#      below for the default message.
# $6 - Optional error message to display if a value is defined but does not
#      match the expected value.  See code below for the default message.
#
# Return Codes:
# 1 - Verifications failed.
# 0 - Verifications passed.
# 
# Sample Usage: 
# check_configurable $SDP_INSTANCE journalPrefix

# check_configurable $SDP_INSTANCE journalPrefix any "$CHECKPOINTS/$P4SERVER"
#
# check_configurable $SDP_INSTANCE journalPrefix any "$CHECKPOINTS/$P4SERVER" ||\
#   bail "Yikes, journalPrefix is not set, all bets are off. Aborting."
#------------------------------------------------------------------------------
function check_configurable () {
   local instance="$1"
   local configurable="$2"
   local scope="${3:-any}"
   local expectedValue="${4:-NoExpectedValue}"
   local errMsgMissing="${5:-No value defined}"
   local errMsgBadValue="${6:-Value does not match what is expected}"
   local value=
   CheckCount+=1

   value=$($P4DBIN -r "$P4ROOT" -cshow | grep "^${scope}: ${configurable} = ")

   if [[ -n "$value" ]]; then
      value=${value##* = }
      msg "Verified: Configurable ${scope}:${configurable} is defined."
   else
      errmsg "$errMsgMissing for configurable [${scope}:${configurable}]."
      return 1
   fi

   [[ "$expectedValue" == "NoExpectedValue" ]] && return 0

   CheckCount+=1

   if [[ "$value" == "$expectedValue" ]]; then
      msg "Verified: Configurable ${scope}:${configurable} has expected value [$value]."
   else
      errmsg "$errMsgBadValue for variable [${scope}:${configurable}]\n\tExpected value: [$expectedValue]\n\tActual value:   [$value]"
      return 1
   fi

   return 0
}

#------------------------------------------------------------------------------
# Function: check_env_var ($instance, $var, $expectedval, $msg1, $msg2)
#
# Check that a shell environment variable is set when sourcing the SDP
# environment. Optionally checks that variables are set to expected values.
#
# Inputs:
# $1 - SDP Instance. Required.
# $2 - Variable name. Required.
# $3 - Expected value of variable. Optional. If defined, an additional check is
#      done, checking the current value against the expected value.
# $4 - Optional error message to display if no value is defined.  See code
#      below for the default message.
# $5 - Optional error message to display if a value is defined but does not match
#      the expected value.  See code below for the default message.
# 
# Return Codes:
# 1 - Verifications failed.
# 0 - Verifications passed.
# Sample Usage: 
# check_env_var $SDP_INSTANCE P4JOURNAL "/p4/$SDP_INSTANCE/logs/journal"
#
# check_env_var $SDP_INSTANCE P4JOURNAL "/p4/$SDP_INSTANCE/logs/journal" ||\
#   bail "Yikes, P4JOURNAL is not set, all bets are off. Aborting."
#------------------------------------------------------------------------------
function check_env_var () {
   local instance="$1"
   local var="$2"
   local expectedValue="${3:-NoExpectedValue}"
   local errMsgMissing="${4:-No value defined}"
   local errMsgBadValue="${5:-Value does not match what is expected}"
   local value=
   CheckCount+=1

   eval unset "${var}"
   source "$VS_SDP_ENV" "$instance"

   set +u
   if [[ -n "$(eval echo \$"${var}")" ]]; then
      msg "Verified: Variable ${var} is defined."
      set -u
   else
      errmsg "$errMsgMissing for variable [$var]."
      set -u
      return 1
   fi

   [[ "$expectedValue" == "NoExpectedValue" ]] && return 0

   CheckCount+=1
   value="$(eval echo \$"${var}")"

   if [[ "$value" == "$expectedValue" ]]; then
      msg "Verified: Variable ${var} has expected value [$value]."
   else
      errmsg "$errMsgBadValue for variable [$var]\n\tExpected value: [$expectedValue]\n\tActual value:   [$value]"
      return 1
   fi

   return 0
}

#==============================================================================
# Command Line Processing

declare -i shiftArgs=0

set +u
while [[ $# -gt 0 ]]; do
   case $1 in
      (-h) usage -h;;
      (-man) usage -man;;
      (-online) ServerOnline=1;;
      (-si) ShowLog=0;;
      (-L) LOGFILE="$2"; shiftArgs=1;;
      (-D) set -x;; # Debug; use 'set -x' mode.
      (-*) usage -h "Unknown command line option ($1).";;
      (*) export SDP_INSTANCE=$1;;
   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

[[ $SDP_INSTANCE == Unset ]] && \
   bail "The \$SDP_INSTANCE setting is not defined. It must be defined by doing:\n\n\tsource $VS_SDP_ENV <instance>\n\nor by passing in the instance name as a parameter to this script.\n"

#==============================================================================
# Main Program

source "$VS_SDP_ENV" "$SDP_INSTANCE" ||\
   bail "Failed to load SDP environment for instance $SDP_INSTANCE."

source "$P4CBIN/backup_functions.sh" ||\
   bail "Failed to load backup_functions.sh."

[[ "$LOGFILE" == Unset ]] && export LOGFILE="$LOGS/verify_sdp.log"

rotate_log_file "$LOGFILE" ".gz"

msg "${0##*/} v$Version Starting SDP verification at $(date +'%a %Y-%m-%d %H:%M:%S %Z')."

msg "\nIf you have any questions about the output from this script, contact support@perforce.com."

do_preflight_checks ||\
   bail "Preflight checks failed. Aborting further checks."

msg "${H2}\nChecking environment variables."
check_env_var "$SDP_INSTANCE" SDP_INSTANCE
check_env_var "$SDP_INSTANCE" P4ROOT "/p4/$SDP_INSTANCE/root"
check_env_var "$SDP_INSTANCE" P4JOURNAL "/p4/$SDP_INSTANCE/logs/journal"

check_env_var "$SDP_INSTANCE" SDP_ADMIN_PASSWORD_FILE "$P4CCFG/.p4passwd.$P4SERVER.admin"
if [[ $? -eq 0 ]]; then
   check_file "$SDP_ADMIN_PASSWORD_FILE" "SDP admin password file doesn't exist"
fi

msg "${H2}\nRunning standard checks typically called within SDP scripts."
check_vars
set_vars
check_dirs

msg "${H2}\nChecking for a few database files."
# Check db files exist
file=db.counters
for dir in $P4ROOT $OFFLINE_DB; do
   check_file "$dir/$file" "Expected database file doesn't exist"
done

msg "${H2}\nChecking for key files."
check_file "$OFFLINE_DB/offline_db_usable.txt" "Offline database not in a usable state."
check_file "$P4BIN" "The p4 binary (or symlink) doesn't exist"
check_file "$RC" "The p4d init script doesn't exist"
check_file "$P4TICKETS" "The P4TICKETS file doesn't exist"

msg "${H2}\nChecking configurables values."
check_configurable "$SDP_INSTANCE" journalPrefix any "$CHECKPOINTS/$P4SERVER"
check_configurable "$SDP_INSTANCE" server.depot.root any "$DEPOTS"

msg "${H2}\nChecking SDP structure."
if [[ -L "$P4HOME" ]]; then
   errmsg "This is a symlink, should be a local directory: $P4HOME"
fi

if [[ "$ServerOnline" -eq 1 ]]; then
   msg "${H2}\nDoing online checks."
   CheckCount+=1
   run "$P4CBIN/p4login" "Login check" 0
   if [[ $? -eq 0 ]]; then
      msg "Verified: Login OK."
   else
      errmsg "Login as P4USER $P4USER to P4PORT $P4PORT could not be verified."
   fi
fi

### P4="$P4BIN -p $P4PORT -u $P4USER"

# TODO:
# - links all present and correct for instance
# - db files present (and quick verification OK)
# - offline db directories named appropriately (e.g. db1/db2)
# - crontab includes at least daily_checkpoint.sh
# - checkpoints dir contains a checkpoint or two
# - service password verified for a replica
# - Add flag to check less-critical SDP configurables, and generate
#   warnings (rather than errors) if they are not set as expected.

if [[ $ErrorCount -eq 0 ]]; then
   msg "\n${H1}\n\nALL CLEAN: $CheckCount verifications completed OK."
else
   msg "\n${H1}\n\nVerifications completed, with $ErrorCount errors detected in $CheckCount checks."
   ExitCode=1
fi

[[ "$ShowLog" -eq 1 && -s "$LOGFILE" ]] && cat "$LOGFILE"
 
exit $ExitCode
# Change User Description Committed
#1 25113 Robert Cowham Merge latest changes from dev
//guest/perforce_software/sdp/dev/Server/Unix/p4/common/bin/verify_sdp.sh
#1 24804 C. Thomas Tyler Terminology tweak, 'validate' -> 'verify'.

#review @robert_cowham
//guest/perforce_software/sdp/dev/Server/Unix/p4/common/bin/validate_sdp.sh
#3 24534 C. Thomas Tyler Various enhancements and internal refactoring for validate_sdp.sh.

Added important check that /p4/N is a dir, not a symlink.

#review-24532 @robert_cowham
#2 24356 C. Thomas Tyler Enhancements to validate_sdp.sh:
* Added simple bold ALL CLEAN message to look for.
* Added check_env_var() function to check shell environment
  variables, with some calls to it.
* Added check_configurable() to check for configurables.  This
  is implemented using back-door check methodology (using
  'p4d_N -cshow') so values can be checked with P4D offline.
  This replaced stub function check_var().
* Removed stub function check_configurables().  It's easier
  to understand if all checks in the Main section of the code.
* Changed so checks requiring p4d to be online are not done
  by default; added '-online' flag to run those tests.  This
  is because I anticpate typical usage of the validator to
  be requiring it to be report ALL CLEAN before starting
  P4D after a server upgrade.
* Added check for new $SDP_ADMIN_PASSWORD_FILE variable.
* Added check admin password file pointed to by $SDP_ADMIN_PASSWORD_FILE.
* Added errmsg() function, with corresponding tweak to bail().
* Consolidated Log an LOGIFLE to just LOGFILE.
* Removed a few items from TOOD comments that got done.
* Made a few tweaks for style normalization:
  - Functions are lowercase with undescore separators.
  - Functions vars are lowercase-initiated camelCase.
  - Indentation: 3 spaces for functions/loops/etc.
* Added run() function replacing cmd() stub function.
       * Enhanced p4login check.
* Added comment noting why this script uses self-contained
  copies of functions defined in other SDP files in /p4/common/lib.
* And other things.

Warning: In the short run, this may fail tests as the new
SDP_ADMIN_PASSWORD_FILE variable is also pending review.

#review @robert_cowham
#1 23640 Robert Cowham Super basic validation - placeholder for many more tests to come!