Skip to content

Instantly share code, notes, and snippets.

@mosuka
Last active February 29, 2016 05:25
Show Gist options
  • Save mosuka/f124b0c2d0dbaa3278d4 to your computer and use it in GitHub Desktop.
Save mosuka/f124b0c2d0dbaa3278d4 to your computer and use it in GitHub Desktop.
Script that provide a way to build the ensemble with ease.
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# If this scripted is run out of /usr/bin or some other system bin directory
# it should be linked to and not copied. Things like java jar files are found
# relative to the canonical path of this script.
#
set -e
# use POSIX interface, symlink is followed automatically
ZOOBIN="${BASH_SOURCE-$0}"
ZOOBIN="$(dirname "${ZOOBIN}")"
ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)"
ZOOCFGDIR="$(cd "${ZOOBIN}/../conf"; pwd)"
ZOO_DATADIR=/tmp/zookeeper
SEED=""
IP=""
MYID=""
PEER_PORT=""
ELECTION_PORT=""
ROLE=participant
CLIENT_IP=0.0.0.0
CLIENT_PORT=""
FOREGROUND=0
if type ip > /dev/null 2>&1; then
IP_CMD="ip addr show"
else
IP_CMD="ifconfig -a"
fi
# usage
usage() {
printf "usage: $0 {start|stop|status} <parameters>
Commadns:
start Start a node of the ensemble.
Parameters:
--seed Specify the IP address and port of the existing ensemble node that required for 2nd and subsequent nodes.
This is not required for the 1st node. (Example: 127.0.0.1:2181) This parameter is ignored if the seed node has not been started.
--ip Normally, you do not need to specify because it is automatically detected.
If it seems the wrong IP address is found automatically, you can over ride the IP address with this option.
--peerport The port is used to talk to each other (2888 by default).
If omitted, it will use the minimum port number that is available between 2888 to 3142.
--electionport The port is used to leader election (3888 by default).
If omitted, it will use the minimum port number that is available between 3888 to 4142.
--role The role of node, it can be participant or observer (${ROLE} by default).
--clientip The IP address for client connections (${CLIENT_IP} by default).
If omitted, it will use the minimum port number that is available between 2181 to 2435.
--clientport The port is used to client connections (2181 by default).
--confdir Specify a base conf directory (${ZOOCFGDIR} by default).
--datadir Specify a base data directory (${ZOO_DATADIR} by default).
--foreground Start node in foreground.
stop Stop a node of the ensemble.
Parameters:
--ip Normally, you do not need to specify because it is automatically detected.
If it seems the wrong IP address is found automatically, you can over ride the IP address with this option.
--clientport The port is used to client connections (2181 by default).
status Show the ensemble nodes.
Parameters:
--seed Specify the IP address and port of the existing ensemble node (Example: 127.0.0.1:2181).
help Display this message.
"
exit 1
}
# start
start() {
if [ -z "${IP}" ]; then
# generate ip list
declare -a IP_LIST=()
IP_LIST=($(
$IP_CMD | grep -e "inet[^6]" |\
sed -e "s/.*inet[^6][^0-9]*\([0-9.]*\)[^0-9]*.*/\1/" |\
grep -v "^127\."
))
IP=${IP_LIST[0]}
fi
# generate server list from the ensemble
declare -a SERVER_LIST=()
if [ -n "${SEED}" ]; then
declare -a TMP_SEED=()
OIFS="${IFS}"; IFS=":"
TMP_SEED=(${SEED}); IFS="${OIFS}"
SEED_NODE=${TMP_SEED[0]}
SEED_PORT=${TMP_SEED[1]}
if RESPONSE=$(echo "ruok" | nc ${SEED_NODE} ${SEED_PORT} 2>/dev/null); then
if [ "${RESPONSE}" = "imok" ]; then
SERVER_LIST=$(
${ZOOBINDIR}/zkCli.sh -server ${SEED} get /zookeeper/config |\
grep -e "^server"
)
fi
else
echo "${SEED} does not working."
echo "Start this node as seed."
fi
fi
# generate myid list
declare -a MYID_LIST=()
MYID_LIST=($(
for LINE in ${SERVER_LIST}
do
echo ${LINE} | cut -d"=" -f1 | cut -d"." -f2
done | sort -u -n
))
# generate available myid list
declare -a AVAILABLE_MYID_LIST=($(
diff --old-line-format='' \
--new-line-format='%L' \
--unchanged-line-format='' \
<(printf "%s\n" ${MYID_LIST[@]}) \
<(printf "%s\n" $(seq 1 255))
))
# detect myid
if [ -z "${MYID}" ]; then
# get minimum available id
MYID=${AVAILABLE_MYID_LIST[0]}
else
if [[ " ${MYID_LIST[@]} " =~ " ${MYID} " ]]; then
echo "Zookeeper that assigned myid ${MYID} are already running."
exit 1
fi
fi
# generate port in use list
declare -a PORT_IN_USE_LIST=()
for LINE in ${SERVER_LIST}
do
NODE_IP=$(
echo ${LINE} | cut -d"=" -f2 | cut -d":" -f1
)
if [ ${IP} = ${NODE_IP} ]; then
PORT_IN_USE_LIST+=($(
echo ${LINE} | cut -d"=" -f2 | cut -d":" -f2
echo ${LINE} | cut -d"=" -f2 | cut -d":" -f3
echo ${LINE} | cut -d"=" -f2 | cut -d":" -f5
))
fi
done
PORT_IN_USE_LIST+=($(
netstat -ant | grep "LISTEN\|ESTABLISHED" | awk -F " " '{print $4}' | sed -e "s/^.*[\.:]\([0-9]*$\)/\1/"
))
# sort port in use list
declare -a SORTED_PORT_IN_USE_LIST=()
SORTED_PORT_IN_USE_LIST=($(
for PORT in ${PORT_IN_USE_LIST[@]}
do
echo ${PORT}
done | sort -u -n
))
# generate available peer port list
declare -a AVAILABLE_PEER_PORT_LIST=($(
diff --old-line-format="" \
--new-line-format="%L" \
--unchanged-line-format="" \
<(printf "%s\n" ${SORTED_PORT_IN_USE_LIST[@]}) \
<(printf "%s\n" $(seq 2888 3142))
))
# detect peer port
if [ -z "${PEER_PORT}" ]; then
# get minimum available peer port
PEER_PORT=${AVAILABLE_PEER_PORT_LIST[0]}
else
if [[ " ${SORTED_PORT_IN_USE_LIST[@]} " =~ " ${PEER_PORT} " ]]; then
echo "Peer port ${PEER_PORT} has already in use."
exit 1
fi
fi
# generate available election port list
declare -a AVAILABLE_ELECTION_PORT_LIST=($(
diff --old-line-format="" \
--new-line-format="%L" \
--unchanged-line-format="" \
<(printf "%s\n" ${SORTED_PORT_IN_USE_LIST[@]}) \
<(printf "%s\n" $(seq 3888 4142))
))
# detect election port
if [ -z "${ELECTION_PORT}" ]; then
# get minimum available election port
ELECTION_PORT=${AVAILABLE_ELECTION_PORT_LIST[0]}
else
if [[ " ${SORTED_PORT_IN_USE_LIST[@]} " =~ " ${ELECTION_PORT} " ]]; then
echo "Election port ${ELECTION_PORT} has already in use."
exit 1
fi
fi
# generate available client port list
declare -a AVAILABLE_CLIENT_PORT_LIST=($(
diff --old-line-format="" \
--new-line-format="%L" \
--unchanged-line-format="" \
<(printf "%s\n" ${SORTED_PORT_IN_USE_LIST[@]}) \
<(printf "%s\n" $(seq 2181 2435))
))
# detect client port
if [ -z "${CLIENT_PORT}" ]; then
# get minimum available client port
CLIENT_PORT=${AVAILABLE_CLIENT_PORT_LIST[0]}
else
if [[ " ${SORTED_PORT_IN_USE_LIST[@]} " =~ " ${CLIENT_PORT} " ]]; then
echo "Client port ${CLIENT_PORT} has already in use."
exit 1
fi
fi
# check pid file
if [ -e ${ZOO_DATADIR}/server.${MYID}/zookeeper_server.pid ]; then
PID=$(cat "${ZOO_DATADIR}/server.${MYID}/zookeeper_server.pid" | xargs -0 ps -o pid= -o comm= -p | awk -F " " '{print $1}')
if [ -n "${PID}" ]; then
echo "Zookeeper that assigned myid ${MYID} are already running as process ${PID}."
exit 1
fi
fi
# generate configfile
echo "Generating static configuration file that named ${ZOOCFGDIR}/server.${MYID}.cfg ..."
if [ -e ${ZOOCFGDIR}/zoo_sample.cfg ]; then
cp ${ZOOCFGDIR}/zoo_sample.cfg ${ZOOCFGDIR}/server.${MYID}.cfg
echo "standaloneEnabled=false" >> ${ZOOCFGDIR}/server.${MYID}.cfg
echo "dynamicConfigFile=${ZOOCFGDIR}/server.${MYID}.cfg.dynamic" >> ${ZOOCFGDIR}/server.${MYID}.cfg
sed -e "s|^dataDir=.*|dataDir=${ZOO_DATADIR}/server.${MYID}|" ${ZOOCFGDIR}/server.${MYID}.cfg > ${ZOOCFGDIR}/server.${MYID}.cfg.tmp
mv ${ZOOCFGDIR}/server.${MYID}.cfg.tmp ${ZOOCFGDIR}/server.${MYID}.cfg
sed -e "s/\(^clientPort=.*\)/#\1/" ${ZOOCFGDIR}/server.${MYID}.cfg > ${ZOOCFGDIR}/server.${MYID}.cfg.tmp
mv ${ZOOCFGDIR}/server.${MYID}.cfg.tmp ${ZOOCFGDIR}/server.${MYID}.cfg
else
echo "tickTime=2000" > ${ZOOCFGDIR}/server.${MYID}.cfg
echo "initLimit=10" >> ${ZOOCFGDIR}/server.${MYID}.cfg
echo "syncLimit=5" >> ${ZOOCFGDIR}/server.${MYID}.cfg
echo "dataDir=${ZOO_DATADIR}/server.${MYID}" >> ${ZOOCFGDIR}/server.${MYID}.cfg
echo "standaloneEnabled=false" >> ${ZOOCFGDIR}/server.${MYID}.cfg
echo "dynamicConfigFile=${ZOOCFGDIR}/server.${MYID}.cfg.dynamic" >> ${ZOOCFGDIR}/server.${MYID}.cfg
fi
# generate dynamic configfile
if [ -n "${SERVER_LIST}" ]; then
echo "Generating dynamic configuration file for 2nd and subsequent node that named ${ZOOCFGDIR}/server.${MYID}.cfg.dynamic ..."
# add the existing nodes to dynamic configfile
echo "${SERVER_LIST}" > ${ZOOCFGDIR}/server.${MYID}.cfg.dynamic
# add this node to dynamic configfile as observer
echo "server.${MYID}=${IP}:${PEER_PORT}:${ELECTION_PORT}:observer;${CLIENT_IP}:${CLIENT_PORT}" >> ${ZOOCFGDIR}/server.${MYID}.cfg.dynamic
# start this node as observer
${ZOOBIN}/zkServer-initialize.sh --configfile ${ZOOCFGDIR}/server.${MYID}.cfg --myid ${MYID} --force > /dev/null 2>&1
if ! ${ZOOBIN}/zkServer.sh start ${ZOOCFGDIR}/server.${MYID}.cfg > /dev/null 2>&1; then
echo "Could not start ${IP}:${CLIENT_PORT} as observer."
exit 1
fi
sleep 1
if ! ${ZOOBIN}/zkCli.sh -server ${SEED_NODE} reconfig -add "server.${MYID}=${IP}:${PEER_PORT}:${ELECTION_PORT}:${ROLE};${CLIENT_IP}:${CLIENT_PORT}" > /dev/null 2>&1; then
echo "Could not add ${IP}:${CLIENT_PORT} as ${ROLE} to ${SEED_NODE}."
exit 1
fi
sleep 1
# stop this node
${ZOOBIN}/zkServer.sh stop ${ZOOCFGDIR}/server.${MYID}.cfg > /dev/null 2>&1
sleep 1
# change observer to participant
sed -e "s/^server.${MYID}=.*/server.${MYID}=${IP}:${PEER_PORT}:${ELECTION_PORT}:${ROLE};${CLIENT_IP}:${CLIENT_PORT}/" ${ZOOCFGDIR}/server.${MYID}.cfg.dynamic > ${ZOOCFGDIR}/server.${MYID}.cfg.dynamic.tmp
mv ${ZOOCFGDIR}/server.${MYID}.cfg.dynamic.tmp ${ZOOCFGDIR}/server.${MYID}.cfg.dynamic
else
echo "Generating dynamic configuration file for 1st and subsequent node that named ${ZOOCFGDIR}/server.${MYID}.cfg.dynamic ..."
# add this node to dynamic configfile
echo "server.${MYID}=${IP}:${PEER_PORT}:${ELECTION_PORT}:${ROLE};${CLIENT_IP}:${CLIENT_PORT}" > ${ZOOCFGDIR}/server.${MYID}.cfg.dynamic
fi
# start node
${ZOOBIN}/zkServer-initialize.sh --configfile ${ZOOCFGDIR}/server.${MYID}.cfg --myid ${MYID} --force > /dev/null 2>&1
if [ ${FOREGROUND} -eq 0 ]; then
${ZOOBIN}/zkServer.sh start ${ZOOCFGDIR}/server.${MYID}.cfg
else
${ZOOBIN}/zkServer.sh start-foreground ${ZOOCFGDIR}/server.${MYID}.cfg
fi
}
# stop
stop() {
if [ -z "${IP}" ]; then
# generate ip list
declare -a IP_LIST=()
IP_LIST=($(
$IP_CMD | grep -e "inet[^6]" |\
sed -e "s/.*inet[^6][^0-9]*\([0-9.]*\)[^0-9]*.*/\1/" |\
grep -v "^127\."
))
IP=${IP_LIST[0]}
fi
if [ -z "${CLIENT_PORT}" ]; then
# get minimum available id
CLIENT_PORT=2181
fi
# generate server list from the ensemble
declare -a SERVER_LIST=()
if ! RESPONSE=$(echo "ruok" | nc ${IP} ${CLIENT_PORT} 2>/dev/null); then
echo "${IP}:${CLIENT_PORT} does not working."
exit 1
fi
if [ "${RESPONSE}" = "imok" ]; then
SERVER_LIST=$(
${ZOOBINDIR}/zkCli.sh -server ${IP}:${CLIENT_PORT} get /zookeeper/config |\
grep -e "^server"
)
fi
# generate myid list from server list
declare -a MYID_LIST=()
MYID_LIST=($(
for LINE in ${SERVER_LIST}
do
echo ${LINE} | cut -d"=" -f1 | cut -d"." -f2
done | sort -u -n
))
MYID=$(
echo "${SERVER_LIST[@]}" |\
grep -E "^server\.[0-9]+=${IP}.*:${CLIENT_PORT}$" |\
cut -d"=" -f1 |\
cut -d"." -f2
)
# check myid again
if [ -z "${MYID}" ]; then
echo "Could not find ${IP}:${CLIENT_PORT}."
exit 1
fi
# check whether last node
if [[ " ${MYID_LIST[@]} " != " ${MYID} " ]]; then
# remove node from the ensemble
if ! ${ZOOBIN}/zkCli.sh -server ${IP}:${CLIENT_PORT} reconfig -remove ${MYID} > /dev/null 2>&1; then
echo "Could not remove ${IP}:${CLIENT_PORT} that assigned myid ${MYID}."
exit 1
fi
fi
sleep 1
# stop node
${ZOOBIN}/zkServer.sh stop ${ZOOCFGDIR}/server.${MYID}.cfg
}
# status
status() {
if [ -n "${SEED}" ]; then
declare -a TMP_SEED=()
OIFS="${IFS}"; IFS=":"
TMP_SEED=(${SEED}); IFS="${OIFS}"
SEED_NODE=${TMP_SEED[0]}
SEED_PORT=${TMP_SEED[1]}
else
echo "Seed node is not specified."
exit 1
fi
# generate server list from the ensemble
declare -a SERVER_LIST=()
if ! RESPONSE=$(echo "ruok" | nc ${SEED_NODE} ${SEED_PORT} 2>/dev/null); then
echo "${SEED_NODE}:${SEED_PORT} does not working."
exit 1
fi
if [ "${RESPONSE}" = "imok" ]; then
SERVER_LIST=$(
${ZOOBINDIR}/zkCli.sh -server ${SEED_NODE}:${SEED_PORT} get /zookeeper/config |\
grep -e "^server"
)
fi
# show server list
if [ -n "${SERVER_LIST}" ]; then
echo "${SERVER_LIST[@]}"
fi
}
# check command
case "$1" in
start)
COMMAND=start
;;
stop)
COMMAND=stop
;;
status)
COMMAND=status
;;
help)
usage
exit 1
;;
*)
echo "Command is not specified"
usage
exit 1
esac
case "$COMMAND" in
start)
if ! OPTS=$(getopt -n $0 -o "" -l "seed:" -l "ip:" -l "peerport:" -l "electionport:" -l "role:" -l "clientip:" -l "clientport:" -l "confdir:" -l "datadir:" -l "foreground" -- "$@" 2>/dev/null); then
usage
exit 1
fi
# check options
eval set -- "${OPTS}"
while true; do
case "$1" in
--seed)
SEED=$2; shift 2
;;
--ip)
IP=$2; shift 2
;;
--peerport)
PEER_PORT=$2; shift 2
;;
--electionport)
ELECTION_PORT=$2; shift 2
;;
--role)
ROLE=$2; shift 2
;;
--clientip)
CLIENT_IP=$2; shift 2
;;
--clientport)
CLIENT_PORT=$2; shift 2
;;
--confdir)
ZOOCFGDIR=$2; shift 2
;;
--datadir)
ZOO_DATADIR=$2; shift 2
;;
--foreground)
FOREGROUND=1; shift 1
;;
--)
shift
break
;;
*)
echo "unknown option: $1"
usage
exit 1
;;
esac
done
;;
stop)
if ! OPTS=$(getopt -n $0 -o "" -l "ip:" -l "clientport:" -- "$@" 2>/dev/null); then
usage
exit 1
fi
# check options
eval set -- "${OPTS}"
while true; do
case "$1" in
--ip)
IP=$2; shift 2
;;
--clientport)
CLIENT_PORT=$2; shift 2
;;
--)
shift
break
;;
*)
echo "unknown option: $1"
usage
exit 1
;;
esac
done
;;
status)
if ! OPTS=$(getopt -n $0 -o "" -l "seed:" -- "$@" 2>/dev/null); then
usage
exit 1
fi
# check options
eval set -- "${OPTS}"
while true; do
case "$1" in
--seed)
SEED=$2; shift 2
;;
--)
shift
break
;;
*)
echo "unknown option: $1"
usage
exit 1
;;
esac
done
;;
*)
echo "Command is not specified"
usage
exit 1
esac
# execute command
$COMMAND
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment