Charm: oneiric/cassandra
Revision: 5
Hook: cluster-relation-departed
#!/bin/bash
# vim: set tabstop=2 expandtab:
set -e
export LANG=en_US.UTF-8
# Comms-less leader determination
# Picks the unit with the lowest id.
am_i_the_leader () {
# Leader is the lowest unit number and won't be in the
# list of relation peersa
units=`relation-list`
unit_no=`echo $JUJU_UNIT_NAME | cut -d / -f 2`
for unit in $units; do
peer_unit_no=`echo $unit | cut -d / -f 2`
if [ "$peer_unit_no" -lt "$unit_no" ]; then
return 1
fi
done
return 0
}
# Install cassandra source and install packages
install_cassandra () {
juju-log "Installing Cassandra"
# Install the repositories
echo "deb http://www.apache.org/dist/cassandra/debian 10x main" > /etc/apt/sources.list.d/cassandra.list
# Add the key
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 2B5C1B00
# Update the repositories
apt-get update
# Ensure that cassandra does not startup before we have configured it
cat > /usr/sbin/policy-rc.d <<EOF
#!/bin/bash
if [ "\$1" == "cassandra" ] && [ "\$2" == "start" ]; then
exit 101
else
exit 0
fi
EOF
chmod a+x /usr/sbin/policy-rc.d
# Install the package
apt-get -qq install -y cassandra python-cheetah dnsutils
}
# Update the cassandra environment with the appropriate JMX port
configure_jmx_port () {
juju-log "Configuring JMX port"
JMX_PORT=`config-get jmx-port`
sed -i -e "s/^JMX_PORT=.*/JMX_PORT=\"${JMX_PORT}\"/" /etc/cassandra/cassandra-env.sh
# Open port ready for expose
open-port ${JMX_PORT}/TCP
}
get_private_ip () {
# Make sure that we use the resolvable private address
# dig deals with both hostnames and ip addresses in a nice
# way - this has been tested in local and ec2 providers
# with ec2 and openstack
dig +short `unit-get private-address`
}
# Construct the cassandra.yaml file from the appropriate information above
configure_cassandra () {
juju-log "Configuring Cassandra"
IP=`get_private_ip`
CLUSTER_PORT=`config-get cluster-port`
CLIENT_PORT=`config-get client-port`
CLUSTER_NAME=`config-get cluster-name`
AUTO_MEMORY=`config-get auto-memory`
HEAP_SIZE=`config-get heap-size`
NEW_GEN_SIZE=`config-get new-gen-size`
# This needs to 'detect' whether its running in ec2
# and use the ec2Snitch instead if it is - or we could
# specify this as a configuration parameter
sed -i -e "s/^cluster_name:.*/cluster_name: \'${CLUSTER_NAME}\'/" \
-e "s/\- seeds:.*/\- seeds: \"${IP}\"/" \
-e "s/^storage_port:.*/storage_port: ${CLUSTER_PORT}/" \
-e "s/^listen_address:.*/listen_address: ${IP}/" \
-e "s/^rpc_address:.*/rpc_address: ${IP}/" \
-e "s/^rpc_port:.*/rpc_port: ${CLIENT_PORT}/" \
/etc/cassandra/cassandra.yaml
# Open port ready for expose
open-port ${CLIENT_PORT}/TCP
# Configure memory settings as specified in configuration
if [ "$AUTO_MEMORY" = "False" ]; then
juju-log "Configuring Manual Memory Setttings"
sed -i -e "s/^[#]MAX_HEAP_SIZE=.*/MAX_HEAP_SIZE=\"${HEAP_SIZE}\"/" \
-e "s/^[#]HEAP_NEWSIZE=.*/HEAP_NEWSIZE=\"${NEW_GEN_SIZE}\"/" /etc/cassandra/cassandra-env.sh
else
sed -i -e "s/^[#]MAX_HEAP_SIZE=.*/#MAX_HEAP_SIZE=\"${HEAP_SIZE}\"/" \
-e "s/^[#]HEAP_NEWSIZE=.*/#HEAP_NEWSIZE=\"${NEW_GEN_SIZE}\"/" /etc/cassandra/cassandra-env.sh
fi
}
# Service Control Commands
restart_cassandra () {
juju-log "Restarting Cassandra"
service cassandra status && service cassandra restart || :
}
start_cassandra() {
juju-log "Starting Cassandra"
service cassandra status || service cassandra start
}
stop_cassandra() {
juju-log "Stopping Cassandra"
service cassandra stop || :
}
setup_database_interface () {
juju-log "Setup Cassandra database interface"
relation-set port="`config-get client-port`"
}
setup_jmx_interface () {
juju-log "Setup Cassandra JMX interface"
relation-set port="`config-get jmx-port`"
}
reconfigure_cluster_seeds () {
juju-log "Reconfiguring cluster seeds"
# If I'm the initial bootstrap node then I
# don't need to be set to auto_bootstrap
# as everything seeded off me anyway
INIT_SEED=$(echo ${JUJU_UNIT_NAME} | sed s,.*/,,)
[ ${INIT_SEED} == "0" ] && return 0
# If node is not set to autoboostrap then set it to be
AUTO=$(grep '^auto_bootstrap: true$' /etc/cassandra/cassandra.yaml || true)
if [ -z "${AUTO}" ]; then
# Enable bootstrap for this node.
sed -i -e "s/^auto_bootstrap:.*/auto_bootstrap: true/" \
/etc/cassandra/cassandra.yaml
fi
# This needs careful handling - to start with we need
# an initial seed - which should not auto_boostrap
# Build up list of seeds - these are used when new
# nodes startup to build the ring.
for node in `relation-list`
do
NODE_HOSTNAME=`relation-get private-address ${node}`
[ -z ${TMP_SEEDS} ] && TMP_SEEDS=${NODE_HOSTNAME} || TMP_SEEDS="${TMP_SEEDS},${NODE_HOSTNAME}"
done
sed -i -e "s/\- seeds:.*/\- seeds: \"${TMP_SEEDS}\"/" /etc/cassandra/cassandra.yaml
# Restart to pickup changes - this only really needs to
# happen for NEW nodes in the ring - not existing ones
# which have already auto_bootstrapped
if [ -z "${AUTO}" ]; then
restart_cassandra
fi
juju-log "$JUJU_REMOTE_UNIT modified its settings or departed"
}
remove_down_nodes () {
if am_i_the_leader; then
sleep 30
juju-log "Removing tokens for nodes which are marked 'Down'"
PRIVATE_IP=`get_private_ip`
tokens=`nodetool -h $PRIVATE_IP ring | grep Down | awk '{ print $9 }'`
for token in $tokens; do
juju-log "Removing token $token from the ring"
nodetool -h $PRIVATE_IP removetoken $token
done
fi
}
decommission_node () {
juju-log "Decommissioning node - this may take some time..."
nodetool -h `get_private_ip` decommission
}
# Actual processing
COMMAND=`basename $0`
case $COMMAND in
install)
# Install cassandra first run only
[[ -d /usr/share/cassandra ]] || install_cassandra
# Update the cassandra environment with the appropriate JMX port
configure_jmx_port
# Construct the cassandra.yaml file from the appropriate information above
configure_cassandra
;;
config-changed)
# Update the cassandra environment with the appropriate JMX port
configure_jmx_port
# Construct the cassandra.yaml file from the appropriate information above
configure_cassandra
# Restart as required
restart_cassandra
;;
upgrade-charm)
juju-log "Updating this charm - currently no-op"
;;
start)
start_cassandra
;;
stop)
stop_cassandra
;;
database-relation-joined)
setup_database_interface
;;
cluster-relation-joined)
juju-log "Joining cassandra cluster..."
;;
cluster-relation-changed)
reconfigure_cluster_seeds
;;
cluster-relation-departed)
reconfigure_cluster_seeds
remove_down_nodes
;;
jmx-relation-joined)
setup_jmx_interface
;;
*)
juju-log "Command not recognised"
;;
esac