diff --git a/heartbeat/redis b/heartbeat/redis new file mode 100644 index 000000000..44a76312c --- /dev/null +++ b/heartbeat/redis @@ -0,0 +1,368 @@ +#!/bin/bash + +. ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs + +: ${OCF_RESKEY_bin:=/usr/bin/redis-server} +: ${OCF_RESKEY_client_bin:=/usr/bin/redis-cli} +: ${OCF_RESKEY_config:=/etc/redis/redis.conf} +: ${OCF_RESKEY_user:=redis} +: ${OCF_RESKEY_rundir:=/var/run/redis} +: ${OCF_RESKEY_port:=6379} + +REDIS_SERVER="$OCF_RESKEY_bin" +REDIS_CLIENT="$OCF_RESKEY_client_bin" +REDIS_CONFIG="$OCF_RESKEY_config" +REDIS_USER="$OCF_RESKEY_user" +REDIS_RUNDIR="$OCF_RESKEY_rundir" +REDIS_PIDFILE="$OCF_RESKEY_rundir/redis-server.pid" +REDIS_SOCKET="$OCF_RESKEY_rundir/redis.sock" +REDIS_REPLICATION_PORT="$OCF_RESKEY_port" + +function meta_data() { + cat < + + +1.0 + + +Resource agent script for redis server. + +This resource fully supports master/slave replication. The master preference of a node is determined by the 'slave_priority' parameter of the redis config. +When taking the resource from 'unmanaged' to 'managed', the currently active master will be given a priority of 1000 (plus 1 for each active connection). The default 'slave_priority' is 100, so the master will stay master. For a slave to become master after converting the resource to managed, set a slave_priority greater than 1000. + + +Redis server + + + + +Path to \`redis-server\` + +Path to \`redis-server\` + + + + + +Path to \`redis-cli\` + +Path to \`redis-cli\` + + + + + +Path to 'redis.conf' + +Path to 'redis.conf' + + + + + +User to run redis as + +Redis user + + + + + +Directory to store socket and pid file in + +Redis var/run dir + + + + + +Port for replication client to connect to on remote server + +Replication port + + + + + + + + + + + + + + + + + + +EOI +} + +function crm_master() { + "${HA_SBIN_DIR}/crm_master" -l reboot "$@" +} + +function stripcr() { + sed 's/\r//' +} + +function monitor() { + pid="$(<"$REDIS_PIDFILE")" + pidof "$REDIS_SERVER" | grep -q "\<$pid\>" || return $OCF_NOT_RUNNING + + ocf_log debug "monitor: redis-server running under pid $pid" + + typeset -A info + while read line; do + [[ "$line" == "#"* ]] && continue + [[ "$line" != *":"* ]] && continue + IFS=':' read -r key value <<< "$line" + info[$key]="$value" + done < <("$REDIS_CLIENT" -s "$REDIS_SOCKET" info | stripcr) + if [[ -z "${info[role]}" ]]; then + ocf_log err "monitor: Could not get role from \`$REDIS_CLIENT -s $REDIS_SOCKET info\`" + fi + + if ocf_is_ms; then + # Here we see if a score has already been set. + score="$(crm_master --get-value --quiet)" + if [[ -z "$score" ]]; then + score="${info[slave_priority]}" + if [[ -z "$score" ]]; then + if [[ "${info[role]}" == "master" ]]; then + score=1000 + else + score=1 + fi + fi + score=$(( score + info[connected_clients] )) + #score=$(( score + ${info[connected_clients]} )) + ocf_log debug "monitor: Setting master score to '$score'" + crm_master -v "$score" + fi + + if [[ "${info[role]}" == "master" ]]; then + return $OCF_RUNNING_MASTER + fi + + if [[ -n "$CHECK_SLAVE_STATE" ]]; then + if [[ "${info[master_link_status]}" != "up" ]]; then + ocf_log err "monitor: Slave mode link has failed (link=${info[master_link_status]})" + return $OCF_ERR_GENERIC + fi + if [[ "${info[master_host]}" != "${OCF_RESKEY_CRM_meta_notify_master_uname}" ]]; then + ocf_log err "monitor: Slave mode current master does not match running master. current=${info[master_host]}, running=${OCF_RESKEY_CRM_meta_notify_master_uname}" + return $OCF_ERR_GENERIC + fi + fi + fi + return $OCF_SUCCESS +} + +function start() { + monitor + status=$? + + if (( status == OCF_SUCCESS )) || (( status == OCF_RUNNING_MASTER )); then + ocf_log info "start: redis is already running" + return $OCF_SUCCESS + fi + + [[ ! -d "$REDIS_RUNDIR" ]] && mkdir -p "$REDIS_RUNDIR" + chown -R redis "$REDIS_RUNDIR" + + ocf_log info "start: $REDIS_SERVER --daemonize yes --unixsocket '$REDIS_SOCKET' --pidfile '$REDIS_PIDFILE'" + output="$(su "$REDIS_USER" -s /bin/sh -c "cd '$REDIS_RUNDIR'; exec '$REDIS_SERVER' --daemonize yes --unixsocket '$REDIS_SOCKET' --pidfile '$REDIS_PIDFILE'" 2>&1)" + + while true; do + # wait for redis to start + typeset -A info= + while read line; do + [[ "$line" == "#"* ]] && continue + [[ "$line" != *":"* ]] && continue + IFS=':' read -r key value <<< "$line" + info[$key]="$value" + done < <("$REDIS_CLIENT" -s "$REDIS_SOCKET" info | stripcr) + + if (( info[loading] == 0 )); then + break + elif (( info[loading] == 1 )); then + sleep "${info[loading_eta_seconds]}" + else + ocf_log err "start: Unknown error waiting for redis to start" + return $OCF_ERR_GENERIC + fi + done + + ocf_is_ms && demote # pacemaker expects resources to start in slave mode + + monitor + status=$? + if (( status == OCF_SUCCESS )) || (( status == OCF_RUNNING_MASTER )); then + return $OCF_SUCCESS + fi + + ocf_log err "start: Unknown error starting redis. output=${output//$'\n'/; }" + return $status +} + +function stop() { + monitor + status=$? + + if (( status == OCF_NOT_RUNNING )); then + ocf_log info "stop: redis is already stopped" + return $OCF_SUCCESS + fi + + pid="$(<"$REDIS_PIDFILE")" + kill -TERM "$pid" + + for (( i=0; i<10; i+ )); do + monitor + status=$? + if (( status == OCF_NOT_RUNNING )); then + crm_master -D + return $OCF_SUCCESS + fi + if (( status != OCF_RUNNING_MASTER )) && (( status != OCF_SUCCESS )); then + ocf_log err "stop: Unknown error while stopping" + return $OCF_ERR_GENERIC + fi + sleep 1 + done + monitor + status=$? + if (( status == OCF_NOT_RUNNING )); then + crm_master -D + return $OCF_SUCCESS + fi + + ocf_log err "stop: Unknown error while stopping" + return $OCF_ERR_GENERIC +} + +function promote() { + monitor + status=$? + + if (( status == OCF_RUNNING_MASTER )); then + ocf_log info "promote: Already running as master" + return $OCF_SUCCESS + elif (( status != OCF_SUCCESS )); then + ocf_log err "promote: Node is not running as a slave" + return $OCF_ERR_GENERIC + fi + + ocf_log info "$REDIS_CLIENT -s '$REDIS_SOCKET' slaveof no one" + ocf_run "$REDIS_CLIENT" -s "$REDIS_SOCKET" slaveof no one + + monitor + status=$? + if (( status == OCF_RUNNING_MASTER )); then + return $OCF_SUCCESS + fi + + ocf_log err "promote: Unknown error while promoting to master (status=$status)" + return $OCF_ERR_GENERIC +} + +function demote() { + CHECK_SLAVE_STATE=1 monitor + status=$? + + if (( status == OCF_SUCCESS )); then + ocf_log info "demote: Already running as slave" + return $OCF_SUCCESS + fi + + master_host="${OCF_RESKEY_CRM_meta_notify_promote_uname// /}" + : "${master_host:=${OCF_RESKEY_CRM_meta_notify_master_uname// /}}" + master_port="${REDIS_REPLICATION_PORT}" + ocf_log info "$REDIS_CLIENT -s '$REDIS_SOCKET' slaveof '$master_host' '$master_port'" + ocf_run "$REDIS_CLIENT" -s "$REDIS_SOCKET" slaveof "$master_host" "$master_port" + + monitor + status=$? + + if (( status == OCF_SUCCESS )); then + return $OCF_SUCCESS + fi + ocf_log err "demote: Unexpected error setting slave mode (status=$status)" + return $OCF_ERR_GENERIC +} + +function notify() { + mode="${OCF_RESKEY_CRM_meta_notify_type}-${OCF_RESKEY_CRM_meta_notify_operation}" + case "$mode" in + post-demote|post-promote) # change the master + monitor + status=$? + if (( status == OCF_SUCCESS )); then # were a slave + demote + fi + ;; + esac + return $OCF_SUCCESS +} + +function validate() { + if [[ -x "$REDIS_SERVER" ]]; then + ocf_log err "validate: $REDIS_SERVER does not exist or is not executable" + return $OCF_ERR_INSTALLED + fi + if [[ -x "$REDIS_CLIENT" ]]; then + ocf_log err "validate: $REDIS_CLIENT does not exist or is not executable" + return $OCF_ERR_INSTALLED + fi + if [[ -f "$REDIS_CONFIG" ]]; then + ocf_log err "validate: $REDIS_CONFIG does not exist" + return $OCF_ERR_CONFIGURED + fi + if ! getent passwd "$REDIS_USER" &>/dev/null; then + ocf_log err "validate: $REDIS_USER is not a valid user" + return $OCF_ERR_CONFIGURED + fi +} + +ocf_log debug "action=${1:-$__OCF_ACTION} notify_type=${OCF_RESKEY_CRM_meta_notify_type} notify_operation=${OCF_RESKEY_CRM_meta_notify_operation} master_host=${OCF_RESKEY_CRM_meta_notify_master_uname} slave_host=${OCF_RESKEY_CRM_meta_notify_slave_uname} promote_host=${OCF_RESKEY_CRM_meta_notify_promote_uname} demote_host=${OCF_RESKEY_CRM_meta_notify_demote_uname}; params: bin=${OCF_RESKEY_bin} client_bin=${OCF_RESKEY_client_bin} config=${OCF_RESKEY_config} user=${OCF_RESKEY_user} rundir=${OCF_RESKEY_rundir} port=${OCF_RESKEY_port}" + +case "${1:-$__OCF_ACTION}" in + status|monitor) + monitor + ;; + start) + start + ;; + stop) + stop + ;; + restart) + stop && start + ;; + promote) + promote + ;; + demote) + demote + ;; + notify) + notify + ;; + meta-data) + meta_data + ;; + validate-all) + validate + ;; + *) + echo "Usage: $0 {monitor|start|stop|restart|promote|demote|notify|validate-all|meta-data}" + exit $OCF_ERR_UNIMPLEMENTED + ;; +esac +status=$? +ocf_log debug "exit_status=$status" +exit $status