fs-lib.sh

#!/bin/bash
#
# Copyright (C) 1997-2003 Sistina Software, Inc.  All rights reserved.
# Copyright (C) 2004-2011 Red Hat, Inc.  All rights reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#

#
# File system common functions
#

LC_ALL=C
LANG=C
PATH=/bin:/sbin:/usr/bin:/usr/sbin
export LC_ALL LANG PATH

# Private return codes
FAIL=2
NO=1
YES=0
YES_STR="yes"

[ -z "$OCF_RESOURCE_INSTANCE" ] && export OCF_RESOURCE_INSTANCE="filesystem:$OCF_RESKEY_name"

#
# Using a global to contain the return value saves
# clone() operations.  This is important to reduce
# resource consumption during status checks.
#
# There is no way to return a string from a function
# in bash without cloning the process, which is exactly
# what we are trying to avoid.  So, we have to resort
# to using a dedicated global variable.  This one is
# for the real_device() function below.
#
declare REAL_DEVICE
declare LINK_DOWN
LINK_DOWN=0
#
# Stub ocf_log function for when we are using
# quick_status, since ocf_log generally forks (and
# sourcing ocf-shellfuncs forks -a lot-).
#
ocf_log()
{
 echo $*
}

#
# Assume NFS_TRICKS are not available until we are
# proved otherwise.
#
export NFS_TRICKS=1

#
# Quick status doesn't fork() or clone() when using
# device files directly.  (i.e. not symlinks, LABEL= or
# UUID=
#
if [ "$1" = "status" -o "$1" = "monitor" ] &&
   [ "$OCF_RESKEY_quick_status" = "1" ]; then
 echo Using Quick Status

 # XXX maybe we can make ocf-shellfuncs have a 'quick' mode too?
 export OCF_SUCCESS=0
 export OCF_ERR_GENERIC=1
else
 #
 # Grab nfs lock tricks if available
 #
 if [ -f "$(dirname $0)/svclib_nfslock" ]; then
  . $(dirname $0)/svclib_nfslock
  NFS_TRICKS=0
 fi

 . $(dirname $0)/ocf-shellfuncs
fi


verify_name()
{
 if [ -z "$OCF_RESKEY_name" ]; then
  ocf_log err "No file system name specified."
  return $OCF_ERR_ARGS
 fi
 return $OCF_SUCCESS
}


verify_mountpoint()
{
 if [ -z "$OCF_RESKEY_mountpoint" ]; then
  ocf_log err "No mount point specified."
  return $OCF_ERR_ARGS
 fi

 if ! [ -e "$OCF_RESKEY_mountpoint" ]; then
  ocf_log info "Mount point $OCF_RESKEY_mountpoint will be "\
    "created at mount time."
  return $OCF_SUCCESS
 fi

 [ -d "$OCF_RESKEY_mountpoint" ] && return $OCF_SUCCESS

 ocf_log err "$OCF_RESKEY_mountpoint exists but is not a directory."

 return $OCF_ERR_ARGS
}


#
# This used to be called using $(...), but doing this causes bash
# to set up a pipe and clone().  So, the output of this function is
# stored in the global variable REAL_DEVICE, declared previously.
#
real_device()
{
 declare dev="$1"
 declare realdev

 REAL_DEVICE=""

 [ -z "$dev" ] && return $OCF_ERR_ARGS

 # Oops, we have a link.  Sorry, this is going to fork.
 if [ -h "$dev" ]; then
  realdev=$(readlink -f $dev)
  if [ $? -ne 0 ]; then
   return $OCF_ERR_ARGS
  fi
  REAL_DEVICE="$realdev"
  return $OCF_SUCCESS
 fi

 # If our provided blockdev is a device, we are done
 if [ -b "$dev" ]; then
  REAL_DEVICE="$dev"
  return $OCF_SUCCESS
 fi

 # It's not a link, it's not a block device.  If it also
 # does not match UUID= or LABEL=, then findfs is not
 # going to find anything useful, so we should quit now.
 if [ "${dev/UUID=/}" = "$dev" ] &&
    [ "${dev/LABEL=/}" = "$dev" ]; then
  return $OCF_ERR_GENERIC
 fi

 # When using LABEL= or UUID=, we can't save a fork.
 realdev=$(findfs "$dev" 2> /dev/null)
 if [ -n "$realdev" ] && [ -b "$realdev" ]; then
  REAL_DEVICE="$realdev"
  return $OCF_SUCCESS
 fi

 return $OCF_ERR_GENERIC
}


verify_device()
{
 declare realdev

 if [ -z "$OCF_RESKEY_device" ]; then
        ocf_log err "No device or label specified."
        return $OCF_ERR_ARGS
 fi

 real_device "$OCF_RESKEY_device"
 realdev="$REAL_DEVICE"
 if [ -n "$realdev" ]; then
  if [ "$realdev" != "$OCF_RESKEY_device" ]; then
   ocf_log info "Specified $OCF_RESKEY_device maps to $realdev"
  fi
  return $OCF_SUCCESS
 fi

 ocf_log err "Device or label \"$OCF_RESKEY_device\" not valid"

 return $OCF_ERR_ARGS
}


#
# mount_in_use device mount_point
#
# Check to see if either the device or mount point are in use anywhere on
# the system.  It is not required that the device be mounted on the named
# moint point, just if either are in use.
#
mount_in_use () {
 declare mp tmp_mp
 declare dev tmp_dev
 declare junka junkb junkc junkd

 if [ $# -ne 2 ]; then
  ocf_log err "Usage: mount_in_use device mount_point".
  return $FAIL
 fi

 dev="$1"
 mp="$2"

 typeset proc_mounts=$(mktemp /tmp/fs.proc.mounts.XXXXXX)
 cat /proc/mounts > $proc_mounts

 while read -r tmp_dev tmp_mp junka junkb junkc junkd; do
  # XXX fork/clone warning XXX
  if [ "${tmp_dev:0:1}" != "-" ]; then
   tmp_dev="$(printf "$tmp_dev")"
  fi

  if [ -n "$tmp_dev" -a "$tmp_dev" = "$dev" ]; then
    case $OCF_RESKEY_fstype in
      cifs|nfs|nfs4)
        ;;
      *)
        return $YES
        ;;
    esac
  fi

  # Mountpoint from /proc/mounts containing spaces will
  # have spaces represented in octal.  printf takes care
  # of this for us.
  tmp_mp="$(printf "$tmp_mp")"

  if [ -n "$tmp_mp" -a "$tmp_mp" = "$mp" ]; then
   return $YES
  fi
 done < $proc_mounts
 rm -f $proc_mounts

 return $NO
}


#
# is_mounted device mount_point
#
# Check to see if the device is mounted.  Print a warning if its not
# mounted on the directory we expect it to be mounted on.
#
is_mounted () {

 declare mp tmp_mp
 declare dev tmp_dev
 declare ret=$FAIL
 declare found=1
 declare poss_mp

 if [ $# -ne 2 ]; then
  ocf_log err "Usage: is_mounted device mount_point"
  return $FAIL
 fi

 real_device "$1"
 dev="$REAL_DEVICE"
 if [ -z "$dev" ]; then
  ocf_log err "$OCF_RESOURCE_INSTANCE: is_mounted: Could not match $1 with a real device"
  return $OCF_ERR_ARGS
 fi

 if [ -h "$2" ]; then
  mp="$(readlink -f $2)"
 else
  mp="$2"
 fi

 ret=$NO

 # This bash glyph simply removes a trailing slash
 # if one exists.  /a/b/ -> /a/b; /a/b -> /a/b.
 mp="${mp%/}"

 typeset proc_mounts=$(mktemp /tmp/fs.proc.mounts.XXXXXX)
 cat /proc/mounts > $proc_mounts

 while read -r tmp_dev tmp_mp junk_a junk_b junk_c junk_d
 do
  # XXX fork/clone warning XXX
  if [ "${tmp_dev:0:1}" != "-" ]; then
   tmp_dev="$(printf "$tmp_dev")"
  fi

  # CIFS mounts can sometimes have trailing slashes
  # in their first field in /proc/mounts, so strip them.
  tmp_dev="$(echo $tmp_dev | sed 's/\/*$//g')"
  real_device "$tmp_dev"
  tmp_dev="$REAL_DEVICE"

  # XXX fork/clone warning XXX
  # Mountpoint from /proc/mounts containing spaces will
  # have spaces represented in octal.  printf takes care
  # of this for us.
  tmp_mp="$(printf "$tmp_mp")"

  if [ -n "$tmp_dev" -a "$tmp_dev" = "$dev" ]; then
   #
   # Check to see if its mounted in the right
   # place
   #
   if [ -n "$tmp_mp" ]; then
    if [ "$tmp_mp" != "$mp" ]; then
     poss_mp=$tmp_mp
    else
     found=0
    fi
   fi
   ret=$YES
  fi
 done < $proc_mounts
 rm -f $proc_mounts

 if [ $ret -eq $YES ] && [ $found -ne 0 ]; then
  case $OCF_RESKEY_fstype in
    cifs|nfs|nfs4)
      ret=$NO
      ;;
    *)
      ocf_log warn "Device $dev is mounted on $poss_mp instead of $mp"
      ;;
  esac
 fi


 return $ret
}


#
# is_alive mount_point
#
# Check to see if mount_point is alive (testing read/write)
#
is_alive()
{
 declare errcode
 declare mount_point="$1"
 declare file=".writable_test.$(hostname)"
 declare rw

 if [ $# -ne 1 ]; then
         ocf_log err "Usage: is_alive mount_point"
  return $FAIL
 fi

 [ -z "$OCF_CHECK_LEVEL" ] && export OCF_CHECK_LEVEL=0

 test -d "$mount_point"
 if [ $? -ne 0 ]; then
  ocf_log err "${OCF_RESOURCE_INSTANCE}: is_alive: $mount_point is not a directory"
  return $FAIL
 fi

 [ $OCF_CHECK_LEVEL -lt 10 ] && return $YES

 # depth 10 test (read test)
 ls "$mount_point" > /dev/null 2> /dev/null
 errcode=$?
 if [ $errcode -ne 0 ]; then
  ocf_log err "${OCF_RESOURCE_INSTANCE}: is_alive: failed read test on [$mount_point]. Return code: $errcode"
  return $NO
 fi

 [ $OCF_CHECK_LEVEL -lt 20 ] && return $YES

 # depth 20 check (write test)
 rw=$YES
 for o in `echo $OCF_RESKEY_options | sed -e s/,/\ /g`; do
                if [ "$o" = "ro" ]; then
          rw=$NO
                fi
 done
 if [ $rw -eq $YES ]; then
         file="$mount_point"/$file
  while true; do
   if [ -e "$file" ]; then
    file=${file}_tmp
    continue
   else
           break
   fi
  done
  touch $file > /dev/null 2> /dev/null
  errcode=$?
  if [ $errcode -ne 0 ]; then
   ocf_log err "${OCF_RESOURCE_INSTANCE}: is_alive: failed write test on [$mount_point]. Return code: $errcode"
   return $NO
  fi
  rm -f $file > /dev/null 2> /dev/null
 fi

 return $YES
}


#
# Decide which quota options are enabled and return a string
# which we can pass to quotaon
#
quota_opts()
{
 declare quotaopts=""
 declare opts="$1"
 declare mopt

 for mopt in `echo $opts | sed -e s/,/\ /g`; do
  case $mopt in
  quota)
   quotaopts="gu"
   break
   ;;
  usrquota)
   quotaopts="u$quotaopts"
   continue
   ;;
  grpquota)
   quotaopts="g$quotaopts"
   continue
   ;;
  noquota)
   quotaopts=""
   return 0
   ;;
  esac
 done

 echo $quotaopts
 return 0
}

 

#
# Enable quotas on the mount point if the user requested them
#
enable_fs_quotas()
{
 declare -i need_check=0
 declare -i rv
 declare quotaopts=""
 declare mopt
 declare opts="$1"
 declare mp="$2"

 if ! type quotaon &> /dev/null; then
  ocf_log err "quotaon not found in $PATH"
  return $OCF_ERR_GENERIC
 fi

 quotaopts=$(quota_opts $opts)
 [ -z "$quotaopts" ] && return 0

 ocf_log debug "quotaopts = $quotaopts"

 # Ok, create quota files if they don't exist
 for f in quota.user aquota.user quota.group aquota.group; do
  if ! [ -f "$mp/$f" ]; then
   ocf_log info "$mp/$f was missing - creating"
   touch "$mp/$f"
   chmod 600 "$mp/$f"
   need_check=1
  fi
 done

 if [ $need_check -eq 1 ]; then
  ocf_log info "Checking quota info in $mp"
  quotacheck -$quotaopts "$mp"
 fi

 ocf_log info "Enabling Quotas on $mp"
 ocf_log debug "quotaon -$quotaopts \"$mp\""
 quotaon -$quotaopts "$mp"
 rv=$?
 if [ $rv -ne 0 ]; then
  # Just a warning
  ocf_log warn "Unable to turn on quotas for $mp; return = $rv"
 fi

 return $rv
}


# Agent-specific actions to take before mounting
# (if required).  Typically things like fsck.
do_pre_mount() {
 return 0
}

# Default mount handler - for block devices
#
do_mount() {
 declare dev="$1"
 declare mp="$2"
 declare mount_options=""
 declare fstype_option=""
 declare fstype

 #
 # Get the filesystem type, if specified.
 #
 fstype_option=""
 fstype=${OCF_RESKEY_fstype}
 case "$fstype" in
 ""|"[  ]*")
  fstype=""
  ;;
 *) # found it
  fstype_option="-t $fstype"
  ;;
 esac

 #
 # Get the mount options, if they exist.
 #
 mount_options=""
 opts=${OCF_RESKEY_options}
 case "$opts" in
 ""|"[  ]*")
  opts=""
  ;;
 *) # found it
  mount_options="-o $opts"
  ;;
 esac

 #
 # Mount the device
 #
 ocf_log info "mounting $dev on $mp"
 ocf_log err "mount $fstype_option $mount_options $dev $mp"
 mount $fstype_option $mount_options "$dev" "$mp"
 ret_val=$?
 if [ $ret_val -ne 0 ]; then
  ocf_log err "\
'mount $fstype_option $mount_options $dev $mp' failed, error=$ret_val"
  return 1
 fi

 return 0
}


# Agent-specific actions to take after mounting
# (if required).
do_post_mount() {
 return 0
}


# Agent-specific actions to take before unmounting
# (if required)
do_pre_unmount() {
 return 0
}


# Agent-specific actions to take after umount succeeds
# (if required)
do_post_unmount() {
 return 0
}


# Agent-specific force-unmount logic, if required
# return = nonzero if successful, or 0 if unsuccessful
# (unsuccessful = try harder)
do_force_unmount() {
 return 1
}


#
# start_filesystem
#
start_filesystem() {
 declare -i ret_val=$OCF_SUCCESS
 declare mp="${OCF_RESKEY_mountpoint}"
 declare dev=""   # device
 declare fstype=""
 declare opts=""
 declare mount_options=""

 #
 # Check if mount point was specified.  If not, no need to continue.
 #
 case "$mp" in
 ""|"[  ]*")  # nothing to mount
  return $OCF_SUCCESS
  ;;
 /*)   # found it
  ;;
 *)   # invalid format
   ocf_log err \
"start_filesystem: Invalid mount point format (must begin with a '/'): \'$mp\'"
  return $OCF_ERR_ARGS
  ;;
 esac

 #
 # Get the device
 #
 real_device "$OCF_RESKEY_device"
 dev="$REAL_DEVICE"
 if [ -z "$dev" ]; then
   ocf_log err "\
start_filesystem: Could not match $OCF_RESKEY_device with a real device"
   return $OCF_ERR_ARGS
 fi

 #
 # Ensure we've got a valid directory
 #
 if [ -e "$mp" ]; then
  if ! [ -d "$mp" ]; then
   ocf_log err"\
start_filesystem: Mount point $mp exists but is not a directory"
   return $OCF_ERR_ARGS
  fi
 else
  ocf_log err "\
start_filesystem: Creating mount point $mp for device $dev"
  mkdir -p "$mp"
  ret_val=$?
  if [ $ret_val -ne 0 ]; then
   ocf_log err "\
start_filesystem: Unable to create $mp.  Error code: $ret_val"
   return $OCF_ERR_GENERIC
  fi
 fi

 #
 # See if the device is already mounted.
 #
 is_mounted "$dev" "$mp"
 case $? in
 $YES)  # already mounted
  ocf_log debug "$dev already mounted"
  return $OCF_SUCCESS
  ;;
 $NO)  # not mounted, continue
  ;;
 *)
  return $FAIL
  ;;
 esac


 #
 # Make sure that neither the device nor the mount point are mounted
 # (i.e. they may be mounted in a different location).  The'mount_in_use'
 # function checks to see if either the device or mount point are in
 # use somewhere else on the system.
 #
 mount_in_use "$dev" "$mp"
 case $? in
 $YES)  # uh oh, someone is using the device or mount point
  ocf_log err "\
Cannot mount $dev on $mp, the device or mount point is already in use!"
  return $FAIL
  ;;
 $NO)  # good, no one else is using it
  ;;
 $FAIL)
  return $FAIL
  ;;
 *)
  ocf_log err "Unknown return from mount_in_use"
  return $FAIL
  ;;
 esac

 do_pre_mount
 case $? in
 0)
  ;;
 1)
  return $OCF_ERR_GENERIC
  ;;
 2)
  return $OCF_SUCCESS
  ;;
 esac

 do_mount "$dev" "$mp"
 case $? in
 0)
  ;;
 1)
  return $OCF_ERR_GENERIC
  ;;
 2)
  return $OCF_SUCCESS
  ;;
 esac

 do_post_mount
 case $? in
 0)
  ;;
 1)
  return $OCF_ERR_GENERIC
  ;;
 2)
  return $OCF_SUCCESS
  ;;
 esac

 enable_fs_quotas "$opts" "$mp"

 return $OCF_SUCCESS
}


#
# stop_filesystem - unmount a file system; calls out to
#
stop_filesystem() {
 declare -i ret_val=0
 declare -i try
 declare -i sleep_time=5  # time between each umount failure
 declare umount_failed=""
 declare force_umount=""
 declare self_fence=""
 declare quotaopts=""

 #
 # Get the mount point, if it exists.  If not, no need to continue.
 #
 mp=${OCF_RESKEY_mountpoint}
 case "$mp" in
 ""|"[  ]*")  # nothing to mount
  return $OCF_SUCCESS
  ;;
 /*)   # found it
  ;;
 *)  # invalid format
   ocf_log err \
"stop_filesystem: Invalid mount point format (must begin with a '/'): \'$mp\'"
  return $FAIL
  ;;
 esac

 #
 # Get the device
 #
 dflen=$(df -h | grep "$dev" | grep "oracle")
 LEN=$(expr length "$dflen")
 real_device "$OCF_RESKEY_device"
 dev="$REAL_DEVICE"
 if [ -z "$dev" -a "$LEN" -le 0 ]; then
   ocf_log err "\
stop: Could not match $OCF_RESKEY_device with a real device"
   return $OCF_ERR_INSTALLED
 fi
 if [ -z "$dev" ]; then
  if [ "$LEN" -gt 0 ]; then
   LINK_DOWN=1
  fi
 fi

 #
 # Get the force unmount setting if there is a mount point.
 #
 case ${OCF_RESKEY_force_unmount} in
        $YES_STR) force_umount=$YES ;;
 on)  force_umount=$YES ;;
 true)  force_umount=$YES ;;
 1)  force_umount=$YES ;;
        *)  force_umount="" ;;
 esac

 case ${OCF_RESKEY_self_fence} in
        $YES_STR) self_fence=$YES ;;
 on)  self_fence=$YES ;;
 true)  self_fence=$YES ;;
 1)  self_fence=$YES ;;
        *)  self_fence="" ;;
 esac

 do_pre_unmount
 case $? in
 0)
  ;;
 1)
  return $OCF_ERR_GENERIC
  ;;
 2)
  return $OCF_SUCCESS
  ;;
 esac

 #
 # Preparations: sync, turn off quotas
 #
 sync

 quotaopts=$(quota_opts $OCF_RESKEY_options)
 if [ -n "$quotaopts" ]; then
  ocf_log debug "Turning off quotas for $mp"
  quotaoff -$quotaopts "$mp" &> /dev/null
 fi

 #
 # Unmount the device.
 #
 for try in 1 2 3; do
  if [ $try -ne 1 ]; then
   sleep $sleep_time
  fi
 if [ "$LINK_DOWN" -ne 1 ];then
  is_mounted "$dev" "$mp"
 fi
  case $? in
  $NO)
   ocf_log info "$dev is not mounted"
   umount_failed=
   break
   ;;
  $YES) # fallthrough
   ;;
  *)
   return $FAIL
   ;;
  esac

  ocf_log info "unmounting $mp"
  umount "$mp"
  ret_val=$?
  if  [ $ret_val -eq 0 ]; then
   umount_failed=
   break
  fi

  ocf_log debug "umount failed: $ret_val"
  umount_failed=yes

  if [ -z "$force_umount" ]; then
   continue
  fi

  # Force unmount: try #1: send SIGTERM
  if [ $try -eq 1 ]; then
   # Try fs-specific force-unmount, if provided
   do_force_unmount
   if [ $? -eq 0 ]; then
    # if this succeeds, we should be done
    continue
   fi

   ocf_log warning "Sending SIGTERM to processes on $mp"
   fuser -TERM -kvm "$mp"
   continue
  else
   ocf_log warning "Sending SIGKILL to processes on $mp"
   fuser -kvm "$mp"

   case $? in
   0)
    ;;
   1)
    return $OCF_ERR_GENERIC
    ;;
   2)
    break
    ;;
   esac
  fi
 done # for

 do_post_unmount
 case $? in
 0)
  ;;
 1)
  return $OCF_ERR_GENERIC
  ;;
 2)
  return $OCF_SUCCESS
  ;;
 esac

 if [ -n "$umount_failed" ]; then
  ocf_log err "'umount $mp' failed, error=$ret_val"

  if [ "$self_fence" ]; then
   ocf_log alert "umount failed - REBOOTING"
   sync
   reboot -fn
  fi
  return $OCF_ERR_GENERIC
 fi

 return $OCF_SUCCESS
}


do_start() {
 declare tries=0
 declare rv

 while [ $tries -lt 3 ]; do
  start_filesystem
  rv=$?
  if [ $rv -eq 0 ]; then
   return 0
  fi

  ((tries++))
  sleep 3
 done
 return $rv
}


do_stop() {
 stop_filesystem
 return $?
}


do_monitor() {
 ocf_log debug "Checking fs \"$OCF_RESKEY_name\", Level $OCF_CHECK_LEVEL"

 #
 # Get the device
 #
 real_device "$OCF_RESKEY_device"
 dev="$REAL_DEVICE"
 if [ -z "$dev" ]; then
   ocf_log err "\
start_filesystem: Could not match $OCF_RESKEY_device with a real device"
   return $OCF_NOT_RUNNING
 fi

 is_mounted "$dev" "${OCF_RESKEY_mountpoint}"

 if [ $? -ne $YES ]; then
  ocf_log err "${OCF_RESOURCE_INSTANCE}: ${OCF_RESKEY_device} is not mounted on ${OCF_RESKEY_mountpoint}"
  return $OCF_NOT_RUNNING
 fi

 if [ "$OCF_RESKEY_quick_status" = "1" ]; then
  return 0
 fi

 is_alive "${OCF_RESKEY_mountpoint}"
 [ $? -eq $YES ] && return 0

 ocf_log err "fs:${OCF_RESKEY_name}: Mount point is not accessible!"
 return $OCF_ERR_GENERIC
}


do_restart() {
 stop_filesystem
 if [ $? -ne 0 ]; then
  return $OCF_ERR_GENERIC
 fi

 start_filesystem
 if [ $? -ne 0 ]; then
  return $OCF_ERR_GENERIC
 fi

 return 0
}


# MUST BE OVERRIDDEN
do_metadata() {
 return 1
}


do_validate() {
 return 1
}


main() {
 case $1 in
 start)
  do_start
  exit $?
  ;;
 stop)
  do_stop
  exit $?
  ;;
 status|monitor)
  do_monitor
  exit $?
  ;;
 restart)
  do_restart
  exit $?
  ;;
 meta-data)
  do_metadata
  exit $?
  ;;
 validate-all)
  do_validate
  ;;
 *)
  echo "usage: $0 {start|stop|status|monitor|restart|meta-data|validate-all}"
  exit $OCF_ERR_UNIMPLEMENTED
  ;;
 esac
 exit 0
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值