summaryrefslogtreecommitdiffstats
path: root/heartbeat/mysql
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xheartbeat/mysql1074
-rwxr-xr-xheartbeat/mysql-common.sh332
-rwxr-xr-xheartbeat/mysql-proxy741
3 files changed, 2147 insertions, 0 deletions
diff --git a/heartbeat/mysql b/heartbeat/mysql
new file mode 100755
index 0000000..1df2fc0
--- /dev/null
+++ b/heartbeat/mysql
@@ -0,0 +1,1074 @@
+#!/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: users@clusterlabs.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
+. ${OCF_FUNCTIONS_DIR}/mysql-common.sh
+#######################################################################
+
+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>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.
+Note, when master/slave replication is in use, the resource must
+be setup to use notifications. Set 'notify=true' in the metadata
+attributes when defining a MySQL master/slave instance.
+
+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, PROCESS and RELOAD
+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="replication_require_ssl" unique="0" required="0">
+<longdesc lang="en">
+Enables SSL connection to local MySQL service for replication user.
+i.e. if REQUIRE SSL for replication user in MySQL set, this should be set to "true".
+</longdesc>
+<shortdesc lang="en">MySQL replication require ssl</shortdesc>
+<content type="string" default="${OCF_RESKEY_replication_require_ssl_default}" />
+</parameter>
+
+<parameter name="replication_master_ssl_ca" unique="0" required="0">
+<longdesc lang="en">
+The SSL CA certificate to be used for replication over SSL.
+</longdesc>
+<shortdesc lang="en">MySQL replication SSL CA certificate</shortdesc>
+<content type="string" default="${OCF_RESKEY_replication_master_ssl_ca_default}" />
+</parameter>
+
+<parameter name="replication_master_ssl_cert" unique="0" required="0">
+<longdesc lang="en">
+The SSL CA certificate to be used for replication over SSL.
+</longdesc>
+<shortdesc lang="en">MySQL replication SSL certificate</shortdesc>
+<content type="string" default="${OCF_RESKEY_replication_master_ssl_cert_default}" />
+</parameter>
+
+<parameter name="replication_master_ssl_key" unique="0" required="0">
+<longdesc lang="en">
+The SSL certificate key to be used for replication over SSL.
+</longdesc>
+<shortdesc lang="en">MySQL replication SSL certificate key</shortdesc>
+<content type="string" default="${OCF_RESKEY_replication_master_ssl_key_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="120s" />
+<action name="stop" timeout="120s" />
+<action name="status" timeout="60s" />
+<action name="monitor" depth="0" timeout="30s" interval="20s" />
+<action name="monitor" role="Promoted" depth="0" timeout="30s" interval="10s" />
+<action name="monitor" role="Unpromoted" depth="0" timeout="30s" interval="30s" />
+<action name="promote" timeout="120s" />
+<action name="demote" timeout="120s" />
+<action name="notify" timeout="90s" />
+<action name="validate-all" timeout="5s" />
+<action name="meta-data" timeout="5s" />
+</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 \
+ --skip-column-names -e "SHOW VARIABLES LIKE '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=$?
+ rm -f $tmpfile
+
+ 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_exit_reason "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_exit_reason "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
+
+ # 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_exit_reason "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" = "NULL" ] || [ $secs_behind -gt $OCF_RESKEY_max_slave_lag ]; then
+ ocf_exit_reason "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
+ fi
+
+ # is the slave ok to have a VIP on it
+ if [ "$secs_behind" = "NULL" ] || [ $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_exit_reason "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 master_ssl_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 last master the slave replicated from, or freshly
+ # reset with RESET MASTER.
+ if [ -n "$OCF_RESKEY_replication_master_ssl_ca" ] && [ -n "$OCF_RESKEY_replication_master_ssl_cert" ] && [ -n "$OCF_RESKEY_replication_master_ssl_key" ]; then
+ master_ssl_params=", MASTER_SSL=1, \
+ MASTER_SSL_CA='$OCF_RESKEY_replication_master_ssl_ca', \
+ MASTER_SSL_CERT='$OCF_RESKEY_replication_master_ssl_cert', \
+ MASTER_SSL_KEY='$OCF_RESKEY_replication_master_ssl_key'"
+ fi
+
+ ocf_run $MYSQL $MYSQL_OPTIONS_REPL \
+ -e "CHANGE MASTER TO MASTER_HOST='$new_master', \
+ MASTER_PORT=$OCF_RESKEY_replication_port, \
+ MASTER_USER='$OCF_RESKEY_replication_user', \
+ MASTER_PASSWORD='$OCF_RESKEY_replication_passwd' $master_params $master_ssl_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_exit_reason "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_exit_reason "Error stopping rest slave threads"
+ exit $OCF_ERR_GENERIC
+ fi
+
+ ocf_run $MYSQL $MYSQL_OPTIONS_REPL \
+ -e "RESET SLAVE /*!50516 ALL */;"
+ if [ $? -gt 0 ]; then
+ ocf_exit_reason "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_monitor() {
+ local rc
+ local status_loglevel="err"
+
+ # Set loglevel to info during probe
+ if ocf_is_probe; then
+ status_loglevel="info"
+ fi
+
+ if ocf_is_ms; then
+ OCF_CHECK_LEVEL=10
+ fi
+
+ mysql_common_status $status_loglevel
+ rc=$?
+
+ # TODO: check max connections error
+
+ # If status returned an error, return that immediately
+ if [ $rc -ne $OCF_SUCCESS ]; then
+ if ocf_is_ms ; then
+ # This is a master slave setup but monitored host returned some errors.
+ # Immediately remove it from the pool of possible masters by erasing its master-mysql key
+ # When new mysql master election is started and node got no or negative master-mysql attribute the following is logged
+ # nodename.com pengine: debug: master_color: mysql:0 master score: -1
+ # If there are NO nodes with positive vaule election of mysql master will fail with
+ # nodename.com pengine: info: master_color: ms_mysql: Promoted 0 instances of a possible 1 to master
+ ocf_promotion_score -D
+ fi
+
+ return $rc
+ fi
+
+ if [ $OCF_CHECK_LEVEL -eq 10 ]; then
+ if [ -z "$OCF_RESKEY_test_table" ]; then
+ ocf_exit_reason "test_table not set"
+ return $OCF_ERR_CONFIGURED
+
+ fi
+
+ # 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
+ # We are master/slave and test failed. Delete master score for this node as it is considered unhealthy because of this particular failed check.
+ ocf_is_ms && ocf_promotion_score -D
+ ocf_exit_reason "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)";
+ # Always set master score for the master
+ ocf_promotion_score -v $((${OCF_RESKEY_max_slave_lag}+1))
+ return $OCF_RUNNING_MASTER
+ else
+ ocf_log debug "MySQL monitor succeeded";
+ ocf_is_ms && ocf_promotion_score -v 1
+ return $OCF_SUCCESS
+ fi
+}
+
+mysql_start() {
+ local rc
+
+ if ocf_is_ms; then
+ # Initialize the ReaderVIP attribute, monitor will enable it
+ set_reader_attr 0
+ fi
+
+ mysql_common_status info
+ if [ $? = $OCF_SUCCESS ]; then
+ ocf_log info "MySQL already running"
+ return $OCF_SUCCESS
+ fi
+
+ mysql_common_prepare_dirs
+
+ # 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
+
+ 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
+ # 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" != ${NODENAME} ]; then
+ ocf_log info "Changing MySQL configuration to replicate from $master_host."
+ set_master
+ start_slave
+ if [ $? -ne 0 ]; then
+ ocf_exit_reason "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.
+ ocf_promotion_score -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_exit_reason "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
+ ocf_promotion_score -D
+
+ # Remove VIP capability
+ set_reader_attr 0
+ fi
+
+ mysql_common_stop
+}
+
+mysql_promote() {
+ local master_info
+
+ if ( ! mysql_common_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
+ ocf_promotion_score -v $((${OCF_RESKEY_max_slave_lag}+1))
+
+ # A master can accept reads
+ set_reader_attr 1
+
+ return $OCF_SUCCESS
+}
+
+mysql_demote() {
+ if ! mysql_common_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
+ ocf_promotion_score -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" = ${NODENAME} ]; 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_exit_reason "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 = ${NODENAME} ]; then
+ ocf_log info "post-demote notification for $demote_host"
+ set_read_only on
+ if [ $? -ne 0 ]; then
+ ocf_exit_reason "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 = ${NODENAME} ]; 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
+}
+
+#######################################################################
+
+case "$1" in
+ meta-data) meta_data
+ exit $OCF_SUCCESS;;
+ usage|help) usage
+ exit $OCF_SUCCESS;;
+esac
+
+mysql_common_validate
+rc=$?
+LSB_STATUS_STOPPED=3
+if [ $rc -ne 0 ]; then
+ case "$1" in
+ stop) ;;
+ monitor)
+ mysql_common_status "info"
+ if [ $? -eq $OCF_SUCCESS ]; then
+ # if validatation fails and pid is active, always treat this as an error
+ ocf_exit_reason "environment validation failed, active pid is in unknown state."
+ exit $OCF_ERR_GENERIC
+ fi
+ # validation failed and pid is not active, it's safe to say this instance is inactive.
+ 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_common_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:
diff --git a/heartbeat/mysql-common.sh b/heartbeat/mysql-common.sh
new file mode 100755
index 0000000..d6b4e3c
--- /dev/null
+++ b/heartbeat/mysql-common.sh
@@ -0,0 +1,332 @@
+#!/bin/sh
+
+#######################################################################
+
+# Use runuser if available for SELinux.
+if [ -x /sbin/runuser ]; then
+ SU=runuser
+else
+ SU=su
+fi
+
+# 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_user_default="root"
+OCF_RESKEY_replication_passwd_default=""
+OCF_RESKEY_replication_port_default="3306"
+OCF_RESKEY_replication_require_ssl_default="false"
+OCF_RESKEY_replication_master_ssl_ca_default=""
+OCF_RESKEY_replication_master_ssl_cert_default=""
+OCF_RESKEY_replication_master_ssl_key_default=""
+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_replication_require_ssl=${OCF_RESKEY_replication_require_ssl_default}}
+: ${OCF_RESKEY_replication_master_ssl_ca=${OCF_RESKEY_replication_master_ssl_ca_default}}
+: ${OCF_RESKEY_replication_master_ssl_cert=${OCF_RESKEY_replication_master_ssl_cert_default}}
+: ${OCF_RESKEY_replication_master_ssl_key=${OCF_RESKEY_replication_master_ssl_key_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
+if ocf_is_true "$OCF_RESKEY_replication_require_ssl"; then
+ MYSQL_OPTIONS_LOCAL_SSL_OPTIONS="--ssl-mode=REQUIRED"
+else
+ MYSQL_OPTIONS_LOCAL_SSL_OPTIONS=""
+fi
+MYSQL_OPTIONS_LOCAL="-S $OCF_RESKEY_socket"
+MYSQL_OPTIONS_REPL="$MYSQL_OPTIONS_LOCAL_SSL_OPTIONS $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
+
+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()
+{
+
+ if ! have_binary "$OCF_RESKEY_binary"; then
+ ocf_exit_reason "Setup problem: couldn't find command: $OCF_RESKEY_binary"
+ return $OCF_ERR_INSTALLED;
+ fi
+
+ if ! have_binary "$OCF_RESKEY_client_binary"; then
+ ocf_exit_reason "Setup problem: couldn't find command: $OCF_RESKEY_client_binary"
+ return $OCF_ERR_INSTALLED;
+ fi
+
+ if [ ! -f $OCF_RESKEY_config ]; then
+ ocf_exit_reason "Config $OCF_RESKEY_config doesn't exist";
+ return $OCF_ERR_INSTALLED;
+ fi
+
+ if [ ! -d $OCF_RESKEY_datadir ]; then
+ ocf_exit_reason "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_exit_reason "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_exit_reason "Group $OCF_RESKEY_group doesn't exist";
+ return $OCF_ERR_INSTALLED;
+ fi
+
+ return $OCF_SUCCESS
+}
+
+mysql_common_check_pid() {
+ local pid=$1
+
+ 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
+ return $?
+}
+
+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
+
+ mysql_common_check_pid $pid
+
+
+ if [ $? -eq 0 ]; then
+ return $OCF_SUCCESS;
+ else
+ if [ -e $OCF_RESKEY_pid ]; then
+ ocf_log $loglevel "MySQL not running: removing old PID file"
+ rm -f $OCF_RESKEY_pid
+ fi
+ 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_exit_reason "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 $OCF_RESKEY_datadir; do
+ if ! $SU -s /bin/sh - $OCF_RESKEY_user -c "test -w $dir"; then
+ ocf_exit_reason "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
+
+ $SU - $OCF_RESKEY_user -s /bin/sh -c \
+ "${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 \
+ $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_exit_reason "MySQL server failed to start (pid=$pid) (rc=$?). Check $OCF_RESKEY_log for details"
+ 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 `
+
+ mysql_common_check_pid $pid
+ if [ $? -ne 0 ]; then
+ rm -f $OCF_RESKEY_pid
+ ocf_log info "MySQL is already stopped"
+ return $OCF_SUCCESS;
+ fi
+
+ /bin/kill $pid > /dev/null
+ rc=$?
+ if [ $rc != 0 ]; then
+ ocf_exit_reason "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
+ mysql_common_status info $pid
+ if [ $? != $OCF_NOT_RUNNING ]; then
+ return $OCF_ERR_GENERIC
+ fi
+ fi
+
+ ocf_log info "MySQL stopped";
+ rm -f /var/lock/subsys/mysqld
+ rm -f $OCF_RESKEY_socket
+ return $OCF_SUCCESS
+
+}
diff --git a/heartbeat/mysql-proxy b/heartbeat/mysql-proxy
new file mode 100755
index 0000000..013c5e4
--- /dev/null
+++ b/heartbeat/mysql-proxy
@@ -0,0 +1,741 @@
+#!/bin/sh
+#
+# Resource script for MySQL Proxy
+#
+# Description: Manages MySQL Proxy as an OCF resource in
+# an high-availability setup.
+#
+# Tested with MySQL Proxy 0.8.1 and 0.8.3 on Debian 6.0.
+#
+# Based on the mysql and Pure-Ftpd OCF resource agents.
+#
+# Author: Raoul Bhatia <r.bhatia@ipax.at> : Original Author
+# License: GNU General Public License (GPL)
+#
+#
+# usage: $0 {start|stop|reload|status|monitor|validate-all|meta-data}
+#
+# The "start" arg starts a MySQL Proxy instance
+#
+# The "stop" arg stops it.
+#
+# TODO
+# * add in-depth monitoring by querying the mysql-proxy admin port
+#
+# Test via
+# (note: this did not work with MySQL Proxy 0.8.1 and ocf-tester from resource-agents 3.9.2 on Debian 6.0)
+#
+# * /usr/sbin/ocf-tester -n mp -o binary="/usr/sbin/mysql-proxy" -o defaults_file="" -o parameters="--proxy-skip-profiling" \
+# -o admin_address="127.0.0.1:4041" -o admin_username="root" -o admin_password="la" -o admin_lua_script="/usr/lib/mysql-proxy/lua/admin.lua" \
+# -o proxy_backend_addresses="192.168.100.200:42006" -o proxy_address="/var/run/mysqld/mysqld.sock" /usr/lib/ocf/resource.d/heartbeat/mysql-proxy
+#
+#
+# OCF parameters:
+# OCF_RESKEY_binary
+# OCF_RESKEY_client_binary
+# OCF_RESKEY_defaults_file
+# OCF_RESKEY_proxy_backend_addresses
+# OCF_RESKEY_proxy_read_only_backend_addresses
+# OCF_RESKEY_proxy_address
+# OCF_RESKEY_log_level
+# OCF_RESKEY_keepalive
+# OCF_RESKEY_plugins
+# OCF_RESKEY_admin_address
+# OCF_RESKEY_admin_username
+# OCF_RESKEY_admin_password
+# OCF_RESKEY_admin_lua_script
+# OCF_RESKEY_test_table
+# OCF_RESKEY_test_user
+# OCF_RESKEY_test_passwd
+# OCF_RESKEY_parameters
+# OCF_RESKEY_pidfile
+#
+##########################################################################
+
+# Initialization:
+
+: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
+. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
+
+# Parameter defaults
+
+OCF_RESKEY_binary_default="/usr/sbin/mysql-proxy"
+OCF_RESKEY_client_binary_default="mysql"
+OCF_RESKEY_defaults_file_default=""
+OCF_RESKEY_proxy_backend_addresses_default="127.0.0.1:3306"
+OCF_RESKEY_proxy_read_only_backend_addresses_default=""
+OCF_RESKEY_proxy_address_default=":4040"
+OCF_RESKEY_log_level_default=""
+OCF_RESKEY_keepalive_default=""
+OCF_RESKEY_plugins_default=""
+OCF_RESKEY_admin_address_default="127.0.0.1:4041"
+OCF_RESKEY_admin_username_default=""
+OCF_RESKEY_admin_password_default=""
+OCF_RESKEY_admin_lua_script_default=""
+OCF_RESKEY_test_table_default="mysql.user"
+OCF_RESKEY_test_user_default=""
+OCF_RESKEY_test_passwd_default=""
+OCF_RESKEY_parameters_default=""
+OCF_RESKEY_pidfile_default="${HA_RSCTMP}/mysql-proxy-${OCF_RESOURCE_INSTANCE}.pid"
+
+: ${OCF_RESKEY_binary=${OCF_RESKEY_binary_default}}
+: ${OCF_RESKEY_client_binary=${OCF_RESKEY_client_binary_default}}
+: ${OCF_RESKEY_defaults_file=${OCF_RESKEY_defaults_file_default}}
+: ${OCF_RESKEY_proxy_backend_addresses=${OCF_RESKEY_proxy_backend_addresses_default}}
+: ${OCF_RESKEY_proxy_read_only_backend_addresses=${OCF_RESKEY_proxy_read_only_backend_addresses_default}}
+: ${OCF_RESKEY_proxy_address=${OCF_RESKEY_proxy_address_default}}
+: ${OCF_RESKEY_log_level=${OCF_RESKEY_log_level_default}}
+: ${OCF_RESKEY_keepalive=${OCF_RESKEY_keepalive_default}}
+: ${OCF_RESKEY_plugins=${OCF_RESKEY_plugins_default}}
+: ${OCF_RESKEY_admin_address=${OCF_RESKEY_admin_address_default}}
+: ${OCF_RESKEY_admin_username=${OCF_RESKEY_admin_username_default}}
+: ${OCF_RESKEY_admin_password=${OCF_RESKEY_admin_password_default}}
+: ${OCF_RESKEY_admin_lua_script=${OCF_RESKEY_admin_lua_script_default}}
+: ${OCF_RESKEY_test_table=${OCF_RESKEY_test_table_default}}
+: ${OCF_RESKEY_test_user=${OCF_RESKEY_test_user_default}}
+: ${OCF_RESKEY_test_passwd=${OCF_RESKEY_test_passwd_default}}
+: ${OCF_RESKEY_parameters=${OCF_RESKEY_parameters_default}}
+: ${OCF_RESKEY_pidfile=${OCF_RESKEY_pidfile_default}}
+
+USAGE="Usage: $0 {start|stop|reload|status|monitor|validate-all|meta-data}"
+
+##########################################################################
+
+usage() {
+ echo $USAGE >&2
+}
+
+meta_data() {
+ cat <<END
+<?xml version="1.0"?>
+<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
+<resource-agent name="mysql-proxy" version="0.1">
+<version>1.0</version>
+<longdesc lang="en">
+This script manages MySQL Proxy as an OCF resource in a high-availability setup.
+
+The default monitor operation will verify that mysql-proxy is running.
+
+The level 10 monitor operation is left out intentionally for possible future enhancements in conjunction with the admin plugin.
+
+The level 20 monitor operation will perform a SELECT on a given table to verify that the connection to a back-end server is actually working.
+
+Tested with MySQL Proxy 0.8.1 and 0.8.3 on Debian 6.0.
+</longdesc>
+<shortdesc lang="en">Manages a MySQL Proxy instance</shortdesc>
+
+<parameters>
+
+<parameter name="binary" unique="0" required="0">
+<longdesc lang="en">
+Full path to the MySQL Proxy binary.
+For example, "/usr/sbin/mysql-proxy".
+</longdesc>
+<shortdesc lang="en">Full path to MySQL Proxy 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="defaults_file" unique="0" required="0">
+<longdesc lang="en">
+Full path to a MySQL Proxy configuration file.
+For example, "/etc/mysql-proxy.conf".
+</longdesc>
+<shortdesc lang="en">Full path to configuration file</shortdesc>
+<content type="string" default="${OCF_RESKEY_defaults_file_default}" />
+</parameter>
+
+<parameter name="proxy_backend_addresses" unique="0" required="0">
+<longdesc lang="en">
+Address:port of the remote back-end servers (default: 127.0.0.1:3306).
+</longdesc>
+<shortdesc lang="en">MySQL Proxy back-end servers</shortdesc>
+<content type="string" default="${OCF_RESKEY_proxy_backend_addresses_default}" />
+</parameter>
+
+<parameter name="proxy_read_only_backend_addresses" unique="0" required="0">
+<longdesc lang="en">
+Address:port of the remote (read only) unpromoted-server (default: ).
+</longdesc>
+<shortdesc lang="en">MySql Proxy read only back-end servers</shortdesc>
+<content type="string" default="${OCF_RESKEY_proxy_read_only_backend_addresses_default}" />
+</parameter>
+
+<parameter name="proxy_address" unique="0" required="0">
+<longdesc lang="en">
+Listening address:port of the proxy server (default: :4040).
+You can also specify a socket like "/var/run/mysql-proxy.sock".
+</longdesc>
+<shortdesc lang="en">MySQL Proxy listening address</shortdesc>
+<content type="string" default="${OCF_RESKEY_proxy_address_default}" />
+</parameter>
+
+<parameter name="log_level" unique="0" required="0">
+<longdesc lang="en">
+Log all messages of level (error|warning|info|message|debug|) or higher.
+An empty value disables logging.
+</longdesc>
+<shortdesc lang="en">MySQL Proxy log level.</shortdesc>
+<content type="string" default="${OCF_RESKEY_log_level_default}" />
+</parameter>
+
+<parameter name="keepalive" unique="0" required="0">
+<longdesc lang="en">
+Try to restart the proxy if it crashed (default: ).
+Valid values: true or false. An empty value equals "false".
+</longdesc>
+<shortdesc lang="en">Use keepalive option</shortdesc>
+<content type="string" default="${OCF_RESKEY_keepalive_default}" />
+</parameter>
+
+<parameter name="plugins" unique="0" required="0">
+<longdesc lang="en">
+Whitespace separated list of plugins to load (default: ).
+Note: The admin plugin will be auto-loaded in case you specify an admin_* parameter.
+</longdesc>
+<shortdesc lang="en">MySQL Proxy plugins</shortdesc>
+<content type="string" default="${OCF_RESKEY_plugins_default}" />
+</parameter>
+
+<parameter name="admin_address" unique="0" required="0">
+<longdesc lang="en">
+Listening address:port of the admin plugin (default: 127.0.0.1:4041).
+Note: The admin plugin will be auto-loaded in case you specify an admin_* parameter.
+</longdesc>
+<shortdesc lang="en">MySQL Proxy admin plugin listening address</shortdesc>
+<content type="string" default="${OCF_RESKEY_admin_address_default}" />
+</parameter>
+
+<parameter name="admin_username" unique="0" required="0">
+<longdesc lang="en">
+Username for the admin plugin (default: ).
+Required since MySQL Proxy 0.8.1, if the admin plugin is loaded.
+Note: The admin plugin will be auto-loaded in case you specify an admin_* parameter.
+</longdesc>
+<shortdesc lang="en">MySQL Proxy admin plugin username</shortdesc>
+<content type="string" default="${OCF_RESKEY_admin_username_default}" />
+</parameter>
+
+<parameter name="admin_password" unique="0" required="0">
+<longdesc lang="en">
+Password for the admin plugin (default: ).
+Required since MySQL Proxy 0.8.1, if the admin plugin is loaded.
+Note: The admin plugin will be auto-loaded in case you specify an admin_* parameter.
+</longdesc>
+<shortdesc lang="en">MySQL Proxy admin plugin password</shortdesc>
+<content type="string" default="${OCF_RESKEY_admin_password_default}" />
+</parameter>
+
+<parameter name="admin_lua_script" unique="0" required="0">
+<longdesc lang="en">
+Script to execute by the admin plugin.
+Required since MySQL Proxy 0.8.1, if the admin plugin is loaded.
+Note: The admin plugin will be auto-loaded in case you specify an admin_* parameter.
+</longdesc>
+<shortdesc lang="en">MySQL Proxy admin plugin lua script</shortdesc>
+<content type="string" default="${OCF_RESKEY_admin_lua_script_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
+</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="parameters" unique="0" required="0">
+<longdesc lang="en">
+The MySQL Proxy daemon may be called with additional parameters.
+Specify any of them here.
+</longdesc>
+<shortdesc lang="en">MySQL Proxy additional parameters</shortdesc>
+<content type="string" default="${OCF_RESKEY_parameters_default}" />
+</parameter>
+
+<parameter name="pidfile" unique="1" required="0">
+<longdesc lang="en">PID file</longdesc>
+<shortdesc lang="en">PID file</shortdesc>
+<content type="string" default="${OCF_RESKEY_pidfile_default}" />
+</parameter>
+
+</parameters>
+
+<actions>
+<action name="start" timeout="30s" />
+<action name="stop" timeout="30s" />
+<action name="reload" timeout="30s" />
+<action name="monitor" depth="0" timeout="20s" interval="60s" />
+<action name="validate-all" timeout="30s" />
+<action name="meta-data" timeout="5s" />
+</actions>
+</resource-agent>
+END
+}
+
+isRunning()
+{
+ kill -s 0 "$1" 2>/dev/null
+}
+
+mysqlproxy_status()
+{
+ local PID
+
+ if [ -f "${pidfile}" ]; then
+ # MySQL Proxy is probably running
+ PID=`head -n 1 "${pidfile}"`
+ if [ ! -z "$PID" ] ; then
+ isRunning "$PID"
+ return $?
+ fi
+ fi
+
+ # MySQL Proxy is not running
+ false
+}
+
+mysqlproxy_start()
+{
+ local PARAM_PREFIX OPTIONS
+ local p pa pba proba
+ local pid_dir socket_dir
+
+ # if MySQL Proxy is running return success
+ if mysqlproxy_status ; then
+ ocf_log info "MySQL Proxy already running."
+ return $OCF_SUCCESS
+ fi
+
+ PARAM_PREFIX=''
+
+ # MySQL Proxy plugins to load
+ # @TODO check if the plugins are actually available?
+ if ocf_is_true $plugin_support; then
+ for p in $plugins; do
+ PARAM_PREFIX="$PARAM_PREFIX --plugins=$p"
+ done
+ fi
+
+ # check if the MySQL Proxy defaults-file exist
+ if [ -f "$defaults_file" ]; then
+ PARAM_PREFIX="$PARAM_PREFIX --defaults-file=$defaults_file"
+ fi
+
+ # set log-level
+ if [ ! -z "$log_level" ]; then
+ PARAM_PREFIX="$PARAM_PREFIX --log-level=$log_level"
+ fi
+
+ # set keepalive
+ if [ "$keepalive" = "true" ]; then
+ PARAM_PREFIX="$PARAM_PREFIX --keepalive"
+ fi
+
+ # honor admin_* options
+ if [ ! -z "$admin_username" ]; then
+ PARAM_PREFIX="$PARAM_PREFIX --admin-username=$admin_username"
+ fi
+ if [ ! -z "$admin_password" ]; then
+ PARAM_PREFIX="$PARAM_PREFIX --admin-password=$admin_password"
+ fi
+ if [ ! -z "$admin_lua_script" ]; then
+ PARAM_PREFIX="$PARAM_PREFIX --admin-lua-script=$admin_lua_script"
+ fi
+
+ # make sure that the pid directory exists
+ pid_dir=`dirname $pidfile`
+ if [ ! -d $pid_dir ] ; then
+ ocf_log info "Creating PID directory '$pid_dir'."
+ mkdir -p $pid_dir
+ #chown $OCF_RESKEY_user:$OCF_RESKEY_group $pid_dir # c/p from mysql ra; currently not needed
+ fi
+
+ # split multiple proxy-address options.
+ # currently unsupported but let us hope for the future ;)
+ for pa in $proxy_address; do
+ [ -z "$pa" ] && continue
+ OPTIONS=" $OPTIONS --proxy-address=$pa"
+
+ # if $pa contains a slash, we are dealing with a socket
+ # make sure that the socket directory exists
+ if echo "$pa" | grep -q '/' ; then
+ socket_dir=`dirname $pa`
+ if [ ! -d $socket_dir ] ; then
+ ocf_log info "Creating socket directory '$socket_dir'."
+ mkdir -p $socket_dir
+ #chown $OCF_RESKEY_user:$OCF_RESKEY_group $socket_dir # c/p from mysql ra; currently not needed
+ fi
+ fi
+ done
+
+ # split multiple proxy-backend-addresses options.
+ for pba in $proxy_backend_addresses; do
+ [ -z "$pba" ] && continue
+ OPTIONS=" $OPTIONS --proxy-backend-addresses=$pba"
+ done
+
+ # split multiple proxy-backend-addresses options.
+ for proba in $proxy_read_only_backend_addresses; do
+ [ -z "$proba" ] && continue
+ OPTIONS=" $OPTIONS --proxy-read-only-backend-addresses=$proba"
+ done
+
+ # build $OPTIONS and add admin-address and pidfile
+ OPTIONS="$PARAM_PREFIX $OPTIONS --admin-address=$admin_address --pid-file=${pidfile}"
+
+ # add additional parameters
+ if [ -n "$parameters" ]; then
+ OPTIONS="$OPTIONS $parameters"
+ fi
+
+ # start MySQL Proxy
+ #start-stop-daemon --start --quiet --pidfile $pidfile --make-pidfile --name mysql-proxy --startas $binary -b -- $OPTIONS
+ $binary --daemon $OPTIONS
+ ret=$?
+
+ if [ $ret -ne 0 ]; then
+ ocf_log err "MySQL Proxy returned error: " $ret
+ return $OCF_ERR_GENERIC
+ fi
+
+ # @TODO add an initial monitoring action?
+
+ return $OCF_SUCCESS
+}
+
+
+mysqlproxy_stop()
+{
+ local ret
+ local pa
+
+ if mysqlproxy_status ; then
+ #start-stop-daemon --stop --quiet --retry 3 --exec $binary --pidfile $pidfile
+ /bin/kill `cat "${pidfile}"`
+ ret=$?
+
+ if [ $ret -ne 0 ]; then
+ ocf_log err "MySQL Proxy returned an error while stopping: " $ret
+ return $OCF_ERR_GENERIC
+ fi
+
+ # grant some time for shutdown and recheck
+ sleep 1
+ if mysqlproxy_status ; then
+ ocf_log err "MySQL Proxy failed to stop."
+ return $OCF_ERR_GENERIC
+ fi
+
+ # remove dangling socketfile, if specified
+ for pa in $proxy_address; do
+ if [ -S "$pa" ]; then
+ ocf_log info "Removing dangling socket file '$pa'."
+ rm -f "$pa"
+ fi
+ done
+
+ # remove dangling pidfile
+ if [ -f "${pidfile}" ]; then
+ ocf_log info "Removing dangling pidfile '${pidfile}'."
+ rm -f "${pidfile}"
+ fi
+ fi
+
+ return $OCF_SUCCESS
+}
+
+mysqlproxy_reload()
+{
+ # @TODO check if pidfile is empty
+ # PID=`head -n 1 "${pidfile}"`
+ # if [ ! -z "$PID" ] ; then
+
+ if mysqlproxy_status; then
+ ocf_log info "Reloading MySQL Proxy."
+ kill -s HUP `cat ${pidfile}`
+ fi
+}
+
+mysqlproxy_monitor()
+{
+ local rc
+
+ if [ "${OCF_RESKEY_CRM_meta_interval:-0}" -eq "0" ]; then
+ # in case of probe, monitor operation is surely treated as
+ # under suspension. This will call start operation.
+ # (c/p from ocf:heartbeat:sfex)
+ mysqlproxy_validate_all
+ rc=$?
+ [ $rc -ne 0 ] && return $rc
+ fi
+
+ if ! mysqlproxy_status ; then
+ return $OCF_NOT_RUNNING
+ fi
+
+ if [ $OCF_CHECK_LEVEL -eq 20 ]; then
+ mysqlproxy_monitor_20
+ rc=$?
+ [ $rc -ne 0 ] && return $rc
+ fi
+
+ return $OCF_SUCCESS
+}
+
+mysqlproxy_monitor_20()
+{
+ local rc
+ local mysql_options pa
+ local mysql_server_parameter mysql_server_host mysql_server_port
+
+ if [ -z "$OCF_RESKEY_test_table" -o -z "$OCF_RESKEY_test_user" -a -z "$OCF_RESKEY_test_passwd" ]; then
+ ocf_log warn "Missing proper configuration for OCF_CHECK_LEVEL=20 (test_table=[$OCF_RESKEY_test_table] test_user=[$OCF_RESKEY_test_user] test_password=[$OCF_RESKEY_test_passwd]). Not running in-depth monitoring."
+ return $OCF_SUCCESS
+ fi
+
+ mysql_options="--connect_timeout=10 --user=$OCF_RESKEY_test_user --password=$OCF_RESKEY_test_passwd"
+
+ # cycle each address
+ for pa in $proxy_address; do
+ # build correct connect parameter
+ if [ -S "$pa" ]; then
+ # we need to monitor a mysql socket
+ mysql_server_parameter="--socket=$pa"
+ else
+ # we need to monitor a host address
+ mysql_server_parameter=""
+
+ # split host:port
+ # @TODO correctly handle IPv6 address
+ # @TODO correctly handle 0.0.0.0 address
+ mysql_server_host=`echo $pa | cut -d : -f 1`
+ mysql_server_port=`echo $pa | cut -d : -f 2`
+
+ if [ -n "$mysql_server_host" ]; then
+ mysql_server_parameter="$mysql_server_parameter --host=$mysql_server_host"
+ fi
+ if [ -n "$mysql_server_port" ]; then
+ mysql_server_parameter="$mysql_server_parameter --port=$mysql_server_port"
+ fi
+ fi
+
+ # Check for test table
+ ocf_run $mysql $mysql_server_parameter $mysql_options \
+ -e "SELECT COUNT(*) FROM $OCF_RESKEY_test_table"
+ rc=$?
+
+ if [ $rc -ne 0 ]; then
+ ocf_log err "Failed to select from $OCF_RESKEY_test_table: " $rc
+ return $OCF_ERR_GENERIC
+ fi
+ done
+
+ return $OCF_SUCCESS
+}
+
+mysqlproxy_validate_all()
+{
+ # local variables
+ local config_error=0
+
+ # check that the MySQL Proxy binary exists and can be executed
+ check_binary $binary
+
+ # check MySQL client binary only if in-depth monitoring is requested
+ # do not break backwards compatibility otherwise
+ if [ $OCF_CHECK_LEVEL -gt 0 ]; then
+ check_binary $mysql
+ fi
+
+ # check for valid log-level
+ echo $log_level | egrep -q "^(error|warning|info|message|debug|)$"
+ if [ $? -ne 0 ]; then
+ ocf_log err "MySQL Proxy log level '$log_level' not in valid range error|warning|info|message|debug"
+ return $OCF_ERR_CONFIGURED
+ fi
+
+
+ # if we're running MySQL Proxy > 0.8.1 and there is any admin parameter set,
+ # explicitly load the admin (and the proxy) plugin.
+ # (version 0.8.2 does not load the admin plugin by default anymore)
+ ocf_version_cmp "$version" "0.8.1"
+ ret=$?
+ if [ $ret -eq 2 ]; then
+ # simple check: concat all parameters and check if the string has non-zero length
+ if [ -n "$admin_username$admin_password$admin_lua_script$admin_address" ]; then
+ plugins="proxy admin"
+ has_plugin_admin=1
+ else
+ has_plugin_admin=0
+ fi
+ fi
+
+
+ # check for required admin_* parameters for 0.8.1 and 0.8.2 (with admin module)
+ # translated: if (version == 0.8.1 or (version > 0.8.1 and has_plugin_admin))
+ if [ $ret -eq 1 -o \( $ret -eq 2 -a $has_plugin_admin -eq 1 \) ]; then
+ if [ -z "$admin_username" ]; then
+ ocf_log err "Missing required parameter \"admin_username\""
+ config_error=1
+ fi
+ if [ -z "$admin_password" ]; then
+ ocf_log err "Missing required parameter \"admin_password\""
+ config_error=1
+ fi
+ if [ -z "$admin_lua_script" ]; then
+ ocf_log err "Missing required parameter \"admin_lua_script\""
+ config_error=1
+ fi
+
+ # check if the admin_lua_script, if specified, exists
+ if [ -n "$admin_lua_script" -a ! -e "$admin_lua_script" ]; then
+ ocf_log err "MySQL Proxy admin lua script '$admin_lua_script' does not exist or is not readable."
+ fi
+ fi
+
+ # issue a warning during start if the user wants to load a plugin
+ # but this version of MySQL Proxy does not support the plugin architecture.
+ if [ -n "$plugins" ] && ocf_is_false "$plugin_support" && [ $__OCF_ACTION = 'start' ]; then
+ ocf_log warn "You are running MySQL Proxy version '$version'. This version does not support the plugin architecture. Please use version 0.7.0 or later to load the plugins '$plugins'."
+ fi
+
+ # exit in case we have found relevant config errors
+ if [ $config_error -eq 1 ]; then
+ exit $OCF_ERR_CONFIGURED
+ fi
+
+ return $OCF_SUCCESS
+}
+
+#
+# Main
+#
+
+if [ $# -ne 1 ]; then
+ usage
+ exit $OCF_ERR_ARGS
+fi
+
+pidfile=$OCF_RESKEY_pidfile
+binary=$OCF_RESKEY_binary
+defaults_file=$OCF_RESKEY_defaults_file
+proxy_backend_addresses=$OCF_RESKEY_proxy_backend_addresses
+proxy_read_only_backend_addresses=$OCF_RESKEY_proxy_read_only_backend_addresses
+admin_address=$OCF_RESKEY_admin_address
+admin_username=$OCF_RESKEY_admin_username
+admin_password=$OCF_RESKEY_admin_password
+admin_lua_script=$OCF_RESKEY_admin_lua_script
+proxy_address=$OCF_RESKEY_proxy_address
+log_level=$OCF_RESKEY_log_level
+keepalive=$OCF_RESKEY_keepalive
+plugins=`echo $OCF_RESKEY_plugins | tr "[:space:]" "\n" | sort -u`
+mysql=$OCF_RESKEY_client_binary
+parameters=$OCF_RESKEY_parameters
+plugin_support=false
+has_plugin_admin=0 # 0 because this simplifies the if statements
+
+# debugging stuff
+#echo OCF_RESKEY_binary=$OCF_RESKEY_binary >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE
+#echo OCF_RESKEY_defaults_file=$OCF_RESKEY_defaults_file >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE
+#echo OCF_RESKEY_proxy_backend_addresses=$OCF_RESKEY_proxy_backend_addresses >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE
+#echo OCF_RESKEY_proxy_read_only_backend_addresses=$OCF_RESKEY_proxy_read_only_backend_addresses >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE
+#echo OCF_RESKEY_proxy_address=$OCF_RESKEY_proxy_address >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE
+#echo OCF_RESKEY_log_level=$OCF_RESKEY_log_level >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE
+#echo OCF_RESKEY_keepalive=$OCF_RESKEY_keepalive >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE
+#echo OCF_RESKEY_admin_address=$OCF_RESKEY_admin_address >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE
+#echo OCF_RESKEY_admin_username=$OCF_RESKEY_admin_username >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE
+#echo OCF_RESKEY_admin_password=$OCF_RESKEY_admin_password >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE
+#echo OCF_RESKEY_admin_lua_script=$OCF_RESKEY_admin_lua_script >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE
+#echo OCF_RESKEY_parameters=$OCF_RESKEY_parameters >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE
+#echo OCF_RESKEY_pidfile=$OCF_RESKEY_pidfile >> /tmp/prox_conf_$OCF_RESOURCE_INSTANCE
+
+
+# handle some parameters before performing any additional checks
+case $1 in
+ meta-data) meta_data
+ exit $?
+ ;;
+
+ usage) usage
+ exit $OCF_SUCCESS
+ ;;
+esac
+
+
+# determine MySQL Proxy version
+check_binary $binary
+version=`$binary --version | grep ^mysql-proxy | awk '{print $NF}'`
+
+# version 0.7.0 (and later) support the plugin architecture and load the admin plugin by default
+# version 0.8.1 loads admin plugin by default and requires the admin parameters to be set
+# version 0.8.2 does not load the admin plugin by default anymore
+ocf_version_cmp "$version" "0.7.0"
+ret=$?
+if [ $ret -eq 1 -o $ret -eq 2 ]; then
+ plugin_support=true
+ has_plugin_admin=1
+fi
+
+
+# perform action
+case $1 in
+ start) mysqlproxy_validate_all &&
+ mysqlproxy_start
+ exit $?
+ ;;
+
+ stop) mysqlproxy_validate_all &&
+ mysqlproxy_stop
+ exit $?
+ ;;
+
+ reload) mysqlproxy_reload
+ exit $?
+ ;;
+
+ status) if mysqlproxy_status; then
+ ocf_log info "MySQL Proxy is running."
+ exit $OCF_SUCCESS
+ else
+ ocf_log info "MySQL Proxy is stopped."
+ exit $OCF_NOT_RUNNING
+ fi
+ ;;
+
+ monitor) mysqlproxy_monitor
+ exit $?
+ ;;
+
+ validate-all) mysqlproxy_validate_all
+ exit $?
+ ;;
+
+
+ *) usage
+ exit $OCF_ERR_UNIMPLEMENTED
+ ;;
+esac