1#!@BASH_SHELL@ 2# 3# Copyright (c) 2014 David Vossel <davidvossel@gmail.com> 4# All Rights Reserved. 5# 6# This program is free software; you can redistribute it and/or modify 7# it under the terms of version 2 of the GNU General Public License as 8# published by the Free Software Foundation. 9# 10# This program is distributed in the hope that it would be useful, but 11# WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13# 14# Further, this software is distributed without any warranty that it is 15# free of the rightful claim of any third person regarding infringement 16# or the like. Any license provided herein, whether implied or 17# otherwise, applies only to this software file. Patent licenses, if 18# any, provided herein do not apply to combinations of this program with 19# other software, or any other product whatsoever. 20# 21# You should have received a copy of the GNU General Public License 22# along with this program; if not, write the Free Software Foundation, 23# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. 24# 25 26####################################################################### 27# Initialization: 28 29: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} 30. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs 31. ${OCF_FUNCTIONS_DIR}/ocf-directories 32 33# Parameter defaults 34 35OCF_RESKEY_with_cmirrord_default="false" 36OCF_RESKEY_daemon_options_default="-d0" 37OCF_RESKEY_activate_vgs_default="true" 38OCF_RESKEY_exclusive_default="false" 39 40: ${OCF_RESKEY_with_cmirrord=${OCF_RESKEY_with_cmirrord_default}} 41: ${OCF_RESKEY_daemon_options=${OCF_RESKEY_daemon_options_default}} 42: ${OCF_RESKEY_activate_vgs=${OCF_RESKEY_activate_vgs_default}} 43: ${OCF_RESKEY_exclusive=${OCF_RESKEY_exclusive_default}} 44 45####################################################################### 46 47meta_data() { 48 cat <<END 49<?xml version="1.0"?> 50<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd"> 51<resource-agent name="clvm"> 52<version>1.0</version> 53 54<longdesc lang="en"> 55This agent manages the clvmd daemon. 56</longdesc> 57<shortdesc lang="en">clvmd</shortdesc> 58 59<parameters> 60<parameter name="with_cmirrord" unique="0" required="0"> 61<longdesc lang="en"> 62Start with cmirrord (cluster mirror log daemon). 63</longdesc> 64<shortdesc lang="en">activate cmirrord</shortdesc> 65<content type="boolean" default="${OCF_RESKEY_with_cmirrord_default}" /> 66</parameter> 67 68<parameter name="daemon_options" unique="0"> 69<longdesc lang="en"> 70Options to clvmd. Refer to clvmd.8 for detailed descriptions. 71</longdesc> 72<shortdesc lang="en">Daemon Options</shortdesc> 73<content type="string" default="${OCF_RESKEY_daemon_options_default}"/> 74</parameter> 75 76<parameter name="activate_vgs" unique="0"> 77<longdesc lang="en"> 78Whether or not to activate all cluster volume groups after starting 79the clvmd or not. Note that clustered volume groups will always be 80deactivated before the clvmd stops regardless of what this option 81is set to. 82</longdesc> 83<shortdesc lang="en">Activate volume groups</shortdesc> 84<content type="boolean" default="${OCF_RESKEY_activate_vgs_default}"/> 85</parameter> 86 87<parameter name="exclusive" unique="0" required="0"> 88<longdesc lang="en"> 89If set, only exclusive volume groups will be monitored. 90</longdesc> 91<shortdesc lang="en">Only monitor exclusive volume groups</shortdesc> 92<content type="boolean" default="${OCF_RESKEY_exclusive_default}" /> 93</parameter> 94 95 96</parameters> 97 98<actions> 99<action name="start" timeout="90s" /> 100<action name="stop" timeout="90s" /> 101<action name="monitor" timeout="90s" interval="30s" depth="0" /> 102<action name="meta-data" timeout="10s" /> 103<action name="validate-all" timeout="20s" /> 104</actions> 105</resource-agent> 106END 107} 108 109####################################################################### 110 111sbindir=$HA_SBIN_DIR 112if [ -z $sbindir ]; then 113 sbindir=/usr/sbin 114fi 115DAEMON="clvmd" 116CMIRROR="cmirrord" 117DAEMON_PATH="${sbindir}/clvmd" 118CMIRROR_PATH="${sbindir}/cmirrord" 119LVMCONF="${sbindir}/lvmconf" 120LOCK_FILE="/var/lock/subsys/$DAEMON" 121 122# attempt to detect where the vg tools are located 123# for some reason this isn't consistent with sbindir 124# in some distros. 125vgtoolsdir=$(dirname $(which vgchange 2> /dev/null) 2> /dev/null) 126if [ -z "$vgtoolsdir" ]; then 127 vgtoolsdir="$sbindir" 128fi 129 130LVM_VGCHANGE=${vgtoolsdir}/vgchange 131LVM_VGDISPLAY=${vgtoolsdir}/vgdisplay 132LVM_VGSCAN=${vgtoolsdir}/vgscan 133 134# Leaving this in for legacy. We do not want to advertize 135# the abilty to set options in the systconfig exists, we want 136# to expand the OCF style options as necessary instead. 137[ -f /etc/sysconfig/cluster ] && . /etc/sysconfig/cluster 138[ -f /etc/sysconfig/$DAEMON ] && . /etc/sysconfig/$DAEMON 139 140CLVMD_TIMEOUT="90" 141if [ -n "$OCF_RESKEY_CRM_meta_timeout" ]; then 142 CLVMD_TIMEOUT=$(($OCF_RESKEY_CRM_meta_timeout/1000)) 143fi 144 145clvmd_usage() 146{ 147 cat <<END 148usage: $0 {start|stop|monitor|validate-all|meta-data} 149 150Expects to have a fully populated OCF RA-compliant environment set. 151END 152} 153 154clvmd_validate() 155{ 156 # check_binary will exit with OCF_ERR_INSTALLED 157 # when binary is missing 158 check_binary "pgrep" 159 check_binary $DAEMON_PATH 160 if ocf_is_true $OCF_RESKEY_with_cmirrord; then 161 check_binary $CMIRROR_PATH 162 fi 163 164 if [ "$__OCF_ACTION" != "monitor" ]; then 165 check_binary "killall" 166 check_binary $LVM_VGCHANGE 167 check_binary $LVM_VGDISPLAY 168 check_binary $LVM_VGSCAN 169 fi 170 171 # Future validation checks here. 172 return $OCF_SUCCESS 173} 174 175check_process() 176{ 177 local binary=$1 178 local pidfile="${HA_RSCTMP}/${binary}-${OCF_RESOURCE_INSTANCE}.pid" 179 local pid 180 181 ocf_log debug "Checking status for ${binary}." 182 if [ -e "$pidfile" ]; then 183 cat /proc/$(cat $pidfile)/cmdline 2>/dev/null | grep -a "${binary}" > /dev/null 2>&1 184 if [ $? -eq 0 ];then 185 # shortcut without requiring pgrep to search through all procs 186 return $OCF_SUCCESS 187 fi 188 fi 189 190 pid=$(pgrep ${binary}) 191 case $? in 192 0) 193 ocf_log info "PID file (pid:${pid} at $pidfile) created for ${binary}." 194 echo "$pid" > $pidfile 195 return $OCF_SUCCESS;; 196 1) 197 rm -f "$pidfile" > /dev/null 2>&1 198 ocf_log info "$binary is not running" 199 return $OCF_NOT_RUNNING;; 200 *) 201 rm -f "$pidfile" > /dev/null 2>&1 202 ocf_exit_reason "Error encountered detecting pid status of $binary" 203 return $OCF_ERR_GENERIC;; 204 esac 205} 206 207clvmd_status() 208{ 209 local rc 210 local mirror_rc 211 clvmd_validate 212 if [ $? -ne $OCF_SUCCESS ]; then 213 ocf_exit_reason "Unable to monitor, Environment validation failed." 214 return $? 215 fi 216 217 check_process $DAEMON 218 rc=$? 219 mirror_rc=$rc 220 221 if ocf_is_true $OCF_RESKEY_with_cmirrord; then 222 check_process $CMIRROR 223 mirror_rc=$? 224 fi 225 226 # If these ever don't match, return error to force recovery 227 if [ $mirror_rc -ne $rc ]; then 228 return $OCF_ERR_GENERIC 229 fi 230 231 return $rc 232} 233 234# NOTE: replace this with vgs, once display filter per attr is implemented. 235clustered_vgs() { 236 if ! ocf_is_true "$OCF_RESKEY_exclusive"; then 237 ${LVM_VGDISPLAY} 2>/dev/null | awk 'BEGIN {RS="VG Name"} {if (/Clustered/) print $1;}' 238 else 239 for vg in $(vgs --select "clustered=yes" -o name --noheadings); do 240 lvs --select lv_active=~'local.*exclusive' -o vg_name --noheadings $vg 2> /dev/null | awk '!seen[$1]++ {print $1}' 241 done 242 fi 243} 244 245wait_for_process() 246{ 247 local binary=$1 248 local timeout=$2 249 local count=0 250 251 ocf_log info "Waiting for $binary to exit" 252 while [ $count -le $timeout ]; do 253 check_process $binary 254 if [ $? -eq $OCF_NOT_RUNNING ]; then 255 ocf_log info "$binary terminated" 256 return $OCF_SUCCESS 257 fi 258 sleep 1 259 count=$((count+1)) 260 done 261 262 return $OCF_ERR_GENERIC 263} 264 265time_left() 266{ 267 local end=$1 268 local default=$2 269 local now=$SECONDS 270 local result=0 271 272 result=$(( $end - $now )) 273 if [ $result -lt $default ]; then 274 return $default 275 fi 276 return $result 277} 278 279clvmd_stop() 280{ 281 local LVM_VGS 282 local rc=$OCF_SUCCESS 283 local end=$(( $SECONDS + $CLVMD_TIMEOUT )) 284 285 clvmd_status 286 if [ $? -eq $OCF_NOT_RUNNING ]; then 287 return $OCF_SUCCESS 288 fi 289 290 check_process $DAEMON 291 if [ $? -ne $OCF_NOT_RUNNING ]; then 292 LVM_VGS="$(clustered_vgs)" 293 294 if [ -n "$LVM_VGS" ]; then 295 ocf_log info "Deactivating clustered VG(s):" 296 ocf_run ${LVM_VGCHANGE} -anl $LVM_VGS 297 if [ $? -ne 0 ]; then 298 ocf_exit_reason "Failed to deactivate volume groups, cluster vglist = $LVM_VGS" 299 return $OCF_ERR_GENERIC 300 fi 301 fi 302 303 ocf_log info "Signaling $DAEMON to exit" 304 killall -TERM $DAEMON 305 if [ $? != 0 ]; then 306 ocf_exit_reason "Failed to signal -TERM to $DAEMON" 307 return $OCF_ERR_GENERIC 308 fi 309 310 wait_for_process $DAEMON $CLVMD_TIMEOUT 311 rc=$? 312 if [ $rc -ne $OCF_SUCCESS ]; then 313 ocf_exit_reason "$DAEMON failed to exit" 314 return $rc 315 fi 316 317 rm -f $LOCK_FILE 318 fi 319 320 check_process $CMIRROR 321 if [ $? -ne $OCF_NOT_RUNNING ] && ocf_is_true $OCF_RESKEY_with_cmirrord; then 322 local timeout 323 ocf_log info "Signaling $CMIRROR to exit" 324 killall -INT $CMIRROR 325 326 time_left $end 10; timeout=$? 327 wait_for_process $CMIRROR $timeout 328 rc=$? 329 if [ $rc -ne $OCF_SUCCESS ]; then 330 killall -KILL $CMIRROR 331 time_left $end 10; timeout=$? 332 wait_for_process $CMIRROR $(time_left $end 10) 333 rc=$? 334 fi 335 fi 336 337 return $rc 338} 339 340start_process() 341{ 342 local binary_path=$1 343 local opts=$2 344 345 check_process "$(basename $binary_path)" 346 if [ $? -ne $OCF_SUCCESS ]; then 347 ocf_log info "Starting $binary_path: " 348 ocf_run $binary_path $opts 349 rc=$? 350 if [ $rc -ne 0 ]; then 351 ocf_exit_reason "Failed to launch $binary_path, exit code $rc" 352 exit $OCF_ERR_GENERIC 353 fi 354 fi 355 356 return $OCF_SUCCESS 357} 358 359clvmd_activate_all() 360{ 361 362 if ! ocf_is_true "$OCF_RESKEY_activate_vgs"; then 363 ocf_log info "skipping vg activation, activate_vgs is set to $OCF_RESKEY_activate_vgs" 364 return $OCF_SUCCESS 365 fi 366 # Activate all volume groups by leaving the 367 # "volume group name" parameter empty 368 ocf_run ${LVM_VGCHANGE} -aay 369 if [ $? -ne 0 ]; then 370 ocf_log info "Failed to activate VG(s):" 371 clvmd_stop 372 return $OCF_ERR_GENERIC 373 fi 374 return $OCF_SUCCESS 375} 376 377clvmd_start() 378{ 379 local rc=0 380 local CLVMDOPTS="-T${CLVMD_TIMEOUT} $OCF_RESKEY_daemon_options" 381 382 clvmd_validate 383 if [ $? -ne $OCF_SUCCESS ]; then 384 ocf_exit_reason "Unable to start, Environment validation failed." 385 return $? 386 fi 387 388 # systemd drop-in to stop process before storage services during 389 # shutdown/reboot 390 if systemd_is_running ; then 391 systemd_drop_in "99-clvmd" "After" "blk-availability.service" 392 fi 393 394 clvmd_status 395 if [ $? -eq $OCF_SUCCESS ]; then 396 ocf_log debug "$DAEMON already started" 397 clvmd_activate_all 398 return $?; 399 fi 400 401 # autoset locking type to clustered when lvmconf tool is available 402 if [ -x "$LVMCONF" ]; then 403 $LVMCONF --enable-cluster > /dev/null 2>&1 404 fi 405 406 # if either of these fail, script will exit OCF_ERR_GENERIC 407 if ocf_is_true $OCF_RESKEY_with_cmirrord; then 408 start_process $CMIRROR_PATH 409 fi 410 start_process $DAEMON_PATH "$CLVMDOPTS" 411 412 # Refresh local cache. 413 # 414 # It's possible that new PVs were added to this, or other VGs 415 # while this node was down. So we run vgscan here to avoid 416 # any potential "Missing UUID" messages with subsequent 417 # LVM commands. 418 419 # The following step would be better and more informative to the user: 420 # 'action "Refreshing VG(s) local cache:" ${LVM_VGSCAN}' 421 # but it could show warnings such as: 422 # 'clvmd not running on node x-y-z Unable to obtain global lock.' 423 # and the action would be shown as FAILED when in reality it didn't. 424 # Ideally vgscan should have a startup mode that would not print 425 # unnecessary warnings. 426 427 ${LVM_VGSCAN} > /dev/null 2>&1 428 touch $LOCK_FILE 429 430 clvmd_activate_all 431 432 clvmd_status 433 return $? 434} 435 436case $__OCF_ACTION in 437 meta-data) meta_data 438 exit $OCF_SUCCESS;; 439 440 start) clvmd_start;; 441 442 stop) clvmd_stop;; 443 444 monitor) clvmd_status;; 445 446 validate-all) clvmd_validate;; 447 448 usage|help) clvmd_usage;; 449 450 *) clvmd_usage 451 exit $OCF_ERR_UNIMPLEMENTED;; 452esac 453 454rc=$? 455ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : $rc" 456exit $rc 457 458