| #!/bin/bash |
| # Copyright (c) 2005,2008 Oracle |
| # Copyright (c) 2008 Andrew Beekhof |
| # All Rights Reserved. |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of version 2 of the GNU General Public License as |
| # published by the Free Software Foundation. |
| # |
| # This program is distributed in the hope that it would be useful, but |
| # WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| # |
| # Further, this software is distributed without any warranty that it is |
| # free of the rightful claim of any third person regarding infringement |
| # or the like. Any license provided herein, whether implied or |
| # otherwise, applies only to this software file. Patent licenses, if |
| # any, provided herein do not apply to combinations of this program with |
| # other software, or any other product whatsoever. |
| # |
| ####################################################################### |
| |
| : ${OCF_FUNCTIONS=${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs} |
| . ${OCF_FUNCTIONS} |
| : ${__OCF_ACTION=$1} |
| |
| : ${OCF_RESKEY_stack:="pcmk"} |
| : ${OCF_RESKEY_sysfs:="/sys/fs"} |
| : ${OCF_RESKEY_configfs:="/sys/kernel/config"} |
| : ${OCF_RESKEY_daemon_timeout:="10"} # How long to wait for things to start |
| : ${OCF_RESKEY_CRM_meta_globally_unique:="false"} |
| |
| DAEMON="/usr/sbin/ocfs2_controld.${OCF_RESKEY_stack}" |
| CLUSTER_STACK_FILE="${OCF_RESKEY_sysfs}/ocfs2/cluster_stack" |
| LOADED_PLUGINS_FILE="${OCF_RESKEY_sysfs}/ocfs2/loaded_cluster_plugins" |
| |
| # |
| # Check to see if a filesystem driver is loaded. |
| # 0 is loaded, 1 is not. |
| # |
| driver_filesystem() { |
| if [ -z "$1" ] |
| then |
| ocf_log err "driver_filesystem(): Missing an argument" |
| exit 1 |
| fi |
| FSNAME="$1" |
| |
| FSOUT="$(awk '(NF == 1 && $1 ~ /^'$FSNAME'$/) || $2 ~ /^'$FSNAME'$/{ |
| print $1;exit |
| }' /proc/filesystems 2>/dev/null)" |
| |
| test -n "$FSOUT" |
| return $? |
| } |
| |
| # |
| # Check to see if a filesystem of type $1 is mounted at $2. |
| # |
| # 0 is mounted, 1 is not. |
| # |
| check_filesystem() |
| { |
| if [ "$#" != "2" -o -z "$1" -o -z "$2" ] |
| then |
| ocf_log err "check_filesystem(): Missing arguments" |
| exit 4 |
| fi |
| FSNAME="$1" |
| MOUNTPOINT="$2" |
| |
| FULL_MOUNTSEARCH="`echo "$MOUNTPOINT" | sed -e 's/\//\\\\\//g'`" |
| MOUNTOUT="`awk '$2 ~ /^'$FULL_MOUNTSEARCH'$/ && $3 ~ /^'$FSNAME'$/{print $2; exit}' < /proc/mounts 2>/dev/null`" |
| test -n "$MOUNTOUT" |
| return $? |
| } |
| |
| # |
| # Unload a filesystem driver. |
| # Be careful to notice if the driver is built-in and do nothing. |
| # |
| # 0 is success, 1 is error, 2 is already unloaded. |
| # |
| unload_filesystem() |
| { |
| if [ "$#" != "1" -o -z "$1" ] |
| then |
| ocf_log err "unload_filesystem(): Missing an argument" |
| return 1 |
| fi |
| FSNAME="$1" |
| |
| driver_filesystem "$FSNAME" || return 2 |
| |
| MODOUT="`awk '$1 ~ /^'$FSNAME'$/{print $1,$3;exit}' < /proc/modules 2>/dev/null`" |
| if [ -z "$MODOUT" ]; then |
| # The driver is built in, we can't unload it. |
| return 0 |
| fi |
| |
| case "$MODOUT" in |
| $FSNAME\ 0) |
| ;; |
| $FSNAME\ *) |
| # The driver is busy, leave it alone |
| ocf_log err "Module $FSNAME is still in use" |
| return 1 |
| ;; |
| *) |
| ocf_log err "Invalid module parsing! " |
| return 1 |
| ;; |
| esac |
| |
| modprobe -rs "$FSNAME" |
| if [ "$?" != 0 ]; then |
| ocf_log err "Unable to unload module: $FSNAME" |
| return 1 |
| fi |
| |
| return 0 |
| } |
| |
| status_daemon() |
| { |
| PID=`pidof "$DAEMON"` |
| if [ -n "$PID" ]; then |
| return $OCF_SUCCESS |
| fi |
| return $OCF_NOT_RUNNING |
| } |
| |
| bringup_daemon() |
| { |
| if [ ! -e "$DAEMON" ]; then |
| ocf_log err "Required binary not found: $DAEMON" |
| return $OCF_ERR_INSTALLED |
| fi |
| |
| "$DAEMON"; rc=$? |
| if [ $rc != 0 ]; then |
| ocf_log err "Could not start $DAEMON" |
| return $OCF_ERR_GENERIC |
| fi |
| |
| sleep 1 |
| COUNT=0 |
| rc=$OCF_NOT_RUNNING |
| |
| while [ $rc = $OCF_NOT_RUNNING ]; do |
| COUNT=`expr $COUNT + 1` |
| if [ $COUNT -gt $OCF_RESKEY_daemon_timeout ]; then |
| ocf_log err "`basename $DAEMON` did not come up" |
| return $OCF_ERR_GENERIC |
| fi |
| status_daemon; rc=$? |
| sleep 1 |
| done |
| |
| return $rc |
| } |
| |
| kill_daemon() |
| { |
| status_daemon; rc=$? |
| if [ $rc != $OCF_SUCCESS ]; then |
| return $rc |
| fi |
| |
| ocf_log info "Stopping `basename "$DAEMON"`" |
| killproc "$DAEMON" |
| |
| while [ $rc = $OCF_NOT_RUNNING ]; do |
| sleep 1 |
| status_daemon; rc=$? |
| done |
| |
| return $OCF_SUCCESS |
| } |
| |
| # |
| # Unload a module |
| # 0 is success, 1 is error, 2 is not loaded |
| # |
| unload_module() |
| { |
| if [ "$#" -lt "1" -o -z "$1" ] |
| then |
| ocf_log err "unload_module(): Requires an argument" |
| return 1 |
| fi |
| MODNAME="$1" |
| |
| MODOUT="`awk '$1 ~ /^'$MODNAME'$/{print $1,$3;exit}' < /proc/modules 2>/dev/null`" |
| if [ -z "$MODOUT" ] |
| then |
| return 2 |
| fi |
| |
| case "$MODOUT" in |
| $MODNAME\ 0) |
| ;; |
| $MODNAME\ *) |
| return 2 |
| ;; |
| *) |
| ocf_log err "Invalid module parsing!" |
| return 1 |
| ;; |
| esac |
| |
| modprobe -rs "$MODNAME" |
| if [ "$?" != 0 ]; then |
| ocf_log err "Unable to unload module \"$MODNAME\"" |
| return 1 |
| fi |
| |
| return 0 |
| } |
| |
| o2cb_start() { |
| |
| o2cb_monitor; rc=$? |
| if [ $rc != $OCF_NOT_RUNNING ]; then |
| return $rc |
| fi |
| |
| ocf_log info "Starting $OCF_RESOURCE_INSTANCE" |
| |
| if [ ! -e "$CLUSTER_STACK_FILE" ]; then |
| modprobe -s ocfs2_stackglue |
| if [ $? != 0 ]; then |
| ocf_log err "Could not load ocfs2_stackglue" |
| return $OCF_ERR_INSTALLED |
| fi |
| fi |
| |
| SP_OUT="$(awk '/^'user'$/{print; exit}' "$LOADED_PLUGINS_FILE" 2>/dev/null)" |
| if [ -z "$SP_OUT" ] |
| then |
| modprobe -s ocfs2_stack_user |
| if [ $? != 0 ]; then |
| ocf_log err "Could not load ocfs2_stack_user" |
| return $OCF_ERR_INSTALLED |
| fi |
| fi |
| |
| SP_OUT="$(awk '/^'user'$/{print; exit}' "$LOADED_PLUGINS_FILE" 2>/dev/null)" |
| if [ -z "$SP_OUT" ]; then |
| ocf_log err "Switch to userspace stack unsuccessful" |
| return $OCF_ERR_INSTALLED |
| fi |
| |
| if [ -f "$CLUSTER_STACK_FILE" ]; then |
| echo "$OCF_RESKEY_stack" >"$CLUSTER_STACK_FILE" |
| if [ $? != 0 ]; then |
| ocf_log err "Userspace stack '$OCF_RESKEY_stack' not supported" |
| return $OCF_ERR_INSTALLED |
| fi |
| else |
| ocf_log err "Switch to userspace stack not supported" |
| return $OCF_ERR_INSTALLED |
| fi |
| |
| driver_filesystem ocfs2; rc=$? |
| if [ $rc != 0 ]; then |
| modprobe -s ocfs2 |
| if [ "$?" != 0 ]; then |
| ocf_log err "Unable to load ocfs2 module" |
| return $OCF_ERR_INSTALLED |
| fi |
| fi |
| |
| bringup_daemon |
| return $? |
| } |
| |
| o2cb_stop() { |
| o2cb_monitor; rc=$? |
| case $rc in |
| $OCF_NOT_RUNNING) return $OCF_SUCCESS;; |
| esac |
| |
| ocf_log info "Stopping $OCF_RESOURCE_INSTANCE" |
| |
| kill_daemon |
| if [ $? != 0 ]; then |
| ocf_log err "Unable to unload modules: the cluster is still online" |
| return $OCF_ERR_GENERIC |
| fi |
| |
| unload_filesystem ocfs2 |
| if [ $? = 1 ]; then |
| ocf_log err "Unable to unload ocfs2 module" |
| return $OCF_ERR_GENERIC |
| fi |
| |
| # If we can't find the stack glue, we have nothing to do. |
| [ ! -e "$LOADED_PLUGINS_FILE" ] && return $OCF_SUCCESS |
| |
| while read plugin |
| do |
| unload_module "ocfs2_stack_${plugin}" |
| if [ $? = 1 ]; then |
| ocf_log err "Unable to unload ocfs2_stack_${plugin}" |
| return $OCF_ERR_GENERIC |
| fi |
| done <"$LOADED_PLUGINS_FILE" |
| |
| unload_module "ocfs2_stackglue" |
| if [ $? = 1 ]; then |
| ocf_log err "Unable to unload ocfs2_stackglue" |
| return $OCF_ERR_GENERIC |
| fi |
| |
| # Don't unmount configfs - it's always in use by libdlm |
| } |
| |
| o2cb_monitor() { |
| o2cb_validate |
| |
| # Assume that ocfs2_controld will terminate if any of the conditions below are met |
| |
| driver_filesystem configfs; rc=$? |
| if [ $rc != 0 ]; then |
| ocf_log info "configfs not loaded" |
| return $OCF_NOT_RUNNING |
| fi |
| |
| check_filesystem configfs "${OCF_RESKEY_configfs}"; rc=$? |
| if [ $rc != 0 ]; then |
| ocf_log info "configfs not mounted" |
| return $OCF_NOT_RUNNING |
| fi |
| |
| if [ ! -e "$LOADED_PLUGINS_FILE" ]; then |
| ocf_log info "Stack glue driver not loaded" |
| return $OCF_NOT_RUNNING |
| fi |
| |
| grep user "$LOADED_PLUGINS_FILE" >/dev/null 2>&1; rc=$? |
| if [ $rc != 0 ]; then |
| ocf_log err "Wrong stack `cat $LOADED_PLUGINS_FILE`" |
| return $OCF_ERR_INSTALLED |
| fi |
| |
| driver_filesystem ocfs2; rc=$? |
| if [ $rc != 0 ]; then |
| ocf_log info "ocfs2 not loaded" |
| return $OCF_NOT_RUNNING |
| fi |
| |
| status_daemon |
| return $? |
| } |
| |
| o2cb_usage() { |
| echo "usage: $0 {start|stop|monitor|validate-all|meta-data}" |
| echo " Expects to have a fully populated OCF RA-compliant environment set." |
| echo " In particualr, a value for OCF_ROOT" |
| } |
| |
| o2cb_validate() { |
| check_binary ${DAEMON} |
| |
| case ${OCF_RESKEY_CRM_meta_globally_unique} in |
| yes|Yes|true|True|1) |
| ocf_log err "$OCF_RESOURCE_INSTANCE must be configured with the globally_unique=false meta attribute" |
| exit $OCF_ERR_CONFIGURED |
| ;; |
| esac |
| |
| return $OCF_SUCCESS |
| } |
| |
| meta_data() { |
| cat <<END |
| <?xml version="1.0"?> |
| <!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd"> |
| <resource-agent name="o2cb" version="1.0"> |
| <version>1.0</version> |
| <shortdesc lang="en">OCFS2 daemon resource agent</shortdesc> |
| <longdesc lang="en"> |
| This Resource Agent controls the userspace daemon needed by OCFS2. |
| </longdesc> |
| <parameters> |
| |
| <parameter name="sysfs" unique="0"> |
| <longdesc lang="en"> |
| Location where sysfs is mounted |
| </longdesc> |
| <shortdesc lang="en">Sysfs location</shortdesc> |
| <content type="string" default="/sys/fs"/> |
| </parameter> |
| |
| <parameter name="configfs" unique="0"> |
| <longdesc lang="en"> |
| Location where configfs is mounted |
| </longdesc> |
| <shortdesc lang="en">Configfs location</shortdesc> |
| <content type="string" default="/sys/kernel/config"/> |
| </parameter> |
| |
| <parameter name="stack" unique="0"> |
| <longdesc lang="en"> |
| Which userspace stack to use. Known values: pcmk, cman |
| </longdesc> |
| <shortdesc lang="en">Userspace stack</shortdesc> |
| <content type="string" default="pcmk"/> |
| </parameter> |
| |
| <parameter name="daemon_timeout" unique="0"> |
| <longdesc lang="en"> |
| Number of seconds to allow the control daemon to come up |
| </longdesc> |
| <shortdesc lang="en">Daemon Timeout</shortdesc> |
| <content type="string" default="10"/> |
| </parameter> |
| |
| </parameters> |
| <actions> |
| <action name="start" timeout="90" /> |
| <action name="stop" timeout="100" /> |
| <action name="monitor" timeout="20" depth="0"/> |
| <action name="meta-data" timeout="5" /> |
| <action name="validate-all" timeout="30" /> |
| </actions> |
| </resource-agent> |
| END |
| } |
| |
| case $__OCF_ACTION in |
| meta-data) meta_data |
| exit $OCF_SUCCESS |
| ;; |
| start) o2cb_start |
| ;; |
| stop) o2cb_stop |
| ;; |
| monitor) o2cb_monitor |
| ;; |
| validate-all) o2cb_validate |
| ;; |
| usage|help) o2cb_usage |
| exit $OCF_SUCCESS |
| ;; |
| *) o2cb_usage |
| exit $OCF_ERR_UNIMPLEMENTED |
| ;; |
| esac |
| |
| exit $? |