c495267330
Move OCF scripts to module_name/files/ocf dir due to http://osdir.com/ml/openstack-dev/2014-10/msg00959.html Change-Id: I26735f7439c7df9968fb0d211e694d6aee77693c Closes-Bug: 1386117
1297 lines
42 KiB
Bash
Executable File
1297 lines
42 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
#
|
|
# MySQL
|
|
#
|
|
# Description: Manages a MySQL database as Linux-HA resource
|
|
#
|
|
# Authors: Alan Robertson: DB2 Script
|
|
# Jakub Janczak: rewrite as MySQL
|
|
# Andrew Beekhof: cleanup and import
|
|
# Sebastian Reitenbach: add OpenBSD defaults, more cleanup
|
|
# Narayan Newton: add Gentoo/Debian defaults
|
|
# Marian Marinov, Florian Haas: add replication capability
|
|
# Yves Trudeau, Baron Schwartz: add VIP support and improve replication
|
|
#
|
|
# Support: linux-ha@lists.linux-ha.org
|
|
# License: GNU General Public License (GPL)
|
|
#
|
|
# (c) 2002-2005 International Business Machines, Inc.
|
|
# 2005-2010 Linux-HA contributors
|
|
#
|
|
# An example usage in /etc/ha.d/haresources:
|
|
# node1 10.0.0.170 mysql
|
|
#
|
|
# See usage() function below for more details...
|
|
#
|
|
# OCF instance parameters:
|
|
# OCF_RESKEY_binary
|
|
# OCF_RESKEY_client_binary
|
|
# OCF_RESKEY_config
|
|
# OCF_RESKEY_datadir
|
|
# OCF_RESKEY_user
|
|
# OCF_RESKEY_group
|
|
# OCF_RESKEY_test_table
|
|
# OCF_RESKEY_test_user
|
|
# OCF_RESKEY_test_passwd
|
|
# OCF_RESKEY_enable_creation
|
|
# OCF_RESKEY_additional_parameters
|
|
# OCF_RESKEY_log
|
|
# OCF_RESKEY_pid
|
|
# OCF_RESKEY_socket
|
|
# OCF_RESKEY_replication_user
|
|
# OCF_RESKEY_replication_passwd
|
|
# OCF_RESKEY_replication_port
|
|
# OCF_RESKEY_max_slave_lag
|
|
# OCF_RESKEY_evict_outdated_slaves
|
|
# OCF_RESKEY_reader_attribute
|
|
|
|
#######################################################################
|
|
# Initialization:
|
|
|
|
: ${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="${HA_RSCTMP}/p_${__SCRIPT_NAME}/p_${__SCRIPT_NAME}.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="${HA_RSCTMP}/p_${__SCRIPT_NAME}/p_${__SCRIPT_NAME}.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
|
|
MYSQL_TABLE_DOESNT_EXIST=1146
|
|
|
|
CRM_MASTER="${HA_SBIN_DIR}/crm_master -l reboot "
|
|
HOSTNAME=`uname -n`
|
|
CRM_ATTR="${HA_SBIN_DIR}/crm_attribute -N $HOSTNAME "
|
|
INSTANCE_ATTR_NAME=`echo ${OCF_RESOURCE_INSTANCE}| awk -F : '{print $1}'`
|
|
#Track replication per-slave, not globally
|
|
MASTER_HOST=`echo $OCF_RESKEY_CRM_meta_notify_master_uname|tr -d " "`
|
|
|
|
CRM_ATTR_REPL_INFO="${HA_SBIN_DIR}/crm_attribute --type crm_config --name ${INSTANCE_ATTR_NAME}_REPL_INFO -s mysql_replication"
|
|
|
|
#######################################################################
|
|
|
|
usage() {
|
|
cat <<UEND
|
|
usage: $0 (start|stop|validate-all|meta-data|monitor|promote|demote|notify)
|
|
|
|
$0 manages a MySQL Database as an HA resource.
|
|
|
|
The 'start' operation starts the database.
|
|
The 'stop' operation stops the database.
|
|
The 'status' operation reports whether the database is running
|
|
The 'monitor' operation reports whether the database seems to be working
|
|
The 'promote' operation makes this mysql server run as master
|
|
The 'demote' operation makes this mysql server run as slave
|
|
The 'validate-all' operation reports whether the parameters are valid
|
|
|
|
UEND
|
|
}
|
|
|
|
meta_data() {
|
|
cat <<END
|
|
<?xml version="1.0"?>
|
|
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
|
|
<resource-agent name="mysql">
|
|
<version>1.0</version>
|
|
|
|
<longdesc lang="en">
|
|
Resource script for MySQL.
|
|
May manage a standalone MySQL database, a clone set with externally
|
|
managed replication, or a complete master/slave replication setup.
|
|
|
|
While managing replication, the default behavior is to use uname -n
|
|
values in the change master to command. Other IPs can be specified
|
|
manually by adding a node attribute \${INSTANCE_ATTR_NAME}_mysql_master_IP
|
|
giving the IP to use for replication. For example, if the mysql primitive
|
|
you are using is p_mysql, the attribute to set will be
|
|
p_mysql_mysql_master_IP.
|
|
</longdesc>
|
|
<shortdesc lang="en">Manages a MySQL database instance</shortdesc>
|
|
<parameters>
|
|
|
|
<parameter name="binary" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
Location of the MySQL server binary
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL server binary</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_binary_default}" />
|
|
</parameter>
|
|
|
|
<parameter name="client_binary" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
Location of the MySQL client binary
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL client binary</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_client_binary_default}" />
|
|
</parameter>
|
|
|
|
<parameter name="config" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
Configuration file
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL config</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_config_default}" />
|
|
</parameter>
|
|
|
|
<parameter name="datadir" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
Directory containing databases
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL datadir</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_datadir_default}" />
|
|
</parameter>
|
|
|
|
<parameter name="user" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
User running MySQL daemon
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL user</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_user_default}" />
|
|
</parameter>
|
|
|
|
<parameter name="group" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
Group running MySQL daemon (for logfile and directory permissions)
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL group</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_group_default}"/>
|
|
</parameter>
|
|
|
|
<parameter name="log" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
The logfile to be used for mysqld.
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL log file</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_log_default}"/>
|
|
</parameter>
|
|
|
|
<parameter name="pid" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
The pidfile to be used for mysqld.
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL pid file</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_pid_default}"/>
|
|
</parameter>
|
|
|
|
<parameter name="socket" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
The socket to be used for mysqld.
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL socket</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_socket_default}"/>
|
|
</parameter>
|
|
|
|
<parameter name="test_table" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
Table to be tested in monitor statement (in database.table notation)
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL test table</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_test_table_default}" />
|
|
</parameter>
|
|
|
|
<parameter name="test_user" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
MySQL test user, must have select privilege on test_table
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL test user</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_test_user_default}" />
|
|
</parameter>
|
|
|
|
<parameter name="test_passwd" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
MySQL test user password
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL test user password</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_test_passwd_default}" />
|
|
</parameter>
|
|
|
|
<parameter name="enable_creation" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
If the MySQL database does not exist, it will be created
|
|
</longdesc>
|
|
<shortdesc lang="en">Create the database if it does not exist</shortdesc>
|
|
<content type="boolean" default="${OCF_RESKEY_enable_creation_default}"/>
|
|
</parameter>
|
|
|
|
<parameter name="additional_parameters" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
Additional parameters which are passed to the mysqld on startup.
|
|
(e.g. --skip-external-locking or --skip-grant-tables)
|
|
</longdesc>
|
|
<shortdesc lang="en">Additional parameters to pass to mysqld</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_additional_parameters_default}"/>
|
|
</parameter>
|
|
|
|
<parameter name="replication_user" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
MySQL replication user. This user is used for starting and stopping
|
|
MySQL replication, for setting and resetting the master host, and for
|
|
setting and unsetting read-only mode. Because of that, this user must
|
|
have SUPER, REPLICATION SLAVE, REPLICATION CLIENT, and PROCESS
|
|
privileges on all nodes within the cluster. Mandatory if you define
|
|
a master-slave resource.
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL replication user</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_replication_user_default}" />
|
|
</parameter>
|
|
|
|
<parameter name="replication_passwd" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
MySQL replication password. Used for replication client and slave.
|
|
Mandatory if you define a master-slave resource.
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL replication user password</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_replication_passwd_default}" />
|
|
</parameter>
|
|
|
|
<parameter name="replication_port" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
The port on which the Master MySQL instance is listening.
|
|
</longdesc>
|
|
<shortdesc lang="en">MySQL replication port</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_replication_port_default}" />
|
|
</parameter>
|
|
|
|
<parameter name="max_slave_lag" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
The maximum number of seconds a replication slave is allowed to lag
|
|
behind its master. Do not set this to zero. What the cluster manager
|
|
does in case a slave exceeds this maximum lag is determined by the
|
|
evict_outdated_slaves parameter.
|
|
</longdesc>
|
|
<shortdesc lang="en">Maximum time (seconds) a MySQL slave is allowed
|
|
to lag behind a master</shortdesc>
|
|
<content type="integer" default="${OCF_RESKEY_max_slave_lag_default}"/>
|
|
</parameter>
|
|
|
|
<parameter name="evict_outdated_slaves" unique="0" required="0">
|
|
<longdesc lang="en">
|
|
If set to true, any slave which is more than max_slave_lag seconds
|
|
behind the master has its MySQL instance shut down. If this parameter
|
|
is set to false in a primitive or clone resource, it is simply
|
|
ignored. If set to false in a master/slave resource, then exceeding
|
|
the maximum slave lag will merely push down the master preference so
|
|
the lagging slave is never promoted to the new master.
|
|
</longdesc>
|
|
<shortdesc lang="en">Determines whether to shut down badly lagging
|
|
slaves</shortdesc>
|
|
<content type="boolean" default="${OCF_RESKEY_evict_outdated_slaves_default}" />
|
|
</parameter>
|
|
|
|
<parameter name="reader_attribute" unique="1" required="0">
|
|
<longdesc lang="en">
|
|
An attribute that the RA can manage to specify whether a node
|
|
can be read from. This node attribute will be 1 if it's fine to
|
|
read from the node, and 0 otherwise (for example, when a slave
|
|
has lagged too far behind the master).
|
|
|
|
A typical example for the use of this attribute would be to tie
|
|
a set of IP addresses to MySQL slaves that can be read from.
|
|
|
|
This parameter is only meaningful in master/slave set configurations.
|
|
</longdesc>
|
|
<shortdesc lang="en">Sets the node attribute that determines
|
|
whether a node is usable for clients to read from.</shortdesc>
|
|
<content type="string" default="${OCF_RESKEY_reader_attribute_default}" />
|
|
</parameter>
|
|
</parameters>
|
|
|
|
<actions>
|
|
<action name="start" timeout="120" />
|
|
<action name="stop" timeout="120" />
|
|
<action name="status" timeout="60" />
|
|
<action name="monitor" depth="0" timeout="30" interval="20" />
|
|
<action name="monitor" role="Master" depth="0" timeout="30" interval="10" />
|
|
<action name="monitor" role="Slave" depth="0" timeout="30" interval="30" />
|
|
<action name="promote" timeout="120" />
|
|
<action name="demote" timeout="120" />
|
|
<action name="notify" timeout="90" />
|
|
<action name="validate-all" timeout="5" />
|
|
<action name="meta-data" timeout="5" />
|
|
</actions>
|
|
</resource-agent>
|
|
END
|
|
}
|
|
|
|
# Convenience functions
|
|
|
|
set_read_only() {
|
|
# Sets or unsets read-only mode. Accepts one boolean as its
|
|
# optional argument. If invoked without any arguments, defaults to
|
|
# enabling read only mode. Should only be set in master/slave
|
|
# setups.
|
|
# Returns $OCF_SUCCESS if the operation succeeds, or
|
|
# $OCF_ERR_GENERIC if it fails.
|
|
local ro_val
|
|
if ocf_is_true $1; then
|
|
ro_val="on"
|
|
else
|
|
ro_val="off"
|
|
fi
|
|
ocf_run $MYSQL $MYSQL_OPTIONS_REPL \
|
|
-e "SET GLOBAL read_only=${ro_val}"
|
|
}
|
|
|
|
get_read_only() {
|
|
# Check if read-only is set
|
|
local read_only_state
|
|
|
|
read_only_state=`$MYSQL $MYSQL_OPTIONS_REPL \
|
|
-e "SHOW VARIABLES" | grep read_only | awk '{print $2}'`
|
|
|
|
if [ "$read_only_state" = "ON" ]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
is_slave() {
|
|
# Determine whether the machine is currently running as a MySQL
|
|
# slave, as determined per SHOW SLAVE STATUS. Returns 1 if SHOW
|
|
# SLAVE STATUS creates an empty result set, 0 otherwise.
|
|
local rc
|
|
local tmpfile
|
|
|
|
# Check whether this machine should be slave
|
|
if ! ocf_is_ms || ! get_read_only; then
|
|
return 1
|
|
fi
|
|
|
|
get_slave_info
|
|
rc=$?
|
|
|
|
if [ $rc -eq 0 ]; then
|
|
# show slave status is not empty
|
|
# Is there a master_log_file defined? (master_log_file is deleted
|
|
# by reset slave
|
|
if [ "$master_log_file" ]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
else
|
|
# "SHOW SLAVE STATUS" returns an empty set if instance is not a
|
|
# replication slave
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
parse_slave_info() {
|
|
# Extracts field $1 from result of "SHOW SLAVE STATUS\G" from file $2
|
|
sed -ne "s/^.* $1: \(.*\)$/\1/p" < $2
|
|
}
|
|
|
|
get_slave_info() {
|
|
# Warning: this sets $tmpfile and LEAVE this file! You must delete it after use!
|
|
local mysql_options
|
|
|
|
if [ "$master_log_file" -a "$master_host" ]; then
|
|
# variables are already defined, get_slave_info has been run before
|
|
return $OCF_SUCCESS
|
|
else
|
|
tmpfile=`mktemp ${HA_RSCTMP}/check_slave.${OCF_RESOURCE_INSTANCE}.XXXXXX`
|
|
|
|
$MYSQL $MYSQL_OPTIONS_REPL \
|
|
-e 'SHOW SLAVE STATUS\G' > $tmpfile
|
|
|
|
if [ -s $tmpfile ]; then
|
|
master_host=`parse_slave_info Master_Host $tmpfile`
|
|
master_user=`parse_slave_info Master_User $tmpfile`
|
|
master_port=`parse_slave_info Master_Port $tmpfile`
|
|
master_log_file=`parse_slave_info Master_Log_File $tmpfile`
|
|
master_log_pos=`parse_slave_info Read_Master_Log_Pos $tmpfile`
|
|
slave_sql=`parse_slave_info Slave_SQL_Running $tmpfile`
|
|
slave_io=`parse_slave_info Slave_IO_Running $tmpfile`
|
|
last_errno=`parse_slave_info Last_Errno $tmpfile`
|
|
secs_behind=`parse_slave_info Seconds_Behind_Master $tmpfile`
|
|
ocf_log debug "MySQL instance running as a replication slave"
|
|
else
|
|
# Instance produced an empty "SHOW SLAVE STATUS" output --
|
|
# instance is not a slave
|
|
ocf_log err "check_slave invoked on an instance that is not a replication slave."
|
|
return $OCF_ERR_GENERIC
|
|
fi
|
|
|
|
return $OCF_SUCCESS
|
|
fi
|
|
}
|
|
|
|
check_slave() {
|
|
# Checks slave status
|
|
local rc new_master
|
|
|
|
get_slave_info
|
|
rc=$?
|
|
|
|
if [ $rc -eq 0 ]; then
|
|
# Did we receive an error other than max_connections?
|
|
if [ $last_errno -ne 0 -a $last_errno -ne "$MYSQL_TOO_MANY_CONN_ERR" ]; then
|
|
# Whoa. Replication ran into an error. This slave has
|
|
# diverged from its master. Make sure this resource
|
|
# doesn't restart in place.
|
|
ocf_log err "MySQL instance configured for replication, but replication has failed."
|
|
ocf_log err "See $tmpfile for details"
|
|
|
|
# Just pull the reader VIP away, killing MySQL here would be pretty evil
|
|
# on a loaded server
|
|
|
|
set_reader_attr 0
|
|
exit $OCF_SUCCESS
|
|
|
|
fi
|
|
|
|
# # Did we replicate incorrectly?
|
|
# if [ $last_errno -ne 0 -a $last_errno -ne "$MYSQL_TABLE_DOESNT_EXIST" ]; then
|
|
# # Replication ran into an error. This slave didn't start
|
|
# # replication from the beginning.
|
|
# ocf_log err "MySQL instance configured for replication, but replication has failed."
|
|
# ocf_log err "See $tmpfile for details"
|
|
#
|
|
# ocf_run $MYSQL $MYSQL_OPTIONS_REPL \
|
|
# -e "RESET SLAVE;"
|
|
# ocf_RUN $MYSQL $MYSQL_OPTIONS_REPL \
|
|
# ocf_run $MYSQL $MYSQL_OPTIONS_REPL \
|
|
# -e "RESET SLAVE;"
|
|
#
|
|
# exit $OCF_SUCCESS
|
|
#
|
|
# fi
|
|
|
|
# If we got max_connections, let's remove the vip
|
|
if [ $last_errno -eq "$MYSQL_TOO_MANY_CONN_ERR" ]; then
|
|
set_reader_attr 0
|
|
exit $OCF_SUCCESS
|
|
fi
|
|
|
|
if [ "$slave_io" != 'Yes' ]; then
|
|
# Not necessarily a bad thing. The master may have
|
|
# temporarily shut down, and the slave may just be
|
|
# reconnecting. A warning can't hurt, though.
|
|
ocf_log warn "MySQL Slave IO threads currently not running."
|
|
|
|
# Sanity check, are we at least on the right master
|
|
new_master=`$CRM_ATTR_REPL_INFO --query -q | cut -d'|' -f1`
|
|
|
|
if [ "$master_host" != "$new_master" ]; then
|
|
# Not pointing to the right master, not good, removing the VIPs
|
|
set_reader_attr 0
|
|
|
|
exit $OCF_SUCCESS
|
|
fi
|
|
|
|
fi
|
|
|
|
if [ "$slave_sql" != 'Yes' ]; then
|
|
# We don't have a replication SQL thread running. Not a
|
|
# good thing. Try to recoved by restarting the SQL thread
|
|
# and remove reader vip. Prevent MySQL restart.
|
|
ocf_log err "MySQL Slave SQL threads currently not running."
|
|
ocf_log err "See $tmpfile for details"
|
|
|
|
# Remove reader vip
|
|
set_reader_attr 0
|
|
|
|
# try to restart slave
|
|
ocf_run $MYSQL $MYSQL_OPTIONS_REPL \
|
|
-e "START SLAVE"
|
|
|
|
# Return success to prevent a restart
|
|
exit $OCF_SUCCESS
|
|
fi
|
|
|
|
if ocf_is_true $OCF_RESKEY_evict_outdated_slaves; then
|
|
# We're supposed to bail out if we lag too far
|
|
# behind. Let's check our lag.
|
|
if [ $secs_behind -gt $OCF_RESKEY_max_slave_lag ]; then
|
|
ocf_log err "MySQL Slave is $secs_behind seconds behind master (allowed maximum: $OCF_RESKEY_max_slave_lag)."
|
|
ocf_log err "See $tmpfile for details"
|
|
|
|
# Remove reader vip
|
|
set_reader_attr 0
|
|
|
|
exit $OCF_ERR_INSTALLED
|
|
fi
|
|
elif ocf_is_ms; then
|
|
# Even if we're not set to evict lagging slaves, we can
|
|
# still use the seconds behind master value to set our
|
|
# master preference.
|
|
local master_pref
|
|
master_pref=$((${OCF_RESKEY_max_slave_lag}-${secs_behind}))
|
|
if [ $master_pref -lt 0 ]; then
|
|
# Sanitize a below-zero preference to just zero
|
|
master_pref=0
|
|
fi
|
|
$CRM_MASTER -v $master_pref
|
|
fi
|
|
|
|
# is the slave ok to have a VIP on it
|
|
if [ $secs_behind -gt $OCF_RESKEY_max_slave_lag ]; then
|
|
set_reader_attr 0
|
|
else
|
|
set_reader_attr 1
|
|
fi
|
|
|
|
ocf_log debug "MySQL instance running as a replication slave"
|
|
rm -f $tmpfile
|
|
else
|
|
# Instance produced an empty "SHOW SLAVE STATUS" output --
|
|
# instance is not a slave
|
|
# TODO: Needs to handle when get_slave_info will return too many connections error
|
|
rm -f $tmpfile
|
|
ocf_log err "check_slave invoked on an instance that is not a replication slave."
|
|
exit $OCF_ERR_GENERIC
|
|
fi
|
|
}
|
|
|
|
set_master() {
|
|
local new_master master_log_file master_log_pos
|
|
local master_params
|
|
|
|
new_master=`$CRM_ATTR_REPL_INFO --query -q | cut -d'|' -f1`
|
|
|
|
# Keep replication position
|
|
get_slave_info
|
|
|
|
if [ "$master_log_file" -a "$new_master" = "$master_host" ]; then
|
|
# master_params=", MASTER_LOG_FILE='$master_log_file', \
|
|
# MASTER_LOG_POS=$master_log_pos"
|
|
ocf_log info "Kept master pos for $master_host : $master_log_file:$master_log_pos"
|
|
rm -f $tmpfile
|
|
return
|
|
else
|
|
master_log_file=`$CRM_ATTR_REPL_INFO --query -q | cut -d'|' -f2`
|
|
master_log_pos=`$CRM_ATTR_REPL_INFO --query -q | cut -d'|' -f3`
|
|
if [ -n "$master_log_file" -a -n "$master_log_pos" ]; then
|
|
master_params=", MASTER_LOG_FILE='$master_log_file', \
|
|
MASTER_LOG_POS=$master_log_pos"
|
|
ocf_log info "Restored master pos for $new_master : $master_log_file:$master_log_pos"
|
|
fi
|
|
fi
|
|
|
|
# Informs the MySQL server of the master to replicate
|
|
# from. Accepts one mandatory argument which must contain the host
|
|
# name of the new master host. The master must either be unchanged
|
|
# from the laste master the slave replicated from, or freshly
|
|
# reset with RESET MASTER.
|
|
|
|
ocf_run $MYSQL $MYSQL_OPTIONS_REPL \
|
|
-e "CHANGE MASTER TO MASTER_HOST='$new_master', \
|
|
MASTER_USER='$OCF_RESKEY_replication_user', \
|
|
MASTER_PASSWORD='$OCF_RESKEY_replication_passwd' $master_params"
|
|
rm -f $tmpfile
|
|
}
|
|
|
|
unset_master(){
|
|
# Instructs the MySQL server to stop replicating from a master
|
|
# host.
|
|
|
|
# If we're currently not configured to be replicating from any
|
|
# host, then there's nothing to do. But we do log a warning as
|
|
# no-one but the CRM should be touching the MySQL master/slave
|
|
# configuration.
|
|
if ! is_slave; then
|
|
ocf_log warn "Attempted to unset the replication master on an instance that is not configured as a replication slave"
|
|
return $OCF_SUCCESS
|
|
fi
|
|
|
|
local tmpfile
|
|
tmpfile=`mktemp ${HA_RSCTMP}/unset_master.${OCF_RESOURCE_INSTANCE}.XXXXXX`
|
|
|
|
# At this point, the master is read only so there should not be much binlogs to transfer
|
|
# Let's wait for the last bits
|
|
while true; do
|
|
$MYSQL $MYSQL_OPTIONS_REPL \
|
|
-e 'SHOW PROCESSLIST\G' > $tmpfile
|
|
if grep -i 'Waiting for master to send event' $tmpfile >/dev/null; then
|
|
ocf_log info "MySQL slave has finished reading master binary log"
|
|
break
|
|
fi
|
|
if grep -i 'Reconnecting after a failed master event read' $tmpfile >/dev/null; then
|
|
ocf_log info "Master is down, no more binary logs to come"
|
|
break
|
|
fi
|
|
if grep -i 'Connecting to master' $tmpfile >/dev/null; then
|
|
ocf_log info "Master is down, no more binary logs to come"
|
|
break
|
|
fi
|
|
if ! grep 'system user' $tmpfile >/dev/null; then
|
|
ocf_log info "Slave is not running - not waiting to finish"
|
|
break
|
|
fi
|
|
|
|
sleep 1
|
|
done
|
|
|
|
# Now, stop the slave I/O thread and wait for relay log
|
|
# processing to complete
|
|
ocf_run $MYSQL $MYSQL_OPTIONS_REPL \
|
|
-e "STOP SLAVE IO_THREAD"
|
|
if [ $? -gt 0 ]; then
|
|
ocf_log err "Error stopping slave IO thread"
|
|
exit $OCF_ERR_GENERIC
|
|
fi
|
|
|
|
while true; do
|
|
$MYSQL $MYSQL_OPTIONS_REPL \
|
|
-e 'SHOW PROCESSLIST\G' > $tmpfile
|
|
if grep -i 'Has read all relay log' $tmpfile >/dev/null; then
|
|
ocf_log info "MySQL slave has finished processing relay log"
|
|
break
|
|
fi
|
|
if ! grep -q 'system user' $tmpfile; then
|
|
ocf_log info "Slave not runnig - not waiting to finish"
|
|
break
|
|
fi
|
|
ocf_log info "Waiting for MySQL slave to finish processing relay log"
|
|
sleep 1
|
|
done
|
|
rm -f $tmpfile
|
|
|
|
# Now, stop all slave activity and unset the master host
|
|
ocf_run $MYSQL $MYSQL_OPTIONS_REPL \
|
|
-e "STOP SLAVE"
|
|
if [ $? -gt 0 ]; then
|
|
ocf_log err "Error stopping rest slave threads"
|
|
exit $OCF_ERR_GENERIC
|
|
fi
|
|
|
|
ocf_run $MYSQL $MYSQL_OPTIONS_REPL \
|
|
-e "RESET SLAVE;"
|
|
if [ $? -gt 0 ]; then
|
|
ocf_log err "Failed to reset slave"
|
|
exit $OCF_ERR_GENERIC
|
|
fi
|
|
}
|
|
|
|
# Start replication as slave
|
|
start_slave() {
|
|
|
|
ocf_run $MYSQL $MYSQL_OPTIONS_REPL \
|
|
-e "START SLAVE"
|
|
}
|
|
|
|
# Set the attribute controlling the readers VIP
|
|
set_reader_attr() {
|
|
local curr_attr_value
|
|
|
|
curr_attr_value=$(get_reader_attr)
|
|
|
|
if [ "$curr_attr_value" -ne "$1" ]; then
|
|
$CRM_ATTR -l reboot --name ${OCF_RESKEY_reader_attribute} -v $1
|
|
fi
|
|
|
|
}
|
|
|
|
# get the attribute controlling the readers VIP
|
|
get_reader_attr() {
|
|
local attr_value
|
|
local rc
|
|
|
|
attr_value=`$CRM_ATTR -l reboot --name ${OCF_RESKEY_reader_attribute} --query -q`
|
|
rc=$?
|
|
if [ "$rc" -eq "0" ]; then
|
|
echo $attr_value
|
|
else
|
|
echo -1
|
|
fi
|
|
|
|
}
|
|
|
|
# Stores data for MASTER STATUS from MySQL
|
|
update_data_master_status() {
|
|
|
|
master_status_file="${HA_RSCTMP}/master_status.${OCF_RESOURCE_INSTANCE}"
|
|
|
|
$MYSQL $MYSQL_OPTIONS_REPL -e "SHOW MASTER STATUS\G" > $master_status_file
|
|
}
|
|
|
|
|
|
# Returns the specified value from the stored copy of SHOW MASTER STATUS.
|
|
# should be call after update_data_master_status for tmpfile
|
|
# Arguments:
|
|
# $1 The value to get.
|
|
get_master_status() {
|
|
awk -v var="$1" '$1 == var ":" {print substr($0, index($0, ":") + 2)}' "$master_status_file"
|
|
}
|
|
|
|
|
|
# Determines what IP address is attached to the current host. The output of the
|
|
# crm_attribute command looks like this:
|
|
# scope=nodes name=IP value=10.2.2.161
|
|
# If the ${INSTANCE_ATTR_NAME}_MYSQL_MASTER_IP node attribute is not defined, fallback is to uname -n
|
|
# The ${INSTANCE_ATTR_NAME}_MYSQL_MASTER_IP is the IP address that will be used for the
|
|
# change master to command.
|
|
get_local_ip() {
|
|
local IP
|
|
IP=`$CRM_ATTR -l forever -n ${INSTANCE_ATTR_NAME}_mysql_master_IP -q -G`
|
|
if [ ! $? -eq 0 ]; then
|
|
uname -n
|
|
else
|
|
echo $IP
|
|
fi
|
|
}
|
|
|
|
#######################################################################
|
|
|
|
# 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"
|
|
|
|
# Set loglevel to info during probe
|
|
if ocf_is_probe; then
|
|
status_loglevel="info"
|
|
fi
|
|
|
|
mysql_status $status_loglevel
|
|
|
|
rc=$?
|
|
|
|
# TODO: check max connections error
|
|
|
|
# If status returned an error, return that immediately
|
|
if [ $rc -ne $OCF_SUCCESS ]; then
|
|
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
|
|
if is_slave; then
|
|
check_slave
|
|
fi
|
|
|
|
# Check for test table
|
|
ocf_run -q $MYSQL $MYSQL_OPTIONS_TEST \
|
|
-e "SELECT COUNT(*) FROM $OCF_RESKEY_test_table"
|
|
rc=$?
|
|
|
|
if [ $rc -ne 0 ]; then
|
|
ocf_log err "Failed to select from $test_table";
|
|
return $OCF_ERR_GENERIC;
|
|
fi
|
|
fi
|
|
|
|
if ocf_is_ms && ! get_read_only; then
|
|
ocf_log debug "MySQL monitor succeeded (master)";
|
|
return $OCF_RUNNING_MASTER
|
|
else
|
|
ocf_log debug "MySQL monitor succeeded";
|
|
return $OCF_SUCCESS
|
|
fi
|
|
}
|
|
|
|
mysql_start() {
|
|
local rc pid
|
|
|
|
if ocf_is_ms; then
|
|
# Initialize the ReaderVIP attribute, monitor will enable it
|
|
set_reader_attr 0
|
|
fi
|
|
|
|
mysql_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
|
|
|
|
# Uncomment to perform permission clensing
|
|
# - not convinced this should be enabled by default
|
|
#
|
|
#chmod 0755 $OCF_RESKEY_datadir
|
|
#chown -R $OCF_RESKEY_user $OCF_RESKEY_datadir
|
|
#chgrp -R $OCF_RESKEY_group $OCF_RESKEY_datadir
|
|
mysql_extra_params=
|
|
if ocf_is_ms; then
|
|
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
|
|
|
|
if ocf_is_ms; then
|
|
# We're configured as a stateful resource. We must start as
|
|
# slave by default. At this point we don't know if the CRM has
|
|
# already promoted a master. So, we simply start in read only
|
|
# mode.
|
|
set_read_only on
|
|
|
|
# Now, let's see whether there is a master. We might be a new
|
|
# node that is just joining the cluster, and the CRM may have
|
|
# promoted a master before.
|
|
master_host=`echo $OCF_RESKEY_CRM_meta_notify_master_uname|tr -d " "`
|
|
if [ "$master_host" -a "$master_host" != ${HOSTNAME} ]; then
|
|
ocf_log info "Changing MySQL configuration to replicate from $master_host."
|
|
set_master
|
|
start_slave
|
|
if [ $? -ne 0 ]; then
|
|
ocf_log err "Failed to start slave"
|
|
return $OCF_ERR_GENERIC
|
|
fi
|
|
else
|
|
ocf_log info "No MySQL master present - clearing replication state"
|
|
unset_master
|
|
fi
|
|
|
|
# We also need to set a master preference, otherwise Pacemaker
|
|
# won't ever promote us in the absence of any explicit
|
|
# preference set by the administrator. We choose a low
|
|
# greater-than-zero preference.
|
|
$CRM_MASTER -v 1
|
|
|
|
fi
|
|
|
|
# Initial monitor action
|
|
if [ -n "$OCF_RESKEY_test_table" -a -n "$OCF_RESKEY_test_user" -a -n "$OCF_RESKEY_test_passwd" ]; then
|
|
OCF_CHECK_LEVEL=10
|
|
fi
|
|
mysql_monitor
|
|
rc=$?
|
|
if [ $rc != $OCF_SUCCESS -a $rc != $OCF_RUNNING_MASTER ]; then
|
|
ocf_log err "Failed initial monitor action"
|
|
return $rc
|
|
fi
|
|
|
|
ocf_log info "MySQL started"
|
|
return $OCF_SUCCESS
|
|
}
|
|
|
|
mysql_stop() {
|
|
|
|
if ocf_is_ms; then
|
|
# clear preference for becoming master
|
|
$CRM_MASTER -D
|
|
|
|
# Remove VIP capability
|
|
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_promote() {
|
|
local master_info
|
|
|
|
if ( ! mysql_status err ); then
|
|
return $OCF_NOT_RUNNING
|
|
fi
|
|
ocf_run $MYSQL $MYSQL_OPTIONS_REPL \
|
|
-e "STOP SLAVE"
|
|
|
|
# Set Master Info in CIB, cluster level attribute
|
|
update_data_master_status
|
|
master_info="$(get_local_ip)|$(get_master_status File)|$(get_master_status Position)"
|
|
${CRM_ATTR_REPL_INFO} -v "$master_info"
|
|
rm -f $tmpfile
|
|
|
|
set_read_only off || return $OCF_ERR_GENERIC
|
|
|
|
# Existing master gets a higher-than-default master preference, so
|
|
# the cluster manager does not shuffle the master role around
|
|
# unnecessarily
|
|
$CRM_MASTER -v $((${OCF_RESKEY_max_slave_lag}+1))
|
|
|
|
# A master can accept reads
|
|
set_reader_attr 1
|
|
|
|
return $OCF_SUCCESS
|
|
}
|
|
|
|
mysql_demote() {
|
|
if ! mysql_status err; then
|
|
return $OCF_NOT_RUNNING
|
|
fi
|
|
|
|
# Return master preference to default, so the cluster manager gets
|
|
# a chance to select a new master
|
|
$CRM_MASTER -v 1
|
|
}
|
|
|
|
mysql_notify() {
|
|
# If not configured as a Stateful resource, we make no sense of
|
|
# notifications.
|
|
if ! ocf_is_ms; then
|
|
ocf_log info "This agent makes no use of notifications unless running in master/slave mode."
|
|
return $OCF_SUCCESS
|
|
fi
|
|
|
|
local type_op
|
|
type_op="${OCF_RESKEY_CRM_meta_notify_type}-${OCF_RESKEY_CRM_meta_notify_operation}"
|
|
|
|
ocf_log debug "Received $type_op notification."
|
|
|
|
case "$type_op" in
|
|
'pre-promote')
|
|
# Nothing to do now here, new replication info not yet published
|
|
|
|
;;
|
|
'post-promote')
|
|
# The master has completed its promotion. Now is a good
|
|
# time to check whether our replication slave is working
|
|
# correctly.
|
|
master_host=`echo $OCF_RESKEY_CRM_meta_notify_promote_uname|tr -d " "`
|
|
if [ "$master_host" = ${HOSTNAME} ]; then
|
|
ocf_log info "This will be the new master, ignoring post-promote notification."
|
|
else
|
|
ocf_log info "Resetting replication"
|
|
unset_master
|
|
if [ $? -ne 0 ]; then
|
|
return $OCF_ERR_GENERIC
|
|
fi
|
|
|
|
ocf_log info "Changing MySQL configuration to replicate from $master_host"
|
|
set_master
|
|
if [ $? -ne 0 ]; then
|
|
return $OCF_ERR_GENERIC
|
|
fi
|
|
|
|
start_slave
|
|
if [ $? -ne 0 ]; then
|
|
ocf_log err "Failed to start slave"
|
|
return $OCF_ERR_GENERIC
|
|
fi
|
|
fi
|
|
return $OCF_SUCCESS
|
|
;;
|
|
'pre-demote')
|
|
demote_host=`echo $OCF_RESKEY_CRM_meta_notify_demote_uname|tr -d " "`
|
|
if [ $demote_host = ${HOSTNAME} ]; then
|
|
ocf_log info "post-demote notification for $demote_host"
|
|
set_read_only on
|
|
if [ $? -ne 0 ]; then
|
|
ocf_log err "Failed to set read-only";
|
|
return $OCF_ERR_GENERIC;
|
|
fi
|
|
|
|
# Must kill all existing user threads because they are still Read/write
|
|
# in order for the slaves to complete the read of binlogs
|
|
local tmpfile
|
|
tmpfile=`mktemp ${HA_RSCTMP}/threads.${OCF_RESOURCE_INSTANCE}.XXXXXX`
|
|
$MYSQL $MYSQL_OPTIONS_REPL \
|
|
-e "SHOW PROCESSLIST" > $tmpfile
|
|
|
|
for thread in `awk '$0 !~ /Binlog Dump|system user|event_scheduler|SHOW PROCESSLIST/ && $0 ~ /^[0-9]/ {print $1}' $tmpfile`
|
|
do
|
|
ocf_run $MYSQL $MYSQL_OPTIONS_REPL \
|
|
-e "KILL ${thread}"
|
|
done
|
|
else
|
|
ocf_log info "Ignoring post-demote notification execpt for my own demotion."
|
|
fi
|
|
return $OCF_SUCCESS
|
|
;;
|
|
'post-demote')
|
|
demote_host=`echo $OCF_RESKEY_CRM_meta_notify_demote_uname|tr -d " "`
|
|
if [ $demote_host = ${HOSTNAME} ]; then
|
|
ocf_log info "Ignoring post-demote notification for my own demotion."
|
|
return $OCF_SUCCESS
|
|
fi
|
|
ocf_log info "post-demote notification for $demote_host."
|
|
# The former master has just been gracefully demoted.
|
|
unset_master
|
|
;;
|
|
*)
|
|
return $OCF_SUCCESS
|
|
;;
|
|
esac
|
|
}
|
|
|
|
#######################################################################
|
|
|
|
|
|
##########################################################################
|
|
# If DEBUG_LOG is set, make this resource agent easy to debug: set up the
|
|
# debug log and direct all output to it. Otherwise, redirect to /dev/null.
|
|
# The log directory must be a directory owned by root, with permissions 0700,
|
|
# and the log must be writable and not a symlink.
|
|
##########################################################################
|
|
DEBUG_LOG="/tmp/mysql.ocf.ra.debug/log"
|
|
if [ "${DEBUG_LOG}" -a -w "${DEBUG_LOG}" -a ! -L "${DEBUG_LOG}" ]; then
|
|
DEBUG_LOG_DIR="${DEBUG_LOG%/*}"
|
|
if [ -d "${DEBUG_LOG_DIR}" ]; then
|
|
exec 9>>"$DEBUG_LOG"
|
|
exec 2>&9
|
|
date >&9
|
|
echo "$*" >&9
|
|
env | grep OCF_ | sort >&9
|
|
set -x
|
|
else
|
|
exec 9>/dev/null
|
|
fi
|
|
fi
|
|
|
|
case "$1" in
|
|
meta-data) meta_data
|
|
exit $OCF_SUCCESS;;
|
|
usage|help) usage
|
|
exit $OCF_SUCCESS;;
|
|
esac
|
|
|
|
mysql_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
|
|
|
|
# What kind of method was invoked?
|
|
case "$1" in
|
|
start) mysql_start;;
|
|
stop) mysql_stop;;
|
|
status) mysql_status err;;
|
|
monitor) mysql_monitor;;
|
|
promote) mysql_promote;;
|
|
demote) mysql_demote;;
|
|
notify) mysql_notify;;
|
|
validate-all) exit $OCF_SUCCESS;;
|
|
|
|
*) usage
|
|
exit $OCF_ERR_UNIMPLEMENTED;;
|
|
esac
|
|
|
|
# vi:sw=4:ts=4:et:
|
|
|