Last active
February 29, 2016 05:25
-
-
Save mosuka/f124b0c2d0dbaa3278d4 to your computer and use it in GitHub Desktop.
Script that provide a way to build the ensemble with ease.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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