From 591302a81c5961a03ffad70369433c20b31aebbe Mon Sep 17 00:00:00 2001 From: David Vossel Date: Fri, 18 Jul 2014 13:01:48 -0500 Subject: [PATCH] galera resource agent --- doc/man/Makefile.am | 1 + heartbeat/Makefile.am | 2 + heartbeat/galera | 693 ++++++++++++++++++++++++++++++++++++++++++++ heartbeat/mysql | 273 +---------------- heartbeat/mysql-common.sh | 279 ++++++++++++++++++ heartbeat/ocf-shellfuncs.in | 1 + 6 files changed, 991 insertions(+), 258 deletions(-) create mode 100644 heartbeat/galera create mode 100644 heartbeat/mysql-common.sh diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index a3517aa..e97c7e9 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -102,6 +102,7 @@ man_MANS = ocf_heartbeat_AoEtarget.7 \ ocf_heartbeat_ethmonitor.7 \ ocf_heartbeat_exportfs.7 \ ocf_heartbeat_fio.7 \ + ocf_heartbeat_galera.7 \ ocf_heartbeat_iSCSILogicalUnit.7 \ ocf_heartbeat_iSCSITarget.7 \ ocf_heartbeat_ids.7 \ diff --git a/heartbeat/Makefile.am b/heartbeat/Makefile.am index 65a2cfb..aab521f 100644 --- a/heartbeat/Makefile.am +++ b/heartbeat/Makefile.am @@ -73,6 +73,7 @@ ocf_SCRIPTS = ClusterMon \ exportfs \ Filesystem \ fio \ + galera \ ids \ iscsi \ ICP \ @@ -140,6 +141,7 @@ ocfcommon_DATA = ocf-shellfuncs \ sapdb-nosha.sh \ sapdb.sh \ ora-common.sh \ + mysql-common.sh \ findif.sh # Legacy locations diff --git a/heartbeat/galera b/heartbeat/galera new file mode 100644 index 0000000..6d8cf12 --- /dev/null +++ b/heartbeat/galera @@ -0,0 +1,693 @@ +#!/bin/sh +# +# Copyright (c) 2014 David Vossel +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of version 2 of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Further, this software is distributed without any warranty that it is +# free of the rightful claim of any third person regarding infringement +# or the like. Any license provided herein, whether implied or +# otherwise, applies only to this software file. Patent licenses, if +# any, provided herein do not apply to combinations of this program with +# other software, or any other product whatsoever. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. +# + +## +# README. +# +# This agent only supports being configured as a multistate Master +# resource. +# +# Slave vs Master role: +# +# During the 'Slave' role, galera instances are in read-only mode and +# will not attempt to connect to the cluster. This role exists only as +# a means to determine which galera instance is the most up-to-date. The +# most up-to-date node will be used to bootstrap a galera cluster that +# has no current members. +# +# The galera instances will only begin to be promoted to the Master role +# once all the nodes in the 'wsrep_cluster_address' connection address +# have entered read-only mode. At that point the node containing the +# database that is most current will be promoted to Master. Once the first +# Master instance bootstraps the galera cluster, the other nodes will be +# promoted to Master as well. +# +# Example: Create a galera cluster using nodes rhel7-node1 rhel7-node2 rhel7-node3 +# +# pcs resource create db galera enable_creation=true \ +# wsrep_cluster_address="gcomm://rhel7-auto1,rhel7-auto2,rhel7-auto3" meta master-max=3 --master +# +# By setting the 'enable_creation' option, the database will be automatically +# generated at startup. The meta attribute 'master-max=3' means that all 3 +# nodes listed in the wsrep_cluster_address list will be allowed to connect +# to the galera cluster and perform replication. +# +# NOTE: If you have more nodes in the pacemaker cluster then you wish +# to have in the galera cluster, make sure to use location contraints to prevent +# pacemaker from attempting to place a galera instance on a node that is +# not in the 'wsrep_cluster_address" list. +# +## + +####################################################################### +# Initialization: + +: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} +. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs +. ${OCF_FUNCTIONS_DIR}/mysql-common.sh + +# It is common for some galera instances to store +# check user that can be used to query status +# in this file +if [ -f "/etc/sysconfig/clustercheck" ]; then + . /etc/sysconfig/clustercheck +fi + +####################################################################### + +usage() { + cat < + + +1.0 + + +Resource script for managing galara database. + +Manages a galara instance + + + + +Location of the MySQL server binary + +MySQL server binary + + + + + +Location of the MySQL client binary + +MySQL client binary + + + + + +Configuration file + +MySQL config + + + + + +Directory containing databases + +MySQL datadir + + + + + +User running MySQL daemon + +MySQL user + + + + + +Group running MySQL daemon (for logfile and directory permissions) + +MySQL group + + + + + +The logfile to be used for mysqld. + +MySQL log file + + + + + +The pidfile to be used for mysqld. + +MySQL pid file + + + + + +The socket to be used for mysqld. + +MySQL socket + + + + + +If the MySQL database does not exist, it will be created + +Create the database if it does not exist + + + + + +Additional parameters which are passed to the mysqld on startup. +(e.g. --skip-external-locking or --skip-grant-tables) + +Additional parameters to pass to mysqld + + + + + + +The galera cluster address. This takes the form of: +gcomm://node,node,node + +Only nodes present in this node list will be allowed to start a galera instance. +It is expected that the galera node names listed in this address match valid +pacemaker node names. + +Galera cluster address + + + + + +Cluster check user. + +MySQL test user + + + + + +Cluster check user password + +check password + + + + + + + + + + + + + + + + + + + +END +} + +get_option_variable() +{ + local key=$1 + + $MYSQL $MYSQL_OPTIONS_CHECK -e "SHOW VARIABLES like '$key';" | tail -1 +} + +get_status_variable() +{ + local key=$1 + + $MYSQL $MYSQL_OPTIONS_CHECK -e "show status like '$key';" | tail -1 +} + +set_bootstrap_node() +{ + local node=$1 + + ${HA_SBIN_DIR}/crm_attribute -N $node -l reboot --name "${INSTANCE_ATTR_NAME}-bootstrap" -v "true" +} + +clear_bootstrap_node() +{ + ${HA_SBIN_DIR}/crm_attribute -N $NODENAME -l reboot --name "${INSTANCE_ATTR_NAME}-bootstrap" -D +} + +is_bootstrap() +{ + ${HA_SBIN_DIR}/crm_attribute -N $NODENAME -l reboot --name "${INSTANCE_ATTR_NAME}-bootstrap" -Q 2>/dev/null + +} + +clear_last_commit() +{ + ${HA_SBIN_DIR}/crm_attribute -N $NODENAME -l reboot --name "${INSTANCE_ATTR_NAME}-last-committed" -D +} + +set_last_commit() +{ + ${HA_SBIN_DIR}/crm_attribute -N $NODENAME -l reboot --name "${INSTANCE_ATTR_NAME}-last-committed" -v $1 +} + +get_last_commit() +{ + local node=$1 + + if [ -z "$node" ]; then + ${HA_SBIN_DIR}/crm_attribute -N $NODENAME -l reboot --name "${INSTANCE_ATTR_NAME}-last-committed" -Q 2>/dev/null + else + ${HA_SBIN_DIR}/crm_attribute -N $node -l reboot --name "${INSTANCE_ATTR_NAME}-last-committed" -Q 2>/dev/null + fi +} + +wait_for_sync() +{ + local state=$(get_status_variable "wsrep_local_state") + + ocf_log info "Waiting for database to sync with the cluster. " + while [ "$state" != "4" ]; do + sleep 1 + state=$(get_status_variable "wsrep_local_state") + done + ocf_log info "Database synced." +} + +is_primary() +{ + cluster_status=$(get_status_variable "wsrep_cluster_status") + if [ "$cluster_status" = "Primary" ]; then + return 0 + fi + + if [ -z "$cluster_status" ]; then + ocf_log err "Unable to retrieve wsrep_cluster_status, verify check_user '$OCF_RESKEY_check_user' has permissions to view status" + else + ocf_log info "Galera instance wsrep_cluster_status=${cluster_status}" + fi + return 1 +} + +is_readonly() +{ + local res=$(get_option_variable "read_only") + + if ! ocf_is_true "$res"; then + return 1 + fi + + cluster_status=$(get_status_variable "wsrep_cluster_status") + if ! [ "$cluster_status" = "Disconnected" ]; then + return 1 + fi + + return 0 +} + +master_exists() +{ + # determine if a master instance is already up and is healthy + crm_mon --as-xml | grep "resource.*id=\"${OCF_RESOURCE_INSTANCE}\".*role=\"Master\".*active=\"true\".*orphaned=\"false\".*failed=\"false\"" > /dev/null 2>&1 + return $? +} + +clear_master_score() +{ + local node=$1 + if [ -z "$node" ]; then + $CRM_MASTER -D + else + $CRM_MASTER -D -N $node + fi +} + +set_master_score() +{ + local node=$1 + + if [ -z "$node" ]; then + $CRM_MASTER -v 100 + else + $CRM_MASTER -N $node -v 100 + fi +} + +promote_everyone() +{ + + for node in $(echo "$OCF_RESKEY_wsrep_cluster_address" | sed 's/gcomm:\/\///g' | tr -d ' ' | tr -s ',' ' '); do + + set_master_score $node + done +} + +greater_than_equal_long() +{ + # there are values we need to compare in this script + # that are too large for shell -gt to process + echo | awk -v n1="$1" -v n2="$2" '{if (n1>=n2) printf ("true"); else printf ("false");}' | grep -q "true" +} + +detect_first_master() +{ + local best_commit=0 + local best_node="$NODENAME" + local last_commit=0 + local missing_nodes=0 + + for node in $(echo "$OCF_RESKEY_wsrep_cluster_address" | sed 's/gcomm:\/\///g' | tr -d ' ' | tr -s ',' ' '); do + last_commit=$(get_last_commit $node) + + if [ -z "$last_commit" ]; then + ocf_log info "Waiting on node <${node}> to report database status before Master instances can start." + missing_nodes=1 + continue + fi + + # this means -1, or that no commit has occured yet. + if [ "$last_commit" = "18446744073709551615" ]; then + last_commit="0" + fi + + greater_than_equal_long "$last_commit" "$best_commit" + if [ $? -eq 0 ]; then + best_node=$node + best_commit=$last_commit + fi + + done + + if [ $missing_nodes -eq 1 ]; then + return + fi + + ocf_log info "Promoting $best_node to be our bootstrap node" + set_master_score $best_node + set_bootstrap_node $best_node +} + +# For galera, promote is really start +galera_promote() +{ + local rc + local extra_opts + local bootstrap + + master_exists + if [ $? -eq 0 ]; then + # join without bootstrapping + extra_opts="--wsrep-cluster-address=${OCF_RESKEY_wsrep_cluster_address}" + else + bootstrap=$(is_bootstrap) + + if ocf_is_true $bootstrap; then + ocf_log info "Node <${NODENAME}> is bootstrapping the cluster" + extra_opts="--wsrep-cluster-address=gcomm://" + else + ocf_log err "Failure, Attempted to promote Master instance of $OCF_RESOURCE_INSTANCE before bootstrap node has been detected." + return $OCF_ERR_GENERIC + fi + + fi + + # make sure the read only instance is stopped + mysql_common_stop + rc=$? + if [ $rc -ne $OCF_SUCCESS ] && [ $rc -ne $OCF_NOT_RUNNING ]; then + ocf_log err "Failed to stop read-only galera instance during promotion to Master" + return $rc + fi + + sleep 4 + + mysql_common_prepare_dirs + mysql_common_start "$extra_opts" + rc=$? + if [ $rc != $OCF_SUCCESS ]; then + return $rc + fi + + galera_monitor + rc=$? + if [ $rc != $OCF_SUCCESS -a $rc != $OCF_RUNNING_MASTER ]; then + ocf_log err "Failed initial monitor action" + return $rc + fi + + is_readonly + if [ $? -eq 0 ]; then + ocf_log err "Failure. Master instance started in read-only mode, check configuration." + return $OCF_ERR_GENERIC + fi + + is_primary + if [ $? -ne 0 ]; then + ocf_log err "Failure. Master instance started, but is not in Primary mode." + return $OCF_ERR_GENERIC + fi + + if ocf_is_true $bootstrap; then + promote_everyone + clear_bootstrap_node + ocf_log info "Bootstrap complete, promoting the rest of the galera instances." + else + # if this is not the bootstrap node, make sure this instance + # syncs with the rest of the cluster before promotion returns. + wait_for_sync + fi + + # last commit is no longer relevant once promoted + clear_last_commit + + ocf_log info "Galera started" + return $OCF_SUCCESS +} + +galera_demote() +{ + mysql_common_stop + rc=$? + if [ $rc -ne $OCF_SUCCESS ] && [ $rc -ne $OCF_NOT_RUNNING ]; then + ocf_log err "Failed to stop Master galera instance during demotion to Master" + return $rc + fi + + # if this node was previously a bootstrap node, that is no longer the case. + clear_bootstrap_node + + # start again in slave mode so the new last commit is recorded + galera_start +} + +galera_start() +{ + local extra_opts='--read-only=true' + local last_commit + + echo $OCF_RESKEY_wsrep_cluster_address | grep -q $NODENAME + if [ $? -ne 0 ]; then + ocf_log err "local node <${NODENAME}> must be a member of the wsrep_cluster_address <${OCF_RESKEY_wsrep_cluster_address}>to start this galera instance" + return $OCF_ERR_CONFIGURED + fi + + mysql_common_prepare_dirs + mysql_common_start "$extra_opts" + + is_readonly + if [ $? -ne 0 ]; then + ocf_log err "Failure. Slave instance did not start correctly in read-only mode, Make sure local galera.cnf does not have wsrep_cluster_address set." + return $OCF_ERR_GENERIC + fi + + ocf_log info "attempting to detect last commit version" + while [ -z "$last_commit" ]; do + last_commit=$(get_status_variable "wsrep_last_committed") + if [ -z "$last_commit" ]; then + sleep 1 + fi + done + ocf_log info "Last commit version found: $last_commit" + + set_last_commit $last_commit + + master_exists + if [ $? -eq 0 ]; then + ocf_log info "Master instances are already up, setting master score so this instance will join galera cluster." + set_master_score $NODENAME + else + clear_master_score + detect_first_master + fi + + return $OCF_SUCCESS +} + +galera_monitor() +{ + local rc + local status_loglevel="err" + + # Set loglevel to info during probe + if ocf_is_probe; then + status_loglevel="info" + fi + + mysql_common_status $status_loglevel + rc=$? + + # If status returned an error, return that immediately + if [ $rc -ne $OCF_SUCCESS ]; then + return $rc + fi + + echo $OCF_RESKEY_wsrep_cluster_address | grep -q $NODENAME + if [ $? -ne 0 ]; then + ocf_log err "local node <${NODENAME}> is started, but is not a member of the wsrep_cluster_address <${OCF_RESKEY_wsrep_cluster_address}>" + return $OCF_ERR_GENERIC + fi + + is_readonly + if [ $? -ne 0 ]; then + is_primary + if [ $? -ne 0 ]; then + ocf_log err "local node <${NODENAME}> is neither in primary mode nor in read_only mode. Unknown state." + return $OCF_ERR_GENERIC + fi + + if ocf_is_probe; then + # restore master score during probe + # if we detect this is a master instance + set_master_score + fi + rc=$OCF_RUNNING_MASTER + else + master_exists + if [ $? -ne 0 ]; then + detect_first_master + else + # a master instance exists and is healthy, promote this + # local read only instance + # so it can join the master galera cluster. + set_master_score + fi + fi + # TODO look at what is done in the wait script + + return $rc +} + +galera_stop() +{ + local rc + # make sure the process is stopped + mysql_common_stop + rc=$1 + + clear_last_commit + clear_master_score + clear_bootstrap_node + return $rc +} + +galera_validate() +{ + if ! ocf_is_ms; then + ocf_log err "Galera must be configured as a multistate Master/Slave resource." + return $OCF_ERR_CONFIGURED + fi + + if [ -z "$OCF_RESKEY_wsrep_cluster_address" ]; then + ocf_log err "Galera must be configured with a wsrep_cluster_address value." + return $OCF_ERR_CONFIGURED + fi + + mysql_common_validate +} + +case "$1" in + meta-data) meta_data + exit $OCF_SUCCESS;; + usage|help) usage + exit $OCF_SUCCESS;; +esac + +galera_validate +rc=$? +LSB_STATUS_STOPPED=3 +if [ $rc -ne 0 ]; then + case "$1" in + stop) exit $OCF_SUCCESS;; + monitor) exit $OCF_NOT_RUNNING;; + status) exit $LSB_STATUS_STOPPED;; + *) exit $rc;; + esac +fi + +if [ -z "${OCF_RESKEY_check_passwd}" ]; then + # This value is automatically sourced from /etc/sysconfig/checkcluster if available + OCF_RESKEY_check_passwd=${MYSQL_PASSWORD} +fi +if [ -z "${OCF_RESKEY_check_user}" ]; then + # This value is automatically sourced from /etc/sysconfig/checkcluster if available + OCF_RESKEY_check_user=${MYSQL_USERNAME} +fi +: ${OCF_RESKEY_check_user="root"} + +MYSQL_OPTIONS_CHECK="-nNE --user=${OCF_RESKEY_check_user}" +if [ -n "${OCF_RESKEY_check_passwd}" ]; then + MYSQL_OPTIONS_CHECK="$MYSQL_OPTIONS_CHECK --password=${MYSQL_PASSWORD}" +fi + +# What kind of method was invoked? +case "$1" in + start) galera_start;; + stop) galera_stop;; + status) mysql_common_status err;; + monitor) galera_monitor;; + promote) galera_promote;; + demote) galera_demote;; + notify) galera_notify;; + validate-all) exit $OCF_SUCCESS;; + + *) usage + exit $OCF_ERR_UNIMPLEMENTED;; +esac + +# vi:sw=4:ts=4:et: diff --git a/heartbeat/mysql b/heartbeat/mysql index f7eb9f2..41287d0 100755 --- a/heartbeat/mysql +++ b/heartbeat/mysql @@ -51,97 +51,7 @@ : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs - -####################################################################### - -# Attempt to detect a default binary -OCF_RESKEY_binary_default=$(which mysqld_safe 2> /dev/null) -if [ "$OCF_RESKEY_binary_default" = "" ]; then - OCF_RESKEY_binary_default=$(which safe_mysqld 2> /dev/null) -fi - -# Fill in some defaults if no values are specified -HOSTOS=`uname` -if [ "X${HOSTOS}" = "XOpenBSD" ];then - if [ "$OCF_RESKEY_binary_default" = "" ]; then - OCF_RESKEY_binary_default="/usr/local/bin/mysqld_safe" - fi - OCF_RESKEY_config_default="/etc/my.cnf" - OCF_RESKEY_datadir_default="/var/mysql" - OCF_RESKEY_user_default="_mysql" - OCF_RESKEY_group_default="_mysql" - OCF_RESKEY_log_default="/var/log/mysqld.log" - OCF_RESKEY_pid_default="/var/mysql/mysqld.pid" - OCF_RESKEY_socket_default="/var/run/mysql/mysql.sock" -else - if [ "$OCF_RESKEY_binary_default" = "" ]; then - OCF_RESKEY_binary_default="/usr/bin/safe_mysqld" - fi - OCF_RESKEY_config_default="/etc/my.cnf" - OCF_RESKEY_datadir_default="/var/lib/mysql" - OCF_RESKEY_user_default="mysql" - OCF_RESKEY_group_default="mysql" - OCF_RESKEY_log_default="/var/log/mysqld.log" - OCF_RESKEY_pid_default="/var/run/mysql/mysqld.pid" - OCF_RESKEY_socket_default="/var/lib/mysql/mysql.sock" -fi -OCF_RESKEY_client_binary_default="mysql" -OCF_RESKEY_test_user_default="root" -OCF_RESKEY_test_table_default="mysql.user" -OCF_RESKEY_test_passwd_default="" -OCF_RESKEY_enable_creation_default=0 -OCF_RESKEY_additional_parameters_default="" -OCF_RESKEY_replication_port_default="3306" -OCF_RESKEY_max_slave_lag_default="3600" -OCF_RESKEY_evict_outdated_slaves_default="false" -OCF_RESKEY_reader_attribute_default="readable" - -: ${OCF_RESKEY_binary=${OCF_RESKEY_binary_default}} -MYSQL_BINDIR=`dirname ${OCF_RESKEY_binary}` - -: ${OCF_RESKEY_client_binary=${OCF_RESKEY_client_binary_default}} - -: ${OCF_RESKEY_config=${OCF_RESKEY_config_default}} -: ${OCF_RESKEY_datadir=${OCF_RESKEY_datadir_default}} - -: ${OCF_RESKEY_user=${OCF_RESKEY_user_default}} -: ${OCF_RESKEY_group=${OCF_RESKEY_group_default}} - -: ${OCF_RESKEY_log=${OCF_RESKEY_log_default}} -: ${OCF_RESKEY_pid=${OCF_RESKEY_pid_default}} -: ${OCF_RESKEY_socket=${OCF_RESKEY_socket_default}} - -: ${OCF_RESKEY_test_user=${OCF_RESKEY_test_user_default}} -: ${OCF_RESKEY_test_table=${OCF_RESKEY_test_table_default}} -: ${OCF_RESKEY_test_passwd=${OCF_RESKEY_test_passwd_default}} - -: ${OCF_RESKEY_enable_creation=${OCF_RESKEY_enable_creation_default}} -: ${OCF_RESKEY_additional_parameters=${OCF_RESKEY_additional_parameters_default}} - -: ${OCF_RESKEY_replication_user=${OCF_RESKEY_replication_user_default}} -: ${OCF_RESKEY_replication_passwd=${OCF_RESKEY_replication_passwd_default}} -: ${OCF_RESKEY_replication_port=${OCF_RESKEY_replication_port_default}} - -: ${OCF_RESKEY_max_slave_lag=${OCF_RESKEY_max_slave_lag_default}} -: ${OCF_RESKEY_evict_outdated_slaves=${OCF_RESKEY_evict_outdated_slaves_default}} - -: ${OCF_RESKEY_reader_attribute=${OCF_RESKEY_reader_attribute_default}} - -####################################################################### -# Convenience variables - -MYSQL=$OCF_RESKEY_client_binary -MYSQL_OPTIONS_LOCAL="-S $OCF_RESKEY_socket --connect_timeout=10" -MYSQL_OPTIONS_REPL="$MYSQL_OPTIONS_LOCAL --user=$OCF_RESKEY_replication_user --password=$OCF_RESKEY_replication_passwd" -MYSQL_OPTIONS_TEST="$MYSQL_OPTIONS_LOCAL --user=$OCF_RESKEY_test_user --password=$OCF_RESKEY_test_passwd" -MYSQL_TOO_MANY_CONN_ERR=1040 - -CRM_MASTER="${HA_SBIN_DIR}/crm_master -l reboot " -NODENAME=$(ocf_local_nodename) -CRM_ATTR="${HA_SBIN_DIR}/crm_attribute -N $NODENAME " -INSTANCE_ATTR_NAME=`echo ${OCF_RESOURCE_INSTANCE}| awk -F : '{print $1}'` -CRM_ATTR_REPL_INFO="${HA_SBIN_DIR}/crm_attribute --type crm_config --name ${INSTANCE_ATTR_NAME}_REPL_INFO -s mysql_replication" - +. ${OCF_FUNCTIONS_DIR}/mysql-common.sh ####################################################################### usage() { @@ -794,57 +704,6 @@ get_local_ip() { # Functions invoked by resource manager actions -mysql_validate() { - check_binary $OCF_RESKEY_binary - check_binary $OCF_RESKEY_client_binary - - if [ ! -f $OCF_RESKEY_config ]; then - ocf_log err "Config $OCF_RESKEY_config doesn't exist"; - return $OCF_ERR_INSTALLED; - fi - - if [ ! -d $OCF_RESKEY_datadir ]; then - ocf_log err "Datadir $OCF_RESKEY_datadir doesn't exist"; - return $OCF_ERR_INSTALLED; - fi - - getent passwd $OCF_RESKEY_user >/dev/null 2>&1 - if [ ! $? -eq 0 ]; then - ocf_log err "User $OCF_RESKEY_user doesn't exit"; - return $OCF_ERR_INSTALLED; - fi - - getent group $OCF_RESKEY_group >/dev/null 2>&1 - if [ ! $? -eq 0 ]; then - ocf_log err "Group $OCF_RESKEY_group doesn't exist"; - return $OCF_ERR_INSTALLED; - fi - - true -} - -mysql_status() { - if [ ! -e $OCF_RESKEY_pid ]; then - ocf_log $1 "MySQL is not running" - return $OCF_NOT_RUNNING; - fi - - pid=`cat $OCF_RESKEY_pid`; - if [ -d /proc -a -d /proc/1 ]; then - [ "u$pid" != "u" -a -d /proc/$pid ] - else - kill -s 0 $pid >/dev/null 2>&1 - fi - - if [ $? -eq 0 ]; then - return $OCF_SUCCESS; - else - ocf_log $1 "MySQL not running: removing old PID file" - rm -f $OCF_RESKEY_pid - return $OCF_NOT_RUNNING; - fi -} - mysql_monitor() { local rc local status_loglevel="err" @@ -854,7 +713,7 @@ mysql_monitor() { status_loglevel="info" fi - mysql_status $status_loglevel + mysql_common_status $status_loglevel rc=$? @@ -865,7 +724,6 @@ mysql_monitor() { return $rc fi - if [ $OCF_CHECK_LEVEL -gt 0 -a -n "$OCF_RESKEY_test_table" ]; then # Check if this instance is configured as a slave, and if so # check slave status @@ -894,58 +752,20 @@ mysql_monitor() { } mysql_start() { - local rc pid + local rc if ocf_is_ms; then # Initialize the ReaderVIP attribute, monitor will enable it set_reader_attr 0 fi - mysql_status info + mysql_common_status info if [ $? = $OCF_SUCCESS ]; then ocf_log info "MySQL already running" return $OCF_SUCCESS fi - touch $OCF_RESKEY_log - chown $OCF_RESKEY_user:$OCF_RESKEY_group $OCF_RESKEY_log - chmod 0640 $OCF_RESKEY_log - [ -x /sbin/restorecon ] && /sbin/restorecon $OCF_RESKEY_log - - if ocf_is_true "$OCF_RESKEY_enable_creation" && [ ! -d $OCF_RESKEY_datadir/mysql ] ; then - ocf_log info "Initializing MySQL database: " - $MYSQL_BINDIR/mysql_install_db --datadir=$OCF_RESKEY_datadir - rc=$? - if [ $rc -ne 0 ] ; then - ocf_log err "Initialization failed: $rc"; - exit $OCF_ERR_GENERIC - fi - chown -R $OCF_RESKEY_user:$OCF_RESKEY_group $OCF_RESKEY_datadir - fi - - pid_dir=`dirname $OCF_RESKEY_pid` - if [ ! -d $pid_dir ] ; then - ocf_log info "Creating PID dir: $pid_dir" - mkdir -p $pid_dir - chown $OCF_RESKEY_user:$OCF_RESKEY_group $pid_dir - fi - - socket_dir=`dirname $OCF_RESKEY_socket` - if [ ! -d $socket_dir ] ; then - ocf_log info "Creating socket dir: $socket_dir" - mkdir -p $socket_dir - chown $OCF_RESKEY_user:$OCF_RESKEY_group $socket_dir - fi - - # Regardless of whether we just created the directory or it - # already existed, check whether it is writable by the configured - # user - for dir in $pid_dir $socket_dir; do - if ! su -s /bin/sh - $OCF_RESKEY_user -c "test -w $dir"; then - ocf_log err "Directory $dir is not writable by $OCF_RESKEY_user" - exit $OCF_ERR_PERM; - fi - done + mysql_common_prepare_dirs # Uncomment to perform permission clensing # - not convinced this should be enabled by default @@ -958,34 +778,11 @@ mysql_start() { mysql_extra_params="--skip-slave-start" fi - ${OCF_RESKEY_binary} --defaults-file=$OCF_RESKEY_config \ - --pid-file=$OCF_RESKEY_pid \ - --socket=$OCF_RESKEY_socket \ - --datadir=$OCF_RESKEY_datadir \ - --log-error=$OCF_RESKEY_log \ - --user=$OCF_RESKEY_user $OCF_RESKEY_additional_parameters \ - $mysql_extra_params >/dev/null 2>&1 & - pid=$! - - # Spin waiting for the server to come up. - # Let the CRM/LRM time us out if required. - start_wait=1 - while [ $start_wait = 1 ]; do - if ! ps $pid > /dev/null 2>&1; then - wait $pid - ocf_log err "MySQL server failed to start (rc=$?), please check your installation" - return $OCF_ERR_GENERIC - fi - mysql_status info - rc=$? - if [ $rc = $OCF_SUCCESS ]; then - start_wait=0 - elif [ $rc != $OCF_NOT_RUNNING ]; then - ocf_log info "MySQL start failed: $rc" - return $rc - fi - sleep 2 - done + mysql_common_start $mysql_extra_params + rc=$? + if [ $rc != $OCF_SUCCESS ]; then + return $rc + fi if ocf_is_ms; then # We're configured as a stateful resource. We must start as @@ -1035,7 +832,6 @@ mysql_start() { } mysql_stop() { - if ocf_is_ms; then # clear preference for becoming master $CRM_MASTER -D @@ -1044,52 +840,13 @@ mysql_stop() { set_reader_attr 0 fi - if [ ! -f $OCF_RESKEY_pid ]; then - ocf_log info "MySQL is not running" - return $OCF_SUCCESS - fi - - pid=`cat $OCF_RESKEY_pid 2> /dev/null ` - /bin/kill $pid > /dev/null - rc=$? - if [ $rc != 0 ]; then - ocf_log err "MySQL couldn't be stopped" - return $OCF_ERR_GENERIC - fi - # stop waiting - shutdown_timeout=15 - if [ -n "$OCF_RESKEY_CRM_meta_timeout" ]; then - shutdown_timeout=$((($OCF_RESKEY_CRM_meta_timeout/1000)-5)) - fi - count=0 - while [ $count -lt $shutdown_timeout ] - do - mysql_status info - rc=$? - if [ $rc = $OCF_NOT_RUNNING ]; then - break - fi - count=`expr $count + 1` - sleep 1 - ocf_log debug "MySQL still hasn't stopped yet. Waiting..." - done - - mysql_status info - if [ $? != $OCF_NOT_RUNNING ]; then - ocf_log info "MySQL failed to stop after ${shutdown_timeout}s using SIGTERM. Trying SIGKILL..." - /bin/kill -KILL $pid > /dev/null - fi - - ocf_log info "MySQL stopped"; - rm -f /var/lock/subsys/mysqld - rm -f $OCF_RESKEY_socket - return $OCF_SUCCESS + mysql_common_stop } mysql_promote() { local master_info - if ( ! mysql_status err ); then + if ( ! mysql_common_status err ); then return $OCF_NOT_RUNNING fi ocf_run $MYSQL $MYSQL_OPTIONS_REPL \ @@ -1115,7 +872,7 @@ mysql_promote() { } mysql_demote() { - if ! mysql_status err; then + if ! mysql_common_status err; then return $OCF_NOT_RUNNING fi @@ -1244,7 +1001,7 @@ case "$1" in exit $OCF_SUCCESS;; esac -mysql_validate +mysql_common_validate rc=$? LSB_STATUS_STOPPED=3 if [ $rc -ne 0 ]; then @@ -1260,7 +1017,7 @@ fi case "$1" in start) mysql_start;; stop) mysql_stop;; - status) mysql_status err;; + status) mysql_common_status err;; monitor) mysql_monitor;; promote) mysql_promote;; demote) mysql_demote;; diff --git a/heartbeat/mysql-common.sh b/heartbeat/mysql-common.sh new file mode 100644 index 0000000..5b6a991 --- /dev/null +++ b/heartbeat/mysql-common.sh @@ -0,0 +1,279 @@ +#!/bin/sh + +####################################################################### + +# Attempt to detect a default binary +OCF_RESKEY_binary_default=$(which mysqld_safe 2> /dev/null) +if [ "$OCF_RESKEY_binary_default" = "" ]; then + OCF_RESKEY_binary_default=$(which safe_mysqld 2> /dev/null) +fi + +# Fill in some defaults if no values are specified +HOSTOS=`uname` +if [ "X${HOSTOS}" = "XOpenBSD" ];then + if [ "$OCF_RESKEY_binary_default" = "" ]; then + OCF_RESKEY_binary_default="/usr/local/bin/mysqld_safe" + fi + OCF_RESKEY_config_default="/etc/my.cnf" + OCF_RESKEY_datadir_default="/var/mysql" + OCF_RESKEY_user_default="_mysql" + OCF_RESKEY_group_default="_mysql" + OCF_RESKEY_log_default="/var/log/mysqld.log" + OCF_RESKEY_pid_default="/var/mysql/mysqld.pid" + OCF_RESKEY_socket_default="/var/run/mysql/mysql.sock" +else + if [ "$OCF_RESKEY_binary_default" = "" ]; then + OCF_RESKEY_binary_default="/usr/bin/safe_mysqld" + fi + OCF_RESKEY_config_default="/etc/my.cnf" + OCF_RESKEY_datadir_default="/var/lib/mysql" + OCF_RESKEY_user_default="mysql" + OCF_RESKEY_group_default="mysql" + OCF_RESKEY_log_default="/var/log/mysqld.log" + OCF_RESKEY_pid_default="/var/run/mysql/mysqld.pid" + OCF_RESKEY_socket_default="/var/lib/mysql/mysql.sock" +fi +OCF_RESKEY_client_binary_default="mysql" +OCF_RESKEY_test_user_default="root" +OCF_RESKEY_test_table_default="mysql.user" +OCF_RESKEY_test_passwd_default="" +OCF_RESKEY_enable_creation_default=0 +OCF_RESKEY_additional_parameters_default="" +OCF_RESKEY_replication_port_default="3306" +OCF_RESKEY_max_slave_lag_default="3600" +OCF_RESKEY_evict_outdated_slaves_default="false" +OCF_RESKEY_reader_attribute_default="readable" + +: ${OCF_RESKEY_binary=${OCF_RESKEY_binary_default}} +MYSQL_BINDIR=`dirname ${OCF_RESKEY_binary}` + +: ${OCF_RESKEY_client_binary=${OCF_RESKEY_client_binary_default}} + +: ${OCF_RESKEY_config=${OCF_RESKEY_config_default}} +: ${OCF_RESKEY_datadir=${OCF_RESKEY_datadir_default}} + +: ${OCF_RESKEY_user=${OCF_RESKEY_user_default}} +: ${OCF_RESKEY_group=${OCF_RESKEY_group_default}} + +: ${OCF_RESKEY_log=${OCF_RESKEY_log_default}} +: ${OCF_RESKEY_pid=${OCF_RESKEY_pid_default}} +: ${OCF_RESKEY_socket=${OCF_RESKEY_socket_default}} + +: ${OCF_RESKEY_test_user=${OCF_RESKEY_test_user_default}} +: ${OCF_RESKEY_test_table=${OCF_RESKEY_test_table_default}} +: ${OCF_RESKEY_test_passwd=${OCF_RESKEY_test_passwd_default}} + +: ${OCF_RESKEY_enable_creation=${OCF_RESKEY_enable_creation_default}} +: ${OCF_RESKEY_additional_parameters=${OCF_RESKEY_additional_parameters_default}} + +: ${OCF_RESKEY_replication_user=${OCF_RESKEY_replication_user_default}} +: ${OCF_RESKEY_replication_passwd=${OCF_RESKEY_replication_passwd_default}} +: ${OCF_RESKEY_replication_port=${OCF_RESKEY_replication_port_default}} + +: ${OCF_RESKEY_max_slave_lag=${OCF_RESKEY_max_slave_lag_default}} +: ${OCF_RESKEY_evict_outdated_slaves=${OCF_RESKEY_evict_outdated_slaves_default}} + +: ${OCF_RESKEY_reader_attribute=${OCF_RESKEY_reader_attribute_default}} + +####################################################################### +# Convenience variables + +MYSQL=$OCF_RESKEY_client_binary +MYSQL_OPTIONS_LOCAL="-S $OCF_RESKEY_socket --connect_timeout=10" +MYSQL_OPTIONS_REPL="$MYSQL_OPTIONS_LOCAL --user=$OCF_RESKEY_replication_user --password=$OCF_RESKEY_replication_passwd" +MYSQL_OPTIONS_TEST="$MYSQL_OPTIONS_LOCAL --user=$OCF_RESKEY_test_user --password=$OCF_RESKEY_test_passwd" +MYSQL_TOO_MANY_CONN_ERR=1040 + +CRM_MASTER="${HA_SBIN_DIR}/crm_master -l reboot " +NODENAME=$(ocf_local_nodename) +CRM_ATTR="${HA_SBIN_DIR}/crm_attribute -N $NODENAME " +INSTANCE_ATTR_NAME=`echo ${OCF_RESOURCE_INSTANCE}| awk -F : '{print $1}'` +CRM_ATTR_REPL_INFO="${HA_SBIN_DIR}/crm_attribute --type crm_config --name ${INSTANCE_ATTR_NAME}_REPL_INFO -s mysql_replication" + +####################################################################### + +mysql_common_validate() +{ + check_binary $OCF_RESKEY_binary + check_binary $OCF_RESKEY_client_binary + + if [ ! -f $OCF_RESKEY_config ]; then + ocf_log err "Config $OCF_RESKEY_config doesn't exist"; + return $OCF_ERR_INSTALLED; + fi + + if [ ! -d $OCF_RESKEY_datadir ]; then + ocf_log err "Datadir $OCF_RESKEY_datadir doesn't exist"; + return $OCF_ERR_INSTALLED; + fi + + getent passwd $OCF_RESKEY_user >/dev/null 2>&1 + if [ ! $? -eq 0 ]; then + ocf_log err "User $OCF_RESKEY_user doesn't exit"; + return $OCF_ERR_INSTALLED; + fi + + getent group $OCF_RESKEY_group >/dev/null 2>&1 + if [ ! $? -eq 0 ]; then + ocf_log err "Group $OCF_RESKEY_group doesn't exist"; + return $OCF_ERR_INSTALLED; + fi + + return $OCF_SUCCESS +} + +mysql_common_status() { + local loglevel=$1 + local pid=$2 + if [ -z "$pid" ]; then + if [ ! -e $OCF_RESKEY_pid ]; then + ocf_log $loglevel "MySQL is not running" + return $OCF_NOT_RUNNING; + fi + + pid=`cat $OCF_RESKEY_pid`; + fi + if [ -d /proc -a -d /proc/1 ]; then + [ "u$pid" != "u" -a -d /proc/$pid ] + else + kill -s 0 $pid >/dev/null 2>&1 + fi + + if [ $? -eq 0 ]; then + return $OCF_SUCCESS; + else + ocf_log $loglevel "MySQL not running: removing old PID file" + rm -f $OCF_RESKEY_pid + return $OCF_NOT_RUNNING; + fi +} + +mysql_common_prepare_dirs() +{ + local rc + + touch $OCF_RESKEY_log + chown $OCF_RESKEY_user:$OCF_RESKEY_group $OCF_RESKEY_log + chmod 0640 $OCF_RESKEY_log + [ -x /sbin/restorecon ] && /sbin/restorecon $OCF_RESKEY_log + + if ocf_is_true "$OCF_RESKEY_enable_creation" && [ ! -d $OCF_RESKEY_datadir/mysql ] ; then + ocf_log info "Initializing MySQL database: " + $MYSQL_BINDIR/mysql_install_db --datadir=$OCF_RESKEY_datadir + rc=$? + if [ $rc -ne 0 ] ; then + ocf_log err "Initialization failed: $rc"; + exit $OCF_ERR_GENERIC + fi + chown -R $OCF_RESKEY_user:$OCF_RESKEY_group $OCF_RESKEY_datadir + fi + + pid_dir=`dirname $OCF_RESKEY_pid` + if [ ! -d $pid_dir ] ; then + ocf_log info "Creating PID dir: $pid_dir" + mkdir -p $pid_dir + chown $OCF_RESKEY_user:$OCF_RESKEY_group $pid_dir + fi + + socket_dir=`dirname $OCF_RESKEY_socket` + if [ ! -d $socket_dir ] ; then + ocf_log info "Creating socket dir: $socket_dir" + mkdir -p $socket_dir + chown $OCF_RESKEY_user:$OCF_RESKEY_group $socket_dir + fi + + # Regardless of whether we just created the directory or it + # already existed, check whether it is writable by the configured + # user + for dir in $pid_dir $socket_dir; do + if ! su -s /bin/sh - $OCF_RESKEY_user -c "test -w $dir"; then + ocf_log err "Directory $dir is not writable by $OCF_RESKEY_user" + exit $OCF_ERR_PERM; + fi + done +} + +mysql_common_start() +{ + local mysql_extra_params="$1" + local pid + + ${OCF_RESKEY_binary} --defaults-file=$OCF_RESKEY_config \ + --pid-file=$OCF_RESKEY_pid \ + --socket=$OCF_RESKEY_socket \ + --datadir=$OCF_RESKEY_datadir \ + --log-error=$OCF_RESKEY_log \ + --user=$OCF_RESKEY_user $OCF_RESKEY_additional_parameters \ + $mysql_extra_params >/dev/null 2>&1 & + pid=$! + + # Spin waiting for the server to come up. + # Let the CRM/LRM time us out if required. + start_wait=1 + while [ $start_wait = 1 ]; do + if ! ps $pid > /dev/null 2>&1; then + wait $pid + ocf_log err "MySQL server failed to start (pid=$pid) (rc=$?), please check your installation" + return $OCF_ERR_GENERIC + fi + mysql_common_status info + rc=$? + if [ $rc = $OCF_SUCCESS ]; then + start_wait=0 + elif [ $rc != $OCF_NOT_RUNNING ]; then + ocf_log info "MySQL start failed: $rc" + return $rc + fi + sleep 2 + done + + return $OCF_SUCCESS +} + +mysql_common_stop() +{ + local pid + local rc + + if [ ! -f $OCF_RESKEY_pid ]; then + ocf_log info "MySQL is not running" + return $OCF_SUCCESS + fi + + pid=`cat $OCF_RESKEY_pid 2> /dev/null ` + /bin/kill $pid > /dev/null + rc=$? + if [ $rc != 0 ]; then + ocf_log err "MySQL couldn't be stopped" + return $OCF_ERR_GENERIC + fi + # stop waiting + shutdown_timeout=15 + if [ -n "$OCF_RESKEY_CRM_meta_timeout" ]; then + shutdown_timeout=$((($OCF_RESKEY_CRM_meta_timeout/1000)-5)) + fi + count=0 + while [ $count -lt $shutdown_timeout ] + do + mysql_common_status info $pid + rc=$? + if [ $rc = $OCF_NOT_RUNNING ]; then + break + fi + count=`expr $count + 1` + sleep 1 + ocf_log debug "MySQL still hasn't stopped yet. Waiting..." + done + + mysql_common_status info $pid + if [ $? != $OCF_NOT_RUNNING ]; then + ocf_log info "MySQL failed to stop after ${shutdown_timeout}s using SIGTERM. Trying SIGKILL..." + /bin/kill -KILL $pid > /dev/null + fi + + ocf_log info "MySQL stopped"; + rm -f /var/lock/subsys/mysqld + rm -f $OCF_RESKEY_socket + return $OCF_SUCCESS + +} diff --git a/heartbeat/ocf-shellfuncs.in b/heartbeat/ocf-shellfuncs.in index 8820961..254da57 100644 --- a/heartbeat/ocf-shellfuncs.in +++ b/heartbeat/ocf-shellfuncs.in @@ -518,6 +518,7 @@ ocf_local_nodename() { which pacemakerd > /dev/null 2>&1 if [ $? -eq 0 ]; then local version=$(pacemakerd -$ | grep "Pacemaker .*" | awk '{ print $2 }') + version=$(echo $version | awk -F- '{ print $1 }') ocf_version_cmp "$version" "1.1.8" if [ $? -eq 2 ]; then which crm_node > /dev/null 2>&1 -- 1.8.4.2