#!/bin/bash
#==============================================================================
set -u
#==============================================================================
# Declarations and Environment
declare -i ErrorCount=0
declare -i ImportUserErrorCount=0
declare -i ImportUserCount=0
declare -i ImportUserOKCount=0
declare -i ImportGroupErrorCount=0
declare -i ImportGroupCount=0
declare -i ImportGroupOKCount=0
declare -i SkipUserByTypeCount=0
declare -i SkipExistingUserCount=0
declare -i NoOp=0
declare -i Debug=0
declare -i i=0
declare ThisScript=${0##*/}
declare CmdLine="$0 $*"
declare Version=1.2.0
declare ThisUser=
declare UserSpecFile=
declare GroupSpecFile=
declare TmpDir=
declare ThisHost="${HOSTNAME%%.*}"
declare SDPInstance=
declare -A ExistingUsers
declare TargetServerPort=
declare TmpFile=
declare User=
declare Type=
declare Group=
declare FoundUsers=
declare FoundGroups=
declare P4CBin=/p4/common/bin
declare H1="=============================================================================="
declare H2="------------------------------------------------------------------------------"
declare Log=
declare OldLog=
#==============================================================================
# Local Functions
function msg () { echo -e "$*"; }
function dbg () { [[ "$Debug" -eq 0 ]] || msg "DEBUG: $*"; }
function errmsg () { msg "\\nError: ${1:-Unknown Error}\\n"; ErrorCount+=1; }
function bail () { errmsg "${1:-Unknown Error}"; exit "${2:-1}"; }
#------------------------------------------------------------------------------
# Function: terminate
function terminate
{
# Disable signal trapping.
trap - EXIT SIGINT SIGTERM
[[ "$Log" == "off" ]] || msg "Log is: $Log\\n${H1}"
# With the trap removed, exit.
exit "${ErrorCount}"
}
#------------------------------------------------------------------------------
# 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
# error, usually $1 should be -h so that the longer usage message doesn't
# obscure 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
msg "\\n\\nUsage Error:\\n\\n$errorMessage\\n\\n"
fi
msg "USAGE for $ThisScript v$Version:
$ThisScript -t <TargetPort> [-i <SDPInstance>] [-L <log>] [-n] [-D]
or
$ThisScript [-h|-man|-V]
"
if [[ $style == -man ]]; then
msg "
DESCRIPTION:
This script targets a designated server and pulls user and group
spec data from that, and loads them into the local server.
Existing users are skipped. Groups are always re-added to capture
any group membership changes.
This script might be useful after a perfmerge is done in which
the servers to be merged were using a P4AUTH server, and after
which the P4AUTH is to be deprected. This script can be used
following the perfmerge to target the P4AUTH server running from
the environment of the post-perfmerge server.
OPTIONS:
-t <TargetPort>
Specify the target server port. If SSL is enabled, this server
must be trusted, with a valid trust enrtry in the P4TRUST file
defined for the local SDP environment. A valid login ticket must
be available in the P4TICKETS file defined for the local SDP
environment.
This paraemter is required.
-i <SDPInstance>
Specify the local SDP instance to which user and group specs
will be imported. This is required unless the SDP_INSTANCE
environment variable is defined; it will be
if the standard SDP shell environment is loaded.
-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:
/p4/N/logs/${ThisScript%.sh}.log
(where N is the SDP instance name).
If a log by that name exists from prior runs, it is rotated to:
/p4/N/logs/${ThisScript%.sh}.<timestamp>.log
NOTE: This script is self-logging. That is, output displayed on the screen
is simultaneously captured in the log file. Do not run this script with
redirection operators like '> log' or '2>&1', and do not use 'tee.'
-n No-Op. Prints commands instead of running them.
-d Enable debugging messages.
-D Set extreme debugging verbosity. Implies '-d'.
HELP OPTIONS:
-h Display short help message
-man Display man-style help message
EXAMPLES:
Get a quick usage message:
${ThisScript} -h
Get the full manual page:
${ThisScript} -man
Normal Usage is with no arguments:
${ThisScript}
"
fi
exit 1
}
#==============================================================================
# Command Line Processing
declare -i shiftArgs=0
set +u
while [[ $# -gt 0 ]]; do
case $1 in
(-h) usage -h;;
(-man) usage -man;;
(-t) TargetServerPort="$2"; shiftArgs=1;;
(-i) SDPInstance="$2"; shiftArgs=1;;
(-L) Log="$2"; shiftArgs=1;;
(-n) NoOp=1;;
(-d) Debug=1;;
(-D) Debug=1; set -x;; # Extreme Debug; use bash 'set -x' mode.
(*) usage -h "Unknown arg ($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
[[ -n "$TargetServerPort" ]] ||\
usage -h "Missing required '-t <TargetServerPort>' parameter."
if [[ -z "$SDPInstance" ]]; then
if [[ -n "${SDP_INSTANCE:-}" ]]; then
SDPInstance="$SDP_INSTANCE"
else
bail "The '-i <SDPInstance>' option is required if the SDP shell environmnt s not loaded (and SDP_INSTANCE is not set)."
fi
fi
[[ -z "$Log" ]] && Log="/p4/${SDPInstance}/logs/${ThisScript%.sh}.log"
#==============================================================================
# Main Program
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" | awk '{print $6}')"
OldLog="/p4/${SDPInstance}/logs/${ThisScript%.sh}.$OldLogTimestamp.log"
mv -f "$Log" "$OldLog" || bail "Could not do: mv -f \"$Log\" \"$OldLog\""
msg "Rotated $Log to $OldLog"
fi
touch "$Log" || bail "Couldn't touch log file [$Log]."
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: $CmdLine"
msg "${H2}\\nStarting Preflight Checks."
msg "Loading SDP Environment for instance $SDPInstance."
# shellcheck disable=SC1091
source "$P4CBin/p4_vars" "$SDPInstance" ||\
errmsg "Failed to load SDP shell environment for instance $SDPInstance."
msg "Checking access to target server, P4PORT=$TargetServerPort."
if p4 -p "$TargetServerPort" login -s; then
msg "Verified: Login to target server is valid."
else
errmsg "Login to target server could not be verified."
fi
if p4 login -s; then
msg "Verified: Login to local server is valid."
else
errmsg "Login to local server could be verified."
fi
if [[ "$ErrorCount" -eq 0 ]]; then
msg "Preflight checks OK."
else
bail "Aborted due to failed preflight checks."
fi
msg "${H1}\\nImport Users"
TmpFile=$(mktemp)
dbg "Getting list of local users for instance $SDPInstance with:\\np4 -ztag -F %User% users -a .LT. $TmpFile"
if p4 -ztag -F %User% users -a > "$TmpFile"; then
if [[ -s "$TmpFile" ]]; then
i=0
while read -r User; do
ExistingUsers[$User]=1
i+=1
done < "$TmpFile"
else
bail "Found no existing users on local instance $SDPInstance server."
fi
else
bail "Could not get list of users for local instance $SDPInstance server."
fi
msg "Found ${#ExistingUsers[@]} existing users on server."
dbg "Getting list of users from P4AUTH server with:\\np4 -p $TargetServerPort -ztag -F %Type%:%User% users .GT. $TmpFile"
if p4 -p "$TargetServerPort" -ztag -F %Type%:%User% users > "$TmpFile"; then
if [[ -s "$TmpFile" ]]; then
FoundUsers=$(grep -c '^standard:' "$TmpFile")
msg "Found $FoundUsers standard users on target server."
else
bail "Found no users on target server to import."
fi
else
bail "Could not get list of users and types from target server."
fi
TmpDir=$(mktemp -d)
if [[ ! -d "$TmpDir" ]]; then
msg "Creating temp dir [$TmpDir]"
mkdir -d "$TmpDir" || bail "Could not create temp dir."
fi
while read -r TypeAndUser; do
Type=${TypeAndUser%%:*}
User=${TypeAndUser#*:}
if [[ "$Type" != "standard" ]]; then
msg "Skipping user $User of non-standard type [$Type]."
SkipUserByTypeCount+=1
continue
fi
if [[ "${ExistingUsers[$User]:-}" == "1" ]]; then
msg "Skipping existing user $User."
SkipExistingUserCount+=1
continue
fi
msg "Adding user $User."
ImportUserCount+=1
UserSpecFile="$TmpDir/$User.user.p4s"
p4 -p "$TargetServerPort" user -o "$User" | grep -E -v '^(#|Access:|Update:)' > "$UserSpecFile"
if grep -q ^User: "$UserSpecFile"; then
dbg "BEGIN USER SPEC FILE $UserSpecFile:\\n${H2}\\n$(cat "$UserSpecFile")\\n${H2}"
if [[ "$NoOp" -eq 0 ]]; then
if p4 user -f -i < "$UserSpecFile"; then
ImportUserOKCount+=1
else
errmsg "Failed to add user $User with this spec:\\nBEGIN USER SPEC FILE $UserSpecFile:\\n${H2}\\n$(cat "$UserSpecFile")\\n${H2}"
ImportUserErrorCount+=1
fi
else
msg "NO_OP: Would add user $User"
ImportUserOKCount+=1
fi
else
errmsg "Could not generate user spec for user $User."
fi
done < "$TmpFile"
dbg "Getting list of groups from P4AUTH server with:\\np4 -p $TargetServerPort -ztag -F %group% groups .GT. $TmpFile"
if p4 -p "$TargetServerPort" -ztag -F %group% groups > "$TmpFile"; then
if [[ -s "$TmpFile" ]]; then
if mv -f "$TmpFile" "${TmpFile}.2"; then
if sort -u "${TmpFile}.2" > "$TmpFile"; then
FoundGroups=$(wc -l "$TmpFile" | awk '{print $1}')
msg "Found $FoundGroups groups on target server."
else
bail "Could not sort group spec list file with: sort -u ${TmpFile}.2 .GT. $TmpFile"
fi
else
bail "Could not move group spec list file with: mv -f $TmpFile ${TmpFile}.2"
fi
else
bail "Found no groups on target server to import."
fi
else
bail "Could not get list of groups from target server."
fi
while read -r Group; do
msg "Adding group $Group."
ImportGroupCount+=1
GroupSpecFile="$TmpDir/$Group.group.p4s"
p4 -p "$TargetServerPort" group -o "$Group" | grep -E -v '^(#|Access:|Update:)' > "$GroupSpecFile"
if grep -q ^Group: "$GroupSpecFile"; then
dbg "BEGIN GROUP SPEC FILE $GroupSpecFile:\\n${H2}\\n$(cat "$GroupSpecFile")\\n${H2}"
if [[ "$NoOp" -eq 0 ]]; then
if p4 group -i < "$GroupSpecFile"; then
ImportGroupOKCount+=1
else
errmsg "Failed to add group $Group with this spec:\\nBEGIN USER SPEC FILE $GroupSpecFile:\\n${H2}\\n$(cat "$GroupSpecFile")\\n${H2}"
ImportGroupErrorCount+=1
fi
else
msg "NO_OP: Would add group $Group"
ImportGroupOKCount+=1
fi
else
errmsg "Could not generate group spec for group $Group."
fi
done < "$TmpFile"
rm -rf "$TmpDir"
msg "${H2}\\nSummary:
User Adds Attempted: $ImportUserCount
Users Added OK $ImportUserOKCount
User Add Errors: $ImportUserErrorCount
Group Updates Attempted: $ImportGroupCount
Group Updates Attempted OK: $ImportGroupOKCount
Group Update Errors: $ImportGroupErrorCount
Total Errors: $ErrorCount
Non-standard users skipped: $SkipUserByTypeCount
Pre-existing users skipped: $SkipExistingUserCount
"
if [[ "$ErrorCount" -eq 0 ]]; then
msg "All processing completed successfully."
else
errmsg "Processing complete, but $ErrorCount errors."
fi
msg "\\nTotal $ThisScript processing took $((SECONDS/3600)) hours $((SECONDS%3600/60)) minutes $((SECONDS%60)) seconds.\\n"
exit "${ErrorCount}"
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #1 | 31397 | C. Thomas Tyler | Populate -b SDP_Classic_to_Streams -s //guest/perforce_software/sdp/...@31368. | ||
| //guest/perforce_software/sdp/dev/Unsupported/Maintenance/import_users_and_groups.sh | |||||
| #1 | 29225 | C. Thomas Tyler |
Added script to import users and groups from another Helix Core server. Useful when decomissioning a P4AUTH server. |
||