diff options
Diffstat (limited to '')
-rwxr-xr-x | heartbeat/mysql | 1074 | ||||
-rwxr-xr-x | heartbeat/mysql-common.sh | 332 | ||||
-rwxr-xr-x | heartbeat/mysql-proxy | 741 |
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 |