rename_edge_serverid.sh #2

  • //
  • guest/
  • tom_tyler/
  • sw/
  • main/
  • rename_edge/
  • rename_edge_serverid.sh
  • View
  • Commits
  • Open Download .zip Download (14 KB)
#!/bin/bash
set -u

#==============================================================================
# Copyright and license info is available in the LICENSE file included with
# this package.
#------------------------------------------------------------------------------

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

declare -i ErrorCount=0
declare -i WarningCount=0
declare -i Debug=0
declare -i NoOp=1
declare ThisScript=${0##*/}
declare ThisUser=
declare ThisHost=${HOSTNAME%%.*}
declare Version=1.0.2
declare CmdLine="$0 $*"
declare LogsDir="${LOGS:-${HOME:-/tmp}}"
declare LogDatestamp=
declare LogLink=
declare Log=
declare H1="=============================================================================="
declare H2="------------------------------------------------------------------------------"
declare RequiredUtils="awk date grep id ls tail wc"

declare AccessLevel=
declare CurrentServerServices=
declare EdgeServerServices=
declare OldEdgeServerID=
declare NewEdgeServerID=
declare SDPInstance=${SDP_INSTANCE:-}
declare SDPEnv=/p4/common/bin/p4_vars
declare SDPInstanceVars=
declare TmpFile=

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

function msg () { echo -e "$*"; }
function dbg () { [[ "$Debug" -eq 0 ]] || msg "DEBUG: $*"; }
function errmsg () { msg "\\nError: ${1:-Unknown Error}\\n"; ErrorCount+=1; }
#function warnmsg () { msg "\\nWarning: ${1:-Unknown Warning}\\n"; WarningCount+=1; }
function bail () { errmsg "${1:-Unknown Error}"; exit "$ErrorCount"; }

#------------------------------------------------------------------------------
# Function: terminate
# shellcheck disable=SC2317
function terminate
{
   # Disable signal trapping.
   trap - EXIT SIGINT SIGTERM

   dbg "$ThisScript: EXITCODE: $ErrorCount"

   # Stop logging.
   [[ "$Log" == off ]] || msg "\\nLog is: $Log\\n${H1}"

   # With the trap removed, exit.
   exit "$ErrorCount"
}

#------------------------------------------------------------------------------
# Function: usage (documentation)
#
# Input:
# $1 - style, '-h' (for short usage synopsis) or '-man' (for full man-page
# documentation).  Default is -h.
#
# $2 - error message (optional).  Specify this if usage() is called due to
# usage error, in which case the given message displayed first, followed by
# the standard usage message (short or long depending on $1).  If displaying an
# error, usually $1 should be -h so that the longer usage message doesn't
# obscure the supplied usage error message.
#
# Sample Usage:
# usage
# usage -h
# usage -man
# usage -h "Incorrect command line usage."
#------------------------------------------------------------------------------
function usage
{
   declare style=${1:--h}
   declare errorMessage=${2:-}

   [[ -n "$errorMessage" ]] && \
      errmsg "\\n\\nUsage Error:\\n\\n$errorMessage\\n\\n"

   msg "USAGE for $ThisScript v$Version:

$ThisScript -old <OldEdgeServerID> -new <NewEdgeServerID> [-L <log>] [-y] [-d|-D]

or

$ThisScript [-h|-man]
"
   if [[ $style == -man ]]; then
      msg "
DESCRIPTION:
	This script renames an existing edge server.

	WARNING: THIS IS NOT A STANDARD OPERATION!  This is only suitable
	for operation during a maintenance window when users are prevented
	from accessing systems (e.g. via Protections table lockout).  If
	downtime for the commit server (and any other edege servers) must
	be minimized, the downtime needed for the commit and other edges
	can be small.  However, the renamed edge server will need
	additional downtime due to the need to initialize it's checkpoint.
	

OPERATIONAL PROCEDURE:

	This script must be executed on the commit server.

	The start state for this script is:

	* A 'p4 protects -m' returns super.
	* There is an operating commit server and edge server.
	* The edge server to be renamed has an ExternalAddress field
	  value that is correct that and can be used such that 'super'
	  is returned when the following is run:
	  p4 -p <EdgeServerExternalAddress> protects -m
	* The edge is currently replicating OK per: p4 -p <EdgeServerExternalAddress> pull -ljv
	* The hostname component of the edge server to be renamed
	  has an ExternalAddress field value that can be used with
	  ssh, e.g. to do 'ssh edge_host echo hello' without prompting
	  for a password.

	This will rename the ServerID of the edge by doing the following:

	* Generate a new server spec and configurables.
	* Generate and apply a journal patch to adjust client specs associated
	  with the old edge ServerID, associating them with the new ServerID.
	* SSH to the edge server to create SDP directories and symlinks needed
	  for the new ServerID.
	* Take an initial live checkpoint on the new edge server.

OPTIONS:
 -L <log>
	Specify the path to a log file, or the special value 'off' to disable
	logging.  By default, all output (stdout and stderr) goes to

	$LogsDir/${ThisScript%.sh}.<Datestamp>.log

	If -L is not used, a log link is updated such that a symlink
	$LOGS/${ThisScript%.sh}.log will point to the datestamped log.

	NOTE: This script is self-logging.  That is, all standard and error
	output displayed is also captured in the log file.  Use of 'tee' and
	redirection with '>' and '2>&1' are unnecessary.

 -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.  This script is then responsible for
	email handling, if any is to be done.

 -y	Live operation mode.  By default, this script does a dry run, displaying
	commands that would be executed rather than running them.

DEBUGGING OPTIONS:
 -d     Enable additional debug output.

 -D     Set extreme debugging verbosity using bash 'set -x' mode. Implies '-d'.

HELP OPTIONS:
 -h	Display short command line usage synopsis.
 -man	Display this documentation manual page.

EXAMPLES:
	Example 1: Standard usage (Dry Run):

	$ThisScript -old p4d_edge_syd -new p4d_edge_mlb

	Example 2: Standard usage (Live Run)

	$ThisScript -old p4d_edge_syd -new p4d_edge_mlb -y

SEE ALSO:
	See also: convert_edge_to_commit.sh
"
   fi

   exit 2
}

#------------------------------------------------------------------------------
# Function: get_old_log_timestamp ($log)
#
# Get the last modified timestamp of the old log in a cross-platform manner.
# If we don't get a correct value using 'stat' (which varies across the
# UNIX/Linux/MacOSX spectrum), use the current time as a fallback. In that
# case, the timestamp will reflect the time the log was moved rather than when
# it was last modified, but that's still reasonable.  The file timestamp will
# still have the correct last-modified time.
#------------------------------------------------------------------------------
function get_old_log_timestamp () {
   local log=${1:-}
   local oldLogTimestamp=
   [[ -n "$log" ]] || return

   if [[ "$(uname -s)" == "Darwin" ]]; then
      oldLogTimestamp=$(stat -L -f %Sm -t '%Y-%m-%d-%H%M%S' "$log" 2>/dev/null)
   else
      oldLogTimestamp="$(stat -L -c '%10y' "$log" | sed -e 's@[.].*$@@g' -e 's@:@@g' -e 's@ @-@g')"
   fi

   [[ "$oldLogTimestamp" =~ ^[2-9]{1}[0-9]{3}- ]] ||
      oldLogTimestamp=$(date +'%Y-%m-%d-%H%M%S')

   echo "$oldLogTimestamp"
}

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

declare -i shiftArgs=0

set +u
while [[ $# -gt 0 ]]; do
   case $1 in
      (-h) usage -h;;
      (-V) msg "$ThisScript v$Version"; exit 2;;
      (-old) OldEdgeServerID="$2"; shiftArgs=1;;
      (-new) NewEdgeServerID="$2"; shiftArgs=1;;
      (-i) SDPInstance="$2"; shiftArgs=1;;
      (-man) usage -man;;
      (-y) NoOp=0;;
      (-L) Log="$2"; shiftArgs=1;;
      (-d) Debug=1;; # Debug mode.
      (-D) Debug=1; set -x;; # Use bash 'set -x' extreme debugging mode.
      (-*) usage -h "Unknown option: ($1)";;
      (*) usage -h "Unknown parameter: ($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

if [[ -z "$Log" ]]; then
   LogDatestamp=$(date +'%Y-%m-%d-%H%M%S')
   LogLink="${LogsDir}/${ThisScript%.sh}.log"
   Log="${LogsDir}/${ThisScript%.sh}.${LogDatestamp}.log"
fi

[[ -n "$SDPInstance" ]] || usage -h "The SDP instance must be defined; either do 'source /p4/common/bin/p4_vars N' (where N is the SDP instance name)' or else use '-i <N>'."
SDPInstanceVars="/p4/common/config/p4_${SDPInstance}.vars"
[[ -r "$SDPInstanceVars" ]] || usage -h "Invalid SDP instance name; missing instance vars file: $SDPInstanceVars"
[[ -n "$OldEdgeServerID" ]] || usage -h "The '-old <OldEdgeServerID>' parameter is required."
[[ -n "$NewEdgeServerID" ]] || usage -h "The '-new <NewEdgeServerID>' parameter is required."

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

# shellcheck disable=SC1090
source "$SDPEnv" "$SDPInstance" ||\
   bail "Could not do: /p4/common/bin/p4_vars \"$SDPInstance\""

for u in $RequiredUtils; do
   [[ -n "$(command -v "$u")" ]] || errmsg "Missing required utility: $u"
done
[[ "$ErrorCount" -eq 0 ]] || bail "Aborted early due to missing required utilitiies."

trap terminate EXIT SIGINT SIGTERM

if [[ "$Log" != off ]]; then
   if [[ -e "$Log" ]]; then
      # shellcheck disable=SC2012
      OldLogTimestamp="$(ls -l --time-style +'%Y%m%d-%H%M%S' "$Log" 2>/dev/null | awk '{print $6}')"
      # shellcheck disable=SC2012
      [[ -n "$OldLogTimestamp" ]] ||\
         OldLogTimestamp="$(ls -l -D '%Y%m%d-%H%M%S' "$Log" 2>/dev/null | awk '{print $6}')"
      [[ -n "$OldLogTimestamp" ]] ||\
         OldLogTimestamp="$(date +'%Y%m%d-%H%M%S' "$Log" 2>/dev/null)"
      [[ -n "$OldLogTimestamp" ]] ||\
         OldLogTimestamp="old"
      OldLog="${LogsDir}/${ThisScript%.sh}.${OldLogTimestamp}.log"
      mv -f "$Log" "$OldLog" || bail "Could not do: mv -f \"$Log\" \"$OldLog\""
      dbg "Rotated $Log to $OldLog"
   fi

   if [[ -n "$LogLink" && -e "$LogLink" ]]; then
      if [[ -L "$LogLink" ]]; then
         rm -f "$LogLink"
      else
         # If the name that should be a symlink is not a symlink, move it aside before
         # creating the symlink.
         OldLogTimestamp=$(get_old_log_timestamp "$LogLink")
         mv -f "$LogLink" "${LogLink%.log}.${OldLogTimestamp}.log" ||\
            bail "Could not move old log file aside; tried: mv -f \"$LogLink\" \"${LogLink%.log}.${OldLogTimestamp}.log\""
      fi
   fi

   touch "$Log" || bail "Couldn't touch log file [$Log]."

   if [[ -n "$LogLink" ]]; then
      # Use a subshell so the 'cd' doesn't persist.
      ( cd "$LogsDir"; ln -s "${Log##*/}" "${LogLink##*/}"; ) ||\
          bail "Couldn't initialize log symlink; tried: ln -s \"$Log\" \"$LogLink\""
   fi

   # Redirect stdout and stderr to a log file.
   exec > >(tee "$Log")
   exec 2>&1

   msg "${H1}\\nLog is: $Log\\n"
fi

ThisUser=$(id -n -u)
msg "Started $ThisScript v$Version as $ThisUser@$ThisHost at $(date).\\nCommand Line was: $CmdLine"

if [[ "$NoOp" -eq 0 ]]; then
   msg "Operating in LIVE mode."
else
   msg "NO_OP: Operating in DRY RUN mode."
fi

msg "Renaming edge ServerID from '$OldEdgeServerID' to '$NewEdgeServerID'."

msg "${H2}\\nStarting Preflight Checks."

CurrentServerServices=$("$P4BIN" -ztag -F %serverServices% info -s 2>/dev/null)
if [[ "$CurrentServerServices" == commit-server ]]; then
   msg "Verified: Current server is the commit server."
else
   errmsg "Current server services is '$CurrentServerServices'; must be 'commit-server'."
fi

AccessLevel=$("$P4BIN" protects -m 2>/dev/null)
if [[ -n "$AccessLevel" ]]; then
   if [[ "$AccessLevel" =~ ^super$ ]]; then
      msg "Verified: User $P4USER has super access."
   else
      errmsg "User $P4USER has only '$AccessLevel' access; 'super' is required."
   fi
else
   errmsg "Could not verify access level; super required."
fi

EdgeServerServices=$("$P4BIN" -ztag -F %Services% server -o "$OldEdgeServerID" 2>/dev/null)
if [[ "$EdgeServerServices" == edge-server ]]; then
   msg "Verified: Old edge ServerID has services edge-server."
else
   errmsg "Old edge ServerID has services '$EdgeServerServices'; must be edge-server to use with $ThisScript."
fi

TmpFile=$(mktemp)

if "$P4BIN" server --exists -o "$NewEdgeServerID" > "$TmpFile" 2>&1; then
   errmsg "New edge ServerID '$NewEdgeServerID' already exists. It must not exist at the start of processing."
else
   if grep -E -q '^Server .* doesn.t exist' "$TmpFile"; then
      msg "Verified: New edge ServerID '$NewEdgeServerID' does not yet exist."
   else
      errmsg "Could not verify that new edge ServerID '$NewEdgeServerID' does not yet exist."
   fi
fi

if [[ "$ErrorCount" -eq 0 ]]; then
   msg "\\nAll preflight checks passed. Proceeding."
else
   bail "Aborting due to failed preflight checks. See errors reported above."
fi

if [[ $ErrorCount -eq 0 && $WarningCount -eq 0 ]]; then
   msg "${H2}\\nAll processing completed successfully.\\n"
elif [[ $ErrorCount -eq 0 ]]; then
   msg "${H2}\\nProcessing completed, but with $WarningCount warning.  Review the output above carefully.\\n" 
else
   msg "${H2}\\nProcessing completed, but with $ErrorCount errors and $WarningCount warnings.  Scan above output carefully.\\n" 
fi

# Display runtime.
msg "That took $((SECONDS/3600)) hours $((SECONDS%3600/60)) minutes $((SECONDS%60)) seconds.\\n"

# See the terminate() function, where this script exits.
exit "$ErrorCount"
# Change User Description Committed
#2 31288 Perforce maintenance Added another preflight check.
Removed 'bc' from list of required utilities.
Added new test for new prelfight check.
#1 31284 Perforce maintenance Added initial files.
Nothing working yet; this is just the start of development.

Files included:
* rename_edge_serverid.sh - Script to rename an edge ServerID.
* convert_edge_to_commit.sh - Script to convert a filtered edge into a commit.
* cli_test.cfg - Test Suite (using SDP run_cli_tests.sh script).
* r - Test suite wrapper.
* .p4ignore - Just a typical P4IGNORE file.