solana/net/net.sh

627 lines
17 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
set -e
here=$(dirname "$0")
SOLANA_ROOT="$(cd "$here"/..; pwd)"
# shellcheck source=net/common.sh
source "$here"/common.sh
usage() {
exitcode=0
if [[ -n "$1" ]]; then
exitcode=1
echo "Error: $*"
fi
cat <<EOF
2018-09-04 15:16:25 -07:00
usage: $0 [start|stop|restart|sanity] [command-specific options]
2018-09-03 21:15:55 -07:00
Operate a configured testnet
2018-09-04 09:21:03 -07:00
start - Start the network
sanity - Sanity check the network
stop - Stop the network
restart - Shortcut for stop then start
update - Live update all network nodes
logs - Fetch remote logs from each network node
2018-09-03 21:15:55 -07:00
start/update-specific options:
-T [tarFilename] - Deploy the specified release tarball
-t edge|beta|stable|vX.Y.Z - Deploy the latest tarball release for the
specified release channel (edge|beta|stable) or release tag
(vX.Y.Z)
-i update_manifest_keypair - Deploy the tarball using 'solana-install deploy ...'
(-t option must be supplied as well)
-f [cargoFeatures] - List of |cargo --feaures=| to activate
(ignored if -s or -S is specified)
-r - Reuse existing node/ledger configuration from a
previous |start| (ie, don't run ./multinode-demo/setup.sh).
-D /path/to/programs - Deploy custom programs from this location
-c clientType=numClients=extraArgs - Number of clientTypes to start. This options can be specified
more than once. Defaults to bench-tps for all clients if not
specified.
Valid client types are:
bench-tps
bench-exchange
User can optionally provide extraArgs that are transparently
supplied to the client program as command line parameters.
For example,
-c bench-tps=2="--tx_count 25000"
This will start 2 bench-tps clients, and supply "--tx_count 25000"
to the bench-tps client.
2018-09-03 21:15:55 -07:00
sanity/start/update-specific options:
2018-09-03 22:33:40 -07:00
-o noLedgerVerify - Skip ledger verification
-o noValidatorSanity - Skip fullnode sanity
-o rejectExtraNodes - Require the exact number of nodes
2018-09-03 22:33:40 -07:00
2018-09-03 21:15:55 -07:00
stop-specific options:
none
logs-specific options:
none
Note: if RUST_LOG is set in the environment it will be propogated into the
network nodes.
EOF
exit $exitcode
}
releaseChannel=
2018-09-03 21:15:55 -07:00
deployMethod=local
2018-09-03 22:33:40 -07:00
sanityExtraArgs=
2018-09-04 23:01:07 -07:00
cargoFeatures=
skipSetup=false
updateNodes=false
customPrograms=
updateManifestKeypairFile=
updateDownloadUrl=
numBenchTpsClients=0
numBenchExchangeClients=0
benchTpsExtraArgs=
benchExchangeExtraArgs=
2018-09-03 21:15:55 -07:00
command=$1
[[ -n $command ]] || usage
shift
while getopts "h?T:t:o:f:rD:i:c:" opt; do
case $opt in
h | \?)
usage
;;
2018-11-07 13:32:48 -08:00
T)
tarballFilename=$OPTARG
2019-03-02 17:08:46 -08:00
[[ -r $tarballFilename ]] || usage "File not readable: $tarballFilename"
2018-11-07 13:32:48 -08:00
deployMethod=tar
;;
t)
case $OPTARG in
2018-11-06 15:02:55 -08:00
edge|beta|stable|v*)
releaseChannel=$OPTARG
deployMethod=tar
;;
*)
usage "Invalid release channel: $OPTARG"
;;
esac
;;
2018-09-04 23:01:07 -07:00
f)
cargoFeatures=$OPTARG
;;
i)
updateManifestKeypairFile=$OPTARG
if [[ ! -r $updateManifestKeypairFile ]]; then
echo "Error: unable to read the file $updateManifestKeypairFile"
exit 1
fi
;;
r)
skipSetup=true
;;
D)
customPrograms=$OPTARG
;;
2018-09-03 22:33:40 -07:00
o)
case $OPTARG in
noLedgerVerify|noValidatorSanity|rejectExtraNodes)
2018-09-03 22:33:40 -07:00
sanityExtraArgs="$sanityExtraArgs -o $OPTARG"
;;
*)
echo "Error: unknown option: $OPTARG"
exit 1
;;
esac
;;
c)
getClientTypeAndNum() {
if ! [[ $OPTARG == *'='* ]]; then
echo "Error: Expecting tuple \"clientType=numClientType=extraArgs\" but got \"$OPTARG\""
exit 1
fi
set -x
local keyValue
IFS='=' read -ra keyValue <<< "$OPTARG"
local clientType=${keyValue[0]}
local numClients=${keyValue[1]}
local extraArgs=${keyValue[2]}
re='^[0-9]+$'
if ! [[ $numClients =~ $re ]] ; then
echo "error: numClientType must be a number but got \"$numClients\""
exit 1
fi
case $clientType in
bench-tps)
numBenchTpsClients=$numClients
benchTpsExtraArgs=$extraArgs
;;
bench-exchange)
numBenchExchangeClients=$numClients
benchExchangeExtraArgs=$extraArgs
;;
*)
echo "Unknown client type: $clientType"
exit 1
;;
esac
}
getClientTypeAndNum
;;
*)
usage "Error: unhandled option: $opt"
;;
esac
done
loadConfigFile
numClients=${#clientIpList[@]}
numClientsRequested=$((numBenchTpsClients+numBenchExchangeClients))
if [[ "$numClientsRequested" -eq 0 ]]; then
numBenchTpsClients=$numClients
numClientsRequested=$((numBenchTpsClients+numBenchExchangeClients))
else
if [[ "$numClientsRequested" -gt "$numClients" ]]; then
echo "Error: More clients requested ($numClientsRequested) then available ($numClients)"
exit 1
fi
fi
annotate() {
[[ -z $BUILDKITE ]] || {
buildkite-agent annotate "$@"
}
}
annotateBlockexplorerUrl() {
declare blockstreamer=${blockstreamerIpList[0]}
if [[ -n $blockstreamer ]]; then
annotate --style info --context blockexplorer-url "Block explorer: http://$blockstreamer/"
fi
}
build() {
supported=("18.04")
declare MAYBE_DOCKER=
if [[ $(uname) != Linux || ! " ${supported[*]} " =~ $(lsb_release -sr) ]]; then
# shellcheck source=ci/rust-version.sh
source "$SOLANA_ROOT"/ci/rust-version.sh
2019-04-15 20:27:52 -07:00
MAYBE_DOCKER="ci/docker-run.sh $rust_stable_docker_image"
fi
SECONDS=0
(
cd "$SOLANA_ROOT"
2018-09-04 09:21:03 -07:00
echo "--- Build started at $(date)"
set -x
rm -rf farf
2018-11-14 19:19:27 -08:00
if [[ -r target/perf-libs/env.sh ]]; then
# shellcheck source=/dev/null
source target/perf-libs/env.sh
fi
2018-11-16 08:04:46 -08:00
$MAYBE_DOCKER bash -c "
set -ex
scripts/cargo-install-all.sh farf \"$cargoFeatures\"
if [[ -n \"$customPrograms\" ]]; then
scripts/cargo-install-custom-programs.sh farf $customPrograms
fi
2018-11-16 08:04:46 -08:00
"
)
echo "Build took $SECONDS seconds"
}
2018-09-07 08:49:22 -07:00
startCommon() {
declare ipAddress=$1
2018-09-04 15:16:25 -07:00
test -d "$SOLANA_ROOT"
2019-01-08 22:11:31 -08:00
if $skipSetup; then
ssh "${sshOptions[@]}" "$ipAddress" "
set -x;
mkdir -p ~/solana/config{,-local}
rm -rf ~/config{,-local};
mv ~/solana/config{,-local} ~;
rm -rf ~/solana;
mkdir -p ~/solana ~/.cargo/bin;
mv ~/config{,-local} ~/solana/
"
else
ssh "${sshOptions[@]}" "$ipAddress" "
set -x;
rm -rf ~/solana;
mkdir -p ~/.cargo/bin
"
fi
[[ -z "$externalNodeSshKey" ]] || ssh-copy-id -f -i "$externalNodeSshKey" "${sshOptions[@]}" "solana@$ipAddress"
2018-09-04 15:16:25 -07:00
rsync -vPrc -e "ssh ${sshOptions[*]}" \
"$SOLANA_ROOT"/{fetch-perf-libs.sh,scripts,net,multinode-demo} \
"$ipAddress":~/solana/
}
startBootstrapLeader() {
declare ipAddress=$1
declare logFile="$2"
echo "--- Starting bootstrap leader: $ipAddress"
2018-09-08 13:48:17 -07:00
echo "start log: $logFile"
# Deploy local binaries to bootstrap fullnode. Other fullnodes and clients later fetch the
# binaries from it
(
set -x
2018-09-07 08:49:22 -07:00
startCommon "$ipAddress" || exit 1
2018-09-03 21:15:55 -07:00
case $deployMethod in
tar)
rsync -vPrc -e "ssh ${sshOptions[*]}" "$SOLANA_ROOT"/solana-release/bin/* "$ipAddress:~/.cargo/bin/"
;;
2018-09-03 21:15:55 -07:00
local)
2018-09-04 15:16:25 -07:00
rsync -vPrc -e "ssh ${sshOptions[*]}" "$SOLANA_ROOT"/farf/bin/* "$ipAddress:~/.cargo/bin/"
2018-09-03 21:15:55 -07:00
;;
*)
usage "Internal error: invalid deployMethod: $deployMethod"
;;
esac
2018-09-04 09:21:03 -07:00
ssh "${sshOptions[@]}" -n "$ipAddress" \
"./solana/net/remote/remote-node.sh \
$deployMethod \
bootstrap-leader \
$publicNetwork \
$entrypointIp \
2019-04-09 16:20:44 -07:00
$((${#fullnodeIpList[@]} + ${#blockstreamerIpList[@]})) \
\"$RUST_LOG\" \
$skipSetup \
2018-12-05 17:33:32 -08:00
$leaderRotation \
"
2018-09-04 15:16:25 -07:00
) >> "$logFile" 2>&1 || {
cat "$logFile"
echo "^^^ +++"
exit 1
}
}
startNode() {
declare ipAddress=$1
declare nodeType=$2
declare logFile="$netLogDir/fullnode-$ipAddress.log"
echo "--- Starting $nodeType: $ipAddress"
2018-09-08 13:48:17 -07:00
echo "start log: $logFile"
(
set -x
2018-09-07 08:49:22 -07:00
startCommon "$ipAddress"
2018-09-04 09:21:03 -07:00
ssh "${sshOptions[@]}" -n "$ipAddress" \
"./solana/net/remote/remote-node.sh \
$deployMethod \
$nodeType \
$publicNetwork \
$entrypointIp \
2019-04-09 16:20:44 -07:00
$((${#fullnodeIpList[@]} + ${#blockstreamerIpList[@]})) \
\"$RUST_LOG\" \
$skipSetup \
2018-12-05 17:33:32 -08:00
$leaderRotation \
"
2018-09-08 13:48:17 -07:00
) >> "$logFile" 2>&1 &
2018-09-04 09:21:03 -07:00
declare pid=$!
ln -sfT "fullnode-$ipAddress.log" "$netLogDir/fullnode-$pid.log"
2018-09-04 09:21:03 -07:00
pids+=("$pid")
}
startClient() {
declare ipAddress=$1
declare clientToRun="$2"
declare logFile="$netLogDir/client-$clientToRun-$ipAddress.log"
echo "--- Starting client: $ipAddress - $clientToRun"
2018-09-08 13:48:17 -07:00
echo "start log: $logFile"
2018-09-03 22:33:40 -07:00
(
set -x
2018-09-07 08:49:22 -07:00
startCommon "$ipAddress"
2018-09-03 22:33:40 -07:00
ssh "${sshOptions[@]}" -f "$ipAddress" \
"./solana/net/remote/remote-client.sh $deployMethod $entrypointIp \
$clientToRun \"$RUST_LOG\" \"$benchTpsExtraArgs\" \"$benchExchangeExtraArgs\""
2018-09-04 15:16:25 -07:00
) >> "$logFile" 2>&1 || {
cat "$logFile"
echo "^^^ +++"
exit 1
}
}
2018-09-03 22:33:40 -07:00
sanity() {
2018-09-06 13:00:01 -07:00
$metricsWriteDatapoint "testnet-deploy net-sanity-begin=1"
2019-04-09 17:16:10 -07:00
declare ok=true
2019-04-09 16:20:44 -07:00
declare bootstrapLeader=${fullnodeIpList[0]}
declare blockstreamer=${blockstreamerIpList[0]}
2019-04-09 17:16:10 -07:00
annotateBlockexplorerUrl
2019-04-09 17:16:10 -07:00
echo "--- Sanity: $bootstrapLeader"
2018-09-03 22:33:40 -07:00
(
set -x
2018-09-04 14:36:35 -07:00
# shellcheck disable=SC2029 # remote-client.sh args are expanded on client side intentionally
2019-04-09 16:20:44 -07:00
ssh "${sshOptions[@]}" "$bootstrapLeader" \
"./solana/net/remote/remote-sanity.sh $sanityExtraArgs \"$RUST_LOG\""
2019-04-09 17:16:10 -07:00
) || ok=false
$ok || exit 1
2019-04-09 16:20:44 -07:00
2019-04-09 17:16:10 -07:00
if [[ -n $blockstreamer ]]; then
2019-04-09 16:20:44 -07:00
# If there's a blockstreamer node run a reduced sanity check on it as well
2019-04-09 17:16:10 -07:00
echo "--- Sanity: $blockstreamer"
(
set -x
2019-04-09 16:20:44 -07:00
# shellcheck disable=SC2029 # remote-client.sh args are expanded on client side intentionally
ssh "${sshOptions[@]}" "$blockstreamer" \
"./solana/net/remote/remote-sanity.sh $sanityExtraArgs -o noLedgerVerify -o noValidatorSanity \"$RUST_LOG\""
2019-04-09 17:16:10 -07:00
) || ok=false
$ok || exit 1
fi
2018-09-06 13:00:01 -07:00
$metricsWriteDatapoint "testnet-deploy net-sanity-complete=1"
2018-09-03 22:33:40 -07:00
}
2018-09-03 21:15:55 -07:00
deployUpdate() {
if [[ -z $updateManifestKeypairFile ]]; then
return
fi
[[ $deployMethod = tar ]] || exit 1
[[ -n $updateDownloadUrl ]] || exit 1
declare ok=true
declare bootstrapLeader=${fullnodeIpList[0]}
echo "--- Deploying solana-install update: $updateDownloadUrl"
(
set -x
timeout 30s scp "${sshOptions[@]}" \
"$updateManifestKeypairFile" "$bootstrapLeader:solana/update_manifest_keypair.json"
# shellcheck disable=SC2029 # remote-deploy-update.sh args are expanded on client side intentionally
ssh "${sshOptions[@]}" "$bootstrapLeader" \
"./solana/net/remote/remote-deploy-update.sh $updateDownloadUrl \"$RUST_LOG\""
) || ok=false
$ok || exit 1
}
2018-09-03 22:33:40 -07:00
start() {
2018-09-03 21:15:55 -07:00
case $deployMethod in
tar)
if [[ -n $releaseChannel ]]; then
rm -f "$SOLANA_ROOT"/solana-release.tar.bz2
updateDownloadUrl=http://release.solana.com/"$releaseChannel"/solana-release-x86_64-unknown-linux-gnu.tar.bz2
(
set -x
curl -o "$SOLANA_ROOT"/solana-release.tar.bz2 "$updateDownloadUrl"
)
tarballFilename="$SOLANA_ROOT"/solana-release.tar.bz2
else
if [[ -n $updateManifestKeypairFile ]]; then
echo "Error: -i argument was provided but -t was not"
exit 1
fi
fi
(
set -x
rm -rf "$SOLANA_ROOT"/solana-release
(cd "$SOLANA_ROOT"; tar jxv) < "$tarballFilename"
cat "$SOLANA_ROOT"/solana-release/version.yml
)
;;
2018-09-03 21:15:55 -07:00
local)
build
;;
*)
usage "Internal error: invalid deployMethod: $deployMethod"
;;
esac
echo "Deployment started at $(date)"
if $updateNodes; then
$metricsWriteDatapoint "testnet-deploy net-update-begin=1"
else
$metricsWriteDatapoint "testnet-deploy net-start-begin=1"
fi
declare bootstrapLeader=true
declare nodeType=fullnode
for ipAddress in "${fullnodeIpList[@]}" - "${blockstreamerIpList[@]}"; do
if [[ $ipAddress = - ]]; then
nodeType=blockstreamer
continue
fi
if $updateNodes; then
stopNode "$ipAddress"
fi
if $bootstrapLeader; then
SECONDS=0
declare bootstrapNodeDeployTime=
startBootstrapLeader "$ipAddress" "$netLogDir/bootstrap-leader-$ipAddress.log"
bootstrapNodeDeployTime=$SECONDS
$metricsWriteDatapoint "testnet-deploy net-bootnode-leader-started=1"
bootstrapLeader=false
SECONDS=0
pids=()
loopCount=0
else
startNode "$ipAddress" $nodeType
# Stagger additional node start time. If too many nodes start simultaneously
# the bootstrap node gets more rsync requests from the additional nodes than
# it can handle.
((loopCount++ % 2 == 0)) && sleep 2
fi
2018-09-04 09:21:03 -07:00
done
for pid in "${pids[@]}"; do
declare ok=true
wait "$pid" || ok=false
if ! $ok; then
cat "$netLogDir/fullnode-$pid.log"
2018-09-04 09:21:03 -07:00
echo ^^^ +++
exit 1
fi
done
2018-09-04 09:21:03 -07:00
$metricsWriteDatapoint "testnet-deploy net-fullnodes-started=1"
additionalNodeDeployTime=$SECONDS
annotateBlockexplorerUrl
if $updateNodes; then
for ipAddress in "${clientIpList[@]}"; do
stopNode "$ipAddress"
done
fi
sanity
2018-09-03 22:33:40 -07:00
2018-09-03 21:15:55 -07:00
SECONDS=0
for ((i=0; i < "$numClients" && i < "$numClientsRequested"; i++)) do
if [[ $i -lt "$numBenchTpsClients" ]]; then
startClient "${clientIpList[$i]}" "solana-bench-tps"
else
startClient "${clientIpList[$i]}" "solana-bench-exchange"
fi
done
clientDeployTime=$SECONDS
if $updateNodes; then
$metricsWriteDatapoint "testnet-deploy net-update-complete=1"
else
$metricsWriteDatapoint "testnet-deploy net-start-complete=1"
fi
declare networkVersion=unknown
case $deployMethod in
tar)
networkVersion="$(
(
set -o pipefail
2019-04-25 11:13:45 -07:00
grep "^commit: " "$SOLANA_ROOT"/solana-release/version.yml | head -n1 | cut -d\ -f2
) || echo "tar-unknown"
)"
;;
local)
networkVersion="$(git rev-parse HEAD || echo local-unknown)"
;;
*)
usage "Internal error: invalid deployMethod: $deployMethod"
;;
esac
$metricsWriteDatapoint "testnet-deploy version=\"${networkVersion:0:9}\""
2018-09-03 21:15:55 -07:00
deployUpdate
echo
2018-09-08 14:12:32 -07:00
echo "+++ Deployment Successful"
echo "Bootstrap leader deployment took $bootstrapNodeDeployTime seconds"
echo "Additional fullnode deployment (${#fullnodeIpList[@]} full nodes, ${#blockstreamerIpList[@]} blockstreamer nodes) took $additionalNodeDeployTime seconds"
echo "Client deployment (${#clientIpList[@]} instances) took $clientDeployTime seconds"
echo "Network start logs in $netLogDir:"
ls -l "$netLogDir"
}
2018-09-07 08:49:22 -07:00
stopNode() {
local ipAddress=$1
2018-09-04 09:21:03 -07:00
echo "--- Stopping node: $ipAddress"
(
set -x
2019-01-09 21:06:58 -08:00
# shellcheck disable=SC2029 # It's desired that PS4 be expanded on the client side
ssh "${sshOptions[@]}" "$ipAddress" "
PS4=\"$PS4\"
2018-09-07 08:34:42 -07:00
set -x
! tmux list-sessions || tmux kill-session
for pid in solana/{net-stats,oom-monitor}.pid; do
pgid=\$(ps opgid= \$(cat \$pid) | tr -d '[:space:]')
sudo kill -- -\$pgid
done
for pattern in node solana- remote-; do
2018-09-07 08:34:42 -07:00
pkill -9 \$pattern
done
"
) || true
}
stop() {
SECONDS=0
2018-09-06 13:00:01 -07:00
$metricsWriteDatapoint "testnet-deploy net-stop-begin=1"
2018-09-03 21:15:55 -07:00
for ipAddress in "${fullnodeIpList[@]}" "${blockstreamerIpList[@]}" "${clientIpList[@]}"; do
2018-09-07 08:49:22 -07:00
stopNode "$ipAddress"
done
2018-09-06 13:00:01 -07:00
$metricsWriteDatapoint "testnet-deploy net-stop-complete=1"
echo "Stopping nodes took $SECONDS seconds"
}
2018-09-03 22:33:40 -07:00
case $command in
2018-09-04 09:21:03 -07:00
restart)
2018-09-03 22:33:40 -07:00
stop
start
;;
2018-09-04 09:21:03 -07:00
start)
start
;;
update)
$leaderRotation || {
echo Warning: unable to update because leader rotation is disabled
exit 1
}
skipSetup=true
updateNodes=true
start
;;
2018-09-03 22:33:40 -07:00
sanity)
sanity
;;
stop)
stop
;;
logs)
fetchRemoteLog() {
declare ipAddress=$1
declare log=$2
echo "--- fetching $log from $ipAddress"
(
set -x
timeout 30s scp "${sshOptions[@]}" \
2018-12-23 10:33:40 -08:00
"$ipAddress":solana/"$log".log "$netLogDir"/remote-"$log"-"$ipAddress".log
) || echo "failed to fetch log"
}
fetchRemoteLog "${fullnodeIpList[0]}" drone
for ipAddress in "${fullnodeIpList[@]}"; do
fetchRemoteLog "$ipAddress" fullnode
done
for ipAddress in "${clientIpList[@]}"; do
fetchRemoteLog "$ipAddress" client
done
for ipAddress in "${blockstreamerIpList[@]}"; do
fetchRemoteLog "$ipAddress" fullnode
done
;;
2018-09-03 22:33:40 -07:00
*)
echo "Internal error: Unknown command: $command"
usage
2018-09-03 22:33:40 -07:00
exit 1
esac