devstack/lib/rpc_backend
Sean Dague 537532931d Make changes such that -o nounset runs
This makes a bunch of variable cleanups that will let -o nounset
function, for the time being we hide nounset behind another setting
variable so that it's not on by default.

Because this is bash, and things are only executed on demand, this
probably only works in the config it was run in. Expect cleaning up
all the paths to be something that takes quite a while.

This also includes a new set of unit tests around the trueorfalse
function, because my change in how it worked, didn't. Tests are good
m'kay.

Change-Id: I71a896623ea9e1f042a73dc0678ce85acf0dc87d
2015-01-15 13:06:14 -05:00

381 lines
13 KiB
Bash

#!/bin/bash
#
# lib/rpc_backend
# Interface for interactig with different rpc backend
# rpc backend settings
# Dependencies:
#
# - ``functions`` file
# - ``RABBIT_{HOST|PASSWORD|USERID}`` must be defined when RabbitMQ is used
# - ``RPC_MESSAGING_PROTOCOL`` option for configuring the messaging protocol
# ``stack.sh`` calls the entry points in this order:
#
# - check_rpc_backend
# - install_rpc_backend
# - restart_rpc_backend
# - iniset_rpc_backend
# Save trace setting
XTRACE=$(set +o | grep xtrace)
set +o xtrace
RPC_MESSAGING_PROTOCOL=${RPC_MESSAGING_PROTOCOL:-0.9}
# TODO(sdague): RPC backend selection is super wonky because we treat
# messaging server as a service, which it really isn't for multi host
QPID_HOST=${QPID_HOST:-}
# Functions
# ---------
# Make sure we only have one rpc backend enabled.
# Also check the specified rpc backend is available on your platform.
function check_rpc_backend {
local c svc
local rpc_needed=1
# We rely on the fact that filenames in lib/* match the service names
# that can be passed as arguments to is_service_enabled.
# We check for a call to iniset_rpc_backend in these files, meaning
# the service needs a backend.
rpc_candidates=$(grep -rl iniset_rpc_backend $TOP_DIR/lib/ | awk -F/ '{print $NF}')
for c in ${rpc_candidates}; do
if is_service_enabled $c; then
rpc_needed=0
break
fi
done
local rpc_backend_cnt=0
for svc in qpid zeromq rabbit; do
is_service_enabled $svc &&
(( rpc_backend_cnt++ )) || true
done
if [ "$rpc_backend_cnt" -gt 1 ]; then
echo "ERROR: only one rpc backend may be enabled,"
echo " set only one of 'rabbit', 'qpid', 'zeromq'"
echo " via ENABLED_SERVICES."
elif [ "$rpc_backend_cnt" == 0 ] && [ "$rpc_needed" == 0 ]; then
echo "ERROR: at least one rpc backend must be enabled,"
echo " set one of 'rabbit', 'qpid', 'zeromq'"
echo " via ENABLED_SERVICES."
fi
if is_service_enabled qpid && ! qpid_is_supported; then
die $LINENO "Qpid support is not available for this version of your distribution."
fi
}
# clean up after rpc backend - eradicate all traces so changing backends
# produces a clean switch
function cleanup_rpc_backend {
if is_service_enabled rabbit; then
# Obliterate rabbitmq-server
uninstall_package rabbitmq-server
sudo killall epmd || sudo killall -9 epmd
if is_ubuntu; then
# And the Erlang runtime too
apt_get purge -y erlang*
fi
elif is_service_enabled qpid; then
if is_fedora; then
uninstall_package qpid-cpp-server
elif is_ubuntu; then
uninstall_package qpidd
else
exit_distro_not_supported "qpid installation"
fi
elif is_service_enabled zeromq; then
if is_fedora; then
uninstall_package zeromq python-zmq
if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
uninstall_package redis python-redis
fi
elif is_ubuntu; then
uninstall_package libzmq1 python-zmq
if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
uninstall_package redis-server python-redis
fi
elif is_suse; then
uninstall_package libzmq1 python-pyzmq
if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
uninstall_package redis python-redis
fi
else
exit_distro_not_supported "zeromq installation"
fi
fi
# Remove the AMQP 1.0 messaging libraries
if [ "$RPC_MESSAGING_PROTOCOL" == "AMQP1" ]; then
if is_fedora; then
uninstall_package qpid-proton-c-devel
uninstall_package python-qpid-proton
fi
# TODO(kgiusti) ubuntu cleanup
fi
}
# install rpc backend
function install_rpc_backend {
# Regardless of the broker used, if AMQP 1.0 is configured load
# the necessary messaging client libraries for oslo.messaging
if [ "$RPC_MESSAGING_PROTOCOL" == "AMQP1" ]; then
if is_fedora; then
install_package qpid-proton-c-devel
install_package python-qpid-proton
elif is_ubuntu; then
# TODO(kgiusti) The QPID AMQP 1.0 protocol libraries
# are not yet in the ubuntu repos. Enable these installs
# once they are present:
#install_package libqpid-proton2-dev
#install_package python-qpid-proton
# Also add 'uninstall' directives in cleanup_rpc_backend()!
exit_distro_not_supported "QPID AMQP 1.0 Proton libraries"
else
exit_distro_not_supported "QPID AMQP 1.0 Proton libraries"
fi
# Install pyngus client API
# TODO(kgiusti) can remove once python qpid bindings are
# available on all supported platforms _and_ pyngus is added
# to the requirements.txt file in oslo.messaging
pip_install pyngus
fi
if is_service_enabled rabbit; then
# Install rabbitmq-server
install_package rabbitmq-server
elif is_service_enabled qpid; then
if is_fedora; then
install_package qpid-cpp-server
elif is_ubuntu; then
install_package qpidd
else
exit_distro_not_supported "qpid installation"
fi
_configure_qpid
elif is_service_enabled zeromq; then
# NOTE(ewindisch): Redis is not strictly necessary
# but there is a matchmaker driver that works
# really well & out of the box for multi-node.
if is_fedora; then
install_package zeromq python-zmq
if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
install_package redis python-redis
fi
elif is_ubuntu; then
install_package libzmq1 python-zmq
if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
install_package redis-server python-redis
fi
elif is_suse; then
install_package libzmq1 python-pyzmq
if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
install_package redis python-redis
fi
else
exit_distro_not_supported "zeromq installation"
fi
# Necessary directory for socket location.
sudo mkdir -p /var/run/openstack
sudo chown $STACK_USER /var/run/openstack
fi
# If using the QPID broker, install the QPID python client API
if is_service_enabled qpid || [ -n "$QPID_HOST" ]; then
install_package python-qpid
fi
}
# restart the rpc backend
function restart_rpc_backend {
if is_service_enabled rabbit; then
# Start rabbitmq-server
echo_summary "Starting RabbitMQ"
# NOTE(bnemec): Retry initial rabbitmq configuration to deal with
# the fact that sometimes it fails to start properly.
# Reference: https://bugzilla.redhat.com/show_bug.cgi?id=1144100
local i
for i in `seq 10`; do
local rc=0
[[ $i -eq "10" ]] && die $LINENO "Failed to set rabbitmq password"
if is_fedora || is_suse; then
# service is not started by default
restart_service rabbitmq-server
fi
rabbit_setuser "$RABBIT_USERID" "$RABBIT_PASSWORD" || rc=$?
if [ $rc -ne 0 ]; then
continue
fi
# change the rabbit password since the default is "guest"
sudo rabbitmqctl change_password \
$RABBIT_USERID $RABBIT_PASSWORD || rc=$?
if [ $rc -ne 0 ]; then
continue;
fi
break
done
if is_service_enabled n-cell; then
# Add partitioned access for the child cell
if [ -z `sudo rabbitmqctl list_vhosts | grep child_cell` ]; then
sudo rabbitmqctl add_vhost child_cell
sudo rabbitmqctl set_permissions -p child_cell $RABBIT_USERID ".*" ".*" ".*"
fi
fi
elif is_service_enabled qpid; then
echo_summary "Starting qpid"
restart_service qpidd
fi
}
# iniset cofiguration
function iniset_rpc_backend {
local package=$1
local file=$2
local section=$3
if is_service_enabled zeromq; then
iniset $file $section rpc_backend "zmq"
iniset $file $section rpc_zmq_matchmaker \
oslo.messaging._drivers.matchmaker_redis.MatchMakerRedis
# Set MATCHMAKER_REDIS_HOST if running multi-node.
MATCHMAKER_REDIS_HOST=${MATCHMAKER_REDIS_HOST:-127.0.0.1}
iniset $file matchmaker_redis host $MATCHMAKER_REDIS_HOST
elif is_service_enabled qpid || [ -n "$QPID_HOST" ]; then
# For Qpid use the 'amqp' oslo.messaging transport when AMQP 1.0 is used
if [ "$RPC_MESSAGING_PROTOCOL" == "AMQP1" ]; then
iniset $file $section rpc_backend "amqp"
else
iniset $file $section rpc_backend ${package}.openstack.common.rpc.impl_qpid
fi
iniset $file $section qpid_hostname ${QPID_HOST:-$SERVICE_HOST}
if [ -n "$QPID_USERNAME" ]; then
iniset $file $section qpid_username $QPID_USERNAME
iniset $file $section qpid_password $QPID_PASSWORD
fi
elif is_service_enabled rabbit || { [ -n "$RABBIT_HOST" ] && [ -n "$RABBIT_PASSWORD" ]; }; then
iniset $file $section rpc_backend ${package}.openstack.common.rpc.impl_kombu
iniset $file $section rabbit_hosts $RABBIT_HOST
iniset $file $section rabbit_password $RABBIT_PASSWORD
iniset $file $section rabbit_userid $RABBIT_USERID
fi
}
# Check if qpid can be used on the current distro.
# qpid_is_supported
function qpid_is_supported {
if [[ -z "$DISTRO" ]]; then
GetDistro
fi
# Qpid is not in openSUSE
( ! is_suse )
}
function rabbit_setuser {
local user="$1" pass="$2" found="" out=""
out=$(sudo rabbitmqctl list_users) ||
{ echo "failed to list users" 1>&2; return 1; }
found=$(echo "$out" | awk '$1 == user { print $1 }' "user=$user")
if [ "$found" = "$user" ]; then
sudo rabbitmqctl change_password "$user" "$pass" ||
{ echo "failed changing pass for '$user'" 1>&2; return 1; }
else
sudo rabbitmqctl add_user "$user" "$pass" ||
{ echo "failed changing pass for $user"; return 1; }
fi
sudo rabbitmqctl set_permissions "$user" ".*" ".*" ".*"
}
# Set up the various configuration files used by the qpidd broker
function _configure_qpid {
# the location of the configuration files have changed since qpidd 0.14
local qpid_conf_file
if [ -e /etc/qpid/qpidd.conf ]; then
qpid_conf_file=/etc/qpid/qpidd.conf
elif [ -e /etc/qpidd.conf ]; then
qpid_conf_file=/etc/qpidd.conf
else
exit_distro_not_supported "qpidd.conf file not found!"
fi
# force the ACL file to a known location
local qpid_acl_file=/etc/qpid/qpidd.acl
if [ ! -e $qpid_acl_file ]; then
sudo mkdir -p -m 755 `dirname $qpid_acl_file`
sudo touch $qpid_acl_file
sudo chmod o+r $qpid_acl_file
fi
sudo sed -i.bak '/^acl-file=/d' $qpid_conf_file
echo "acl-file=$qpid_acl_file" | sudo tee --append $qpid_conf_file
sudo sed -i '/^auth=/d' $qpid_conf_file
if [ -z "$QPID_USERNAME" ]; then
# no QPID user configured, so disable authentication
# and access control
echo "auth=no" | sudo tee --append $qpid_conf_file
cat <<EOF | sudo tee $qpid_acl_file
acl allow all all
EOF
else
# Configure qpidd to use PLAIN authentication, and add
# QPID_USERNAME to the ACL:
echo "auth=yes" | sudo tee --append $qpid_conf_file
if [ -z "$QPID_PASSWORD" ]; then
read_password QPID_PASSWORD "ENTER A PASSWORD FOR QPID USER $QPID_USERNAME"
fi
# Create ACL to allow $QPID_USERNAME full access
cat <<EOF | sudo tee $qpid_acl_file
group admin ${QPID_USERNAME}@QPID
acl allow admin all
acl deny all all
EOF
# Add user to SASL database
if is_ubuntu; then
install_package sasl2-bin
elif is_fedora; then
install_package cyrus-sasl-lib
fi
local sasl_conf_file=/etc/sasl2/qpidd.conf
sudo sed -i.bak '/PLAIN/!s/mech_list: /mech_list: PLAIN /' $sasl_conf_file
local sasl_db=`sudo grep sasldb_path $sasl_conf_file | cut -f 2 -d ":" | tr -d [:blank:]`
if [ ! -e $sasl_db ]; then
sudo mkdir -p -m 755 `dirname $sasl_db`
fi
echo $QPID_PASSWORD | sudo saslpasswd2 -c -p -f $sasl_db -u QPID $QPID_USERNAME
sudo chmod o+r $sasl_db
fi
# If AMQP 1.0 is specified, ensure that the version of the
# broker can support AMQP 1.0 and configure the queue and
# topic address patterns used by oslo.messaging.
if [ "$RPC_MESSAGING_PROTOCOL" == "AMQP1" ]; then
QPIDD=$(type -p qpidd)
if ! $QPIDD --help | grep -q "queue-patterns"; then
exit_distro_not_supported "qpidd with AMQP 1.0 support"
fi
if ! grep -q "queue-patterns=exclusive" $qpid_conf_file; then
cat <<EOF | sudo tee --append $qpid_conf_file
queue-patterns=exclusive
queue-patterns=unicast
topic-patterns=broadcast
EOF
fi
fi
}
# Restore xtrace
$XTRACE
# Tell emacs to use shell-script-mode
## Local variables:
## mode: shell-script
## End: