diff --git a/.hgtags b/.hgtags index ee99fe62e..fb43b9c0e 100644 --- a/.hgtags +++ b/.hgtags @@ -1,31 +1,37 @@ 19d388079befe5d069c73bccd043f1db4a95413a STABLE-2.0.5 +1b9ca9365728029a9972b4c4f0102d8373bfcd57 sle11-rc7 1eeb9262052a95e31d3d2e2a1eed1009dae4464d obs-2.1.2-1 1eeb9262052a95e31d3d2e2a1eed1009dae4464d obs-2.1.2-2 +24641938d44a65f7430ce734e8ffbd4ff43b8aa5 sle11-rc5 2bae9adf9c098126635e835b0c2f07695a56b820 Beta-0.4.9f 35135f93dd8695702d9bd1b44504788373bf73df STABLE-2.0.2 425aa3d2b818ae952d425e732942f18d8439f9ef STABLE-2.0.1 483281e0bacaade2c87ee43a5a28d49976eef47e STABLE-2.0.8 4ed7779c480c1db676b4d1af6d8db1622657c3f3 STABLE-2.0.0 52b6be2a2ef73fe262114a1e57a846ab946d3b0b Beta-0.4.9a 5ac6b8584d5bee0326ebf5a65a12d4c148b5dced STABLE-2.1.2 +6c6dff3aa6132de8c8263904a6eb88ebeb6b93ec sle11-beta6 6e879c884437689d171cab5bded84c806c06db27 beta-2.99.2 7c19f502341b2e9758b23adcf3c98fc90384372b STABLE-2.0.6 7d996df373c5b6a9eb58590a5e0a5f8a2ad105e6 STABLE-0.4.9c +7ddf73677956f6233b7f6ec9fc4827f5a98985d3 sle11-rc3 936a8b0eb71a838e9e50b5fd66be7fce72e5ad24 STABLE-2.1.3 9c8d66dd24d7e71c581fe29016eab5a48c7373a1 STABLE-1.1.3 +9fd049c9227bca051cc059e730a3ebc87bfaf42e sle11-rc2 a47a67cd81eac0fbbabb7dbf5a2d5d2f0589d227 STABLE-1.1.5 a6a918342b3428eba91f665b66e6f16e799cb554 obs-2.1.2-24 aba8153b997d328df4d6a7fce4ffc868137594dc SLE10-SP1 b50c26ac8a7e0ece683c9a4529da22d26f2f196e obs-2.1.2-4 b9347ad6361db4177fc0e638331f6ed057058c3e Series-Root-1.2 ba23f61326bfaf94b4fb942599bd0f3e912cb38c beta-2.99.1 c52a0034b36cffb5241d4a709e48ece432a6ee27 STABLE-2.1.1 ccb84a8849e1923a8a97e6e1da9cd03a91f96309 beta-2.99.0 +cd07c521f1b9de268a5eff2e104d4a668be0acc7 sle11-rc9 ceead2d2c5dead2806db48dadf70d797d182f049 obs-2.1.2-15 db31ab54db1582b801e3661208ad9fb349266dda STABLE-2.0.4 e3a51620237fb51b365b1330bcaaba6bf2d40fc7 Series-Root-1.0 f20380d19c0f2f6ee18b7f30c7b748524836a9a8 STABLE-2.0.3 f840eaae3e1a1a24f2d80c8f1610a024cdf78975 STABLE-2.1.0 f8b0200f7de56073525cdba2c0f38f239598cb71 SLES10-GA-2.0.7-1.5 f8b0200f7de56073525cdba2c0f38f239598cb71 STABLE-2.0.7 fd0b7fa32b84489d1d55739732ee29fe2fba14f2 STABLE-0.4.9e diff --git a/configure.in b/configure.in index c21b3a735..016838db8 100644 --- a/configure.in +++ b/configure.in @@ -1,917 +1,917 @@ dnl dnl autoconf for heartbeat dnl Started by David Lee December 2000 dnl automake stuff dnl added by Michael Moerz February 2001 dnl dnl License: GNU General Public License (GPL) dnl Initialiase, with sanity check of a unique file in the hierarchy AC_INIT(configure.in) AC_PREREQ(2.53) AC_CONFIG_AUX_DIR(.) AC_REVISION($Revision: 1.552 $) dnl cvs revision AC_CANONICAL_HOST dnl Where #defines go (e.g. `AC_CHECK_HEADERS' below) dnl dnl Internal header: include/config.h dnl - Contains ALL defines dnl - include/config.h.in is generated automatically by autoheader dnl - NOT to be included in any header files except lha_internal.h dnl (which is also not to be included in any other header files) dnl dnl External header: include/hb_config.h dnl - Contains a subset of defines checked here dnl - Manually edit include/hb_config.h.in to have configure include dnl new defines dnl - Should not include HAVE_* defines dnl - Safe to include anywhere AM_CONFIG_HEADER(include/config.h include/agent_config.h) ALL_LINGUAS="en fr" AC_ARG_WITH(hapkgversion, [ --with-hapkgversion=name Override package version (if you're a packager needing to pretend) ], [ HAPKGVERSION="$withval" ], [ HAPKGVERSION="" ], ) if test -z "$HAPKGVERSION" ; then HAPKGVERSION="0.7" fi AM_INIT_AUTOMAKE(agents, $HAPKGVERSION) HB_PKG=heartbeat CC_IN_CONFIGURE=yes export CC_IN_CONFIGURE echo Our Host OS: $host_os/$host dnl This OS-based decision-making is poor autotools practice; dnl feature-based mechanisms are strongly preferred. dnl dnl So keep this section to a bare minimum; regard as a "necessary evil". pf_argv_set="" case "$host_os" in *bsd*) AC_DEFINE_UNQUOTED(ON_BSD, 1, Compiling for BSD platform);; *linux*) AC_DEFINE_UNQUOTED(ON_LINUX, 1, Compiling for Linux platform);; darwin*) AC_DEFINE_UNQUOTED(ON_DARWIN, 1, Compiling for Darwin platform);; *solaris*) AC_DEFINE_UNQUOTED(ON_SOLARIS, 1, Compiling for Solaris platform);; esac LIBS="$LIBS -L${prefix}/lib" CFLAGS="$CFLAGS -I${prefix}/include" AC_SUBST(REBOOT_OPTIONS) AC_SUBST(POWEROFF_OPTIONS) AC_DEFINE_UNQUOTED(REBOOT_OPTIONS, "$REBOOT_OPTIONS", reboot options) AC_DEFINE_UNQUOTED(POWEROFF_OPTIONS, "$POWEROFF_OPTIONS", poweroff options) dnl Info for building/packaging systems. dnl dnl "pkg" (typically Solaris) generally suggests package names of the form: dnl COMPname dnl where: dnl COMP: (upper case) resembles a four character company "stock ticker"; dnl name: (lower case) is short-form (few character) product name. dnl dnl It is also conventional for the name to be closely related to the dnl installation location, typically "/opt/COMPname". dnl dnl See "linux-ha-dev" discussion, "heartbeat package name", from 12/Oct/2005. dnl "LXHAhb" seems the least evil compromise for a default. dnl Any site or packager may, of course, override this. dnl AC_ARG_WITH(pkgname, [ --with-pkgname=name name for pkg (typically for Solaris) ], [ PKGNAME="$withval" ], [ PKGNAME="LXHAhb" ], ) AC_SUBST(PKGNAME) dnl Keep copy of original (default) prefix and localstatedir localstatedir_orig="$localstatedir" prefix_orig="$prefix" prefix=`eval echo "$prefix"` case $prefix in NONE) prefix=/usr/local;; esac var() { case $1 in *'${'*) res=`eval echo "$1"`;; *) res="$1";; esac case "$res" in ""|NONE) echo "$2";; *) echo "$res";; esac } dnl expand directories exec_prefix=`var "$exec_prefix" "$prefix"` bindir=`var "$bindir" "$exec_prefix/bin"` sbindir=`var "$sbindir" "$exec_prefix/sbin"` datarootdir=`var "$datarootdir" "$prefix/share"` datadir=`var "$datadir" "$prefix/share"` sysconfdir=`var "$sysconfdir" "$prefix/etc"` sharedstatedir=`var "$sharedstatedir" "$prefix/com"` localstatedir=`var "$localstatedir" "$prefix/var"` includedir=`var "$includedir" "$exec_prefix/include"` oldincludedir=`var "$oldincludedir" "$exec_prefix/include"` infodir=`var "$infodir" "$prefix/info"` mandir=`var "$mandir" "$exec_prefix/man"` docdir=${datadir}/doc/${HB_PKG}-${VERSION} +stdocdir=${datadir}/doc/${HB_PKG}-${VERSION}/stonith libdir=`var "$libdir" "$exec_prefix/lib"` libexecdir=`var "$libexecdir" "$exec_prefix/libexec"` noarchlibdir=`var "$noarchlibdir" "$prefix/lib"` LDD=ldd dnl Which C compiler? dnl Defaults to GNU C compiler if available. dnl Always tries to set the compiler to ANSI C via options (AM) dnl Can force other with environment variable "CC". AC_PROG_CC AC_PROG_CC_STDC AM_PROG_CC_C_O dnl The GNU conventions for installation directories don't always dnl sit well with this software. In particular, GNU's stated: dnl dnl '$(localstatedir)' should normally be '/usr/local/var', ... dnl dnl is poor for us: much better would be somewhere under '/var'. dnl dnl Here within "configure" it would be poor practice for us to interfere dnl with such values, irrespective of our opinion: dnl 1. user perspective: we would have gone behind their back; dnl 2. autoconf perspective: autoconf should avoid any OS-specific mindset. dnl dnl So if localstatedir still has its default value, we issue an advisory dnl warning and inform folk of our own "ConfigureMe", which is ideally dnl suited for setting such aspects (by user, and per-OS). dnl dnl [ Another option would be to detect, and to warn of, (post-expansion) dnl non-"/var/[...]" values: something like: dnl if test `expr "$localstatedir" : '^/var/.*'` -ge '5' \ dnl -o `expr "$localstatedir" : '^/var.*'` -eq '4' dnl then else fi dnl ] # If original localstatedir had defaulted then sanity-check the result. if test "x$localstatedir_orig" = 'x${prefix}/var' && # Note quote types test "x$prefix_orig" = xNONE then SNOOZING=10 AC_MSG_WARN(value/default "--localstatedir=$localstatedir" is poor.) AC_MSG_WARN("/var/something" is strongly recommended.) AC_MSG_WARN(We also recommend using "ConfigureMe".) AC_MSG_WARN(Sleeping for $SNOOZING seconds.) sleep $SNOOZING fi HA_LIBDIR="${libdir}" AC_SUBST(HA_LIBDIR) AC_DEFINE_UNQUOTED(HA_LIBDIR,"$HA_LIBDIR", lib directory) HA_DATADIR="${datadir}" AC_SUBST(HA_DATADIR) AC_DEFINE_UNQUOTED(HA_DATADIR,"$HA_DATADIR", data (arch-independent) directory) HA_NOARCHDATAHBDIR="$HA_DATADIR/${HB_PKG}" AC_SUBST(HA_NOARCHDATAHBDIR) AC_DEFINE_UNQUOTED(HA_NOARCHDATAHBDIR,"$HA_NOARCHDATAHBDIR", $HB_PKG noarch data directory) HA_VARRUNDIR="${localstatedir}/run" AC_SUBST(HA_VARRUNDIR) AC_DEFINE_UNQUOTED(HA_VARRUNDIR,"$HA_VARRUNDIR", var run directory) HA_VARRUNHBDIR="$HA_VARRUNDIR/${HB_PKG}" AC_SUBST(HA_VARRUNHBDIR) AC_DEFINE_UNQUOTED(HA_VARRUNHBDIR,"$HA_VARRUNHBDIR", var run heartbeat directory) HA_VARRUNHBRSCDIR="$HA_VARRUNHBDIR/rsctmp" AC_SUBST(HA_VARRUNHBRSCDIR) AC_DEFINE_UNQUOTED(HA_VARRUNHBRSCDIR,"$HA_VARRUNHBRSCDIR", var run heartbeat rsctmp directory) dnl Test the default first (priority) and last (to make sure some value is set) AC_MSG_CHECKING(for the location of the lock directory) for HA_VARLOCKDIR in ${localstatedir}/lock ${localstatedir}/spool/lock ${localstatedir}/spool/locks ${localstatedir}/lock do if test -d "$HA_VARLOCKDIR" then AC_MSG_RESULT($HA_VARLOCKDIR) break fi done AC_SUBST(HA_VARLOCKDIR) AC_DEFINE_UNQUOTED(HA_VARLOCKDIR,"$HA_VARLOCKDIR", System lock directory) # # Other interesting variables: ${host_vendor} and ${host_os} # sample values: suse linux # dnl ************************************************************************* PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin" export PATH dnl checks for programs dnl AM_PATH_PYTHON dnl Replacing AC_PROG_LIBTOOL with AC_CHECK_PROG because LIBTOOL dnl was NOT being expanded all the time thus causing things to fail. AC_CHECK_PROGS(MAKE, gmake make) AC_MSG_CHECKING(for gmake or make) if test x"${MAKE}" = x""; then AC_MSG_ERROR(not found) else AC_MSG_RESULT($MAKE) fi AC_PATH_PROGS(HTML2TXT, lynx w3m) case $HTML2TXT in */*) ;; *) HTML2TXT="";; esac AC_PATH_PROGS(POD2MAN, pod2man, pod2man) AC_PATH_PROGS(ROUTE, route) AC_PATH_PROGS(RPM, rpmbuild) if test x"${RPM}" = x""; then AC_PATH_PROGS(RPM, rpm) fi AC_DEFINE_UNQUOTED(ROUTE, "$ROUTE", path to route command) AC_PATH_PROGS(NETSTAT, netstat, /sbin/netstat) AC_DEFINE_UNQUOTED(NETSTAT, "$NETSTAT", path to the netstat command) AC_PATH_PROGS(PING, ping, /bin/ping) AC_PATH_PROGS(IFCONFIG, ifconfig, /sbin/ifconfig) AC_PATH_PROGS(SSH, ssh, /usr/bin/ssh) AC_PATH_PROGS(SCP, scp, /usr/bin/scp) AC_PATH_PROGS(PYTHON, python) dnl Need a "mail" program which can handle "-s subject" flag. dnl Ideally would write a suitable autoconf test here. dnl Pragmatically observe: dnl Solaris: "mail" doesn't offer "-s", but "mailx" does. Want "mailx". dnl Linux: "mail" offers "-s". Some releases have "mailx" symlink to "mail". dnl *BSD: Online man pages suggest similar to Linux. dnl HP-UX: Online man pages suggest similar to Solaris. dnl Thus "mailx", if it exists, should always be good; look for it first. AC_PATH_PROGS(MAILCMD, mailx mail) AC_SUBST(MAILCMD) AC_DEFINE_UNQUOTED(IFCONFIG, "$IFCONFIG", path to the ifconfig command) AC_PATH_PROGS(GETENT, getent) AC_PATH_PROGS(IP2UTIL, ip, /sbin/ip) AC_PATH_PROGS(XML2CONFIG, xml2-config) AC_PATH_PROGS(REBOOT, reboot, /sbin/reboot) AC_PATH_PROGS(POWEROFF_CMD, poweroff, /sbin/poweroff) AC_SUBST(REBOOT) AC_SUBST(POWEROFF_CMD) AC_DEFINE_UNQUOTED(REBOOT, "$REBOOT", path to the reboot command) AC_DEFINE_UNQUOTED(POWEROFF_CMD, "$POWEROFF_CMD", path to the poweroff command) dnl "whoami", if it exists, may be hidden away on some System-V (e.g. Solaris) AC_PATH_PROGS(WHOAMI, whoami, , ${PATH}:/usr/ucb) AC_PATH_PROGS(SWIG, swig) AC_SUBST(SWIG) AC_PATH_PROGS(EGREP, egrep) AC_SUBST(EGREP) AC_PATH_PROGS(MSGFMT, msgfmt, [msgfmt not found],) AC_SUBST(MSGFMT) AC_PATH_PROGS(HG, hg, /usr/local/hg) AC_SUBST(HG) AC_PATH_PROGS(GZIP_PROG, gzip) AC_PATH_PROGS(TAR, tar) AC_PATH_PROGS(MD5, md5) AC_SUBST(GZIP_PROG) AC_SUBST(TAR) AC_SUBST(MD5) dnl The "test" program can be different from the "sh" builtin. AC_PATH_PROGS(TEST, test) AC_PATH_PROGS(PKGCONFIG, pkg-config) dnl ************************************************************************ dnl Check whether non-root user can chown. dnl ************************************************************************ if test -n "$WHOAMI"; then IAM=`$WHOAMI` fi AC_MSG_CHECKING(if chown works for non-root) F="./.chown-test.$$" touch $F if case "$IAM" in root) chown nobody $F; su nobody -c "chown root $F";; *) chown root $F esac >/dev/null 2>&1 then nonroot_chown=yes AC_MSG_RESULT(yes) else nonroot_chown=no AC_MSG_RESULT(no) fi rm -f $F AM_CONDITIONAL(NONROOT_CHOWN, test "$nonroot_chown" = yes ) EXTRAGLIBMSG="-" AC_MSG_CHECKING(for pkg-config) if test "X${PKGCONFIG}" = "X"; then AC_MSG_ERROR(not found) fi AC_MSG_RESULT(${PKGCONFIG}) GPKGNAME="glib-2.0" AC_MSG_CHECKING(for glib configuration) if test "X${PKGCONFIG}" != "X" && $PKGCONFIG --exists $GPKGNAME; then GLIBCONFIG="$PKGCONFIG $GPKGNAME" else set -x echo PKG_CONFIG_PATH=$PKG_CONFIG_PATH $PKGCONFIG --exists $GPKGNAME; echo $? $PKGCONFIG --cflags $GPKGNAME; echo $? $PKGCONFIG $GPKGNAME; echo $? set +x AC_MSG_ERROR(not found) fi AC_MSG_RESULT(using $GLIBCONFIG) AC_ARG_ENABLE([libnet], [ --enable-libnet Use libnet for ARP based funcationality, [default=try]], [enable_libnet=$withval], [enable_libnet=try]) libnet="" libnet_version="none" LIBNETLIBS="" LIBNETDEFINES="" AC_MSG_CHECKING(if libnet is required) libnet_fatal=$enable_libnet case $enable_libnet in no) ;; yes|libnet10|libnet11|10|11) libnet_fatal=yes;; try) case $host_os in *Linux*|*linux*) libnet_fatal=no;; *) libnet_fatal=yes;; dnl legacy behavior esac ;; *) libnet_fatal=yes; enable_libnet=try;; esac AC_MSG_RESULT($libnet_fatal) if test "x$enable_libnet" != "xno"; then AC_PATH_PROGS(LIBNETCONFIG, libnet-config) AC_CHECK_LIB(nsl, t_open) dnl -lnsl AC_CHECK_LIB(socket, socket) dnl -lsocket AC_CHECK_LIB(net, libnet_get_hwaddr, LIBNETLIBS=" -lnet", []) fi AC_MSG_CHECKING(for libnet) if test "x$LIBNETLIBS" != "x" -o "x$enable_libnet" = "xlibnet11"; then LIBNETDEFINES="" if test "$ac_cv_lib_nsl_t_open" = yes; then LIBNETLIBS="-lnsl $LIBNETLIBS" fi if test "$ac_cv_lib_socket_socket" = yes; then LIBNETLIBS="-lsocket $LIBNETLIBS" fi libnet=net libnet_version="libnet1.1" fi if test "x$enable_libnet" = "xtry" -o "x$enable_libnet" = "xlibnet10"; then if test "x$LIBNETLIBS" = x -a "x${LIBNETCONFIG}" != "x" ; then LIBNETDEFINES="`$LIBNETCONFIG --defines` `$LIBNETCONFIG --cflags`"; LIBNETLIBS="`$LIBNETCONFIG --libs`"; libnet_version="libnet1.0 (old)" case $LIBNETLIBS in *-l*) libnet=`echo $LIBNETLIBS | sed 's%.*-l%%'`;; *) libnet_version=none;; esac CPPFLAGS="$CPPFLAGS $LIBNETDEFINES" AC_CHECK_HEADERS(libnet.h) if test "$ac_cv_header_libnet_h" = no; then libnet_version=none fi fi fi AC_MSG_RESULT(found $libnet_version) if test "$libnet_version" = none; then LIBNETLIBS="" LIBNETDEFINES="" if test $libnet_fatal = yes; then AC_MSG_ERROR(libnet not found) fi else AC_CHECK_LIB($libnet,libnet_init, [new_libnet=yes; AC_DEFINE(HAVE_LIBNET_1_1_API, 1, Libnet 1.1 API)], [new_libnet=no; AC_DEFINE(HAVE_LIBNET_1_0_API, 1, Libnet 1.0 API)],$LIBNETLIBS) fi dnl ************************************************************************ dnl * Check for netinet/icmp6.h to enable the IPv6addr resource agent AC_CHECK_HEADERS(netinet/icmp6.h,[],[],[#include ]) AM_CONDITIONAL(USE_IPV6ADDR, test "$ac_cv_header_netinet_icmp6_h" = yes -a "$new_libnet" = yes ) sendarp_linux=0 case $host_os in *Linux*|*linux*) sendarp_linux=1;; esac AC_SUBST(LIBNETLIBS) AC_SUBST(LIBNETDEFINES) AC_DEFINE(HAVE_LIBNET_API, test "x$libnet_version" != "xnone", Libnet API) AM_CONDITIONAL(SENDARP_LINUX, test $sendarp_linux = 1 ) AM_CONDITIONAL(USE_LIBNET, test "x$libnet_version" != "xnone" ) AC_MSG_CHECKING(where is python installed) if test "x${PYTHON}" = x; then PYTHON="/usr/bin/env python"; fi AC_MSG_RESULT(using $PYTHON); AC_MSG_CHECKING(if netstat supports -f inet flag) $NETSTAT -rn -f inet > /dev/null 2>&1 if test x"$?" = x"0"; then AC_DEFINE(NETSTATPARM, "-rn -f inet ", parameters to netstat to retrieve route information) AC_MSG_RESULT(yes) else AC_DEFINE(NETSTATPARM, "-rn ", parameters to netstat to retrieve route information) AC_MSG_RESULT(no) fi AC_MSG_CHECKING(for ping) if test X${PING} = X then AC_MSG_ERROR(not found) fi AC_MSG_RESULT($PING) dnl Determine if we use -w1 or -t1 for ping (in PYTHON Scripts) AC_MSG_CHECKING(ping option for timeout - w or t) for PING_TIMEOUT_OPT in "-w1" "-t1" do $PING -nq -c1 $PING_TIMEOUT_OPT localhost > /dev/null 2>&1 if test "$?" = 0 then AC_DEFINE_UNQUOTED(PING_TIMEOUT_OPT, "$PING_TIMEOUT_OPT", option for ping timeout) AC_MSG_RESULT($PING_TIMEOUT_OPT) break fi done AC_SUBST(PING_TIMEOUT_OPT) dnl Determining a route (particularly for "findif"). dnl There are various mechanisms on different systems. dnl Some mechanisms require root access to evaluate, but configure is often dnl (indeed, some say should be always) running non-root. dnl dnl Therefore much of the determination has to be run-time. dnl So the principle here, at configure time, is to explore what might be dnl available, and offer as much as possible to run-time. dnl AC_DEFINE(ROUTEPARM, "-n get", paramters for route to retrieve route information) AC_DEFINE(PROCROUTE, "/proc/net/route", path were to find route information in proc) AC_MSG_CHECKING(ifconfig option to list interfaces) for IFCONFIG_A_OPT in "-A" "-a" "" do $IFCONFIG $IFCONFIG_A_OPT > /dev/null 2>&1 if test "$?" = 0 then AC_DEFINE_UNQUOTED(IFCONFIG_A_OPT, "$IFCONFIG_A_OPT", option for ifconfig command) AC_MSG_RESULT($IFCONFIG_A_OPT) break fi done AC_SUBST(IFCONFIG_A_OPT) AC_SUBST(WHOAMI) AC_SUBST(HTML2TXT) AC_SUBST(POD2MAN) AC_SUBST(ROUTEPARM) AC_SUBST(PROCROUTE) dnl dnl Glib allows its headers/libraries to be installed anywhere. dnl And they provide a command to let you know where they are. dnl This is nice, but having them in standard places is nice too ;-) dnl if test "X$GLIBCONFIG" != X; then AC_MSG_CHECKING(for special glib includes: ) GLIBHEAD=`$GLIBCONFIG --cflags` AC_MSG_RESULT($GLIBHEAD) CPPFLAGS="$CPPFLAGS $GLIBHEAD" AC_SUBST(GLIBHEAD) dnl Note: Not bundling "GLIBLIB" with general "LIBS". dnl 1. Only very few programs require GLIBLIB dnl (This isn't true anymore -- AlanR) dnl 2. Danger of creating run-time dependency on build-time LD_LIBRARY_PATH AC_MSG_CHECKING(for glib library flags) GLIBLIB=`$GLIBCONFIG --libs` AC_MSG_RESULT($GLIBLIB) AC_SUBST(GLIBLIB) fi dnl ************************************************************************ dnl Handy function for checking for typedefs or struct defs dnl ************************************************************************ check_for_type() { type="$1" headers="" shift for arg do headers="${headers}${arg} " done program="if ((${type} *) 0) return 0; if (sizeof(${type})) return 0; return 0;" have="HAVE_`echo "$type" | tr ' ' '_' | dd conv=ucase 2>/dev/null`" varhave="heartbeat_cv_$have" AC_CACHE_CHECK([for type $type ],$varhave,[ AC_TRY_COMPILE([$headers], [$program], eval $varhave=yes, eval $varhave=no , eval $varhave=cross) ]) if test x"`eval echo '${'$varhave'}'`" = xyes; then return 0 fi return 1 } check_for_type_member() { type="$1" member="$2" headers="" shift shift for arg do headers="${headers}${arg} " done program="${type} foo; if ((${type} *) 0) return 0; if (sizeof(${type})) return 0; if (sizeof(foo)) return 0; (void*)foo.${member}; return 0;" have="HAVE_`echo "$type" | tr ' ' '_' | dd conv=ucase 2>/dev/null`" varhave="heartbeat_cv_$have" AC_CACHE_CHECK([for type $type ],$varhave,[ AC_TRY_COMPILE([$headers], [$program], eval $varhave=yes, eval $varhave=no , eval $varhave=cross) ]) if test x"`eval echo '${'$varhave'}'`" = xyes; then return 0 fi return 1 } dnl ************************************************************************ dnl checks for typedefs dnl dnl if not known on this system, #define size_t unsigned AC_TYPE_SIZE_T dnl ************************************************************************ dnl checks for compiler characteristics dnl dnl Warnings for C compilers. Note: generic, portable warnings only. dnl Things likely to be specific to a particular OS or module should be dnl carefully handled afterwards. AC_C_STRINGIZE dnl ********************************************************************** dnl Check the size of the integer types dnl So we can have integers of known sizes as needed dnl AC_CHECK_SIZEOF(char) AC_CHECK_SIZEOF(short) AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long long) AC_ARG_ENABLE([fatal-warnings], [ --enable-fatal-warnings very pedantic and fatal warnings for gcc [default=yes]], [], [enable_fatal_warnings=unknown]) if test "$GCC" = yes; then cc_supports_flag() { AC_MSG_CHECKING(whether $CC supports "$@") Cfile=/tmp/foo${$} touch ${Cfile}.c $CC -c "$@" ${Cfile}.c -o ${Cfile}.o >/dev/null 2>&1 rc=$? rm -f ${Cfile}.c ${Cfile}.o case $rc in 0) AC_MSG_RESULT(yes);; *) AC_MSG_RESULT(no);; esac return $rc } EXTRA_WARNINGS="" WARNLIST="all missing-prototypes missing-declarations strict-prototypes declaration-after-statement pointer-arith write-strings cast-qual cast-align bad-function-cast inline missing-format-attribute format=2 format-security format-nonliteral no-long-long no-strict-aliasing" for j in $WARNLIST do if cc_supports_flag -W$j then case $j in "no-long-long") if test "${enable_no_long_long}" = "yes"; then EXTRA_WARNINGS="$EXTRA_WARNINGS -W$j" fi;; *) EXTRA_WARNINGS="$EXTRA_WARNINGS -W$j";; esac fi done dnl Add any system specific options here. if test "${enable_ansi}" = "unknown"; then enable_ansi=yes fi case "$host_os" in *linux*|*bsd*) if test "${enable_fatal_warnings}" = "unknown"; then enable_fatal_warnings=yes fi ;; *solaris*) ;; esac if test "${enable_ansi}" = yes && cc_supports_flag -std=iso9899:199409 ; then echo "Enabling ANSI Compatibility on this platform" ANSI="-ansi -D_GNU_SOURCE -DANSI_ONLY" fi if test "${enable_fatal_warnings}" = yes && cc_supports_flag -Werror ; then echo "Enabling Fatal Warnings (-Werror) on this platform" FATAL_WARNINGS="-Werror" fi if test "$enable_traditional_warning" = yes && \ cc_supports_flag -Wtraditional; then echo "Enabling traditional warnings" EXTRA_WARNINGS="$EXTRA_WARNINGS -Wtraditional" fi CC_WARNINGS="$EXTRA_WARNINGS $FATAL_WARNINGS $ANSI" NON_FATAL_CC_WARNINGS="$EXTRA_WARNINGS" dnl Inline semantics are to be changed as of gcc v4.3. Since dnl 4.1.3 gcc issues a warning in case there are inlines. That's the dnl case with glib2. ver2num() { - awk -F. '{print $3+($2+($1*16))*16;}' + awk -F. '{print $3+($2+($1*100))*100;}' } - GCCVER=`gcc --version | head -1 | - awk '{for(i=1;i<=NF;i++) if($i~/\)$/) {print $(i+1);exit}}' | + GCCVER=`gcc -v 2>&1 | awk 'END{print $3}' | ver2num` - firstver=`echo 4.1.3 | ver2num` - lastver=`echo 4.3.0 | ver2num` + firstver=40103 # 4.1.3 + lastver=40300 # 4.3.0 if test $GCCVER -ge $firstver -a $GCCVER -lt $lastver; then CFLAGS="$CFLAGS -fgnu89-inline" fi if test "x${ENABLE_SNMP}" = "xyes" && echo "${SNMPLIB}" | grep netsnmp >/dev/null; then inline_ver=`echo 4.3.0 | ver2num` if test $GCCVER -ge $inline_ver; then CFLAGS="$CFLAGS -DNETSNMP_BROKEN_INLINE" fi fi fi if test -z "${IP2UTIL}" -o ! -x "${IP2UTIL}" then IP2UTIL=/sbin/ip fi dnl dnl Lex and yacc can't be trusted to produce code that won't produce dnl warnings dnl NON_FATAL_CFLAGS="$CFLAGS $NON_FATAL_CC_WARNINGS" AC_SUBST(NON_FATAL_CFLAGS) dnl dnl We reset CFLAGS to include our warnings *after* all function dnl checking goes on, so that our warning flags don't keep the dnl AC_*FUNCS() calls above from working. In particular, -Werror will dnl *always* cause us troubles if we set it before here. dnl dnl CFLAGS="$CFLAGS $CC_WARNINGS" dnl dnl Make sure that CFLAGS is not exported. If the user did dnl not have CFLAGS in their environment then this should have dnl no effect. However if CFLAGS was exported from the user's dnl environment, then the new CFLAGS will also be exported dnl to sub processes. This causes a problem when configure dnl is run in the libltdl directory. Horms 16th July 2002 dnl if export | fgrep " CFLAGS=" > /dev/null; then export -n CFLAGS || true # We don't want to bomb out if this fails fi if test "$GCC" = yes; then CFLAGS="$CFLAGS -ggdb3" if cc_supports_flag -funsigned-char then CFLAGS="$CFLAGS -funsigned-char" fi else CFLAGS="$CFLAGS -g" fi dnl AC_SUBST(CC_WARNINGS) dnl ************ OCF (Open Cluster Framework) ********************** dnl Should be consistent with the OCF standard fixed pathname /usr/lib/ocf dnl Changing it from this value violates the OCF standard. dnl This is why it is not affected by the PREFIX setting. dnl We do not recommend changing it. dnl OCF_ROOT_DIR="/usr/lib/ocf" AC_ARG_WITH(ocf-root, [ --with-ocf-root=DIR directory for OCF scripts [${OCF_ROOT_DIR}]], [ if test x"$withval" = xprefix; then OCF_ROOT_DIR=${prefix}; else OCF_ROOT_DIR="$withval"; fi ]) AC_MSG_CHECKING(OCF directory) if test "X$OCF_ROOT_DIR" = X; then AC_MSG_ERROR(Not specified) fi AC_MSG_RESULT($OCF_ROOT_DIR) AC_SUBST(OCF_ROOT_DIR) AC_DEFINE_UNQUOTED(OCF_ROOT_DIR,"$OCF_ROOT_DIR", OCF root directory - specified by the OCF standard) OCF_RA_DIR="${OCF_ROOT_DIR}/resource.d/" AC_SUBST(OCF_RA_DIR) AC_DEFINE_UNQUOTED(OCF_RA_DIR,"$OCF_RA_DIR", Location for OCF RAs) dnl ********************************************************************** dnl 'AWK' had been determined via 'aclocal.m4' as the simple name, using dnl the current PATH (i.e. in the context of 'configure'). dnl dnl Things within heartbeat will use 'AWK', but from a different context, dnl so we should determine, and substitute, the full path. dnl dnl Note: Even that may have a flaw, e.g. if 'configure' finds (say) 'gawk', dnl which we here convert to '/path/to/gawk', but the run-time machine lacks it. dnl We won't worry about that for now. dnl (David Lee; March 2007) AC_PATH_PROGS([AWK], $AWK) MAKE="${MAKE} \$(QUIET_MAKE_OPTS)" AC_SUBST(CC) AC_SUBST(MAKE) AC_SUBST(LIBTOOL) AC_SUBST(PRETTY_CC) AC_SUBST(REAL_CC) AC_SUBST(QUIET_MAKE_OPTS) AC_SUBST(QUIET_LIBTOOL_OPTS) dnl *** "echo" adjustments (begin) *** dnl Some run-time scripts require options to "echo". dnl This configure is already determining and using "ac_n" and "ac_c" dnl for internal use, so make available externally. dnl (Not sure how "future proof" this is, but it at least seems clean.) dnl dnl This must be close to the end of "configure.in" otherwise it interferes dnl with output from the AC_MSG_*() macros. ECHO_N="$ac_n" ECHO_C="$ac_c" case $ac_n in -n) ECHO_E="-e";; *) ECHO_E="";; esac ECHO_CMD="echo" if test -x /usr/linux/bin/echo then # This is for AIX. I'm not sure it's necessary... ECHO_CMD="/usr/linux/bin/echo" ECHO_N="-n" ECHO_E="-e" fi AC_SUBST(ECHO_N) AC_SUBST(ECHO_C) AC_SUBST(ECHO_E) AC_SUBST(ECHO_CMD) dnl *** "echo" adjustments (end) *** dnl The Makefiles and shell scripts we output AC_CONFIG_FILES(Makefile \ heartbeat/Makefile \ heartbeat/.ocf-binaries \ heartbeat/.ocf-directories \ heartbeat/.ocf-shellfuncs \ heartbeat/ocf-returncodes \ heartbeat/ocf-shellfuncs \ ) dnl Now process the entire list of files added by previous dnl calls to AC_CONFIG_FILES() AC_OUTPUT() dnl subpackages configuration - perhaps configure it properly some other time dnl when it has been discussed at linux-ha-dev dnl AC_CONFIG_SUBDIRS(stonith heartbeat) dnl ***************** dnl Configure summary dnl ***************** eval my_localstatedir="`eval echo ${localstatedir}`" AC_MSG_RESULT([]) AC_MSG_RESULT([$PACKAGE configuration:]) AC_MSG_RESULT([ Version = "$VERSION"]) AC_MSG_RESULT([ State information = "$my_localstatedir"]) AC_MSG_RESULT([ CC_WARNINGS = "${CC_WARNINGS}"]) AC_MSG_RESULT([ Mangled CFLAGS = "${CFLAGS}"]) AC_MSG_RESULT([ Libraries = "${LIBS}"]) diff --git a/heartbeat/.ocf-shellfuncs.in b/heartbeat/.ocf-shellfuncs.in index 215b5592e..bfd5e7d0a 100644 --- a/heartbeat/.ocf-shellfuncs.in +++ b/heartbeat/.ocf-shellfuncs.in @@ -1,237 +1,244 @@ # # # Common helper functions for the OCF Resource Agents supplied by # heartbeat. # # Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Brée # All Rights Reserved. # # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # TODO: Some of this should probably split out into a generic OCF # library for shell scripts, but for the time being, we'll just use it # ourselves... # # TODO wish-list: # - Generic function for evaluating version numbers # - Generic function(s) to extract stuff from our own meta-data # - Logging function which automatically adds resource identifier etc # prefixes # TODO: Move more common functionality for OCF RAs here. # # This was common throughout all legacy Heartbeat agents unset LC_ALL; export LC_ALL unset LANGUAGE; export LANGUAGE __SCRIPT_NAME=`basename $0` . @HA_HBCONF_DIR@/shellfuncs if [ -z "$OCF_ROOT" ]; then : ${OCF_ROOT=@OCF_ROOT_DIR@} fi . ${OCF_ROOT}/resource.d/heartbeat/.ocf-binaries . ${OCF_ROOT}/resource.d/heartbeat/.ocf-returncodes . ${OCF_ROOT}/resource.d/heartbeat/.ocf-directories ocf_is_root() { case `id` in *'uid=0(root)'*) true;; *) false;; esac } ocf_maybe_random() { local rnd="$RANDOM" # Something sane-ish in case a shell doesn't support $RANDOM [ -n "$rnd" ] || rnd=$$ echo $rnd } # Portability comments: # o The following rely on Bourne "sh" pattern-matching, which is usually # that for filename generation (note: not regexp). # o The "*) true ;;" clause is probably unnecessary, but is included # here for completeness. # o The negation in the pattern uses "!". This seems to be common # across many OSes (whereas the alternative "^" fails on some). # o If an OS is encountered where this negation fails, then a possible # alternative would be to replace the function contents by (e.g.): # [ -z "`echo $1 | tr -d '[0-9]'`" ] # ocf_is_decimal() { case "$1" in ""|*[!0-9]*) # empty, or at least one non-decimal false ;; *) true ;; esac } +ocf_is_true() { + case "$1" in + yes|true|1|YES|TRUE|ja|on|ON) true ;; + *) false ;; + esac +} + ocf_is_hex() { case "$1" in ""|*[!0-9a-fA-F]*) # empty, or at least one non-hex false ;; *) true ;; esac } ocf_is_octal() { case "$1" in ""|*[!0-7]*) # empty, or at least one non-octal false ;; *) true ;; esac } __ocf_set_defaults() { __OCF_ACTION="$1" # Return to sanity for the agents... unset LANG LC_ALL=C export LC_ALL # TODO: Review whether we really should source this. Or rewrite # to match some emerging helper function syntax...? This imports # things which no OCF RA should be using... # Strip the OCF_RESKEY_ prefix from this particular parameter if [ -z "$OCF_RESKEY_OCF_CHECK_LEVEL" ]; then : ${OCF_CHECK_LEVEL:=0} else : ${OCF_CHECK_LEVEL:=$OCF_RESKEY_OCF_CHECK_LEVEL} fi if [ ! -d "$OCF_ROOT" ]; then ha_log "ERROR: OCF_ROOT points to non-directory $OCF_ROOT." exit $OCF_ERR_GENERIC fi if [ -z "$OCF_RESOURCE_TYPE" ]; then : ${OCF_RESOURCE_TYPE:=$__SCRIPT_NAME} fi if [ -z "$OCF_RA_VERSION_MAJOR" ]; then : We are being invoked as an init script. : Fill in some things with reasonable values. : ${OCF_RESOURCE_INSTANCE:="default"} return 0 fi if [ "x$__OCF_ACTION" = "xmeta-data" ]; then OCF_RESOURCE_INSTANCE="undef" fi if [ -z "$OCF_RESOURCE_INSTANCE" ]; then ha_log "ERROR: Need to tell us our resource instance name." exit $OCF_ERR_ARGS fi } ocf_log() { # TODO: Revisit and implement internally. if [ $# -lt 2 ] then ocf_log err "Not enough arguments [$#] to ocf_log." fi __OCF_PRIO="$1" shift __OCF_MSG="$*" case "${__OCF_PRIO}" in crit) __OCF_PRIO="CRIT";; err) __OCF_PRIO="ERROR";; warn) __OCF_PRIO="WARNING";; info) __OCF_PRIO="INFO";; debug)__OCF_PRIO="DEBUG";; *) __OCF_PRIO=`echo ${__OCF_PRIO}| tr '[a-z]' '[A-Z]'`;; esac if [ "${__OCF_PRIO}" = "DEBUG" ]; then ha_debug "${__OCF_PRIO}: $__OCF_MSG" else ha_log "${__OCF_PRIO}: $__OCF_MSG" fi } # # Ocf_run: Run a script, and log its output. # Usage: ocf_run # ocf_run() { output=`"$@" 2>&1` rc=$? output=`echo $output` if [ $rc -eq 0 ]; then if [ ! -z "$output" ]; then ocf_log info "$output" fi return $OCF_SUCCESS else if [ ! -z "$output" ]; then ocf_log err "$output" else ocf_log err "command failed: $*" fi return $OCF_ERR_GENERIC fi } ocf_pidfile_status() { pidfile=$1 if [ ! -e $pidfile ]; then # Not exists return 2 fi pid=`cat $pidfile` kill -0 $pid 2>&1 > /dev/null if [ $? = 0 ]; then return 0 fi # Stale return 1 } ocf_take_lock() { local lockfile=$1 local rnd=$(ocf_maybe_random) sleep 0.$rnd while ocf_pidfile_status $lockfile do ocf_log info "Sleeping until $lockfile is released..." sleep 0.$rnd done echo $$ > $lockfile } ocf_release_lock_on_exit() { lockfile=$1 trap "rm -f $lockfile" EXIT } __ocf_set_defaults "$@" diff --git a/heartbeat/AoEtarget b/heartbeat/AoEtarget new file mode 100644 index 000000000..7cddea639 --- /dev/null +++ b/heartbeat/AoEtarget @@ -0,0 +1,246 @@ +#!/bin/bash +# +# +# AoEtarget OCF RA. +# Manages an ATA-over-Ethernet (AoE) target utilizing the vblade utility. +# +# Copyright (c) 2009 LINBIT HA-Solutions GmbH, Florian Haas +# 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. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. +# + +###################################################################### +# Initialization: + +. ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs +LC_ALL="C" +LANG="C" + +# Defaults +OCF_RESKEY_nic_default="eth0" +OCF_RESKEY_pid_default="${HA_RSCTMP}/AoEtarget-${OCF_RESOURCE_INSTANCE}.pid" +OCF_RESKEY_binary_default="/usr/sbin/vblade" + +: ${OCF_RESKEY_nic=${OCF_RESKEY_nic_default}} +: ${OCF_RESKEY_pid=${OCF_RESKEY_pid_default}} +: ${OCF_RESKEY_binary=${OCF_RESKEY_binary_default}} + +####################################################################### + +meta_data() { + cat < + + + 1.0 + +This resource agent manages an ATA-over-Ethernet (AoE) target using vblade. +It exports any block device, or file, as an AoE target using the +specified Ethernet device, shelf, and slot number. + + ATA-over-Ethernet (AoE) target agent + + + +The local block device (or file) to export as an AoE target. + + Device to export + + + + +The local Ethernet interface to use for exporting this AoE target. + + Ethernet interface + + + + +The AoE shelf number to use when exporting this target. + + AoE shelf number + + + + +The AoE slot number to use when exporting this target. + + AoE slot number + + + + +The file to record the daemon pid to. + + Daemon pid file + + + + +Location of the vblade binary. + + vblade binary + + + + + + + + + + + + +EOF +} + +####################################################################### + +AoEtarget_usage() { + cat <&1 & + rc=$? + pid=$! + if [ $rc -ne 0 ]; then + return $OCF_ERR_GENERIC + fi + echo $pid > ${OCF_RESKEY_pid} && return $OCF_SUCCESS + return $OCF_ERR_GENERIC +} + +AoEtarget_stop() { + AoEtarget_monitor + if [ $? -eq $OCF_SUCCESS ]; then + ocf_log info "Unxporting device ${OCF_RESKEY_device} on ${OCF_RESKEY_nic} as shelf ${OCF_RESKEY_shelf}, slot ${OCF_RESKEY_slot}" + pid=$(cat ${OCF_RESKEY_pid}) + kill -TERM $pid + # loop until we're really stopped, wait for the LRM to time us + # out if not + while AoEtarget_monitor; do + sleep 1 + done + fi + # Clean up pid file + rm -f ${OCF_RESKEY_pid} + return $OCF_SUCCESS +} + +AoEtarget_monitor() { + ocf_pidfile_status ${OCF_RESKEY_pid} >/dev/null 2>&1 + rc=$? + if [ $rc -eq 2 ]; then + # no pid file, must assume we're not running + return $OCF_NOT_RUNNING + elif [ $rc -eq 1 ]; then + # stale pid file, assume something went wrong + return $OCF_ERR_GENERIC + fi + return $OCF_RUNNING +} + +AoEtarget_validate() { + # Is our binary executable? + if [ ! -x ${OCF_RESKEY_binary} ]; then + ocf_log error "${OCF_RESKEY_binary} not found or not executable" + return $OCF_ERR_INSTALLED + fi + + # Do we have all required variables? + for var in device nic shelf slot pid; do + param="OCF_RESKEY_${var}" + if [ -z "${!param}" ]; then + ocf_log error "Missing resource parameter \"$var\"!" + return $OCF_ERR_CONFIGURED + fi + done + + # Is the pid file directory writable? + pid_dir=`dirname "$OCF_RESKEY_pid"` + touch "$pid_dir/$$" + if [ $? != 0 ]; then + ocf_log error "Cannot create pid file in $pid_dir -- check directory permissions" + return $OCF_ERR_INSTALLED + fi + rm "$pid_dir/$$" + + # Does the device we are trying to export exist? + if [ ! -e ${OCF_RESKEY_device} ]; then + ocf_log error "${OCF_RESKEY_device} does not exist" + return $OCF_ERR_INSTALLED + fi + return $OCF_SUCCESS +} + +case $1 in + meta-data) + meta_data + exit $OCF_SUCCESS + ;; + usage|help) + AoEtarget_usage + exit $OCF_SUCCESS + ;; +esac + +# Everything except usage and meta-data must pass the validate test +AoEtarget_validate || exit $? + +case $__OCF_ACTION in + start) + AoEtarget_start + ;; + stop) + AoEtarget_stop + ;; + status|monitor) + AoEtarget_monitor + ;; + reload) + ocf_log err "Reloading..." + AoEtarget_start + ;; + validate-all) + AoEtarget_validate + ;; + *) + AoEtarget_usage + exit $OCF_ERR_UNIMPLEMENTED + ;; +esac + +rc=$? +ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : $rc" +exit $rc + diff --git a/heartbeat/Dummy b/heartbeat/Dummy index 65f335058..2b6dec261 100644 --- a/heartbeat/Dummy +++ b/heartbeat/Dummy @@ -1,165 +1,158 @@ #!/bin/sh # # # Dummy OCF RA. Does nothing but wait a few seconds, can be # configured to fail occassionally. # # Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Brée # 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. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # ####################################################################### # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs ####################################################################### meta_data() { cat < 1.0 This is a Dummy Resource Agent. It does absolutely nothing except keep track of whether its running or not. Its purpose in life is for testing and to serve as a template for RA writers. Dummy resource agent Location to store the resource state in. State file END } ####################################################################### -# don't exit on TERM, to test that lrmd makes sure that we do exit -trap sigterm_handler TERM -sigterm_handler() { - ocf_log info "They use TERM to bring us down. No such luck." - return -} - dummy_usage() { cat < 1.0 Resource script for Filesystem. It manages a Filesystem on a shared storage medium. Filesystem resource agent The name of block device for the filesystem, or -U, -L options for mount, or NFS mount specification. block device The mount point for the filesystem. mount point The optional type of filesystem to be mounted. filesystem type Any extra options to be given as -o options to mount. + +For bind mounts, add "bind" here and set fstype to "none". +We will do the right thing for options such as "bind,ro". options END } # # Make sure the kernel does the right thing with the FS buffers # This function should be called after unmounting and before mounting # It may not be necessary in 2.4 and later kernels, but it shouldn't hurt # anything either... # # It's really a bug that you have to do this at all... # flushbufs() { if have_binary $BLOCKDEV ; then if [ "$blockdevice" = "yes" ] ; then $BLOCKDEV --flushbufs $1 return $? fi fi return 0 } # Take advantage of /etc/mtab if present, use portable mount command # otherwise. Normalize format to "dev mountpoint fstype". list_mounts() { if [ -f "/etc/mtab" -a -r "/etc/mtab" ]; then cut -d' ' -f1,2,3 /dev/null fi } ocfs2_cleanup() { # We'll never see the post-stop notification. We're gone now, # have unmounted, and thus should remove the membership. # # (Do so regardless of whether we were unmounted already, # because the admin might have manually unmounted but not # cleared up the membership directory. Bad admin, no cookie.) # if [ ! -d "$OCFS2_FS_ROOT" ]; then ocf_log info "$OCFS2_FS_ROOT: Filesystem membership already gone." else ocf_log info "$OCFS2_FS_ROOT: Removing membership directory." rm -rf $OCFS2_FS_ROOT/ fi ocfs2_del_cache } ocfs2_fetch_uuid() { mounted.ocfs2 -d $DEVICE|tail -1|awk '{print $3}'|tr -d -- -|tr '[a-z]' '[A-Z]' } ocfs2_set_uuid() { _OCFS2_uuid_cache="$HA_RSCTMP/Filesystem.ocfs2_uuid.$(echo $DEVICE|tr / .)" if [ "$OP" != "start" -a -e "$_OCFS2_uuid_cache" ]; then # Trust the cache. OCFS2_UUID=$(cat $_OCFS2_uuid_cache 2>/dev/null) return 0 fi OCFS2_UUID=$(ocfs2_fetch_uuid) if [ -n "$OCFS2_UUID" -a "$OCFS2_UUID" != "UUID" ]; then # UUID valid: echo $OCFS2_UUID > $_OCFS2_uuid_cache return 0 fi # Ok, no UUID still, but that's alright for stop, because it # very likely means we never got started - if [ "$OP" = "stop" ]; then ocf_log warn "$DEVICE: No UUID; assuming never started!" OCFS2_UUID="UUID_NOT_SET" return 0 fi # Everything else - wrong: ocf_log err "$DEVICE: Could not determine ocfs2 UUID for device." exit $OCF_ERR_GENERIC } ocfs2_init() { # Check & initialize the OCFS2 specific variables. # This check detects whether the special/legacy hooks to # integrate OCFS2 with user-space clustering on SLES10 need to # be activated. # Newer kernels >= 2.6.28, with OCFS2+openAIS+Pacemaker, do # not need this: OCFS2_SLES10="" if [ "X$HA_cluster_type" != "Xopenais" ]; then if grep -q "SUSE Linux Enterprise Server 10" /etc/SuSE-release >/dev/null 2>&1 ; then OCFS2_SLES10="yes" ocf_log info "$DEVICE: Enabling SLES10 compatibility mode for OCFS2." else ocf_log err "$DEVICE: ocfs2 is not compatible with your environment." exit $OCF_ERR_CONFIGURED fi else return fi if [ $OP != "stop" ]; then if [ -z "$OCF_RESKEY_CRM_meta_clone" ]; then ocf_log err "ocfs2 must be run as a clone." exit $OCF_ERR_GENERIC fi fi if [ $blockdevice = "no" ]; then ocf_log err "$DEVICE: ocfs2 needs a block device instead." exit $OCF_ERR_GENERIC fi for f in "$OCF_RESKEY_ocfs2_configfs" /sys/kernel/config/cluster /configfs/cluster ; do if [ -n "$f" -a -d "$f" ]; then OCFS2_CONFIGFS="$f" break fi done if [ ! -d "$OCFS2_CONFIGFS" ]; then ocf_log err "ocfs2 needs configfs mounted." exit $OCF_ERR_GENERIC fi ocfs2_set_uuid if [ -n "$OCF_RESKEY_ocfs2_cluster" ]; then OCFS2_CLUSTER=$(echo $OCF_RESKEY_ocfs2_cluster) else OCFS2_CLUSTER=$(find "$OCFS2_CONFIGFS" -maxdepth 1 -mindepth 1 -type d -printf %f 2>/dev/null) set -- $OCFS2_CLUSTER local n; n="$#" if [ $n -gt 1 ]; then ocf_log err "$OCFS2_CLUSTER: several clusters found." exit $OCF_ERR_GENERIC fi if [ $n -eq 0 ]; then ocf_log err "$OCFS2_CONFIGFS: no clusters found." exit $OCF_ERR_GENERIC fi fi OCFS2_CLUSTER_ROOT="$OCFS2_CONFIGFS/$OCFS2_CLUSTER" if [ ! -d "$OCFS2_CLUSTER_ROOT" ]; then ocf_log err "$OCFS2_CLUSTER: Cluster doesn't exist. Maybe o2cb hasn't been run?" exit $OCF_ERR_GENERIC fi OCFS2_FS_ROOT=$OCFS2_CLUSTER_ROOT/heartbeat/$OCFS2_UUID } +# kernels < 2.6.26 can't handle bind remounts +bind_kernel_check() { + echo "$options" | grep -w ro >/dev/null 2>&1 || + return + uname -r | awk -F. ' + $1==2 && $2==6 { + sub("[^0-9].*","",$3); + if ($3<26) + exit(1); + }' + [ $? -ne 0 ] && + ocf_log warn "kernel `uname -r` cannot handle read only bind mounts" +} +bind_mount() { + if echo "$options" | grep -w bind >/dev/null 2>&1 && + [ "$options" != "-o bind" ] + then + bind_kernel_check + bind_opts=`echo $options | sed 's/bind/remount/'` + $MOUNT $bind_opts $MOUNTPOINT + else + true # make sure to return OK + fi +} + # # START: Start up the filesystem # Filesystem_start() { if [ -n "$OCFS2_SLES10" ]; then # "start" now has the notification data available; that # we're being started means we didn't get the # pre-notification, because we weren't running, so # process the information now first. ocf_log info "$OCFS2_UUID: Faking pre-notification on start." OCF_RESKEY_CRM_meta_notify_type="pre" OCF_RESKEY_CRM_meta_notify_operation="start" Filesystem_notify fi # See if the device is already mounted. if Filesystem_status >/dev/null 2>&1 ; then ocf_log info "Filesystem $MOUNTPOINT is already mounted." return $OCF_SUCCESS fi if [ "X${HOSTOS}" != "XOpenBSD" ];then # Insert SCSI module # TODO: This probably should go away. Why should the filesystem # RA magically load a kernel module? $MODPROBE scsi_hostadapter >/dev/null 2>&1 - if [ -z "$FSTYPE" ]; then + if [ -z "$FSTYPE" -o "$FSTYPE" = none ]; then : No FSTYPE specified, rely on the system has the right file-system support already else # Insert Filesystem module $MODPROBE $FSTYPE >/dev/null 2>&1 grep -e "$FSTYPE"'$' /proc/filesystems >/dev/null if [ $? -ne 0 ] ; then ocf_log err "Couldn't find filesystem $FSTYPE in /proc/filesystems" return $OCF_ERR_ARGS fi fi fi # Check the filesystem & auto repair. # NOTE: Some filesystem types don't need this step... Please modify # accordingly if [ $blockdevice = "yes" ]; then if [ "$DEVICE" != "/dev/null" -a ! -b "$DEVICE" ] ; then ocf_log err "Couldn't find device [$DEVICE]. Expected /dev/??? to exist" - exit $OCF_ERR_ARGS + exit $OCF_ERR_INSTALLED fi if case $FSTYPE in - ext3|reiserfs|reiser4|nss|xfs|jfs|vfat|fat|nfs|cifs|smbfs|ocfs2|gfs2|bind|lustre) false;; + ext3|reiserfs|reiser4|nss|xfs|jfs|vfat|fat|nfs|cifs|smbfs|ocfs2|gfs2|none|lustre) false;; *) true;; esac then ocf_log info "Starting filesystem check on $DEVICE" if [ -z "$FSTYPE" ]; then $FSCK -p $DEVICE else $FSCK -t $FSTYPE -p $DEVICE fi # NOTE: if any errors at all are detected, it returns non-zero # if the error is >= 4 then there is a big problem if [ $? -ge 4 ]; then ocf_log err "Couldn't sucessfully fsck filesystem for $DEVICE" return $OCF_ERR_GENERIC fi fi fi if [ ! -d "$MOUNTPOINT" ] ; then ocf_log err "Couldn't find directory [$MOUNTPOINT] to use as a mount point" - exit $OCF_ERR_ARGS + exit $OCF_ERR_INSTALLED fi flushbufs $DEVICE # Mount the filesystem. - if [ -z "$FSTYPE" ]; then - $MOUNT $options $DEVICE $MOUNTPOINT - else - $MOUNT -t $FSTYPE $options $DEVICE $MOUNTPOINT - fi + case "$FSTYPE" in + none) $MOUNT $options $DEVICE $MOUNTPOINT && + bind_mount + ;; + "") $MOUNT $options $DEVICE $MOUNTPOINT ;; + *) $MOUNT -t $FSTYPE $options $DEVICE $MOUNTPOINT ;; + esac if [ $? -ne 0 ]; then ocf_log err "Couldn't mount filesystem $DEVICE on $MOUNTPOINT" if [ -n "$OCFS2_SLES10" ]; then ocfs2_cleanup fi return $OCF_ERR_GENERIC fi return 0 } # end of Filesystem_start Filesystem_notify() { # Process notifications; this is the essential glue level for # giving user-space membership events to a cluster-aware # filesystem. Right now, only OCFS2 is supported. # # When we get a pre-start notification, we set up all the nodes # which will be active in our membership for the filesystem. # (For the resource to be started, this happens at the time of # the actual 'start' operation.) # # At a post-start, actually there's nothing to do for us really, # but no harm done in re-syncing either. # # pre-stop is meaningless; we can't remove any node yet, it # first needs to unmount. # # post-stop: the node is removed from the membership of the # other nodes. # # Note that this expects that the base cluster is already # active; ie o2cb has been started and populated # $OCFS2_CLUSTER_ROOT/node/ already. This can be achieved by # simply having o2cb run on all nodes by the CRM too. This # probably ought to be mentioned somewhere in the to be written # documentation. ;-) # if [ -z "$OCFS2_SLES10" ]; then # One of the cases which shouldn't occur; it should have # been caught much earlier. Still, you know ... ocf_log err "$DEVICE: Please only enable notifications for SLES10 OCFS2 mounts." # Yes, in theory this is a configuration error, but # simply discarding them allows users to switch from the # SLES10 stack to the new one w/o downtime. # Ignoring the notifications is harmless, afterall, and # they can simply disable them in their own time. return $OCF_SUCCESS fi local n_type; n_type="$OCF_RESKEY_CRM_meta_notify_type" local n_op; n_op="$OCF_RESKEY_CRM_meta_notify_operation" local n_active; n_active="$OCF_RESKEY_CRM_meta_notify_active_uname" local n_stop; n_stop="$OCF_RESKEY_CRM_meta_notify_stop_uname" local n_start; n_start="$OCF_RESKEY_CRM_meta_notify_start_uname" ocf_log info "$OCFS2_UUID: notify: $n_type for $n_op" ocf_log info "$OCFS2_UUID: notify active: $n_active" ocf_log info "$OCFS2_UUID: notify stop: $n_stop" ocf_log info "$OCFS2_UUID: notify start: $n_start" case "$n_type" in pre) case "$n_op" in stop) ocf_log info "$OCFS2_UUID: ignoring pre-notify for stop." return $OCF_SUCCESS ;; start) # These are about to become active; prepare to # communicate with them. # Duplicate removal - start can contain nodes # already on the active list, confusing the # script later on: for UNAME in $n_active; do n_start=`echo ${n_start} | sed s/$UNAME//` done # Merge pruned lists again: n_active="$n_active $n_start" ;; esac ;; post) case "$n_op" in stop) # remove unames from notify_stop_uname; these have been # stopped and can no longer be considered active. for UNAME in $n_stop; do n_active=`echo ${n_active} | sed s/$UNAME//` done ;; start) if [ "$n_op" = "start" ]; then ocf_log info "$OCFS2_UUID: ignoring post-notify for start." return $OCF_SUCCESS fi ;; esac ;; esac ocf_log info "$OCFS2_UUID: post-processed active: $n_active" local n_myself; n_myself=${HA_CURHOST:-$(uname -n | tr '[A-Z]' '[a-z]')} ocf_log info "$OCFS2_UUID: I am node $n_myself." case " $n_active " in *" $n_myself "*) ;; *) ocf_log err "$OCFS2_UUID: $n_myself (local) not on active list!" return $OCF_ERR_GENERIC ;; esac if [ -d "$OCFS2_FS_ROOT" ]; then entry_prefix=$OCFS2_FS_ROOT/ for entry in $OCFS2_FS_ROOT/* ; do n_fs="${entry##$entry_prefix}" # ocf_log info "$OCFS2_UUID: Found current node $n_fs" case " $n_active " in *" $n_fs "*) # Construct a list of nodes which are present # already in the membership. n_exists="$n_exists $n_fs" ocf_log info "$OCFS2_UUID: Keeping node: $n_fs" ;; *) # Node is in the membership currently, but not on our # active list. Must be removed. if [ "$n_op" = "start" ]; then ocf_log warn "$OCFS2_UUID: Removing nodes on start" fi ocf_log info "$OCFS2_UUID: Removing dead node: $n_fs" if ! rm -f $entry ; then ocf_log err "$OCFS2_UUID: Removal of $n_fs failed!" fi ;; esac done else ocf_log info "$OCFS2_UUID: heartbeat directory doesn't exist yet, creating." mkdir -p $OCFS2_FS_ROOT fi ocf_log info "$OCFS2_UUID: Existing node list: $n_exists" # (2) for entry in $n_active ; do # ocf_log info "$OCFS2_UUID: Expected active node: $entry" case " $n_exists " in *" $entry "*) ocf_log info "$OCFS2_UUID: Already active: $entry" ;; *) if [ "$n_op" = "stop" ]; then ocf_log warn "$OCFS2_UUID: Adding nodes on stop" fi ocf_log info "$OCFS2_UUID: Activating node: $entry" if ! ln -s $OCFS2_CLUSTER_ROOT/node/$entry $OCFS2_FS_ROOT/$entry ; then ocf_log err "$OCFS2_CLUSTER_ROOT/node/$entry: failed to link" fi ;; esac done } # # STOP: Unmount the filesystem # Filesystem_stop() { # See if the device is currently mounted Filesystem_status >/dev/null 2>&1 if [ $? -eq $OCF_NOT_RUNNING ]; then # Already unmounted, wonderful. rc=$OCF_SUCCESS else # Determine the real blockdevice this is mounted on (if # possible) prior to unmounting. determine_blockdevice # For networked filesystems, there's merit in trying -f: case "$FSTYPE" in nfs|cifs|smbfs) umount_force="-f" ;; esac # Umount all sub-filesystems mounted under $MOUNTPOINT/ too. for SUB in `list_submounts $MOUNTPOINT` $MOUNTPOINT; do ocf_log info "Trying to unmount $MOUNTPOINT" for sig in SIGTERM SIGTERM SIGTERM SIGKILL SIGKILL SIGKILL; do - if $UMOUNT $umount_force $SUB ; then - rc=$OCF_SUCCESS - ocf_log info "unmounted $SUB successfully" - break - else + $UMOUNT $umount_force $SUB + if list_mounts | grep -q " $SUB " >/dev/null 2>&1; then rc=$OCF_ERR_GENERIC ocf_log err "Couldn't unmount $SUB; trying cleanup with $sig" # fuser returns a non-zero return code if none of the # specified files is accessed or in case of a fatal # error. if [ "X${HOSTOS}" = "XOpenBSD" ];then PIDS=`fstat | grep ${SUB} | awk '{print $3}'` for PID in ${PIDS};do kill -s 9 ${PID} ocf_log info "Sent kill -9 to ${PID}" done else if $FUSER -$sig -m -k $SUB ; then ocf_log info "Some processes on $SUB were signalled" else ocf_log info "No processes on $SUB were signalled" fi fi sleep 1 + else + rc=$OCF_SUCCESS + ocf_log info "unmounted $SUB successfully" + break fi done if [ $rc -ne $OCF_SUCCESS ]; then ocf_log err "Couldn't unmount $SUB, giving up!" fi done fi flushbufs $DEVICE # Yes I know the next blob is ugly, sorry. if [ $rc -eq $OCF_SUCCESS ]; then if [ "$FSTYPE" = "ocfs2" ]; then ocfs2_init if [ -n "$OCFS2_SLES10" ]; then ocfs2_cleanup fi fi fi return $rc } # end of Filesystem_stop # # STATUS: is the filesystem mounted or not? # Filesystem_status() { if list_mounts | grep -q " $MOUNTPOINT " >/dev/null 2>&1; then rc=$OCF_SUCCESS msg="$MOUNTPOINT is mounted (running)" else rc=$OCF_NOT_RUNNING msg="$MOUNTPOINT is unmounted (stopped)" fi # TODO: For ocfs2, or other cluster filesystems, should we be # checking connectivity to other nodes here, or the IO path to # the storage? # Special case "monitor" to check whether the UUID cached and # on-disk still match? case "$OP" in status) ocf_log info "$msg";; esac return $rc } # end of Filesystem_status # # VALIDATE_ALL: Are the instance parameters valid? # FIXME!! The only part that's useful is the return code. # This code always returns $OCF_SUCCESS (!) # Filesystem_validate_all() { if [ -n $MOUNTPOINT -a ! -d $MOUNTPOINT ]; then ocf_log warn "Mountpoint $MOUNTPOINT does not exist" fi # Check if the $FSTYPE is workable # NOTE: Without inserting the $FSTYPE module, this step may be imprecise # TODO: This is Linux specific crap. - if [ ! -z "$FSTYPE" ]; then + if [ ! -z "$FSTYPE" -a "$FSTYPE" != none ]; then cut -f2 /proc/filesystems |grep -q ^$FSTYPE$ if [ $? -ne 0 ]; then modpath=/lib/modules/`uname -r` moddep=$modpath/modules.dep # Do we have $FSTYPE in modules.dep? cut -d' ' -f1 $moddep |grep -q "^$modpath.*$FSTYPE\.k\?o:$" if [ $? -ne 0 ]; then ocf_log info "It seems we do not have $FSTYPE support" fi fi fi #TODO: How to check the $options ? return $OCF_SUCCESS } # Check the arguments passed to this script if [ $# -ne 1 ]; then usage exit $OCF_ERR_ARGS fi # Check the OCF_RESKEY_ environment variables... DEVICE=$OCF_RESKEY_device FSTYPE=$OCF_RESKEY_fstype if [ ! -z "$OCF_RESKEY_options" ]; then options="-o $OCF_RESKEY_options" fi OP=$1 # These operations do not require instance parameters case $OP in meta-data) meta_data exit $OCF_SUCCESS ;; usage) usage exit $OCF_SUCCESS ;; esac blockdevice=no case $DEVICE in "") ocf_log err "Please set OCF_RESKEY_device to the device to be managed" exit $OCF_ERR_ARGS ;; -*) # Oh... An option to mount instead... Typically -U or -L ;; [!/]*:/*) # An NFS filesystem specification... ;; //[!/]*/*) # An SMB filesystem specification... ;; /dev/null) # Special case for BSC blockdevice=yes ;; *) if [ ! -b "$DEVICE" -a ! -d "$DEVICE" -a "X$OP" != Xstart ] ; then ocf_log warn "Couldn't find device [$DEVICE]. Expected /dev/??? to exist" fi if [ ! -d "$DEVICE" ];then blockdevice=yes fi ;; esac # Normalize instance parameters: # It is possible that OCF_RESKEY_directory has one or even multiple trailing "/". # But the output of `mount` and /proc/mounts do not. if [ -z "$OCF_RESKEY_directory" ]; then if [ X$OP = "Xstart" -o $blockdevice = "no" ]; then ocf_log err "Please specify the directory" exit $OCF_ERR_ARGS fi else MOUNTPOINT=$(echo $OCF_RESKEY_directory | sed 's/\/*$//') : ${MOUNTPOINT:=/} # At this stage, $MOUNTPOINT does not contain trailing "/" unless it is "/" # TODO: / mounted via Filesystem sounds dangerous. On stop, we'll # kill the whole system. Is that a good idea? fi # Check to make sure the utilites are found if [ "X${HOSTOS}" != "XOpenBSD" ];then check_binary $MODPROBE check_binary $FUSER fi check_binary $FSCK check_binary $MOUNT check_binary $UMOUNT if [ "$OP" != "monitor" ]; then ocf_log info "Running $OP for $DEVICE on $MOUNTPOINT" fi # These operations do not require the clone checking + OCFS2 # initialization. case $OP in status|monitor) Filesystem_status exit $? ;; validate-all) Filesystem_validate_all exit $? ;; stop) Filesystem_stop exit $? ;; esac case $FSTYPE in ocfs2) ocfs2_init ;; -nfs|bind|gfs2) : # this is kind of safe too +nfs|none|gfs2) : # this is kind of safe too ;; *) if [ -n "$OCF_RESKEY_CRM_meta_clone" ]; then ocf_log err "DANGER! $FSTYPE on $DEVICE is NOT cluster-aware!" ocf_log err "DO NOT RUN IT AS A CLONE!" ocf_log err "Politely refusing to proceed to avoid data corruption." exit $OCF_ERR_CONFIGURED fi ;; esac case $OP in start) Filesystem_start ;; notify) Filesystem_notify ;; *) usage exit $OCF_ERR_UNIMPLEMENTED ;; esac exit $? diff --git a/heartbeat/IPaddr b/heartbeat/IPaddr index c9a39f938..1404666e9 100644 --- a/heartbeat/IPaddr +++ b/heartbeat/IPaddr @@ -1,841 +1,848 @@ #!/bin/sh # # License: GNU General Public License (GPL) # Support: linux-ha@lists.linux-ha.org # # This script manages IP alias IP addresses # # It can add an IP alias, or remove one. # # usage: $0 {start|stop|status|monitor|validate-all|meta-data} # # The "start" arg adds an IP alias. # # Surprisingly, the "stop" arg removes one. :-) # # OCF parameters are as below # OCF_RESKEY_ip # OCF_RESKEY_broadcast # OCF_RESKEY_nic # OCF_RESKEY_cidr_netmask # OCF_RESKEY_lvs_support ( e.g. true, on, 1 ) # OCF_RESKEY_ARP_INTERVAL_MS # OCF_RESKEY_ARP_REPEAT # OCF_RESKEY_ARP_BACKGROUND (e.g. yes ) # OCF_RESKEY_ARP_NETMASK # OCF_RESKEY_local_start_script # OCF_RESKEY_local_stop_script # ####################################################################### # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs HA_HBCONF_DIR=${HA_DIR} SENDARP=$HA_BIN/send_arp FINDIF=$HA_BIN/findif VLDIR=$HA_RSCTMP/IPaddr SENDARPPIDDIR=$HA_RSCTMP/send_arp SENDARPPIDFILE="$SENDARPPIDDIR/send_arp-$OCF_RESKEY_ip" USAGE="usage: $0 {start|stop|status|monitor|validate-all|meta-data}"; ####################################################################### # Prevent ifconfig localization issues unset LC_ALL; export LC_ALL unset LANGUAGE; export LANGUAGE LC_ALL=C; export LC_ALL LC_MESSAGES=C; export LC_MESSAGES . $HA_HBCONF_DIR/shellfuncs SYSTYPE="`uname -s`" case "$SYSTYPE" in SunOS) # `uname -r` = 5.9 -> SYSVERSION = 9 SYSVERSION="`uname -r | cut -d. -f 2`" ;; Darwin) # Treat Darwin the same as the other BSD variants (matched as *BSD) SYSTYPE="${SYSTYPE}BSD" ;; *) ;; esac meta_data() { cat < 1.0 This script manages IP alias IP addresses It can add an IP alias, or remove one. Manages virtual IPv4 addresses The IPv4 address to be configured in dotted quad notation, for example "192.168.1.1". IPv4 address The base network interface on which the IP address will be brought online. If left empty, the script will try and determine this from the routing table. Do NOT specify an alias interface in the form eth0:1 or anything here; rather, specify the base interface only. Network interface The netmask for the interface in CIDR format. (ie, 24), or in dotted quad notation 255.255.255.0). If unspecified, the script will also try to determine this from the routing table. Netmask Broadcast address associated with the IP. If left empty, the script will determine this from the netmask. Broadcast address You can specify an additional label for your IP address here. Interface label Enable support for LVS Direct Routing configurations. In case a IP address is stopped, only move it to the loopback device to allow the local node to continue to service requests, but no longer advertise it on the network. Enable support for LVS DR Script called when the IP is released Script called when the IP is released Script called when the IP is added Script called when the IP is added milliseconds between ARPs milliseconds between gratuitous ARPs How many gratuitous ARPs to send out when bringing up a new address repeat count run in background (no longer any reason to do this) run in background netmask for ARP - in nonstandard hexadecimal format. netmask for ARP END exit $OCF_SUCCESS } # On Linux systems the (hidden) loopback interface may # conflict with the requested IP address. If so, this # unoriginal code will remove the offending loopback address # and save it in VLDIR so it can be added back in later # when the IPaddr is released. # lvs_remove_conflicting_loopback() { ipaddr="$1" ifname="$2" ocf_log info "Removing conflicting loopback $ifname." if echo $ifname > "$VLDIR/$ipaddr" then : Saved loopback information in $VLDIR/$ipaddr else ocf_log err "Could not save conflicting loopback $ifname." \ "it will not be restored." fi delete_interface "$ifname" "$ipaddr" # Forcibly remove the route (if it exists) to the loopback. delete_route "$ipaddr" } # # On Linux systems the (hidden) loopback interface may # need to be restored if it has been taken down previously # by lvs_remove_conflicting_loopback() # lvs_restore_loopback() { ipaddr="$1" if [ ! -s "$VLDIR/$ipaddr" ]; then return fi ifname=`cat "$VLDIR/$ipaddr"` ocf_log info "Restoring loopback IP Address $ipaddr on $ifname." CMD="OCF_RESKEY_cidr_netmask=32 OCF_RESKEY_ip=$1 OCF_RESKEY_nic=$ifname $FINDIF" if NICINFO=`eval $CMD` - NICINFO=`echo $NICINFO | tr -s " "` + NICINFO=`echo $NICINFO | tr " " " " | tr -s " "` then netmask_text=`echo "$NICINFO" | cut -f3 -d " "` broadcast=`echo "$NICINFO" | cut -f5 -d " "` else echo "ERROR: $CMD failed (rc=$rc)" exit $OCF_ERR_GENERIC fi add_interface "$ipaddr" "$ifname" "$ifname" $netmask_text $broadcast rm -f "$VLDIR/$ipaddr" } # # Find out which alias serves the given IP address # The argument is an IP address, and its output # is an aliased interface name (e.g., "eth0:0"). # find_interface_solaris() { ipaddr="$1" $IFCONFIG $IFCONFIG_A_OPT | $AWK '{if ($0 ~ /.*: / && NR > 1) {print "\n"$0} else {print}}' | while read ifname linkstuff do : ifname = $ifname read inet addr junk : inet = $inet addr = $addr while read line && [ "X$line" != "X" ] do : Nothing done case $ifname in *:*) ;; *) continue;; esac # This doesn't look right for a box with multiple NICs. # It looks like it always selects the first interface on # a machine. Yet, we appear to use the results for this case too... ifname=`echo "$ifname" | sed s'%:$%%'` case $addr in addr:$ipaddr) echo $ifname; return $OCF_SUCCESS;; $ipaddr) echo $ifname; return $OCF_SUCCESS;; esac done return $OCF_ERR_GENERIC } find_interface_bsd() { - #$IFCONFIG $IFCONFIG_A_OPT | grep "inet.*[: ]$OCF_RESKEY_ip " - $IFCONFIG | grep "$ipaddr" -B20 | grep "UP," | tail -n 1 | cut -d ":" -f 1 + $IFCONFIG $IFCONFIG_A_OPT | awk -v ip_addr="$ipaddr" ' + /UP,/ && $0 ~ /^[a-z]+[0-9]:/ { + if_name=$1; sub(":$","",if_name); + } + $1 == "inet" && $2 == ip_addr { + print if_name + exit(0) + }' + } # # Find out which alias serves the given IP address # The argument is an IP address, and its output # is an aliased interface name (e.g., "eth0:0"). # find_interface_generic() { ipaddr="$1" $IFCONFIG $IFCONFIG_A_OPT | while read ifname linkstuff do : Read gave us ifname = $ifname read inet addr junk : Read gave us inet = $inet addr = $addr while read line && [ "X$line" != "X" ] do : Nothing done case $ifname in *:*) ;; *) continue;; esac : "comparing $ipaddr to $addr (from ifconfig)" case $addr in addr:$ipaddr) echo $ifname; return $OCF_SUCCESS;; $ipaddr) echo $ifname; return $OCF_SUCCESS;; esac done return $OCF_ERR_GENERIC } # # Find out which alias serves the given IP address # The argument is an IP address, and its output # is an aliased interface name (e.g., "eth0:0"). # find_interface() { ipaddr="$1" case "$SYSTYPE" in SunOS) NIC=`find_interface_solaris $ipaddr`;; *BSD) NIC=`find_interface_bsd $ipaddr`;; *) NIC=`find_interface_generic $ipaddr`;; esac echo $NIC return $OCF_SUCCESS; } # # Find an unused interface/alias name for us to use for new IP alias # The argument is an IP address, and the output # is an aliased interface name (e.g., "eth0:0", "dc0", "le0:0"). # find_free_interface() { NIC="$1" if [ "X$NIC" = "X" ]; then ocf_log err "No free interface found for $OCF_RESKEY_ip" return $OCF_ERR_GENERIC; fi NICBASE="$VLDIR/$NIC" touch "$NICBASE" case "$SYSTYPE" in *BSD) echo $NIC; return $OCF_SUCCESS;; SunOS) j=1 IFLIST=`$IFCONFIG $IFCONFIG_A_OPT | \ grep "^$NIC:[0-9]" | sed 's%: .*%%'`;; *) j=0 IFLIST=`$IFCONFIG $IFCONFIG_A_OPT | \ grep "^$NIC:[0-9]" | sed 's% .*%%'` TRYADRCNT=`ls "${NICBASE}:"* 2>/dev/null | wc -w | tr -d ' '` if [ -f "${NICBASE}:${TRYADRCNT}" ]; then : OK else j="${TRYADRCNT}" fi ;; esac IFLIST=" `echo $IFLIST` " while [ $j -lt 512 ] do case $IFLIST in *" "$NIC:$j" "*) ;; *) NICLINK="$NICBASE:$j" if ln "$NICBASE" "$NICLINK" 2>/dev/null then echo "$NIC:$j" return $OCF_SUCCESS fi ;; esac j=`expr $j + 1` done return $OCF_ERR_GENERIC } delete_route () { ipaddr="$1" case "$SYSTYPE" in SunOS) return 0;; *BSD) CMD="$ROUTE -n delete -host $ipaddr";; *) CMD="$ROUTE -n del -host $ipaddr";; esac $CMD return $? } delete_interface () { ifname="$1" ipaddr="$2" case "$SYSTYPE" in SunOS) if [ "$SYSVERSION" -ge 8 ] ; then CMD="$IFCONFIG $ifname unplumb" else CMD="$IFCONFIG $ifname 0 down" fi;; Darwin*) CMD="$IFCONFIG $ifname $ipaddr delete";; *BSD) CMD="$IFCONFIG $ifname inet $ipaddr delete";; *) CMD="$IFCONFIG $ifname down";; esac ocf_log info "$CMD" $CMD return $? } add_interface () { ipaddr="$1" iface_base="$2" iface="$3" netmask="$4" broadcast="$5" if [ $# != 5 ]; then ocf_log err "Insufficient arguments to add_interface: $*" exit $OCF_ERR_ARGS fi case "$SYSTYPE" in SunOS) if [ "$SYSVERSION" -ge 8 ] ; then $IFCONFIG $iface plumb rc=$? if [ $rc -ne 0 ] ; then echo "ERROR: '$IFCONFIG $iface plumb' failed." return $rc fi fi # At Solaris 10, this single-command version sometimes broke. # Almost certainly an S10 bug. # CMD="$IFCONFIG $iface inet $ipaddr $text up" # So hack the following workaround: CMD="$IFCONFIG $iface inet $ipaddr" CMD="$CMD && $IFCONFIG $iface netmask $netmask" CMD="$CMD && $IFCONFIG $iface up" ;; *BSD) # netmask is always set to 255.255.255.255 for an alias CMD="$IFCONFIG $iface inet $ipaddr netmask 255.255.255.255 alias";; *) CMD="$IFCONFIG $iface $ipaddr netmask $netmask broadcast $broadcast";; esac # Use "eval $CMD" (not "$CMD"): it might be a chain of two or more commands. ocf_log info "eval $CMD" eval $CMD rc=$? if [ $rc != 0 ]; then echo "ERROR: eval $CMD failed (rc=$rc)" fi return $rc } # # Remove the IP alias for the requested IP address... # ip_stop() { echo "In IP Stop" SENDARPPIDFILE="$SENDARPPIDDIR/send_arp-$OCF_RESKEY_ip" NIC=`find_interface $OCF_RESKEY_ip` if [ -f "$SENDARPPIDFILE" ]; then cat "$SENDARPPIDFILE" | xargs kill rm -f "$SENDARPPIDFILE" fi if [ -z "$NIC" ]; then : Requested interface not in use return $OCF_SUCCESS fi if [ ${OCF_RESKEY_lvs_support} = 1 ]; then case $NIC in lo*) : Requested interface is on loopback return $OCF_SUCCESS;; esac fi delete_route "$OCF_RESKEY_ip" delete_interface "$NIC" "$OCF_RESKEY_ip" rc=$? if [ ${OCF_RESKEY_lvs_support} = 1 ]; then lvs_restore_loopback "$OCF_RESKEY_ip" fi # remove lock file... rm -f "$VLDIR/$NIC" if [ $rc != 0 ]; then ocf_log warn "IP Address $OCF_RESKEY_ip NOT released" fi return $rc } # # Add an IP alias for the requested IP address... # # It could be that we already have taken it, in which case it should # do nothing. # ip_start() { # # Do we already service this IP address? # ip_status_internal if [ $? = $OCF_SUCCESS ]; then # Nothing to do, the IP is already active return $OCF_SUCCESS; fi NIC_unique=`find_free_interface $OCF_RESKEY_nic` if [ -n "$NIC_unique" ]; then : OK got interface [$NIC_unique] for $OCF_RESKEY_ip else return $OCF_ERR_GENERIC fi # This logic is mostly to support LVS (If I understand it correctly) if [ ${OCF_RESKEY_lvs_support} = 1 ]; then NIC_current=`find_interface $OCF_RESKEY_ip` case $NIC_unique in lo*) if [ x"$NIC_unique" = x"$NIC_current" ]; then # Its already "running" and not moving, nothing to do. ocf_log err "Could not find a non-loopback device to move $OCF_RESKEY_ip to" return $OCF_ERR_GENERIC fi;; *) lvs_remove_conflicting_loopback "$OCF_RESKEY_ip" "$NIC_current";; esac fi add_interface "$OCF_RESKEY_ip" "$OCF_RESKEY_nic" "$NIC_unique" \ "$OCF_RESKEY_cidr_netmask" "$OCF_RESKEY_broadcast" rc=$? if [ $rc != 0 ]; then ocf_log err "Could not add $OCF_RESKEY_ip to $OCF_RESKEY_nic: $rc" return $rc fi # The address is active, now notify others about it using sendarp if [ "$SYSTYPE" = "DarwinBSD" -a "$NIC_unique" = "lo0" ]; then # Darwin can't send ARPs on loopback devices SENDARP="x$SENDARP" # Prevent the binary from being found fi if [ -x $SENDARP ]; then TARGET_INTERFACE=`echo $NIC_unique | sed 's%:.*%%'` SENDARPPIDFILE="$SENDARPPIDDIR/send_arp-$OCF_RESKEY_ip" ARGS="-i $OCF_RESKEY_ARP_INTERVAL_MS -r $OCF_RESKEY_ARP_REPEAT" ARGS="$ARGS -p $SENDARPPIDFILE $TARGET_INTERFACE $OCF_RESKEY_ip" ARGS="$ARGS auto $OCF_RESKEY_ip $OCF_RESKEY_ARP_NETMASK" ocf_log debug "Sending Gratuitous Arp for $OCF_RESKEY_ip on $NIC_unique [$TARGET_INTERFACE]" case $OCF_RESKEY_ARP_BACKGROUND in yes) ($SENDARP $ARGS || ocf_log err "Could not send gratuitous arps. rc=$?" & ) >&2 ;; *) $SENDARP $ARGS || ocf_log err "Could not send gratuitous arps. rc=$?";; esac fi ip_status_internal return $? } ip_status_internal() { NIC=`find_interface "$OCF_RESKEY_ip"` if [ "x$NIC" = x ]; then return $OCF_NOT_RUNNING elif [ "${OCF_RESKEY_lvs_support}" = "1" ]; then case $NIC in lo*) return $OCF_NOT_RUNNING;; *) return $OCF_SUCCESS;; esac else if [ x$OCF_RESKEY_nic != x ]; then simple_OCF_NIC=`echo $OCF_RESKEY_nic | awk -F: '{print $1}'` simple_NIC=`echo $NIC | awk -F: '{print $1}'` if [ $simple_OCF_NIC != $simple_NIC ]; then ocf_log err "$OCF_RESKEY_ip is running an interface ($simple_NIC) instead of the configured one ($simple_OCF_NIC)" return $OCF_ERR_GENERIC fi fi return $OCF_SUCCESS fi } ip_status() { ip_status_internal rc=$? if [ $rc = $OCF_SUCCESS ]; then echo "running" elif [ $rc = $OCF_NOT_RUNNING ]; then echo "stopped" else echo "unknown" fi return $rc; } # # Determine if this IP address is really being served, or not. # Note that we must distinguish if *we're* serving it locally... # ip_monitor() { ip_status_internal rc=$? if [ $OCF_CHECK_LEVEL = 0 -o $rc != 0 ]; then return $rc fi ocf_log info "Checking IP stack" PINGARGS="`pingargs $OCF_RESKEY_ip`" for j in 1 2 3 4 5 6 7 8 9 10; do if $PING $PINGARGS >/dev/null 2>&1 ; then return $OCF_SUCCESS fi done return $OCF_ERR_GENERIC } is_positive_integer() { ocf_is_decimal $1 && [ $1 -ge 1 ] if [ $? = 0 ]; then return 1 fi return 0 } ip_validate_all() { : ${OCF_RESKEY_ARP_BACKGROUND=yes} : ${OCF_RESKEY_ARP_NETMASK=ffffffffffff} : ${OCF_RESKEY_ARP_INTERVAL_MS=500} : ${OCF_RESKEY_ARP_REPEAT=10} check_binary $AWK check_binary $IFCONFIG check_binary $ROUTE check_binary $PING if [ -d "$VLDIR/" ] || mkdir -p "$VLDIR/" then : Directory $VLDIR now exists else ocf_log err "Could not create \"$VLDIR/\"." return $OCF_ERR_GENERIC fi if is_positive_integer $OCF_RESKEY_ARP_INTERVAL_MS then ocf_log err "Invalid parameter value: ARP_INTERVAL_MS [$OCF_RESKEY_ARP_INTERVAL_MS]" return $OCF_ERR_ARGS fi if is_positive_integer $OCF_RESKEY_ARP_REPEAT then ocf_log err "Invalid parameter value: ARP_REPEAT [$OCF_RESKEY_ARP_REPEAT]" return $OCF_ERR_ARGS fi : ${OCF_RESKEY_lvs_support=0} if [ "$SYSTYPE" = "Linux" -o "$SYSTYPE" = "SunOS" ]; then : else if [ "${OCF_RESKEY_lvs_support}" = "1" ]; then ocf_log err "$SYSTYPE does not support LVS" return $OCF_ERR_GENERIC fi fi case $OCF_RESKEY_ip in "") ocf_log err "Required parameter OCF_RESKEY_ip is missing" return $OCF_ERR_CONFIGURED;; [0-9]*.[0-9]*.[0-9]*.*[0-9]) : OK;; *) ocf_log err "Parameter OCF_RESKEY_ip [$OCF_RESKEY_ip] not an IP address" return $OCF_ERR_CONFIGURED;; esac # Unconditionally do this? case $OCF_RESKEY_nic in *:*) OCF_RESKEY_nic=`echo $OCF_RESKEY_nic | sed 's/:.*//'` ;; esac NICINFO=`$FINDIF` rc=$? if [ $rc != 0 ]; then ocf_log err "$FINDIF failed [rc=$rc]." return $OCF_ERR_GENERIC fi tmp=`echo "$NICINFO" | cut -f1` if [ "x$OCF_RESKEY_nic" = "x" ] then ocf_log info "Using calculated nic for ${OCF_RESKEY_ip}: $tmp" OCF_RESKEY_nic=$tmp elif [ x$tmp != x${OCF_RESKEY_nic} ] then ocf_log err "Invalid parameter value: nic [$OCF_RESKEY_nic] Calculated nic: [$tmp]" return $OCF_ERR_ARGS fi tmp=`echo "$NICINFO" | cut -f2 | cut -d ' ' -f2` if [ "x$OCF_RESKEY_cidr_netmask" != "x$tmp" ] then ocf_log info "Using calculated netmask for ${OCF_RESKEY_ip}: $tmp" fi # Always use the calculated version becuase it might have been specified # using CIDR notation which not every system accepts OCF_RESKEY_netmask=$tmp OCF_RESKEY_cidr_netmask=$tmp; export OCF_RESKEY_cidr_netmask tmp=`echo "$NICINFO" | cut -f3 | cut -d ' ' -f2` if [ "x$OCF_RESKEY_broadcast" = "x" ] then ocf_log debug "Using calculated broadcast for ${OCF_RESKEY_ip}: $tmp" OCF_RESKEY_broadcast=$tmp elif [ x$tmp != x${OCF_RESKEY_broadcast} ]; then ocf_log err "Invalid parameter value: broadcast [$OCF_RESKEY_broadcast] Calculated broadcast: [$tmp]" return $OCF_ERR_ARGS fi return $OCF_SUCCESS } usage() { echo $USAGE >&2 return $1 } if [ $# -ne 1 ]; then usage $OCF_ERR_ARGS fi : ${OCF_RESKEY_lvs_support=0} # Normalize the value of lvs_support if [ "${OCF_RESKEY_lvs_support}" = "true" \ -o "${OCF_RESKEY_lvs_support}" = "on" \ -o "${OCF_RESKEY_lvs_support}" = "yes" \ -o "${OCF_RESKEY_lvs_support}" = "1" ]; then OCF_RESKEY_lvs_support=1 else OCF_RESKEY_lvs_support=0 fi # Note: We had a version out there for a while which used # netmask instead of cidr_netmask. So, don't remove this aliasing code! if [ ! -z "$OCF_RESKEY_netmask" -a -z "$OCF_RESKEY_cidr_netmask" ] then OCF_RESKEY_cidr_netmask=$OCF_RESKEY_netmask export OCF_RESKEY_cidr_netmask fi case $1 in meta-data) meta_data;; start) ip_validate_all && ip_start;; stop) ip_stop;; status) ip_status;; monitor) ip_monitor;; validate-all) ip_validate_all;; usage) usage $OCF_SUCCESS;; *) usage $OCF_ERR_UNIMPLEMENTED;; esac exit $? diff --git a/heartbeat/IPaddr2 b/heartbeat/IPaddr2 index 554988603..867c5d297 100644 --- a/heartbeat/IPaddr2 +++ b/heartbeat/IPaddr2 @@ -1,852 +1,849 @@ #!/bin/sh # # $Id: IPaddr2.in,v 1.24 2006/08/09 13:01:54 lars Exp $ # # OCF Resource Agent compliant IPaddr2 script. # # Based on work by Tuomo Soini, ported to the OCF RA API by Lars # Marowsky-Brée. Implements Cluster Alias IP functionality too. # # Cluster Alias IP cleanup, fixes and testing by Michael Schwartzkopff # # # Copyright (c) 2003 Tuomo Soini # Copyright (c) 2004-2006 SUSE LINUX AG, Lars Marowsky-Brée # 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. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # # # TODO: # - There ought to be an ocf_run_cmd function which does all logging, # timeout handling etc for us # - Make this the standard IP address agent on Linux; the other # platforms simply should ignore the additional parameters OR can use # the legacy heartbeat resource script... # - Check LVS <-> clusterip incompatibilities. # # OCF parameters are as below # OCF_RESKEY_ip # OCF_RESKEY_broadcast # OCF_RESKEY_nic # OCF_RESKEY_cidr_netmask # OCF_RESKEY_iflabel # OCF_RESKEY_mac # OCF_RESKEY_clusterip_hash # OCF_RESKEY_arp_interval # OCF_RESKEY_arp_count # OCF_RESKEY_arp_bg # OCF_RESKEY_arp_mac # # OCF_RESKEY_CRM_meta_clone # OCF_RESKEY_CRM_meta_clone_max ####################################################################### # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs SENDARP=$HA_BIN/send_arp FINDIF=$HA_BIN/findif VLDIR=$HA_RSCTMP/IPaddr SENDARPPIDDIR=$HA_RSCTMP/send_arp CIP_lockfile=$HA_RSCTMP/IPaddr2-CIP-${OCF_RESKEY_ip} ####################################################################### meta_data() { cat < 1.0 This Linux-specific resource manages IP alias IP addresses. It can add an IP alias, or remove one. In addition, it can implement Cluster Alias IP functionality if invoked as a clone resource. Manages virtual IPv4 addresses The IPv4 address to be configured in dotted quad notation, for example "192.168.1.1". IPv4 address The base network interface on which the IP address will be brought online. If left empty, the script will try and determine this from the routing table. Do NOT specify an alias interface in the form eth0:1 or anything here; rather, specify the base interface only. Network interface The netmask for the interface in CIDR format (e.g., 24 and not 255.255.255.0) If unspecified, the script will also try to determine this from the routing table. CIDR netmask Broadcast address associated with the IP. If left empty, the script will determine this from the netmask. Broadcast address You can specify an additional label for your IP address here. This label is appended to your interface name. If a label is specified in nic name, this parameter has no effect. Interface label Enable support for LVS Direct Routing configurations. In case a IP address is stopped, only move it to the loopback device to allow the local node to continue to service requests, but no longer advertise it on the network. Enable support for LVS DR Set the interface MAC address explicitly. Currently only used in case of the Cluster IP Alias. Leave empty to chose automatically. Cluster IP MAC address Specify the hashing algorithm used for the Cluster IP functionality. Cluster IP hashing function If true, add the clone ID to the supplied value of ip to create a unique address to manage Create a unique address for cloned instances Specify the interval between unsolicited ARP packets in milliseconds. ARP packet interval in ms Number of unsolicited ARP packets to send. ARP packet count Whether or not to send the arp packets in the background. ARP from background MAC address to send the ARP packets too. You really shouldn't be touching this. ARP MAC END exit $OCF_SUCCESS } ip_init() { if [ X`uname -s` != "XLinux" ]; then ocf_log err "IPaddr2 only supported Linux." exit $OCF_ERR_INSTALLED fi if case $__OCF_ACTION in start|stop) ocf_is_root;; *) true;; esac then : YAY! else ocf_log err "You must be root for $__OCF_ACTION operation." exit $OCF_ERR_PERM fi BASEIP="$OCF_RESKEY_ip" BRDCAST="$OCF_RESKEY_broadcast" NIC="$OCF_RESKEY_nic" # Note: We had a version out there for a while which used # netmask instead of cidr_netmask. Don't remove this aliasing code! if [ ! -z "$OCF_RESKEY_netmask" -a -z "$OCF_RESKEY_cidr_netmask" ] then OCF_RESKEY_cidr_netmask=$OCF_RESKEY_netmask export OCF_RESKEY_cidr_netmask fi NETMASK="$OCF_RESKEY_cidr_netmask" IFLABEL="$OCF_RESKEY_iflabel" IF_MAC="$OCF_RESKEY_mac" LVS_SUPPORT=0 if [ x"${OCF_RESKEY_lvs_support}" = x"true" \ -o x"${OCF_RESKEY_lvs_support}" = x"on" \ -o x"${OCF_RESKEY_lvs_support}" = x"1" ]; then LVS_SUPPORT=1 fi IP_INC_GLOBAL=${OCF_RESKEY_CRM_meta_clone_max:-1} IP_INC_NO=`expr ${OCF_RESKEY_CRM_meta_clone:-0} + 1` IP_CIP_HASH="${OCF_RESKEY_clusterip_hash:-'sourceip-sourceport'}" if [ $LVS_SUPPORT -gt 0 ] && [ $IP_INC_GLOBAL -gt 1 ]; then ocf_log err "LVS and load sharing do not go together well" - exit OCF_ERR_ARGS + exit $OCF_ERR_ARGS fi ARP_INTERVAL_MS=${OCF_RESKEY_arp_interval:-200} ARP_REPEAT=${OCF_RESKEY_arp_count:-5} ARP_BACKGROUND=${OCF_RESKEY_arp_bg:-yes} ARP_NETMASK=${OCF_RESKEY_arp_mac:-ffffffffffff} if ocf_is_decimal "$IP_INC_GLOBAL" && [ $IP_INC_GLOBAL -gt 0 ]; then : else ocf_log err "Invalid OCF_RESKEY_incarnations_max_global [$IP_INC_GLOBAL], should be positive integer" exit $OCF_ERR_ARGS fi # Validation is performed in ip_validate()... # # $FINDIF now takes its parameters from the environment # if NICINFO=`$FINDIF -C` then NICINFO=`echo $NICINFO | sed -e 's/netmask\ //;s/broadcast\ //'` NIC=`echo "$NICINFO" | cut -d" " -f1` NETMASK=`echo "$NICINFO" | cut -d" " -f2` BRDCAST=`echo "$NICINFO" | cut -d" " -f3` else ocf_log err "[$FINDIF -C] failed" exit $OCF_ERR_ARGS fi SENDARPPIDFILE="$SENDARPPIDDIR/send_arp-$BASEIP" case $NIC in *:*) IFLABEL=$NIC NIC=`echo $NIC | sed 's/:.*//'` ;; *) if [ -n "$IFLABEL" ]; then IFLABEL=${NIC}:${IFLABEL} fi ;; esac IP_CIP= if [ "$IP_INC_GLOBAL" -gt 1 ]; then IP_CIP="yes" if [ -z "$IF_MAC" ]; then # Choose a MAC # 1. Concatenate some input together # 2. This doesn't need to be a cryptographically # secure hash. # 3. Drop everything after the first 6 octets (12 chars) # 4. Delimit the octets with ':' # 5. Make sure the first octet is odd, # so the result is a multicast MAC IF_MAC=`echo $BASEIP $NETMASK $BRDCAST | \ md5sum | \ sed -e 's#\(............\).*#\1#' \ -e 's#..#&:#g; s#:$##' \ -e 's#^\(.\)[02468aAcCeE]#\11#'` fi IP_CIP_FILE="/proc/net/ipt_CLUSTERIP/$BASEIP" fi } # # Find out which interface serves the given IP address # The argument is an IP address, and its output # is an interface name (e.g., "eth0"). # find_interface() { # # List interfaces but exclude FreeS/WAN ipsecN virtual interfaces # local iface=`$IP2UTIL -o -f inet addr show | grep "\ $BASEIP/" \ | cut -d ' ' -f2 | grep -v '^ipsec[0-9][0-9]*$'` echo $iface return 0 } # # Delete an interface # delete_interface () { ipaddr="$1" iface="$2" netmask="$3" CMD="$IP2UTIL -f inet addr delete $ipaddr/$netmask dev $iface" ocf_log info "$CMD" $CMD if [ $? -ne 0 ]; then return $OCF_ERR_GENERIC fi CMD="$IP2UTIL -o -f inet addr show $iface" ocf_log info "$CMD" ADDR=`$CMD` if [ $? -ne 0 -o ! -z "$ADDR" ]; then return $? fi CMD="$IP2UTIL link set $iface down" ocf_log info "$CMD" $CMD return $? } # # Add an interface # add_interface () { ipaddr="$1" netmask="$2" broadcast="$3" iface="$4" label="$5" CMD="$IP2UTIL -f inet addr add $ipaddr/$netmask brd $broadcast dev $iface" if [ ! -z "$label" ]; then CMD="$CMD label $label" fi ocf_log info "$CMD" $CMD if [ $? -ne 0 ]; then return $OCF_ERR_GENERIC fi CMD="$IP2UTIL link set $iface up" ocf_log info "$CMD" $CMD return $? } # # Delete a route # delete_route () { prefix="$1" iface="$2" CMD="$IP2UTIL route delete $prefix dev $iface" ocf_log info "$CMD" $CMD return $? } # On Linux systems the (hidden) loopback interface may # conflict with the requested IP address. If so, this # unoriginal code will remove the offending loopback address # and save it in VLDIR so it can be added back in later # when the IPaddr is released. # # TODO: This is very ugly and should be controlled by an additional # instance parameter. Or even: multi-state, with the IP only being # "active" on the master!? # remove_conflicting_loopback() { ipaddr="$1" netmask="$2" broadcast="$3" ifname="$4" ocf_log info "Removing conflicting loopback $ifname." if [ -d "$VLDIR/" ] || mkdir -p "$VLDIR/"; then : Directory $VLDIR now exists else ocf_log err "Could not create \"$VLDIR/\" conflicting" \ " loopback $ifname cannot be restored." fi if echo "$ipaddr $netmask $broadcast $ifname" > "$VLDIR/$ipaddr" then : Saved loopback information in $VLDIR/$ipaddr else ocf_log err "Could not save conflicting loopback $ifname." \ "it will not be restored." fi delete_interface "$ipaddr" "$ifname" "$netmask" # Forcibly remove the route (if it exists) to the loopback. delete_route "$ipaddr" "$ifname" -} +} # # On Linux systems the (hidden) loopback interface may # need to be restored if it has been taken down previously # by remove_conflicting_loopback() # restore_loopback() { ipaddr="$1" if [ -s "$VLDIR/$ipaddr" ]; then ifinfo=`cat "$VLDIR/$ipaddr"` ocf_log info "Restoring loopback IP Address " \ "$ifinfo." add_interface $ifinfo rm -f "$VLDIR/$ipaddr" fi } # # Run send_arp to note peers about new mac address # run_send_arp() { ARGS="-i $ARP_INTERVAL_MS -r $ARP_REPEAT -p $SENDARPPIDFILE $NIC $BASEIP auto not_used not_used" if [ "x$IP_CIP" = "xyes" ] ; then if [ x = "x$IF_MAC" ] ; then MY_MAC=auto else MY_MAC=`echo ${IF_MAC} | sed -e 's/://'` fi ARGS="-i $ARP_INTERVAL_MS -r $ARP_REPEAT -p $SENDARPPIDFILE $NIC $BASEIP $MY_MAC not_used not_used" fi ocf_log info "$SENDARP $ARGS" case $ARP_BACKGROUND in yes) ($SENDARP $ARGS || ocf_log err "Could not send gratuitous arps" &) >&2 ;; *) $SENDARP $ARGS || ocf_log err "Could not send gratuitous arps" ;; esac } # # Run ipoibarping to note peers about new Infiniband address # run_send_ib_arp() { SENDIBARP="/sbin/ipoibarping" if [ ! -x $SENDIBARP ] ; then ocf_log err "Could not send gratuitous Infiniband arps: file not found: $SENDIBPARP" return fi ARGS="-q -c $ARP_REPEAT -U -I $NIC $BASEIP" ocf_log info "$SENDIBARP $ARGS" case $ARP_BACKGROUND in yes) ($SENDIBARP $ARGS || ocf_log err "Could not send gratuitous arps" &) >&2 ;; *) $SENDIBARP $ARGS || ocf_log err "Could not send gratuitous arps" ;; esac } # Do we already serve this IP address? # # returns: # ok = served (for CIP: + hash bucket) # partial = served and no hash bucket (CIP only) # no = nothing # ip_served() { cur_nic="`find_interface $BASEIP`" if [ -z "$cur_nic" ]; then echo "no" return 0 fi if [ -z "$IP_CIP" ]; then case $cur_nic in lo*) if [ "$LVS_SUPPORT" = "1" ]; then echo "no" return 0 fi ;; esac echo "ok" return 0 fi # Special handling for the CIP: if egrep -q "(^|,)${IP_INC_NO}(,|$)" $IP_CIP_FILE ; then echo "ok" return 0 else echo "partial" return 0 fi exit $OCF_ERR_GENERIC } ####################################################################### ip_usage() { cat <$IP_CIP_FILE fi if [ "$ip_status" = "no" ]; then if [ "$LVS_SUPPORT" = "1" ]; then case `find_interface $BASEIP` in lo*) remove_conflicting_loopback $BASEIP 32 255.255.255.255 lo ;; esac fi add_interface $BASEIP $NETMASK $BRDCAST $NIC $IFLABEL if [ $? -ne 0 ]; then ocf_log err "$CMD failed." exit $OCF_ERR_GENERIC fi fi case $NIC in lo*) : no need to run send_arp on loopback ;; ib*) run_send_ib_arp ;; *) if [ -x $SENDARP ]; then run_send_arp fi ;; esac exit $OCF_SUCCESS } ip_stop() { - ip_init local ip_del_if="yes" if [ -n "$IP_CIP" ]; then # Cluster IPs need special processing when the last bucket # is removed from the node... take a lock to make sure only one # process executes that code ocf_take_lock $CIP_lockfile ocf_release_lock_on_exit $CIP_lockfile fi if [ -f "$SENDARPPIDFILE" ] ; then kill `cat "$SENDARPPIDFILE"` if [ $? -ne 0 ]; then ocf_log warn "Could not kill previously running send_arp for $BASEIP" else ocf_log info "killed previously running send_arp for $BASEIP" rm -f "$SENDARPPIDFILE" fi fi local ip_status=`ip_served` if [ $ip_status = "no" ]; then : Requested interface not in use exit $OCF_SUCCESS fi if [ -n "$IP_CIP" ]; then if [ $ip_status = "partial" ]; then exit $OCF_SUCCESS fi echo "-$IP_INC_NO" >$IP_CIP_FILE if [ "x$(cat $IP_CIP_FILE)" = "x" ]; then ocf_log info $BASEIP, $IP_CIP_HASH i=1 while [ $i -le $IP_INC_GLOBAL ]; do ocf_log info $i $IPTABLES -D INPUT -d $BASEIP -i $NIC -j CLUSTERIP \ --new \ --clustermac $IF_MAC \ --total-nodes $IP_INC_GLOBAL \ --local-node $i \ --hashmode $IP_CIP_HASH i=`expr $i + 1` done else ip_del_if="no" fi fi if [ "$ip_del_if" = "yes" ]; then delete_interface $BASEIP $NIC $NETMASK if [ $? -ne 0 ]; then exit $OCF_ERR_GENERIC fi if [ "$LVS_SUPPORT" = 1 ]; then restore_loopback "$BASEIP" fi fi exit $OCF_SUCCESS } ip_monitor() { - ip_init # TODO: Implement more elaborate monitoring like checking for # interface health maybe via a daemon like FailSafe etc... local ip_status=`ip_served` case $ip_status in ok) return $OCF_SUCCESS ;; partial|no) exit $OCF_NOT_RUNNING ;; *) # Errors on this interface? return $OCF_ERR_GENERIC ;; esac } ip_validate() { check_binary $IP2UTIL if [ -n "$IP_CIP" ]; then check_binary $IPTABLES check_binary $MODPROBE fi ip_init # $BASEIP, $NETMASK, $NIC , $IP_INC_GLOBAL, and $BRDCAST have been checked within ip_init, # do not bother here. if ocf_is_decimal "$ARP_INTERVAL_MS" && [ $ARP_INTERVAL_MS -gt 0 ]; then : else ocf_log err "Invalid OCF_RESKEY_arp_interval [$ARP_INTERVAL_MS]" exit $OCF_ERR_ARGS fi if ocf_is_decimal "$ARP_REPEAT" && [ $ARP_REPEAT -gt 0 ]; then : else ocf_log err "Invalid OCF_RESKEY_arp_count [$ARP_REPEAT]" exit $OCF_ERR_ARGS fi if [ -n "$IP_CIP" ]; then local valid=1 case $IP_CIP_HASH in sourceip|sourceip-sourceport|sourceip-sourceport-destport) ;; *) ocf_log err "Invalid OCF_RESKEY_clusterip_hash [$IP_CIP_HASH]" exit $OCF_ERR_ARGS ;; esac if [ "$LVS_SUPPORT" = 1 ]; then ecf_log err "LVS and load sharing not advised to try" exit $OCF_ERR_ARGS fi case $IF_MAC in [0-9a-zA-Z][1379bBdDfF][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][!0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]) ;; *) valid=0 ;; esac if [ $valid -eq 0 ]; then ocf_log err "Invalid IF_MAC [$IF_MAC]" exit $OCF_ERR_ARGS fi fi - - exit $OCF_SUCCESS } case "$OCF_RESKEY_unique_clone_address" in true|TRUE|True|yes|YES|Yes|1) prefix=`echo $OCF_RESKEY_ip | awk -F. '{print $1"."$2"."$3}'` suffix=`echo $OCF_RESKEY_ip | awk -F. '{print $4}'` suffix=`expr ${OCF_RESKEY_CRM_meta_clone:-0} + $suffix` OCF_RESKEY_ip="$prefix.$suffix" ;; *) ;; esac case $__OCF_ACTION in meta-data) meta_data ;; +usage|help) ip_usage + exit $OCF_SUCCESS + ;; +esac + +ip_validate + +case $__OCF_ACTION in start) ip_start ;; stop) ip_stop ;; -status) ip_init - ip_status=`ip_served` +status) ip_status=`ip_served` if [ $ip_status = "ok" ]; then echo "running" exit $OCF_SUCCESS else echo "stopped" exit $OCF_NOT_RUNNING fi ;; monitor) ip_monitor ;; -validate-all) ip_validate - ;; -usage|help) ip_usage - exit $OCF_SUCCESS - ;; +validate-all) ;; *) ip_usage exit $OCF_ERR_UNIMPLEMENTED ;; esac diff --git a/heartbeat/IPv6addr.c b/heartbeat/IPv6addr.c index 959d8edf7..eb03b8932 100644 --- a/heartbeat/IPv6addr.c +++ b/heartbeat/IPv6addr.c @@ -1,851 +1,866 @@ /* * This program manages IPv6 address with OCF Resource Agent standard. * * Author: Huang Zhen * Copyright (c) 2004 International Business Machines * * This library 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 library 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. */ /* * It can add an IPv6 address, or remove one. * * Usage: IPv6addr {start|stop|status|monitor|meta-data} * * The "start" arg adds an IPv6 address. * The "stop" arg removes one. * The "status" arg shows whether the IPv6 address exists * The "monitor" arg shows whether the IPv6 address can be pinged (ICMPv6 ECHO) * The "meta_data" arg shows the meta data(XML) */ /* * ipv6-address: * * currently the following forms are legal: * address * address/prefix * * E.g. * 3ffe:ffff:0:f101::3 * 3ffe:ffff:0:f101::3/64 * * It should be passed by environment variant: * OCF_RESKEY_ipv6addr=3ffe:ffff:0:f101::3 * */ /* * start: * 1.IPv6addr will choice a proper interface for the new address. * 2.Then assign the new address to the interface. * 3.Wait until the new address is available (reply ICMPv6 ECHO packet) * 4.Send out the unsolicited advertisements. * * return 0(OCF_SUCCESS) for success * return 1(OCF_ERR_GENERIC) for failure * return 2(OCF_ERR_ARGS) for invalid or excess argument(s) * * * stop: * remove the address from the inferface. * * return 0(OCF_SUCCESS) for success * return 1(OCF_ERR_GENERIC) for failure * return 2(OCF_ERR_ARGS) for invalid or excess argument(s) * * status: * return the status of the address. only check whether it exists. * * return 0(OCF_SUCCESS) for existing * return 1(OCF_NOT_RUNNING) for not existing * return 2(OCF_ERR_ARGS) for invalid or excess argument(s) * * * monitor: * ping the address by ICMPv6 ECHO request. * * return 0(OCF_SUCCESS) for response correctly. * return 1(OCF_NOT_RUNNING) for no response. * return 2(OCF_ERR_ARGS) for invalid or excess argument(s) */ #include #include #include #include #include #include #include #include #define PIDFILE_BASE HA_VARRUNDIR "/IPv6addr-" /* 0 No error, action succeeded completely 1 generic or unspecified error (current practice) The "monitor" operation shall return this for a crashed, hung or otherwise non-functional resource. 2 invalid or excess argument(s) Likely error code for validate-all, if the instance parameters do not validate. Any other action is free to also return this exit status code for this case. 3 unimplemented feature (for example, "reload") 4 user had insufficient privilege 5 program is not installed 6 program is not configured 7 program is not running 8 resource is running in "master" mode and fully operational 9 resource is in "master" mode but in a failed state */ #define OCF_SUCCESS 0 #define OCF_ERR_GENERIC 1 #define OCF_ERR_ARGS 2 #define OCF_ERR_UNIMPLEMENTED 3 #define OCF_ERR_PERM 4 #define OCF_ERR_INSTALLED 5 #define OCF_ERR_CONFIGURED 6 #define OCF_NOT_RUNNING 7 const char* IF_INET6 = "/proc/net/if_inet6"; const char* APP_NAME = "IPv6addr"; const char* START_CMD = "start"; const char* STOP_CMD = "stop"; const char* STATUS_CMD = "status"; const char* MONITOR_CMD = "monitor"; const char* ADVT_CMD = "advt"; const char* RECOVER_CMD = "recover"; const char* RELOAD_CMD = "reload"; const char* META_DATA_CMD = "meta-data"; const char* VALIDATE_CMD = "validate-all"; char BCAST_ADDR[] = "ff02::1"; const int UA_REPEAT_COUNT = 5; const int QUERY_COUNT = 5; struct in6_ifreq { struct in6_addr ifr6_addr; uint32_t ifr6_prefixlen; unsigned int ifr6_ifindex; }; static int start_addr6(struct in6_addr* addr6, int prefix_len); static int stop_addr6(struct in6_addr* addr6, int prefix_len); static int status_addr6(struct in6_addr* addr6, int prefix_len); static int monitor_addr6(struct in6_addr* addr6, int prefix_len); static int advt_addr6(struct in6_addr* addr6, int prefix_len); static int meta_data_addr6(void); static void usage(const char* self); int write_pid_file(const char *pid_file); int create_pid_directory(const char *pid_file); static void byebye(int nsig); static char* scan_if(struct in6_addr* addr_target, int* plen_target, int use_mask); static char* find_if(struct in6_addr* addr_target, int* plen_target); static char* get_if(struct in6_addr* addr_target, int* plen_target); static int assign_addr6(struct in6_addr* addr6, int prefix_len, char* if_name); static int unassign_addr6(struct in6_addr* addr6, int prefix_len, char* if_name); int is_addr6_available(struct in6_addr* addr6); static int send_ua(struct in6_addr* src_ip, char* if_name); int main(int argc, char* argv[]) { char pid_file[256]; char* ipv6addr; int ret; char* cp; int prefix_len; struct in6_addr addr6; /* Check the count of parameters first */ if (argc < 2) { usage(argv[0]); return OCF_ERR_ARGS; } /* set termination signal */ siginterrupt(SIGTERM, 1); signal(SIGTERM, byebye); /* open system log */ cl_log_set_entity(APP_NAME); cl_log_set_facility(HA_LOG_FACILITY); /* the meta-data dont need any parameter */ if (0 == strncmp(META_DATA_CMD, argv[1], strlen(META_DATA_CMD))) { ret = meta_data_addr6(); return OCF_SUCCESS; } /* check the OCF_RESKEY_ipv6addr parameter, should be an IPv6 address */ ipv6addr = getenv("OCF_RESKEY_ipv6addr"); if (ipv6addr == NULL) { cl_log(LOG_ERR, "Please set OCF_RESKEY_ipv6addr to the IPv6 address you want to manage."); usage(argv[0]); return OCF_ERR_ARGS; } if ((cp = strchr(ipv6addr, '/'))) { prefix_len = atol(cp + 1); if ((prefix_len < 0) || (prefix_len > 128)) { cl_log(LOG_ERR, "Invalid prefix_len [%s], should be an integer in [0, 128]", cp+1); usage(argv[0]); return OCF_ERR_ARGS; } *cp=0; } else { prefix_len = 0; } if (inet_pton(AF_INET6, ipv6addr, &addr6) <= 0) { cl_log(LOG_ERR, "Invalid IPv6 address [%s]", ipv6addr); usage(argv[0]); return OCF_ERR_ARGS; } /* Check whether this system supports IPv6 */ if (access(IF_INET6, R_OK)) { cl_log(LOG_ERR, "No support for INET6 on this system."); return OCF_ERR_GENERIC; } /* create the pid file so we can make sure that only one IPv6addr * for this address is running */ if (snprintf(pid_file, sizeof(pid_file), "%s%s", PIDFILE_BASE, ipv6addr) >= (int)sizeof(pid_file)) { cl_log(LOG_ERR, "Pid file truncated"); return OCF_ERR_GENERIC; } if (write_pid_file(pid_file) < 0) { return OCF_ERR_GENERIC; } /* switch the command */ if (0 == strncmp(START_CMD,argv[1], strlen(START_CMD))) { ret = start_addr6(&addr6, prefix_len); }else if (0 == strncmp(STOP_CMD,argv[1], strlen(STOP_CMD))) { ret = stop_addr6(&addr6, prefix_len); }else if (0 == strncmp(STATUS_CMD,argv[1], strlen(STATUS_CMD))) { ret = status_addr6(&addr6, prefix_len); }else if (0 ==strncmp(MONITOR_CMD,argv[1], strlen(MONITOR_CMD))) { ret = monitor_addr6(&addr6, prefix_len); }else if (0 ==strncmp(RELOAD_CMD,argv[1], strlen(RELOAD_CMD))) { ret = OCF_ERR_UNIMPLEMENTED; }else if (0 ==strncmp(RECOVER_CMD,argv[1], strlen(RECOVER_CMD))) { ret = OCF_ERR_UNIMPLEMENTED; }else if (0 ==strncmp(VALIDATE_CMD,argv[1], strlen(VALIDATE_CMD))) { /* ipv6addr has been validated by inet_pton, hence a valid IPv6 address */ ret = OCF_SUCCESS; }else if (0 ==strncmp(ADVT_CMD,argv[1], strlen(MONITOR_CMD))) { ret = advt_addr6(&addr6, prefix_len); }else{ usage(argv[0]); ret = OCF_ERR_ARGS; } /* release the pid file */ unlink(pid_file); return ret; } int start_addr6(struct in6_addr* addr6, int prefix_len) { int i; char* if_name; if(OCF_SUCCESS == status_addr6(addr6,prefix_len)) { return OCF_SUCCESS; } /* we need to find a proper device to assign the address */ if_name = find_if(addr6, &prefix_len); if (NULL == if_name) { cl_log(LOG_ERR, "no valid mecahnisms"); return OCF_ERR_GENERIC; } /* Assign the address */ if (0 != assign_addr6(addr6, prefix_len, if_name)) { cl_log(LOG_ERR, "failed to assign the address to %s", if_name); return OCF_ERR_GENERIC; } /* Check whether the address available */ for (i = 0; i < QUERY_COUNT; i++) { if (0 == is_addr6_available(addr6)) { break; } sleep(1); } if (i == QUERY_COUNT) { cl_log(LOG_ERR, "failed to ping the address"); return OCF_ERR_GENERIC; } /* Send unsolicited advertisement packet to neighbor */ for (i = 0; i < UA_REPEAT_COUNT; i++) { send_ua(addr6, if_name); sleep(1); } return OCF_SUCCESS; } int advt_addr6(struct in6_addr* addr6, int prefix_len) { /* First, we need to find a proper device to assign the address */ char* if_name = get_if(addr6, &prefix_len); int i; if (NULL == if_name) { cl_log(LOG_ERR, "no valid mecahnisms"); return OCF_ERR_GENERIC; } /* Send unsolicited advertisement packet to neighbor */ for (i = 0; i < UA_REPEAT_COUNT; i++) { send_ua(addr6, if_name); sleep(1); } return OCF_SUCCESS; } int stop_addr6(struct in6_addr* addr6, int prefix_len) { char* if_name; if(OCF_NOT_RUNNING == status_addr6(addr6,prefix_len)) { return OCF_SUCCESS; } if_name = get_if(addr6, &prefix_len); if (NULL == if_name) { cl_log(LOG_ERR, "no valid mechanisms."); /* I think this should be a success exit according to LSB. */ return OCF_ERR_GENERIC; } /* Unassign the address */ if (0 != unassign_addr6(addr6, prefix_len, if_name)) { cl_log(LOG_ERR, "failed to assign the address to %s", if_name); return OCF_ERR_GENERIC; } return OCF_SUCCESS; } int status_addr6(struct in6_addr* addr6, int prefix_len) { char* if_name = get_if(addr6, &prefix_len); if (NULL == if_name) { return OCF_NOT_RUNNING; } return OCF_SUCCESS; } int monitor_addr6(struct in6_addr* addr6, int prefix_len) { if(0 == is_addr6_available(addr6)) { return OCF_SUCCESS; } return OCF_NOT_RUNNING; } /* Send an unsolicited advertisement packet * Please refer to rfc2461 */ int send_ua(struct in6_addr* src_ip, char* if_name) { int status = -1; libnet_t *l; char errbuf[LIBNET_ERRBUF_SIZE]; struct libnet_in6_addr dst_ip; struct libnet_ether_addr *mac_address; char payload[24]; + int ifindex; if ((l=libnet_init(LIBNET_RAW6, if_name, errbuf)) == NULL) { cl_log(LOG_ERR, "libnet_init failure on %s", if_name); goto err; } + /* set the outgoing interface */ + ifindex = if_nametoindex(if_name); + if (setsockopt(libnet_getfd(l), IPPROTO_IPV6, IPV6_MULTICAST_IF, + &ifindex, sizeof(ifindex)) < 0) { + cl_log(LOG_ERR, "setsockopt(IPV6_MULTICAST_IF): %s", + strerror(errno)); + goto err; + } mac_address = libnet_get_hwaddr(l); if (!mac_address) { cl_log(LOG_ERR, "libnet_get_hwaddr: %s", errbuf); goto err; } dst_ip = libnet_name2addr6(l, BCAST_ADDR, LIBNET_DONT_RESOLVE); memcpy(payload,src_ip->s6_addr,16); payload[16] = 2; /* 2 for Target Link-layer Address */ payload[17] = 1; /* The length of the option */ memcpy(payload+18,mac_address->ether_addr_octet, 6); libnet_seed_prand(l); /* 0x2000: RSO */ libnet_build_icmpv4_echo(136,0,0,0x2000,0,(u_int8_t *)payload ,sizeof(payload), l, LIBNET_PTAG_INITIALIZER); libnet_build_ipv6(0,0,LIBNET_ICMPV6_H + sizeof(payload),IPPROTO_ICMP6, 255,*(struct libnet_in6_addr*)src_ip, dst_ip,NULL,0,l,0); + /* Hack: adjust the correct checksum offset. see LF #2034 */ + libnet_pblock_record_ip_offset(l, l->total_size); + if (libnet_write(l) == -1) { cl_log(LOG_ERR, "libnet_write: %s", libnet_geterror(l)); goto err; } status = 0; err: libnet_destroy(l); return status; } /* find the network interface associated with an address */ char* scan_if(struct in6_addr* addr_target, int* plen_target, int use_mask) { FILE *f; static char devname[21]=""; struct in6_addr addr; struct in6_addr mask; unsigned int plen, scope, dad_status, if_idx; unsigned int addr6p[4]; /* open /proc/net/if_inet6 file */ if ((f = fopen(IF_INET6, "r")) == NULL) { return NULL; } /* Loop for each entry */ while (1) { int i; int n; int s; gboolean same = TRUE; i = fscanf(f, "%08x%08x%08x%08x %02x %02x %02x %02x %20s\n", &addr6p[0], &addr6p[1], &addr6p[2], &addr6p[3], &if_idx, &plen, &scope, &dad_status, devname); if (i == EOF) { break; } else if (i != 9) { cl_log(LOG_INFO, "Error parsing %s, " "perhaps the format has changed\n", IF_INET6); break; } /* Only Global address entry would be considered. * maybe change? */ if (0 != scope) { continue; } /* If specified prefix, only same prefix entry * would be considered. */ if (*plen_target!=0 && plen != *plen_target) { continue; } *plen_target = plen; for (i = 0; i< 4; i++) { addr.s6_addr32[i] = htonl(addr6p[i]); } /* Make the mask based on prefix length */ memset(mask.s6_addr, 0xff, 16); if (use_mask && plen < 128) { n = plen / 32; memset(mask.s6_addr32 + n + 1, 0, (3 - n) * 4); s = 32 - plen % 32; - mask.s6_addr32[n] = 0xffffffff << s; + if (s == 32) + mask.s6_addr32[n] = 0x0; + else + mask.s6_addr32[n] = 0xffffffff << s; mask.s6_addr32[n] = htonl(mask.s6_addr32[n]); } /* compare addr and addr_target */ same = TRUE; for (i = 0; i < 4; i++) { if ((addr.s6_addr32[i]&mask.s6_addr32[i]) != (addr_target->s6_addr32[i]&mask.s6_addr32[i])) { same = FALSE; break; } } /* We found it! */ if (same) { fclose(f); return devname; } } fclose(f); return NULL; } /* find a proper network interface to assign the address */ char* find_if(struct in6_addr* addr_target, int* plen_target) { return scan_if(addr_target, plen_target, 1); } /* get the device name and the plen_target of a special address */ char* get_if(struct in6_addr* addr_target, int* plen_target) { return scan_if(addr_target, plen_target, 0); } int assign_addr6(struct in6_addr* addr6, int prefix_len, char* if_name) { struct in6_ifreq ifr6; /* Get socket first */ int fd; struct ifreq ifr; fd = socket(AF_INET6, SOCK_DGRAM, 0); if (fd < 0) { return 1; } /* Query the index of the if */ strcpy(ifr.ifr_name, if_name); if (ioctl(fd, SIOGIFINDEX, &ifr) < 0) { return -1; } /* Assign the address to the if */ ifr6.ifr6_addr = *addr6; ifr6.ifr6_ifindex = ifr.ifr_ifindex; ifr6.ifr6_prefixlen = prefix_len; if (ioctl(fd, SIOCSIFADDR, &ifr6) < 0) { return -1; } close (fd); return 0; } int unassign_addr6(struct in6_addr* addr6, int prefix_len, char* if_name) { int fd; struct ifreq ifr; struct in6_ifreq ifr6; /* Get socket first */ fd = socket(AF_INET6, SOCK_DGRAM, 0); if (fd < 0) { return 1; } /* Query the index of the if */ strcpy(ifr.ifr_name, if_name); if (ioctl(fd, SIOGIFINDEX, &ifr) < 0) { return -1; } /* Unassign the address to the if */ ifr6.ifr6_addr = *addr6; ifr6.ifr6_ifindex = ifr.ifr_ifindex; ifr6.ifr6_prefixlen = prefix_len; if (ioctl(fd, SIOCDIFADDR, &ifr6) < 0) { return -1; } close (fd); return 0; } #define MINPACKSIZE 64 int is_addr6_available(struct in6_addr* addr6) { struct sockaddr_in6 addr; struct libnet_icmpv6_hdr icmph; u_char outpack[MINPACKSIZE]; int icmp_sock; int ret; struct iovec iov; u_char packet[MINPACKSIZE]; struct msghdr msg; icmp_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); memset(&icmph, 0, sizeof(icmph)); icmph.icmp_type = ICMP6_ECHO; icmph.icmp_code = 0; icmph.icmp_sum = 0; icmph.seq = htons(0); icmph.id = 0; memset(&outpack, 0, sizeof(outpack)); memcpy(&outpack, &icmph, sizeof(icmph)); memset(&addr, 0, sizeof(struct sockaddr_in6)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(IPPROTO_ICMPV6); memcpy(&addr.sin6_addr,addr6,sizeof(struct in6_addr)); /* Only the first 8 bytes of outpack are meaningful... */ ret = sendto(icmp_sock, (char *)outpack, sizeof(outpack), 0, (struct sockaddr *) &addr, sizeof(struct sockaddr_in6)); if (0 >= ret) { return -1; } iov.iov_base = (char *)packet; iov.iov_len = sizeof(packet); msg.msg_name = &addr; msg.msg_namelen = sizeof(addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; ret = recvmsg(icmp_sock, &msg, MSG_DONTWAIT); if (0 >= ret) { return -1; } return 0; } static void usage(const char* self) { printf("usage: %s {start|stop|status|monitor|validate-all|meta-data}\n",self); return; } /* Following code is copied from send_arp.c, linux-HA project. */ void byebye(int nsig) { (void)nsig; /* Avoid an "error exit" log message if we're killed */ exit(0); } int create_pid_directory(const char *pid_file) { int status; int return_status = -1; struct stat stat_buf; char* dir; dir = strdup(pid_file); if (!dir) { cl_log(LOG_INFO, "Memory allocation failure: %s", strerror(errno)); return -1; } dirname(dir); status = stat(dir, &stat_buf); if (status < 0 && errno != ENOENT && errno != ENOTDIR) { cl_log(LOG_INFO, "Could not stat pid-file directory " "[%s]: %s", dir, strerror(errno)); goto err; } if (!status) { if (S_ISDIR(stat_buf.st_mode)) { goto out; } cl_log(LOG_INFO, "Pid-File directory exists but is " "not a directory [%s]", dir); goto err; } if (mkdir(dir, S_IRUSR|S_IWUSR|S_IXUSR | S_IRGRP|S_IXGRP) < 0) { cl_log(LOG_INFO, "Could not create pid-file directory " "[%s]: %s", dir, strerror(errno)); goto err; } out: return_status = 0; err: free(dir); return return_status; } int write_pid_file(const char *pid_file) { int pidfilefd; char pidbuf[11]; unsigned long pid; ssize_t bytes; if (*pid_file != '/') { cl_log(LOG_INFO, "Invalid pid-file name, must begin with a " "'/' [%s]\n", pid_file); return -1; } if (create_pid_directory(pid_file) < 0) { return -1; } while (1) { pidfilefd = open(pid_file, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); if (pidfilefd < 0) { if (errno != EEXIST) { /* Old PID file */ cl_log(LOG_INFO, "Could not open pid-file " "[%s]: %s", pid_file, strerror(errno)); return -1; } } else { break; } pidfilefd = open(pid_file, O_RDONLY, S_IRUSR|S_IWUSR); if (pidfilefd < 0) { cl_log(LOG_INFO, "Could not open pid-file " "[%s]: %s", pid_file, strerror(errno)); return -1; } while (1) { bytes = read(pidfilefd, pidbuf, sizeof(pidbuf)-1); if (bytes < 0) { if (errno == EINTR) { continue; } cl_log(LOG_INFO, "Could not read pid-file " "[%s]: %s", pid_file, strerror(errno)); return -1; } pidbuf[bytes] = '\0'; break; } if(unlink(pid_file) < 0) { cl_log(LOG_INFO, "Could not delete pid-file " "[%s]: %s", pid_file, strerror(errno)); return -1; } if (!bytes) { cl_log(LOG_INFO, "Invalid pid in pid-file " "[%s]: %s", pid_file, strerror(errno)); return -1; } close(pidfilefd); pid = strtoul(pidbuf, NULL, 10); if (pid == ULONG_MAX && errno == ERANGE) { cl_log(LOG_INFO, "Invalid pid in pid-file " "[%s]: %s", pid_file, strerror(errno)); return -1; } if (kill(pid, SIGKILL) < 0 && errno != ESRCH) { cl_log(LOG_INFO, "Error killing old proccess [%lu] " "from pid-file [%s]: %s", pid, pid_file, strerror(errno)); return -1; } cl_log(LOG_INFO, "Killed old send_arp process [%lu]", pid); } if (snprintf(pidbuf, sizeof(pidbuf), "%u" , getpid()) >= (int)sizeof(pidbuf)) { cl_log(LOG_INFO, "Pid too long for buffer [%u]", getpid()); return -1; } while (1) { bytes = write(pidfilefd, pidbuf, strlen(pidbuf)); if (bytes != strlen(pidbuf)) { if (bytes < 0 && errno == EINTR) { continue; } cl_log(LOG_INFO, "Could not write pid-file " "[%s]: %s", pid_file, strerror(errno)); return -1; } break; } close(pidfilefd); return 0; } static int meta_data_addr6(void) { const char* meta_data= "\n" "\n" "\n" " 1.0\n" " \n" " This script manages IPv6 alias IPv6 addresses,It can add an IP6\n" " alias, or remove one.\n" " \n" " manages IPv6 alias\n" " \n" " \n" " \n" " The IPv6 address this RA will manage \n" " \n" " IPv6 address\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n"; printf("%s\n",meta_data); return OCF_SUCCESS; } diff --git a/heartbeat/LVM b/heartbeat/LVM index 3593a4db4..1daabe89f 100644 --- a/heartbeat/LVM +++ b/heartbeat/LVM @@ -1,346 +1,353 @@ #!/bin/sh # # # LVM # # Description: Manages an LVM volume as an HA resource # # # Author: Alan Robertson # Support: linux-ha@lists.linux-ha.org # License: GNU General Public License (GPL) # Copyright: (C) 2002 - 2005 International Business Machines, Inc. # # This code significantly inspired by the LVM resource # in FailSafe by Lars Marowsky-Bree # # # An example usage in /etc/ha.d/haresources: # node1 10.0.0.170 ServeRAID::1::1 LVM::myvolname # # See usage() function below for more details... # # OCF parameters are as below: # OCF_RESKEY_volgrpname # ####################################################################### # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs ####################################################################### usage() { methods=`LVM_methods` methods=`echo $methods | tr ' ' '|'` cat <<-! usage: $0 $methods $0 manages an Linux Volume Manager volume (LVM) as an HA resource The 'start' operation brings the given volume online The 'stop' operation takes the given volume offline The 'status' operation reports whether the volume is available The 'monitor' operation reports whether the volume seems present The 'validate-all' operation checks whether the OCF parameters are valid The 'methods' operation reports on the methods $0 supports ! exit $OCF_ERR_GENERIC } meta_data() { cat < 1.0 Resource script for LVM. It manages an Linux Volume Manager volume (LVM) as an HA resource. LVM resource agent The name of volume group. Volume group name + + +If set, the volume group will be activated exclusively. + +Exclusive activation + + END } # # methods: What methods/operations do we support? # LVM_methods() { cat <<-! start stop status monitor methods validate-all usage ! } # # Return LVM status (silently) # LVM_status() { if [ "$LVM_MAJOR" -eq "1" ] then vgdisplay $1 | grep -i 'Status.*available' >/dev/null return $? else vgdisplay -v $1 | grep -i 'Status[ \t]*available' >/dev/null return $? fi } # # Report on LVM volume status to stdout... # LVM_report_status() { if [ "$LVM_MAJOR" -eq "1" ] then VGOUT=`vgdisplay $1 2>&1` echo "$VGOUT" | grep -i 'Status.*available' >/dev/null rc=$? else VGOUT=`vgdisplay -v $1 2>&1` echo "$VGOUT" | grep -i 'Status[ \t]*available' >/dev/null rc=$? fi if [ $rc -eq 0 ] then : Volume $1 is available else ocf_log debug "LVM Volume $1 is not available (stopped)" return $OCF_NOT_RUNNING fi if echo "$VGOUT" | grep -i 'Access.*read/write' >/dev/null then ocf_log debug "Volume $1 is available read/write (running)" else ocf_log debug "Volume $1 is available read-only (running)" fi return $OCF_SUCCESS } # # Monitor the volume - does it really seem to be working? # # LVM_monitor() { if LVM_status $1 then : OK else ocf_log info "LVM Volume $1 is offline" return $OCF_NOT_RUNNING fi ocf_run vgck $1 return $? } # # Enable LVM volume # LVM_start() { # TODO: This MUST run vgimport as well ocf_log info "Activating volume group $1" - if - [ "$LVM_MAJOR" -eq "1" ] - then + if [ "$LVM_MAJOR" -eq "1" ]; then ocf_run vgscan $1 else ocf_run vgscan fi - # TODO: For v2, this MUST use --refresh - ocf_run vgchange -a y $1 || return $OCF_ERR_GENERIC + active_mode="ly" + if ocf_is_true "$OCF_RESKEY_exclusive" ; then + active_mode="ey" + fi - if - LVM_status $1 - then + ocf_run vgchange -a $active_mode $1 || return $OCF_ERR_GENERIC + + if LVM_status $1; then : OK Volume $1 activated just fine! return $OCF_SUCCESS else ocf_log err "LVM: $1 did not activate correctly" - return $OCF_ERR_GENERIC + return $OCF_NOT_RUNNING fi } # # Disable the LVM volume # LVM_stop() { vgdisplay "$1" 2>&1 | grep 'Volume group .* not found' >/dev/null && { ocf_log info "Volume group $1 not found" return 0 } ocf_log info "Deactivating volume group $1" - ocf_run vgchange -a n $1 || return 1 + ocf_run vgchange -a ln $1 || return 1 if LVM_status $1 then ocf_log err "LVM: $1 did not stop correctly" return $OCF_ERR_GENERIC fi # TODO: This MUST run vgexport as well return $OCF_SUCCESS } # # Check whether the OCF instance parameters are valid # LVM_validate_all() { check_binary $AWK # Off-the-shelf tests... - vgck "$VOLUME" >/dev/nulll 2>&1 + vgck "$VOLUME" >/dev/null 2>&1 if [ $? -ne 0 ]; then ocf_log err "Volume group [$VOLUME] does not exist or contains error!" exit $OCF_ERR_GENERIC fi # Double-check if [ "$LVM_MAJOR" -eq "1" ] then vgdisplay "$VOLUME" >/dev/null 2>&1 else vgdisplay -v "$VOLUME" >/dev/null 2>&1 fi if [ $? -ne 0 ]; then ocf_log err "Volume group [$VOLUME] does not exist or contains error!" exit $OCF_ERR_GENERIC fi return $OCF_SUCCESS } # # 'main' starts here... # if ( [ $# -ne 1 ] ) then usage exit $OCF_ERR_ARGS fi case $1 in meta-data) meta_data exit $OCF_SUCCESS;; methods) LVM_methods exit $?;; usage) usage exit $OCF_SUCCESS;; *) ;; esac if [ -z "$OCF_RESKEY_volgrpname" ] then # echo "You must identify the volume group name!" ocf_log err "You must identify the volume group name!" # usage exit $OCF_ERR_ARGS fi # Get the LVM version number, for this to work we assume(thanks to panjiam): # # LVM1 outputs like this # # # vgchange --version # vgchange: Logical Volume Manager 1.0.3 # Heinz Mauelshagen, Sistina Software 19/02/2002 (IOP 10) # # LVM2 and higher versions output in this format # # # vgchange --version # LVM version: 2.00.15 (2004-04-19) # Library version: 1.00.09-ioctl (2004-03-31) # Driver version: 4.1.0 LVM_VERSION=`vgchange --version 2>&1 | \ $AWK '/Logical Volume Manager/ {print $5"\n"; exit; } /LVM version:/ {printf $3"\n"; exit;}'` rc=$? if ( [ $rc -ne 0 ] || [ -z "$LVM_VERSION" ] ) then ocf_log err "LVM: $1 could not determine LVM version. Try 'vgchange --version' manually and modify $0 ?" - exit $OCF_ERR_GENERIC + exit $OCF_ERR_INSTALLED fi LVM_MAJOR="${LVM_VERSION%%.*}" VOLUME=$OCF_RESKEY_volgrpname # What kind of method was invoked? case "$1" in start) LVM_start $VOLUME exit $?;; stop) LVM_stop $VOLUME exit $?;; status) LVM_report_status $VOLUME exit $?;; monitor) LVM_monitor $VOLUME exit $?;; validate-all) LVM_validate_all ;; *) usage exit $OCF_ERR_AEGS;; esac diff --git a/heartbeat/MailTo b/heartbeat/MailTo index 921411ccb..cea6dd9ce 100644 --- a/heartbeat/MailTo +++ b/heartbeat/MailTo @@ -1,209 +1,215 @@ #!/bin/sh # # Resource script for MailTo # # Author: Alan Robertson # # Description: sends email to a sysadmin whenever a takeover occurs. # # Note: This command requires an argument, unlike normal init scripts. # # This can be given in the haresources file as: # # You can also give a mail subject line or even multiple addresses # MailTo::alanr@unix.sh::BigImportantWebServer # MailTo::alanr@unix.sh,spoppi@gmx.de::BigImportantWebServer # # This will then be put into the message subject and body. # # OCF parameters are as below: # OCF_RESKEY_email # OCF_RESKEY_subject # # License: GNU General Public License (GPL) # # Copyright: (C) 2005 International Business Machines ####################################################################### # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs ####################################################################### ARGS="$0 $*" us=`uname -n` usage() { echo "Usage: $0 {start|stop|status|monitor|meta-data|validate-all}" } meta_data() { cat < 1.0 This is a resource agent for MailTo. It sends email to a sysadmin whenever a takeover occurs. MailTo resource agent The email address of sysadmin. Email address The subject of the email. Subject END } MailProgram() { $MAILCMD -s "$1" "$email" </dev/null if [ $? -eq 0 ]; then : OK, mail to $item@localhost.localdomain else ocf_log err "Invalid email address [$email]" exit $OCF_ERR_ARGS fi ;; esac done # Any subject is OK return $OCF_SUCCESS } # # See how we were called. # # The order in which heartbeat provides arguments to resource # scripts is broken. It should be fixed. # if ( [ $# -ne 1 ] ) then usage exit $OCF_ERR_GENERIC fi case $1 in meta-data) meta_data exit $OCF_SUCCESS ;; status|monitor) MailToStatus exit $? ;; usage) usage exit $OCF_SUCCESS ;; *) ;; esac if [ -z "$OCF_RESKEY_email" ] then ocf_log err "At least 1 Email address has to be given!" # usage exit $OCF_ERR_GENERIC fi email=$OCF_RESKEY_email subject=$OCF_RESKEY_subject +MailToValidateAll + case $1 in start) MailToStart ;; stop) MailToStop ;; - validate-all) MailToValidateAll - ;; + validate-all) ;; *) usage exit $OCF_ERR_UNIMPLEMENTED ;; esac exit $? diff --git a/heartbeat/Makefile.am b/heartbeat/Makefile.am index c48dc9897..0e0e23b2b 100644 --- a/heartbeat/Makefile.am +++ b/heartbeat/Makefile.am @@ -1,105 +1,108 @@ # Makefile.am for OCF RAs # # Author: Sun Jing Dong # Copyright (C) 2004 IBM # # 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. # MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = $(ocf_SCRIPTS) ra-api-1.dtd INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/linux-ha dtddir = $(HA_NOARCHDATAHBDIR) ocfdir = @OCF_RA_DIR@/heartbeat dtd_SCRIPTS = ra-api-1.dtd gliblib = @GLIBLIB@ if USE_IPV6ADDR ocf_PROGRAMS = IPv6addr else ocf_PROGRAMS = endif IPv6addr_SOURCES = IPv6addr.c IPv6addr_LDADD = $(top_builddir)/lib/clplumbing/libplumb.la \ $(gliblib) @LIBNETLIBS@ ocf_SCRIPTS = ClusterMon \ Dummy \ IPaddr \ IPaddr2 \ drbd \ + anything \ + AoEtarget \ apache \ AudibleAlarm \ db2 \ Delay \ drbd \ eDir88 \ EvmsSCC \ Evmsd \ Filesystem \ ids \ iscsi \ ICP \ IPsrcaddr \ LinuxSCSI \ LVM \ MailTo \ ManageRAID \ ManageVE \ mysql \ nfsserver \ oracle \ oralsnr \ pingd \ portblock \ pgsql \ Pure-FTPd \ Raid1 \ Route \ rsyncd \ SAPDatabase \ SAPInstance \ SendArp \ ServeRAID \ SphinxSearchDaemon \ Squid \ Stateful \ SysInfo \ scsi2reservation \ sfex \ tomcat \ VIPArip \ + VirtualDomain \ vmware \ WAS \ WAS6 \ WinPopup \ Xen \ Xinetd \ .ocf-shellfuncs \ .ocf-binaries \ .ocf-directories \ .ocf-returncodes commondir = @HA_LIBHBDIR@ # Legacy locations common_SCRIPTS = ocf-shellfuncs ocf-returncodes diff --git a/heartbeat/Raid1 b/heartbeat/Raid1 index f92ac31d9..f51898255 100644 --- a/heartbeat/Raid1 +++ b/heartbeat/Raid1 @@ -1,409 +1,420 @@ #!/bin/sh # # # License: GNU General Public License (GPL) # Support: linux-ha@lists.linux-ha.org # # Raid1 # Description: Manages a software Raid1 device on a shared storage medium. # Original Author: Eric Z. Ayers (eric.ayers@compgen.com) # Original Release: 25 Oct 2000 # RAID patches: http://people.redhat.com/mingo/raid-patches/ # Word to the Wise: http://lwn.net/2000/0810/a/raid-faq.php3 # Sympathetic Ear: mailto:linux-raid@vger.kernel.org # # usage: $0 {start|stop|status|monitor|validate-all|usage|meta-data} # # OCF parameters are as below: # OCF_RESKEY_raidconf # (name of MD configuration file. e.g. /etc/raidtab or /etc/mdadm.conf) # OCF_RESKEY_raiddev # (of the form /dev/md* the block device to use) # # in /etc/ha.d/haresources, use a line such as: # nodea 10.0.0.170 Raid1::/etc/raidtab.md0::/dev/md0 Filesystem::/dev/md0::/data1::ext2 # (for systems with raidtools) # or # nodea 10.0.0.170 Raid1::/etc/mdadm.conf::/dev/md0 Filesystem::/dev/md0::/data1::ext2 # (for systems with mdadm) # # The "start" arg starts up the raid device # The "stop" arg stops it. NOTE: all filesystems must be unmounted # and no processes should be accessing the device. # The "status" arg just prints out whether the device is running or not # # # DISCLAIMER: Use at your own risk! # # Besides all of the usual legalese that accompanies free software, # I will warn you that I do not yet use this kind of setup (software RAID # over shared storage) in production, and I have reservations about doing so. # # The linux md driver/scsi drivers under Raid 0.90 and kernel version 2.2 # do not behave well when a drive is in the process of going bad. # The kernel slows down, but doesn't completely crash. This is about the # worst possible thing that could happen in an un-attended HA type # environment. (Once the system is rebooted, the sofware raid stuff works # like a champ.) # My other reservation has to do with the interation of RAID recovery with # journaling filesystems and other parts of the kernel. Subscribe to # linux-raid@vger.kernel.org for other opinions and possible solutions. # # -EZA 25 Oct 2000 # # SETUP: # # You might need to pass the command line parameter: raid=noautodetect # in an HA environment so that the kernel doesn't automatically start # up your raid partitions when you boot the node. This means that it isn't # going to work to use RAID for the system disks and the shared disks. # # 0) partition the disks to use for RAID. Use normal Linux partition # types, not the RAID autodetect type for your partitions. # 1) Create /etc/raidtab.md? on both systems (see example file below) # or for systems with mdadm tools create /etc/mdadm.conf (see example below) # 2) Initialize your raid partition with # /sbin/mkraid --configfile /etc/raidtab.md? /dev/md? # or create mirror raid with the following command # mdadm --create /dev/md? -l 1 -n 2 /dev/sdb? /dev/sdb? # 3) Format your filesystem # mke2fs /dev/md0 # for ext2fs... a journaling filesystem would be nice # 3) Create the mount point on both systems. # DO NOT add your raid filesystem to /etc/fstab # 4) copy this script (to /etc/rc.d/init.d if you wish) and edit it to # reflect your desired settings. # 5) Modify the heartbeat 'haresources' (for non-crm heartbeat) or 'cib.xml' (for crm heartbeat) setup file # 6) unmount the filesystem and stop the raid device with 'raidstop' or 'mdadm -S' # 7) fire up heartbeat! # # # EXAMPLE config file /etc/raidtab.md0 # This file must exist on both machines! # # raiddev /dev/md0 # raid-level 1 # nr-raid-disks 2 # chunk-size 64k # persistent-superblock 1 # #nr-spare-disks 0 # device /dev/sda1 # raid-disk 0 # device /dev/sdb1 # raid-disk 1 # # EXAMPLE config file /etc/mdadm.conf (for more info:man mdadm.conf) # # DEVICE /dev/sdb1 /dev/sdb2 # ARRAY /dev/md0 level=raid1 UUID=4a865b55:ba27ef8d:29cd5701:6fb42799 ####################################################################### # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs ####################################################################### usage() { cat <<-EOT usage: $0 {start|stop|status|monitor|validate-all|usage|meta-data} EOT } meta_data() { cat < 1.0 Resource script for RAID1. It manages a software Raid1 device on a shared storage medium. RAID1 resource agent The RAID configuration file. e.g. /etc/raidtab or /etc/mdadm.conf. RAID config file The block device to use. block device The value for the homehost directive; this is an mdadm feature to protect RAIDs against being activated by accident. It is recommended to create RAIDs managed by the cluster with "homehost" set to a special value, so they are not accidentially auto-assembled by nodes not supposed to own them. Homehost for mdadm END } # # START: Start up the RAID device # raid1_start() { # See if the md device is already mounted. $MOUNT | grep -e "^$MDDEV\>" >/dev/null if [ $? -ne 1 ] ; then ocf_log err "Device $MDDEV is already mounted!" return $OCF_ERR_GENERIC fi if [ "running" = `raid1_status` ]; then # We are already online, do not bother return $OCF_SUCCESS fi # Insert SCSI module $MODPROBE scsi_hostadapter if [ $? -ne 0 ] ; then ocf_log warn "Couldn't insert SCSI module." fi # Insert raid personality module $MODPROBE raid1 if [ $? -ne 0 ] ; then # It is not fatal, chance is that we have raid1 builtin... ocf_log warn "Couldn't insert RAID1 module" fi grep -q "^Personalities.*\[raid1\]" /proc/mdstat 2>/dev/null if [ $? -ne 0 ] ; then ocf_log err "We don't have RAID1 support! Exiting" return $OCF_ERR_GENERIC fi if [ $HAVE_RAIDTOOLS = "true" ]; then # Run raidstart to start up the RAID array $RAIDSTART --configfile $RAIDCONF $MDDEV else # Run mdadm $MDADM --assemble $MDDEV --config=$RAIDCONF $MDADM_HOMEHOST fi if [ "running" = `raid1_status` ]; then return $OCF_SUCCESS else ocf_log err "Couldn't start RAID for $MDDEV" return $OCF_ERR_GENERIC fi } # # STOP: stop the RAID device # raid1_stop() { # See if the MD device is online if [ "stopped" = `raid1_status` ]; then return $OCF_SUCCESS fi # See if the MD device is mounted $MOUNT | grep -e "^$MDDEV\>" >/dev/null if [ $? -ne 1 ] ; then # Kill all processes open on filesystem $FUSER -m -k $MDDEV # the return from fuser doesn't tell us much #if [ $? -ne 0 ] ; then # ocf_log "err" "Couldn't kill processes on $MOUNTPOINT" # return 1; #fi # Unmount the filesystem $UMOUNT $MDDEV $MOUNT | grep -e "^$MDDEV\>" >/dev/null if [ $? -ne 1 ] ; then ocf_log err "filesystem for $MDDEV still mounted" return $OCF_ERR_GENERIC fi fi # Turn off raid if [ $HAVE_RAIDTOOLS = "true" ]; then $RAIDSTOP --configfile $RAIDCONF $MDDEV else $MDADM --stop $MDDEV --config=$RAIDCONF $MDADM_HOMEHOST fi if [ $? -ne 0 ] ; then ocf_log err "Couldn't stop RAID for $MDDEV" return $OCF_ERR_GENERIC fi return $OCF_SUCCESS } # -# STATUS: is the raid device online or offline? +# monitor: a less noisy status # -raid1_status() { +raid1_monitor() { + # check if the md device exists first + [ -b $MDDEV ] && + grep -e "^$MD[ \t:]" /proc/mdstat >/dev/null && + return $OCF_SUCCESS + return $OCF_NOT_RUNNING +} +# +# STATUS: is the raid device online or offline? +# +raid1_status() { # See if the MD device is online - grep -e "^$MD[ \t:]" /proc/mdstat >/dev/null - if [ $? -ne 0 ] ; then + raid1_monitor + rc=$? + if [ $rc -ne $OCF_SUCCESS ]; then echo "stopped" - return $OCF_NOT_RUNNING else echo "running" - return $OCF_SUCCESS fi - -} + return $rc +} raid1_validate_all() { # Utilities used by this script check_binary $MODPROBE check_binary $FSCK check_binary $FUSER check_binary $MOUNT check_binary $UMOUNT if [ $HAVE_RAIDTOOLS = "true" ]; then # $MDDEV should be an md device lsraid -a $MDDEV 2>&1 | grep -q -i "is not an md device" if [ $? -eq 0 ]; then ocf_log err "$MDDEV is not an md device!" exit $OCF_ERR_ARGS fi COMMENT="\(#.*\)" grep -q "^[[:space:]]*raiddev[[:space:]]\+$MDDEV[[:space:]]*$COMMENT\?$" $RAIDCONF 2>/dev/null if [ $? -ne 0 ]; then ocf_log err "Raid device $MDDEV does not appear in $RAIDCONF" exit $OCF_ERR_GENERIC fi else error=`$MDADM --query $MDDEV $MDADM_HOMEHOST 2>&1` if [ $? -ne 0 ]; then ocf_log err "$error" exit $OCF_ERR_GENERIC fi echo $error | grep -q -i "^$MDDEV[ \t:].*is not an md array" if [ $? -eq 0 ]; then ocf_log err "$MDDEV is not an md array!" exit $OCF_ERR_ARGS fi fi return $OCF_SUCCESS } if ( [ $# -ne 1 ] ) then usage exit $OCF_ERR_ARGS fi case "$1" in meta-data) meta_data exit $OCF_SUCCESS ;; usage) usage exit $OCF_SUCCESS ;; *) ;; esac # # Check the necessary enviroment virable's setting # RAIDCONF=$OCF_RESKEY_raidconf MDDEV=$OCF_RESKEY_raiddev if [ -z "$RAIDCONF" ] ; then ocf_log err "Please set OCF_RESKEY_raidconf!" exit $OCF_ERR_ARGS fi if [ ! -r "$RAIDCONF" ] ; then ocf_log err "Configuration file [$RAIDCONF] does not exist, or can not be opend!" exit $OCF_ERR_ARGS fi if [ -z "$MDDEV" ] ; then ocf_log err "Please set OCF_RESKEY_raiddev to the Raid device you want to control!" exit $OCF_ERR_ARGS fi -if [ ! -b "$MDDEV" ] ; then +if [ -e "$MDDEV" -a ! -b "$MDDEV" ] ; then ocf_log err "$MDDEV is not a block device!" exit $OCF_ERR_ARGS fi # strip off the /dev/ prefix to get the name of the MD device MD=`echo $MDDEV | sed -e 's/\/dev\///'` HAVE_RAIDTOOLS=false if - have_binary $RAIDSTART + have_binary $RAIDSTART >/dev/null 2>&1 then - check_binary $RAIDSTOP HAVE_RAIDTOOLS=true else check_binary $MDADM if [ -n "$OCF_RESKEY_homehost" ]; then MDADM_HOMEHOST="--homehost=${OCF_RESKEY_homehost}" else MDADM_HOMEHOST="" fi fi # At this stage, # [ $HAVE_RAIDTOOLS = false ] <=> we have $MDADM, # otherwise we have raidtools (raidstart and raidstop) # Look for how we are called case "$1" in start) raid1_start ;; stop) raid1_stop ;; - status|monitor) + status) raid1_status ;; + monitor) + raid1_monitor + ;; validate-all) raid1_validate_all ;; *) usage exit $OCF_ERR_UNIMPLEMENTED ;; esac exit $? diff --git a/heartbeat/SysInfo b/heartbeat/SysInfo index 17a26d454..b4fa045f7 100644 --- a/heartbeat/SysInfo +++ b/heartbeat/SysInfo @@ -1,361 +1,364 @@ -#!/bin/sh +#!/bin/bash # # # SysInfo OCF Resource Agent # It records (in the CIB) various attributes of a node # # Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Brée # 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. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # ####################################################################### # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs ####################################################################### meta_data() { cat < 1.0 This is a SysInfo Resource Agent. It records (in the CIB) various attributes of a node Sample Linux output: arch: i686 os: Linux-2.4.26-gentoo-r14 free_swap: 1999 cpu_info: Intel(R) Celeron(R) CPU 2.40GHz cpu_speed: 4771.02 cpu_cores: 1 cpu_load: 0.00 ram_total: 513 ram_free: 117 root_free: 2.4 Sample Darwin output: arch: i386 os: Darwin-8.6.2 cpu_info: Intel Core Duo cpu_speed: 2.16 cpu_cores: 2 cpu_load: 0.18 ram_total: 2016 ram_free: 787 root_free: 13 Units: free_swap: Mb ram_*: Mb root_free: Gb cpu_speed (Linux): bogomips cpu_speed (Darwin): Ghz SysInfo resource agent PID file PID file Interval to allow values to stabilize Dampening Delay END } ####################################################################### UpdateStat() { name=$1; shift value="$*" echo -e "$name:\t$value" ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -S status -n $name -v "$value" } SysInfoStats() { UpdateStat arch "`uname -m`" UpdateStat os "`uname -s`-`uname -r`" case `uname -s` in "Darwin") mem=`top -l 1 | grep Mem: | awk '{print $10}'` mem_used=`top -l 1 | grep Mem: | awk '{print $8}'` mem=`SysInfo_mem_units $mem` mem_used=`SysInfo_mem_units $mem_used` mem_total=`expr $mem_used + $mem` cpu_type=`system_profiler SPHardwareDataType | grep "CPU Type:"` cpu_type=${cpu_type/*: /} cpu_speed=`system_profiler SPHardwareDataType | grep "CPU Speed:" | awk '{print $3}'` cpu_cores=`system_profiler SPHardwareDataType | grep "Number Of"` cpu_cores=${cpu_cores/*: /} ;; "Linux") if [ -f /proc/cpuinfo ]; then cpu_type=`grep "model name" /proc/cpuinfo | head -n 1` cpu_type=${cpu_type/*: /} cpu_speed=`grep "bogomips" /proc/cpuinfo | head -n 1` cpu_speed=${cpu_speed/*: /} cpu_cores=`grep "^processor" /proc/cpuinfo | wc -l` fi if [ -f /proc/meminfo ]; then # meminfo results are in kB mem=`grep "SwapFree" /proc/meminfo | awk '{print $2"k"}'` if [ ! -z $mem ]; then UpdateStat free_swap `SysInfo_mem_units $mem` fi mem=`grep "Inactive" /proc/meminfo | awk '{print $2"k"}'` mem_total=`grep "MemTotal" /proc/meminfo | awk '{print $2"k"}'` else mem=`top -n 1 | grep Mem: | awk '{print $7}'` fi ;; *) esac if [ x != x"$cpu_type" ]; then UpdateStat cpu_info "$cpu_type" fi if [ x != x"$cpu_speed" ]; then UpdateStat cpu_speed "$cpu_speed" fi if [ x != x"$cpu_cores" ]; then UpdateStat cpu_cores "$cpu_cores" fi loads=`uptime` load15=`echo ${loads} | awk '{print $10}'` UpdateStat cpu_load $load15 if [ ! -z "$mem" ]; then # Massage the memory values UpdateStat ram_total `SysInfo_mem_units $mem_total` UpdateStat ram_free `SysInfo_mem_units $mem` fi # Portability notes: # o df: -h flag not available on Solaris 8. (OK on 9, 10, ...) #FIXME# # o tail: explicit "-n" not available in Solaris; instead simplify # 'tail -n ' to the equivalent 'tail -'. disk=`df -h / | tail -1 | awk '{print $4}'` if [ x != x"$disk" ]; then UpdateStat root_free `SysInfo_hdd_units $disk` fi } SysInfo_mem_units() { mem=$1 if [ -z $1 ]; then return fi memlen=`expr ${#mem} - 1` memlen_alt=`expr ${#mem} - 2` if [ ${mem:$memlen:1} = "G" ]; then mem="${mem:0:$memlen}" if [ $mem != ${mem/./} ]; then mem_before=${mem/.*/} mem_after=${mem/*./} mem=$[mem_before*1024] if [ ${#mem_after} = 0 ]; then : elif [ ${#mem_after} = 1 ]; then mem=$[mem+100*$mem_after] elif [ ${#mem_after} = 2 ]; then mem=$[mem+10*$mem_after] elif [ ${#mem_after} = 3 ]; then mem=$[mem+$mem_after] else mem_after=${mem_after:0:3} mem=$[mem+$mem_after] fi fi elif [ ${mem:$memlen:1} = "M" ]; then mem=${mem/.*/} mem="${mem:0:$memlen}" elif [ ${mem:$memlen:1} = "k" ]; then mem="${mem:0:$memlen}" mem=${mem/.*/} mem=`expr $mem / 1024` elif [ ${mem:$memlen_alt:2} = "kB" ]; then mem="${mem:0:$memlen_alt}" mem=${mem/.*/} mem=`expr $mem / 1024` elif [ ${mem:$memlen_alt:2} = "Mb" ]; then mem="${mem:0:$memlen_alt}" mem=${mem/.*/} elif [ ${mem:$memlen_alt:2} = "MB" ]; then mem="${mem:0:$memlen_alt}" mem=${mem/.*/} fi # Round to the next multiple of 50 memlen=`expr ${#mem} - 2` mem_round="${mem:$memlen:2}" if [ x$mem_round = x ]; then : elif [ $mem_round = "00" ]; then : - elif [ $mem_round -lt "50" ]; then - mem=$[mem+50] - mem=$[mem-$mem_round] - - else - mem=$[mem+100] - mem=$[mem-$mem_round] + else + mem_round=`echo $mem_round | sed 's/^0//'` + if [ $mem_round -lt "50" ]; then + mem=$[mem+50] + mem=$[mem-$mem_round] + + else + mem=$[mem+100] + mem=$[mem-$mem_round] + fi fi echo $mem } SysInfo_hdd_units() { disk=$1 disklen=`expr ${#disk} - 1` disklen_alt=`expr ${#disk} - 2` if [ ${disk:$disklen:1} = "G" ]; then disk="${disk:0:$disklen}" elif [ ${disk:$disklen:1} = "M" ]; then disk="${disk:0:$disklen}" disk=${disk/.*/} disk=`expr $disk / 1024` elif [ ${disk:$disklen:1} = "k" ]; then disk="${disk:0:$disklen}" disk=${disk/.*/} disk=`expr $disk / 1048576` elif [ ${disk:$disklen_alt:2} = "kB" ]; then disk="${disk:0:$disklen_alt}" disk=${disk/.*/} disk=`expr $disk / 1048576` elif [ ${disk:$disklen_alt:2} = "Mb" ]; then disk="${disk:0:$disklen_alt}" disk=${disk/.*/} disk=`expr $disk / 1024` elif [ ${disk:$disklen_alt:2} = "MB" ]; then disk="${disk:0:$disklen_alt}" disk=${disk/.*/} disk=`expr $disk / 1024` fi echo $disk } SysInfo_usage() { cat < $OCF_RESKEY_pidfile SysInfoStats exit $OCF_SUCCESS } SysInfo_stop() { rm $OCF_RESKEY_pidfile exit $OCF_SUCCESS } SysInfo_monitor() { if [ -f $OCF_RESKEY_pidfile ]; then clone=`cat $OCF_RESKEY_pidfile` fi if [ x$clone = x ]; then rm $OCF_RESKEY_pidfile exit $OCF_NOT_RUNNING elif [ $clone = $OCF_RESKEY_clone ]; then SysInfoStats exit $OCF_SUCCESS elif [ x$OCF_RESKEY_CRM_meta_globally_unique = xtrue -o x$OCF_RESKEY_CRM_meta_globally_unique = xTrue -o x$OCF_RESKEY_CRM_meta_globally_unique = xyes -o x$OCF_RESKEY_CRM_meta_globally_unique = xYes ]; then SysInfoStats exit $OCF_SUCCESS fi exit $OCF_NOT_RUNNING } SysInfo_validate() { return $OCF_SUCCESS } if [ $# -ne 1 ]; then SysInfo_usage exit $OCF_ERR_ARGS fi : ${OCF_RESKEY_pidfile:="$HA_RSCTMP/SysInfo-${OCF_RESOURCE_INSTANCE}"} : ${OCF_RESKEY_clone:="0"} if [ x != x${OCF_RESKEY_delay} ]; then OCF_RESKEY_delay="-d ${OCF_RESKEY_delay}" fi case $__OCF_ACTION in meta-data) meta_data exit $OCF_SUCCESS ;; start) SysInfo_start ;; stop) SysInfo_stop ;; monitor) SysInfo_monitor ;; validate-all) SysInfo_validate ;; usage|help) SysInfo_usage exit $OCF_SUCCESS ;; *) SysInfo_usage exit $OCF_ERR_UNIMPLEMENTED ;; esac exit $? diff --git a/heartbeat/VirtualDomain b/heartbeat/VirtualDomain new file mode 100644 index 000000000..081310596 --- /dev/null +++ b/heartbeat/VirtualDomain @@ -0,0 +1,319 @@ +#!/bin/sh +# +# Support: linux-ha@lists.linux-ha.org +# License: GNU General Public License (GPL) +# +# Resource Agent for domains managed by the libvirt API. +# Requires a running libvirt daemon (libvirtd). +# +# usage: $0 {start|stop|status|monitor|migrate_to|migrate_from|meta-data|validate-all} +# +####################################################################### +# Initialization: +. ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs +LC_ALL="C" +LANG="C" + +# Defaults +OCF_RESKEY_force_stop_default=0 +OCF_RESKEY_hypervisor_default="$(virsh --quiet uri)" + +: ${OCF_RESKEY_force_stop=${OCF_RESKEY_force_stop_default}} +: ${OCF_RESKEY_hypervisor=${OCF_RESKEY_hypervisor_default}} +####################################################################### + +usage() { + echo "usage: $0 {start|stop|status|monitor|migrate_to|migrate_from|meta-data|validate-all}" +} + +meta_data() { + cat < + + +1.0 + + +Resource agent for a virtual domain (a.k.a. domU, virtual machine, +virtual environment etc., depending on context) managed by libvirtd. + +Manages virtual domains + + + + + +Absolute path to the libvirt configuration file, +for this virtual domain. + +Virtual domain configuration file + + + + + +Hypervisor URI to connect to. See the libvirt documentation for +details on supported URI formats. The default is system dependent. + +Hypervisor URI + + + + + +Forcefully shut down ("destroy") the domain on stop. Enable this only if +your virtual domain (or your virtualization backend) does not support +graceful shutdown. + +Force shutdown on stop + + + + + +Transport used to connect to the remote hypervisor while +migrating. Please refer to the libvirt documentation for details on +transports available. If this parameter is omitted, the resource will +use libvirt's default transport to connect to the remote hypervisor. + +Remote hypervisor transport + + + + + +To additionally monitor services within the virtual domain, add this +parameter with a list of scripts to monitor. + +Note: when monitor scripts are used, the start and migrate_from operations +will complete only when all monitor scripts have completed successfully. +Be sure to set the timeout of these operations to accommodate this delay. + +space-separated list of monitor scripts + + + + + + + + + + + + + + + + +EOF +} + +# Set options to be passed to virsh: +VIRSH_OPTIONS="--connect=${OCF_RESKEY_hypervisor} --quiet" + +VirtualDomain_Define() { + # Note: passing in the domain name from outside the script is + # intended for testing and debugging purposes only. Don't do this + # in production, instead let the script figure out the domain name + # from the config file. You have been warned. + if [ -z "$DOMAIN_NAME" ]; then + DOMAIN_NAME="$(virsh ${VIRSH_OPTIONS} define ${OCF_RESKEY_config} | sed -e 's/Domain \(.*\) defined from .*$/\1/')" \ + || return $OCF_ERR_GENERIC + else + ocf_log warn "Domain name ${DOMAIN_NAME} already defined, overriding configuration file ${OCF_RESKEY_config}. You should do this for testing only." + fi +} + +VirtualDomain_Status() { + rc=$OCF_ERR_GENERIC + status="`virsh $VIRSH_OPTIONS domstate $DOMAIN_NAME`" + case $status in + "shut off"|"no state") + # shut off: domain is defined, but not started + # no state: domain is migrating (migration target only) + rc=$OCF_NOT_RUNNING + ;; + running|paused|blocked|idle) + rc=$OCF_SUCCESS + ;; + esac + ocf_log debug "Virtual domain $DOMAIN_NAME is currently $status." + return $rc +} + +VirtualDomain_Start() { + if VirtualDomain_Status ${DOMAIN_NAME}; then + ocf_log info "Virtual domain $DOMAIN_NAME already running." + return $OCF_SUCCESS + fi + + virsh $VIRSH_OPTIONS start ${DOMAIN_NAME} + rc=$? + if [ $rc -ne 0 ]; then + ocf_log error "Failed to start virtual domain ${DOMAIN_NAME}." + return $OCF_ERR_GENERIC + fi + + while sleep 1; do + VirtualDomain_Monitor && return $OCF_SUCCESS + done +} + +VirtualDomain_Stop() { + if VirtualDomain_Status ${DOMAIN_NAME}; then + case $OCF_RESKEY_force_stop in + [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) + op="destroy" + ;; + *) + op="shutdown" + ;; + esac + virsh $VIRSH_OPTIONS $op ${DOMAIN_NAME} + while sleep 1; do + VirtualDomain_Status + if [ $? -eq $OCF_NOT_RUNNING ]; then + return $OCF_SUCCESS + fi + done + else + ocf_log info "Domain $DOMAIN_NAME already stopped." + return $OCF_SUCCESS + fi +} + +VirtualDomain_Migrate_To() { + local target_node + local remoteuri + local transport_suffix + + target_node="$OCF_RESKEY_CRM_meta_migrate_target" + + if VirtualDomain_Status ${DOMAIN_NAME}; then + # Find out the remote hypervisor to connect to. That is, turn + # something like "qemu://foo:9999/system" into + # "qemu+tcp://bar:9999/system" + if [ -n "${OCF_RESKEY_migration_transport}" ]; then + transport_suffix="+${OCF_RESKEY_migration_transport}" + fi + # Scared of that sed expression? So am I. :-) + remoteuri=$(echo ${OCF_RESKEY_hypervisor} | sed -e "s,\(.*\)://[^/:]*\(:\?[0-9]*\)/\(.*\),\1${transport_suffix}://${target_node}\2/\3,") + + # OK, we know where to connect to. Now do the actual migration. + ocf_log info "$DOMAIN_NAME: Starting live migration to ${remoteuri}" + virsh ${VIRSH_OPTIONS} migrate --live $DOMAIN_NAME ${remoteuri} + rc=$? + if [ $rc -ne 0 ]; then + ocf_log err "$DOMAIN_NAME: live migration to ${remoteuri} failed: $rc" + return $OCF_ERR_GENERIC + else + ocf_log info "$DOMAIN_NAME: live migration to ${remoteuri} succeeded." + return $OCF_SUCCESS + fi + else + ocf_log err "$DOMAIN_NAME: migrate_to: Not active locally!" + return $OCF_ERR_GENERIC + fi +} + +VirtualDomain_Migrate_From() { + while sleep 1; do + VirtualDomain_Monitor && return $OCF_SUCCESS + done +} + +VirtualDomain_Monitor() { + # First, check the domain status. If that returns anything other + # than $OCF_SUCCESS, something is definitely wrong. + VirtualDomain_Status ${DOMAIN_NAME} + rc=$? + if [ ${rc} -eq ${OCF_SUCCESS} ]; then + # OK, the generic status check turned out fine. Now, if we + # have monitor scripts defined, run them one after another. + for script in ${OCF_RESKEY_monitor_scripts}; do + script_output="$($script 2>&1)" + script_rc=$? + if [ ${script_rc} -ne ${OCF_SUCCESS} ]; then + # A monitor script returned a non-success exit + # code. Stop iterating over the list of scripts, log a + # warning message, and propagate $OCF_ERR_GENERIC. + ocf_log warn "Monitor command \"${script}\" for domain ${DOMAIN_NAME} returned ${script_rc} with output: ${script_output}" + rc=$OCF_ERR_GENERIC + break + else + ocf_log debug "Monitor command \"${script}\" for domain ${DOMAIN_NAME} completed successfully with output: ${script_output}" + fi + done + fi + return ${rc} +} + +VirtualDomain_Validate_All() { + # Required binaries: + for binary in virsh sed; do + check_binary $binary + done + + if [ -z $OCF_RESKEY_config ]; then + ocf_log error "Missing configuration parameter \"config\"." + return $OCF_ERR_CONFIGURED + fi + + # check if we can read the config file (otherwise we're unable to + # deduce $DOMAIN_NAME from it, see below) + if [ ! -r $OCF_RESKEY_config ]; then + ocf_log error "Configuration file $OCF_RESKEY_config does not exist or is not readable." + return $OCF_ERR_INSTALLED + fi +} + +if [ $# -ne 1 ]; then + usage + exit $OCF_ERR_ARGS +fi + +case $1 in + meta-data) meta_data + exit $OCF_SUCCESS + ;; + usage) usage + exit $OCF_SUCCESS + ;; +esac + +# Everything except usage and meta-data must pass the validate test +VirtualDomain_Validate_All || exit $? + +# Define the domain on startup, and re-define (harmlessly) +# on every invocation. This also sets DOMAIN_NAME. If this fails, +# bail out. +VirtualDomain_Define || exit $? + +case $1 in + start) + VirtualDomain_Start + ;; + stop) + VirtualDomain_Stop + ;; + migrate_to) + VirtualDomain_Migrate_To + ;; + migrate_from) + VirtualDomain_Migrate_From + ;; + status) + VirtualDomain_Status + ;; + monitor) + VirtualDomain_Monitor + ;; + validate-all) + ;; + *) + usage + exit $OCF_ERR_UNIMPLEMENTED + ;; +esac +exit $? diff --git a/heartbeat/Xen b/heartbeat/Xen index 94802aac7..d5edeade3 100644 --- a/heartbeat/Xen +++ b/heartbeat/Xen @@ -1,407 +1,407 @@ #!/bin/sh # # # Support: linux-ha@lists.linux-ha.org # License: GNU General Public License (GPL) # # Resource Agent for the Xen Hypervisor. # Manages Xen virtual machine instances by # mapping cluster resource start and stop, # to Xen create and shutdown, respectively. # # usage: $0 {start|stop|status|monitor|meta-data} # # OCF parameters are as below: # OCF_RESKEY_xmfile # Absolute path to the Xen control file, # for this virtual machine. # OCF_RESKEY_allow_mem_management # Change memory usage on start/stop/migration # of virtual machine # OCF_RESKEY_reserved_Dom0_memory # minimum memory reserved for domain 0 # OCF_RESKEY_monitor_scripts # scripts to monitor services within the # virtual domain ####################################################################### # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs ####################################################################### usage() { cat <<-! usage: $0 {start|stop|status|monitor|meta-data|validate-all} ! } : ${OCF_RESKEY_xmfile=/etc/xen/vm/MyDomU} : ${OCF_RESKEY_allow_migrate=0} : ${OCF_RESKEY_allow_mem_management=0} : ${OCF_RESKEY_reserved_Dom0_memory=512} meta_data() { cat < 1.0 Resource Agent for the Xen Hypervisor. Manages Xen virtual machine instances by mapping cluster resource start and stop, to Xen create and shutdown, respectively. A note on names We will try to extract the name from the config file (the xmfile attribute). If you use a simple assignment statement, then you should be fine. Otherwise, if there's some python acrobacy involved such as dynamically assigning names depending on other variables, and we will try to detect this, then please set the name attribute. You should also do that if there is any chance of a pathological situation where a config file might be missing, for example if it resides on a shared storage. If all fails, we finally fall back to the instance id to preserve backward compatibility. Para-virtualized guests can also be migrated by enabling the meta_attribute allow_migrate. Manages Xen DomUs Absolute path to the Xen control file, for this virtual machine. Xen control file Name of the virtual machine. Xen DomU name This bool parameters allows to use live migration for paravirtual machines. Use live migration The Xen agent will first try an orderly shutdown using xm shutdown. Should this not succeed within this timeout, the agent will escalate to xm destroy, forcibly killing the node. If this is not set, it will default to two-third of the stop action timeout. Setting this value to 0 forces an immediate destroy. Shutdown escalation timeout This parameter enables dynamic adjustment of memory for start and stop actions used for Dom0 and the DomUs. The default is to not adjust memory dynamically. Use dynamic memory management In case memory management is used, this parameter defines the minimum amount of memory to be reserved for the dom0. The default minimum memory is 512MB. Minimum Dom0 memory To additionally monitor services within the unprivileged domain, add this parameter with a list of scripts to monitor. NB: In this case make sure to set the start-delay of the monitor operation to at least the time it takes for the DomU to start all services. list of space separated monitor scripts END } Xen_Status() { STATUS=`xm list --long $1 2>/dev/null | grep status 2>/dev/null` if [ "X${STATUS}" != "X" ]; then # we have Xen 3.0.4 or higher STATUS_NOSPACES=`echo "$STATUS" | awk '{ print $1,$2}'` if [ "$STATUS_NOSPACES" = "(status 2)" -o "$STATUS_NOSPACES" = "(status 1)" ]; then return $OCF_SUCCESS else return $OCF_NOT_RUNNING fi else # we have Xen 3.0.3 or lower STATUS=`xm list --long $1 2>/dev/null | grep state 2>/dev/null` echo "${STATUS}" | grep -qs "[-r][-b][-p]---" if [ $? -ne 0 ]; then return $OCF_NOT_RUNNING else return $OCF_SUCCESS fi fi } Xen_Adjust_Memory() { if [ "${OCF_RESKEY_allow_mem_management}" != 0 ]; then CNTNEW=$1 RUNNING=`Xen_List_running` RUNCNT=`Xen_Count_running` MAXMEM=`Xen_Total_Memory` if [ ${RUNCNT} -eq 0 -a ${CNTNEW} -eq 0 ]; then RUNCNT=1 fi #NEWMEM=`echo "(${MAXMEM}-${OCF_RESKEY_reserved_Dom0_memory})/(${RUNCNT}+${CNTNEW})"|bc` NEWMEM=$(( (${MAXMEM} - ${OCF_RESKEY_reserved_Dom0_memory}) / (${RUNCNT} + ${CNTNEW} ) )) # do not rely on ballooning add dom0_mem=512 instead to force memory for dom0 #xm mem-set Domain-0 ${OCF_RESKEY_reserved_Dom0_memory} for DOM in ${RUNNING}; do xm mem-set ${DOM} ${NEWMEM} done ocf_log info "Adjusted memory to: $NEWMEM, for the following $RUNCNT domains: $RUNNING" fi } Xen_List_all() { xm list | grep -v -e "Name" -e "Domain-0" | awk '{print $1}' } Xen_List_running() { ALL_DOMS=`Xen_List_all` for DOM in ${ALL_DOMS}; do if Xen_Status $DOM; then echo "${DOM} " fi done } Xen_Count_running() { Xen_List_running | wc -w } Xen_Monitor() { Xen_Status ${DOMAIN_NAME} if [ $? -eq ${OCF_NOT_RUNNING} ]; then return ${OCF_NOT_RUNNING} fi if [ "X${OCF_RESKEY_monitor_scripts}" = "X" ]; then return ${OCF_SUCCESS} fi for SCRIPT in ${OCF_RESKEY_monitor_scripts}; do $SCRIPT if [ $? -ne 0 ]; then return ${OCF_ERR_GENERIC} fi done return ${OCF_SUCCESS} } Xen_Total_Memory() { xm info | grep "^total_memory" | awk '{print $3}' } Xen_Start() { if Xen_Status ${DOMAIN_NAME}; then ocf_log info "Xen domain $DOMAIN_NAME already running." return $OCF_SUCCESS fi if [ ! -f "${OCF_RESKEY_xmfile}" ]; then ocf_log err "Config file ${OCF_RESKEY_xmfile} for $DOMAIN_NAME does not exist." return $OCF_ERR_INSTALLED fi if [ "${OCF_RESKEY_allow_mem_management}" != 0 ]; then Xen_Adjust_Memory 1 ocf_log info "New memory for virtual domains: ${NEWMEM}" sed -i -e "/^memory=/ s/^memory=.*/memory=${NEWMEM}/" ${OCF_RESKEY_xmfile} xm mem-set ${DOMAIN_NAME} ${NEWMEM} fi xm create ${OCF_RESKEY_xmfile} name=$DOMAIN_NAME rc=$? if [ $rc -ne 0 ]; then return $OCF_ERR_GENERIC else if [ "${OCF_RESKEY_allow_mem_management}" != 0 ]; then xm mem-set ${DOMAIN_NAME} ${NEWMEM} fi fi while sleep 1; do Xen_Monitor && return $OCF_SUCCESS done } Xen_Stop() { if Xen_Status ${DOMAIN_NAME}; then local timeout if [ -n "$OCF_RESKEY_shutdown_timeout" ]; then timeout=$OCF_RESKEY_shutdown_timeout elif [ -n "$OCF_RESKEY_CRM_meta_timeout" ]; then # Allow 2/3 of the action timeout for the orderly shutdown # (The origin unit is ms, hence the conversion) - timeout=$[OCF_RESKEY_CRM_meta_timeout/1500] + timeout=$((OCF_RESKEY_CRM_meta_timeout/1500)) else timeout=60 fi if [ "$timeout" -gt 0 ]; then ocf_log info "Xen domain $DOMAIN_NAME will be stopped (timeout: ${timeout}s)" xm shutdown ${DOMAIN_NAME} while Xen_Status ${DOMAIN_NAME} && [ "$timeout" -gt 0 ]; do - timeout=$[timeout-1] + timeout=$((timeout-1)) sleep 1 done fi if [ "$timeout" -eq 0 ]; then ocf_log warn "Xen domain $DOMAIN_NAME will be destroyed!" xm destroy ${DOMAIN_NAME} while Xen_Status ${DOMAIN_NAME}; do sleep 1 done # Note: This does not give up. stop isn't allowed to to fail. # If xm destroy fails, stop will eventually timeout. # This is the correct behaviour. fi fi ocf_log info "Xen domain $DOMAIN_NAME stopped." Xen_Adjust_Memory 0 return $OCF_SUCCESS } Xen_Migrate_To() { target_node="$OCF_RESKEY_CRM_meta_migrate_target" if Xen_Status ${DOMAIN_NAME}; then ocf_log info "$DOMAIN_NAME: Starting xm migrate to $target_node" xm migrate --live $DOMAIN_NAME $target_node rc=$? if [ $rc -ne 0 ]; then ocf_log err "$DOMAIN_NAME: xm migrate to $target_node failed: $rc" return $OCF_ERR_GENERIC else Xen_Adjust_Memory 0 ocf_log info "$DOMAIN_NAME: xm migrate to $target_node succeeded." return $OCF_SUCCESS fi else ocf_log err "$DOMAIN_NAME: migrate_to: Not active locally!" return $OCF_ERR_GENERIC fi } Xen_Migrate_From() { if Xen_Status ${DOMAIN_NAME}; then Xen_Adjust_Memory 0 ocf_log info "$DOMAIN_NAME: Active locally, migration successful" return $OCF_SUCCESS else ocf_log err "$DOMAIN_NAME: Not active locally, migration failed!" return $OCF_ERR_GENERIC fi } Xen_Validate_All() { return $OCF_SUCCESS } if [ $# -ne 1 ]; then usage exit $OCF_ERR_ARGS fi case $1 in meta-data) meta_data exit $OCF_SUCCESS ;; usage) usage exit $OCF_SUCCESS ;; esac # the name business: # # 1. use the name attribute, or # 2. find the name in the config file (if it exists) and use that # unless it contains funny characters such as '%' or space, or # 3. use the OCF_RESOURCE_INSTANCE if [ x"${OCF_RESKEY_name}" != x ]; then DOMAIN_NAME="${OCF_RESKEY_name}" else if [ -f "${OCF_RESKEY_xmfile}" ]; then DOMAIN_NAME=`awk '$1~/^name(=|$)/{print}' ${OCF_RESKEY_xmfile} | sed 's/.*=[[:space:]]*//' | tr -d "[\"']"` if echo "$DOMAIN_NAME" | grep -qs '[%[:space:]]'; then DOMAIN_NAME="" fi fi DOMAIN_NAME=${DOMAIN_NAME:-${OCF_RESOURCE_INSTANCE}} fi -if [ X`which xm` == X ]; then +if [ "X`which xm`" = X ]; then return $OCF_ERR_INSTALLED fi case $1 in start) Xen_Start ;; stop) Xen_Stop ;; migrate_to) Xen_Migrate_To ;; migrate_from) Xen_Migrate_From ;; monitor) Xen_Monitor ;; status) Xen_Status ${DOMAIN_NAME} ;; validate-all) Xen_Validate_All ;; *) usage exit $OCF_ERR_UNIMPLEMENTED ;; esac exit $? diff --git a/heartbeat/anything b/heartbeat/anything new file mode 100755 index 000000000..a484aae03 --- /dev/null +++ b/heartbeat/anything @@ -0,0 +1,279 @@ +#!/bin/sh +# +# OCF Resource Agent compliant resource script. +# +# Copyright (c) 2009 IN-telegence GmbH & Co. KG, Dominik Klein +# 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. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + +# OCF instance parameters +# OCF_RESKEY_binfile +# OCF_RESKEY_cmdline_options +# OCF_RESKEY_pidfile +# OCF_RESKEY_logfile +# OCF_RESKEY_errlogfile +# OCF_RESKEY_user +# OCF_RESKEY_monitor_hook +# +# This RA starts $binfile with $cmdline_options as $user and writes a $pidfile from that. +# If you want it to, it logs: +# - stdout to $logfile, stderr to $errlogfile or +# - stdout and stderr to $logfile +# - or to will be captured by lrmd if these options are omitted. +# Monitoring is done through $pidfile or your custom $monitor_hook script. +# The RA expects the program to keep running "daemon-like" and +# not just quit and exit. So this is NOT (yet - feel free to +# enhance) a way to just run a single one-shot command which just +# does something and then exits. + +# Initialization: +. ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs + +getpid() { # make sure that the file contains a number + # FIXME: pidfiles could contain spaces + grep '^[0-9][0-9]*$' $1 +} + +anything_status() { + # FIXME: This should use pidofproc + # FIXME: pidfile w/o process means the process died, so should + # be ERR_GENERIC + if test -f "$pidfile" && pid=`getpid $pidfile` && kill -0 $pid + then + return $OCF_RUNNING + else + return $OCF_NOT_RUNNING + fi +} + +anything_start() { + if ! anything_status + # FIXME: use start_daemon + # FIXME: What about daemons which can manage their own pidfiles? + then + if [ -n "$logfile" -a -n "$errlogfile" ] + then + # We have logfile and errlogfile, so redirect STDOUT und STDERR to different files + cmd="su - $user -c \"nohup $binfile $cmdline_options >> $logfile 2>> $errlogfile & \"'echo \$!' " + else if [ -n "$logfile" ] + then + # We only have logfile so redirect STDOUT and STDERR to the same file + cmd="su - $user -c \"nohup $binfile $cmdline_options >> $logfile 2>&1 & \"'echo \$!' " + else + # We have neither logfile nor errlogfile, so we're not going to redirect anything + cmd="su - $user -c \"nohup $binfile $cmdline_options & \"'echo \$!'" + fi + fi + ocf_log debug "Starting $process: $cmd" + # Execute the command as created above + eval $cmd > $pidfile + if anything_status + then + ocf_log debug "$process: $cmd started successfully" + return $OCF_SUCCESS + else + ocf_log err "$process: $cmd could not be started" + return $OCF_ERR_GENERIC + fi + else + # If already running, consider start successful + ocf_log debug "$process: $cmd is already running" + return $OCF_SUCCESS + fi +} + +anything_stop() { + # FIXME: use killproc + if anything_status + then + pid=`getpid $pidfile` + kill $pid + i=0 + # FIXME: escalate to kill -9 before timeout + while sleep 1 + do + if ! anything_status + then + rm -f $pidfile > /dev/null 2>&1 + return $OCF_SUCCESS + fi + let "i++" + done + else + # was not running, so stop can be considered successful + rm -f $pidfile + return $OCF_SUCCESS + fi + # FIXME: Never reached. + return $OCF_ERR_GENERIC +} + +anything_monitor() { + anything_status + ret=$? + if [ $ret -eq $OCF_SUCCESS ] + then + # implement your deeper monitor operation here + if [ -n "$OCF_RESKEY_monitor_hook" ]; then + eval "$OCF_RESKEY_monitor_hook" + # FIXME: Implement a check that this doesn't + # accidentially return NOT_RUNNING? + return + else + true + fi + else + return $ret + fi +} + +# FIXME: Attributes special meaning to the resource id +process="$OCF_RESOURCE_INSTANCE" +binfile="$OCF_RESKEY_binfile" +cmdline_options="$OCF_RESKEY_cmdline_options" +pidfile="$OCF_RESKEY_pidfile" +# FIXME: Why test for $binfile here? +[ -z "$pidfile" -a -n "$binfile" ] && pidfile=${HA_VARRUN}/anything_${process}.pid +logfile="$OCF_RESKEY_logfile" +errlogfile="$OCF_RESKEY_errlogfile" +user="$OCF_RESKEY_user" +[ -z "$user" ] && user=root + +anything_validate() { + # FIXME: Actually this needs to test from the point of view of + # the user. + if [ ! -x "$binfile" ] + then + ocf_log err "binfile $binfile does not exist or is not executable." + exit $OCF_ERR_INSTALLED + fi + if ! getent passwd $user >/dev/null 2>&1 + then + ocf_log err "user $user does not exist." + exit $OCF_ERR_INSTALLED + fi + for logfilename in "$logfile" "$errlogfile" + do + if [ -n "$logfilename" ]; then + mkdir -p `dirname $logfilename` || { + ocf_log err "cannot create $(dirname $logfilename)" + exit $OCF_ERR_INSTALLED + } + fi + done + return $OCF_SUCCESS +} + +anything_meta() { +cat < + + +1.0 + +This is a generic OCF RA to manage almost anything. + +anything + + + + +The full name of the binary to be executed. This is expected to keep running with the same pid and not just do something and exit. + +Full path name of the binary to be executed + + + + +Command line options to pass to the binary + +Command line options + + + + +File to read/write the PID from/to. + +File to write STDOUT to + + + + +File to write STDOUT to + +File to write STDOUT to + + + + +File to write STDERR to + +File to write STDERR to + + + + +User to run the command as + +User to run the command as + + + + +Command to run in monitor operation + +Command to run in monitor operation + + + + + + + + + + + +END +exit 0 +} + +case "$1" in + meta-data|metadata|meta_data) + anything_meta + ;; + start) + anything_start + ;; + stop) + anything_stop + ;; + monitor) + anything_monitor + ;; + validate-all) + anything_validate + ;; + *) + ocf_log err "$0 was called with unsupported arguments: $*" + exit $OCF_ERR_UNIMPLEMENTED + ;; +esac diff --git a/heartbeat/apache b/heartbeat/apache index 4618fccec..257c92d95 100644 --- a/heartbeat/apache +++ b/heartbeat/apache @@ -1,640 +1,657 @@ #!/bin/sh # # High-Availability Apache/IBMhttp control script # # apache (aka IBMhttpd) # # Description: starts/stops apache web servers. # # Author: Alan Robertson # Sun Jiang Dong # # Support: linux-ha@lists.linux-ha.org # # License: GNU General Public License (GPL) # # Copyright: (C) 2002-2005 International Business Machines # # # An example usage in /etc/ha.d/haresources: # node1 10.0.0.170 apache::/opt/IBMHTTPServer/conf/httpd.conf # node1 10.0.0.170 IBMhttpd # # Our parsing of the Apache config files is very rudimentary. # It'll work with lots of different configurations - but not every # possible configuration. # # Patches are being accepted ;-) # # OCF parameters: # OCF_RESKEY_configfile # OCF_RESKEY_httpd # OCF_RESKEY_port # OCF_RESKEY_statusurl # OCF_RESKEY_options # OCF_RESKEY_testregex # OCF_RESKEY_envfiles . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs HA_VARRUNDIR=${HA_VARRUN} ####################################################################### # # Configuration options - usually you don't need to change these # ####################################################################### # IBMHTTPD=/opt/IBMHTTPServer/bin/httpd HTTPDLIST="/sbin/httpd2 /usr/sbin/httpd2 /usr/sbin/apache2 /sbin/httpd /usr/sbin/httpd /usr/sbin/apache $IBMHTTPD" MPM=/usr/share/apache2/find_mpm if [ -x $MPM ] then HTTPDLIST="$HTTPDLIST `$MPM 2>/dev/null`" fi WGETOPTS="-O- -q -L" LOCALHOST="http://localhost" HTTPDOPTS="-DSTATUS" DEFAULT_IBMCONFIG=/opt/IBMHTTPServer/conf/httpd.conf DEFAULT_NORMCONFIG="/etc/apache2/httpd.conf" # # You can also set # HTTPD # PORT # STATUSURL # CONFIGFILE # in this section if what we're doing doesn't work for you... # # End of Configuration options ####################################################################### CMD=`basename $0` # The config-file-pathname is the pathname to the configuration # file for this web server. Various appropriate defaults are # assumed if no config file is specified. If this command is # invoked as *IBM*, then the default config file name is # $DEFAULT_IBMCONFIG, otherwise the default config file # will be $DEFAULT_NORMCONFIG. usage() { cat <<-! usage: $0 action action: start start the web server stop stop the web server status return the status of web server, run or down monitor return TRUE if the web server appears to be working. For this to be supported you must configure mod_status and give it a server-status URL. You have to have installed $WGET for this to work. meta-data show meta data message validate-all validate the instance parameters ! exit $1 } source_envfiles() { for f; do [ -f "$f" -a -r "$f" ] && . "$f" done } apachecat() { awk ' function procline() { split($0,a); if( a[1]=="Include" ) { procinclude(a[2]); } else { if( a[1]=="ServerRoot" ) { rootdir=a[2]; gsub("\"","",rootdir); } print; } } function printfile(infile, a) { while( (getline 0 ) { procline(); } close(infile); } function allfiles(dir, cmd,f) { cmd="find -L "dir" -type f"; while( ( cmd | getline f ) > 0 ) { printfile(f); } close(cmd); } function listfiles(pattern, cmd,f) { cmd="ls "pattern" 2>/dev/null"; while( ( cmd | getline f ) > 0 ) { printfile(f); } close(cmd); } function procinclude(spec) { if( rootdir!="" && spec!~/^\// ) { spec=rootdir"/"spec; } if( isdir(spec) ) { allfiles(spec); # read all files in a directory (and subdirs) } else { listfiles(spec); # there could be jokers } } function isdir(s) { return !system("test -d \""s"\""); } { procline(); } ' $1 | sed 's/#.*//;s/[[:blank:]]*$//;s/^[[:blank:]]*//' | grep -v '^$' } # # set parameters (as shell vars) from our apache config file # get_apache_params() { configfile=$1 shift 1 vars=`echo $@ | sed 's/ /,/g'` eval ` apachecat $configfile | awk -v vars="$vars" ' BEGIN{ split(vars,v,","); for( i in v ) vl[i]=tolower(v[i]); } { for( i in v ) if( tolower($1)==vl[i] ) { print v[i]"="$2 delete vl[i] break } } '` } # # Return the location(s) that are handled by the given handler # FindLocationForHandler() { PerlScript='while (<>) { /"]+)/i && ($loc=$1); '"/SetHandler +$2"'/i && print "$loc\n"; }' apachecat $1 | perl -e "$PerlScript" } # # Check if the port is valid # CheckPort() { ocf_is_decimal "$1" && [ $1 -gt 0 ] } # # Get all the parameters we need from the Apache config file # GetParams() { ConfigFile=$1 if [ ! -f $ConfigFile ]; then return 1 fi get_apache_params $ConfigFile ServerRoot PidFile Port Listen case $PidFile in /*) ;; [[:alnum:]]*) PidFile=$ServerRoot/$PidFile;; *) PidFile=$HA_VARRUNDIR/${httpd_basename}.pid;; esac if CheckPort "$PORT"; then : else PORT=$Port if CheckPort "$PORT"; then : else # Final resort PORT=80 fi fi # # It's difficult to figure out whether the server supports # the status operation. # (we start our server with -DSTATUS - just in case :-)) # # Typically (but not necessarily) the status URL is /server-status # # For us to think status will work, we have to have the following things: # # - $WGET has to exist and be executable # - The server-status handler has to be mapped to some URL somewhere # # We assume that: # # - the "main" web server at $PORT will also support it if we can find it # somewhere in the file # - it will be supported at the same URL as the one we find in the file # - # If this doesn't work for you, then set STATUSURL at the top of the file + # If this doesn't work for you, then set the statusurl attribute. # if [ "X$STATUSURL" = "X" ] then if have_binary $WGET then StatusURL=`FindLocationForHandler $1 server-status | tail -1` if [ "x$Listen" != "x" ] then echo $Listen | grep ':' >/dev/null || # Listen can be only port spec Listen="localhost:$Listen" STATUSURL="http://${Listen}$StatusURL" case $WGET in *wget*) WGETOPTS="$WGETOPTS --bind-address=127.0.0.1";; esac else STATUSURL="${LOCALHOST}:${PORT}$StatusURL" fi fi fi test "$PidFile" } # # return TRUE if a process with given PID is running # ProcessRunning() { ApachePID=$1 # Use /proc if it looks like it's here... if [ -d /proc -a -d /proc/1 ] then [ -d /proc/$ApachePID ] else # This assumes we're running as root... kill -0 "$ApachePID" >/dev/null 2>&1 fi } silent_status() { if [ -f $PidFile ] then ProcessRunning `cat $PidFile` else : No pid file false fi } start_apache() { if silent_status then ocf_log info "$CMD already running (pid $ApachePID)" return $OCF_SUCCESS fi ocf_run $HTTPD $HTTPDOPTS $OPTIONS -f $CONFIGFILE tries=0 while : # wait until the user set timeout do monitor_apache ec=$? if [ $ec -eq $OCF_NOT_RUNNING ] then tries=`expr $tries + 1` ocf_log info "waiting for apache $CONFIGFILE to come up" sleep 1 else break fi done return $ec } stop_apache() { if silent_status then if kill $ApachePID then tries=0 while ProcessRunning $ApachePID && [ $tries -lt 10 ] do sleep 1 kill $ApachePID >/dev/null 2>&1 ocf_log info "Killing apache PID $ApachePID" tries=`expr $tries + 1` done else ocf_log warn "Killing apache PID $ApachePID FAILED." fi if ProcessRunning $ApachePID then ocf_log info "$CMD still running ($ApachePID)." false else ocf_log info "$CMD stopped." fi else ocf_log info "$CMD is not running." fi for sig in SIGTERM SIGHUP SIGKILL ; do if pgrep -f $HTTPD.*$CONFIGFILE >/dev/null 2>&1 ; then pkill -$sig -f $HTTPD.*$CONFIGFILE >/dev/null 2>&1 ocf_log info "apache children were signalled ($sig)" sleep 1 else break fi done } status_apache() { silent_status rc=$? if [ $rc -eq 0 ] then ocf_log info "$CMD is running (pid $ApachePID)." return $OCF_SUCCESS else ocf_log info "$CMD is stopped." return $OCF_NOT_RUNNING fi } monitor_apache() { if ! have_binary $WGET then ocf_log err "Monitoring not supported by $OCF_RESOURCE_INSTANCE" ocf_log info "Please make sure that wget is available" return $OCF_ERR_CONFIGURED elif [ -z "$STATUSURL" ]; then ocf_log err "Monitoring not supported by $CONFIGFILE" ocf_log info "Please set the statusurl parameter" return $OCF_ERR_CONFIGURED fi if silent_status then ocf_run sh -c "$WGET $WGETOPTS $STATUSURL | tr '\012' ' ' | grep -Ei \"$TESTREGEX\" >/dev/null" else ocf_log info "$CMD not running" return $OCF_NOT_RUNNING fi } metadata_apache(){ cat < 1.0 -This is the resource agent for the -Apache web server. +This is the resource agent for the Apache web server. Thie resource agent operates both version 1.x and version 2.x Apache servers. + +The start operation ends with a loop in which monitor is +repeatedly called to make sure that the server started and that +it is operational. Hence, if the monitor operation does not +succeed within the start operation timeout, the apache resource +will end with an error status. + +The monitor operation by default loads the server status page +which depends on the mod_status module and the corresponding +configuration file (usually /etc/apache2/mod_status.conf). +Make sure that the server status page works and that the access +is allowed *only* from localhost (address 127.0.0.1). +See the statusurl and testregex attributes for more details. + See also http://httpd.apache.org/ Apache web server The full pathname of the Apache configuration file. This file is parsed to provide defaults for various other resource agent parameters. configuration file path The full pathname of the httpd binary (optional). httpd binary path A port number that we can probe for status information using the statusurl. This will default to the port number found in the configuration file, or 80, if none can be found in the configuration file. httpd port -The URL of the apache status module. -If left unspecified, it will be inferred -from the apache configuration file. +The URL to monitor (the apache server status page by default). +If left unspecified, it will be inferred from +the apache configuration file. + +If you set this, make sure that it succeeds *only* from the +localhost (127.0.0.1). Otherwise, it may happen that the cluster +complains about the resource being active on multiple nodes. url name - + -Extra options to apply when starting apache. See man httpd(8). +Regular expression to match in the output of statusurl. +It is case insensitive. -command line options - +monitor regular expression + - + -Regular expression to match in the output of statusurl. It -is case insensitive. +Extra options to apply when starting apache. See man httpd(8). -test regular expression +command line options -File(s) which contain extra environment variables, such as -/etc/apache2/envvars. +Files (one or more) which contain extra environment variables, +such as /etc/apache2/envvars. environment settings files END exit $OCF_SUCCESS } validate_all_apache() { if CheckPort $PORT; then # We are sure to succeed here, since we forced $PORT to be valid in GetParams() : OK else ocf_log err "Port number $PORT is invalid!" exit $OCF_ERR_ARGS fi if [ -z $STATUSURL ]; then : OK to be empty else case $STATUSURL in http://*/*) ;; *) ocf_log err "Invalid STATUSURL $STATUSURL" exit $OCF_ERR_ARGS ;; esac fi if [ ! -x $HTTPD ]; then ocf_log err "HTTPD $HTTPD not found or is not an executable!" exit $OCF_ERR_ARGS fi if [ ! -f $CONFIGFILE ]; then # We are sure to succeed here, since we have parsed $CONFIGFILE before getting here ocf_log err "Configuration file $CONFIGFILE not found!" exit $OCF_ERR_CONFIGURED fi return $OCF_SUCCESS } if [ $# -eq 1 ] then COMMAND=$1 HTTPD="$OCF_RESKEY_httpd" PORT="$OCF_RESKEY_port" STATUSURL="$OCF_RESKEY_statusurl" CONFIGFILE="$OCF_RESKEY_configfile" OPTIONS="$OCF_RESKEY_options" TESTREGEX=${OCF_RESKEY_testregex:-'[[:space:]]*'} source_envfiles $OCF_RESKEY_envfiles else usage $OCF_ERR_ARGS fi LSB_STATUS_STOPPED=3 if [ "X$HTTPD" = X -o ! -f "$HTTPD" -o ! -x "$HTTPD" ] then case $0 in *IBM*) HTTPD=$IBMHTTPD DefaultConfig=$DEFAULT_IBMCONFIG;; *) HTTPD= for h in $HTTPDLIST do if [ -f $h -a -x $h ] then HTTPD=$h break fi done # It is possible that we still do not have a valid httpd at this stage if [ -z "$HTTPD" ] then case $COMMAND in stop) exit $OCF_SUCCESS;; monitor) exit $OCF_NOT_RUNNING;; status) exit $LSB_STATUS_STOPPED;; meta-data) metadata_apache;; esac ocf_log err "No valid httpd found! Please revise your item" exit $OCF_ERR_INSTALLED fi # Let the user know that the $HTTPD used is not the one (s)he specified via $OCF_RESKEY_httpd if [ "X$OCF_RESKEY_httpd" != X ] then ocf_log info "Using $HTTPD as HTTPD" fi DefaultConfig=$DEFAULT_NORMCONFIG;; esac fi httpd_basename=`basename $HTTPD` case $httpd_basename in *-*) httpd_basename=`echo "$httpd_basename" | sed -e 's%\-.*%%'`;; esac case "$CONFIGFILE" in "") CONFIGFILE=$DefaultConfig;; *) ;; esac if [ ! -f "$CONFIGFILE" ] then case $COMMAND in stop) ocf_log warn "$CONFIGFILE not found - apache considered stopped" exit $OCF_SUCCESS;; monitor) exit $OCF_NOT_RUNNING;; status) exit $LSB_STATUS_STOPPED;; esac fi if [ "X$COMMAND" = Xmeta-data ] || GetParams $CONFIGFILE then : OK else ocf_log err "Cannot parse config file [$CONFIGFILE]" exit $OCF_ERR_CONFIGURED fi case $COMMAND in start) start_apache;; stop) stop_apache;; status) status_apache;; monitor) monitor_apache;; meta-data) metadata_apache;; validate-all) validate_all_apache;; *) usage $OCF_ERR_UNIMPLEMENTED;; esac diff --git a/heartbeat/drbd b/heartbeat/drbd index c7ea267ea..24b4f90eb 100644 --- a/heartbeat/drbd +++ b/heartbeat/drbd @@ -1,589 +1,595 @@ #!/bin/bash # # # OCF Resource Agent compliant drbd resource script. # # Copyright (c) 2004 - 2007 SUSE LINUX Products GmbH, Lars Marowsky-Bree # 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. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # # # OCF instance parameters # OCF_RESKEY_drbd_resource # OCF_RESKEY_drbdconf # OCF_RESKEY_CRM_meta_clone_max # OCF_RESKEY_CRM_meta_clone_node_max # OCF_RESKEY_master_max # OCF_RESKEY_master_node_max ####################################################################### # Initialization: if [ -n "$OCF_DEBUG_LIBRARY" ]; then . $OCF_DEBUG_LIBRARY else . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs fi ####################################################################### meta_data() { cat < 1.1 Master/Slave OCF Resource Agent for DRBD This resource agent manages a Distributed Replicated Block Device (DRBD) object as a master/slave resource. DRBD is a mechanism for replicating storage; please see the documentation for setup details. The name of the drbd resource from the drbd.conf file. drbd resource name Full path to the drbd.conf file. Path to drbd.conf Whether or not to override the hostname with the clone number. This can be used to create floating peer configurations; drbd will be told to use node_<cloneno> as the hostname instead of the real uname, which can then be used in drbd.conf. Override drbd hostname Number of clones of this drbd resource. Do not fiddle with the default. Number of clones Clones per node. Do not fiddle with the default. Number of nodes Maximum number of active primaries. Do not fiddle with the default. Number of primaries Maximum number of primaries per node. Do not fiddle with the default. Number of primaries per node END exit $OCF_SUCCESS } do_cmd() { local cmd="$*" ocf_log debug "$RESOURCE: Calling $cmd" local cmd_out cmd_out=$($cmd 2>&1) ret=$? if [ $ret -ne 0 ]; then ocf_log err "$RESOURCE: Called $cmd" ocf_log err "$RESOURCE: Exit code $ret" ocf_log err "$RESOURCE: Command output: $cmd_out" else ocf_log debug "$RESOURCE: Exit code $ret" ocf_log debug "$RESOURCE: Command output: $cmd_out" fi echo $cmd_out return $ret } do_drbdadm() { local cmd="$DRBDADM -c $DRBDCONF $*" ocf_log debug "$RESOURCE: Calling $cmd" local cmd_out cmd_out=$($cmd 2>&1) ret=$? # Trim the garbage drbdadm likes to print when using the node # override feature: local cmd_ret=$(echo $cmd_out | sed -e 's/found __DRBD_NODE__.*</dev/null ; then : else ocf_log err "Invalid configuration file $DRBDCONF" return $OCF_ERR_CONFIGURED fi if [ "$OCF_RESKEY_CRM_meta_clone_max" -ne 2 ] \ || [ "$OCF_RESKEY_CRM_meta_clone_node_max" -ne 1 ] \ || [ "$OCF_RESKEY_master_node_max" -ne 1 ] \ || [ "$OCF_RESKEY_master_max" -ne 1 ] ; then ocf_log err "Clone options misconfigured." exit $OCF_ERR_CONFIGURED fi return $OCF_SUCCESS } if [ $# -ne 1 ]; then echo "Incorrect parameter count." drbd_usage exit $OCF_ERR_ARGS fi +: ${OCF_RESKEY_CRM_meta_interval=0} + ACTION=$1 case $ACTION in meta-data) meta_data ;; validate-all) drbd_init drbd_validate_all ;; start|stop|monitor|promote|demote|notify) if ocf_is_root ; then : ; else ocf_log err "You must be root to perform this operation." exit $OCF_ERR_PERM fi drbd_init drbd_$ACTION exit $? ;; usage|help) drbd_usage exit $OCF_SUCCESS ;; *) drbd_usage exit $OCF_ERR_ARGS ;; esac diff --git a/heartbeat/mysql b/heartbeat/mysql index e0135c5ae..ee2a11e32 100644 --- a/heartbeat/mysql +++ b/heartbeat/mysql @@ -1,432 +1,455 @@ #!/bin/sh # # # MySQL # # Description: Manages a MySQL database as Linux-HA resource # # Author: Alan Robertson : DB2 Script # Author: Jakub Janczak : Rewrite as MySQL # Author: Andrew Beekhof : Cleanup and import # Author: Sebastian Reitenbach : add OpenBSD defaults, more cleanup # Author: Narayan Newton : Add Gentoo/Debian defaults # # Support: linux-ha@lists.linux-ha.org # License: GNU General Public License (GPL) # Copyright: (C) 2002 - 2005 International Business Machines, Inc. # # 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_config # OCF_RESKEY_datadir # OCF_RESKEY_user # OCF_RESKEY_group # OCF_RESKEY_test_table # OCF_RESKEY_test_user # OCF_RESKEY_test_password # OCF_RESKEY_enable_creation # OCF_RESKEY_additional_parameters # OCF_RESKEY_log # OCF_RESKEY_pid # OCF_RESKEY_socket ####################################################################### # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs ####################################################################### # Fill in some defaults if no values are specified HOSTOS=`uname` if [ "X${HOSTOS}" = "XOpenBSD" ];then OCF_RESKEY_binary_default="/usr/local/bin/mysqld_safe" 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" 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="" else OCF_RESKEY_binary_default="/usr/bin/safe_mysqld" 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" 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="" fi : ${OCF_RESKEY_binary=${OCF_RESKEY_binary_default}} MYSQL_BINDIR=`dirname ${OCF_RESKEY_binary}` : ${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}} usage() { cat < 1.0 Resource script for MySQL. It manages a MySQL Database instance as an HA resource. MySQL resource agent Location of the MySQL binary MySQL binary Configuration file MySQL config Directory containing databases MySQL datadir User running MySQL daemon MySQL user Group running MySQL daemon (for logfile and directory permissions) MySQL group The logfile to be used for mysqld. MySQL log file The pidfile to be used for mysqld. MySQL pid file The socket to be used for mysqld. MySQL socket Table to be tested in monitor statement (in database.table notation) MySQL test table MySQL test user MySQL test user MySQL test user password MySQL test user password If the MySQL database does not exist, it will be created Create the database if it does not exist Additional parameters which are passed to the mysqld on startup. (e.g. --skip-external-locking or --skip-grant-tables) Additional paramters to pass to mysqld END } mysql_validate() { # checking the parameters + if [ ! -x $OCF_RESKEY_binary ]; then + ocf_log err "mysqld binary $OCF_RESKEY_binary does not exist or is not executable"; + return $OCF_ERR_INSTALLED; + fi + if [ ! -f $OCF_RESKEY_config ]; then - ocf_log err "Config $OCF_RESKEY_mysql_config doesn't exist"; - exit $OCF_ERR_INSTALLED; + ocf_log err "Config $OCF_RESKEY_config doesn't exist"; + return $OCF_ERR_CONFIGURED; fi if [ ! -d $OCF_RESKEY_datadir ]; then - ocf_log err "Datadir $OCF_RESKEY_datadir dosen't exist"; - exit $OCF_ERR_INSTALLED; + ocf_log err "Datadir $OCF_RESKEY_datadir doesn't exist"; + return $OCF_ERR_CONFIGURED; fi getent passwd $OCF_RESKEY_user >/dev/null 2>&1 if [ ! $? -eq 0 ]; then ocf_log err "User $OCF_RESKEY_user doesn't exit"; - exit $OCF_ERR_INSTALLED; + return $OCF_ERR_INSTALLED; fi getent group $OCF_RESKEY_group >/dev/null 2>&1 if [ ! $? -eq 0 ]; then ocf_log err "Group $OCF_RESKEY_group doesn't exist"; - exit $OCF_ERR_INSTALLED; + return $OCF_ERR_INSTALLED; fi + true } mysql_status() { if [ ! -e $OCF_RESKEY_pid ]; then ocf_log debug "MySQL is not running" return $OCF_NOT_RUNNING; fi pid=`cat $OCF_RESKEY_pid`; if [ -d /proc -a -d /proc/1 ]; then [ "u$pid" != "u" -a -d /proc/$pid ] else kill -0 $pid >/dev/null 2>&1 fi if [ $? -eq 0 ]; then return $OCF_SUCCESS; else ocf_log debug "MySQL not running: removing old PID file" rm -f $OCF_RESKEY_pid return $OCF_NOT_RUNNING; fi } mysql_monitor() { mysql_status rc=$? if [ $OCF_CHECK_LEVEL = 0 -o $rc != 0 ]; then return $rc fi # Do a detailed status check buf=`echo "SELECT * FROM $OCF_RESKEY_test_table" | mysql --user=$OCF_RESKEY_test_user --password=$OCF_RESKEY_test_passwd --socket=$OCF_RESKEY_socket -O connect_timeout=1 2>&1` rc=$? if [ ! $rc -eq 0 ]; then ocf_log err "MySQL $test_table monitor failed:"; if [ ! -z "$buf" ]; then ocf_log err $buf; fi return $OCF_ERR_GENERIC; else ocf_log info "MySQL monitor succeded"; return $OCF_SUCCESS; fi } mysql_start() { mysql_status if [ $? = $OCF_SUCCESS ]; then ocf_log info "MySQL already running" return $OCF_SUCCESS fi - + 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_RESKEY_enable_creation" = 1 -a ! -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_log err "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 ! su -s /bin/sh - $OCF_RESKEY_user -c "test -w $pid_dir"; then + ocf_log err "Directory $pid_dir for pidfile $OCF_RESKEY_pid is not writable by $OCF_RESKEY_user" + return $OCF_ERR_PERM; + fi 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 # 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 ${OCF_RESKEY_binary} --defaults-file=$OCF_RESKEY_config --pid-file=$OCF_RESKEY_pid --socket=$OCF_RESKEY_socket --datadir=$OCF_RESKEY_datadir --user=$OCF_RESKEY_user $OCF_RESKEY_additional_parameters >/dev/null 2>&1 & rc=$? if [ $rc != 0 ]; then ocf_log err "MySQL start command failed: $rc" return $rc fi # 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 mysql_status 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 ocf_log info "MySQL started" return $OCF_SUCCESS } mysql_stop() { if [ ! -f $OCF_RESKEY_pid ]; then return $OCF_SUCCESS fi pid=`cat $OCF_RESKEY_pid 2> /dev/null ` /bin/kill $pid > /dev/null 2>&1 rc=$? if [ $rc != 0 ]; then ocf_log err "MySQL couldn't be stopped" return $OCF_ERR_GENERIC fi stop_wait=1 while [ $stop_wait = 1 ]; do mysql_status rc=$? if [ $rc = $OCF_NOT_RUNNING ]; then stop_wait=0 fi sleep 1 done ocf_log info "MySQL stopped"; rm -f /var/lock/subsys/mysqld rm -f $OCF_RESKEY_datadir/mysql.sock return $OCF_SUCCESS } +case "$1" in + meta-data) meta_data + exit $OCF_SUCCESS;; + usage|help) usage + exit $OCF_SUCCESS;; +esac + +mysql_validate +rc=$? +LSB_STATUS_STOPPED=3 +if [ $rc -ne 0 ]; then + case "$1" in + stop) exit $OCF_SUCCESS;; + monitor) exit $OCF_NOT_RUNNING;; + status) exit $LSB_STATUS_STOPPED;; + *) exit $rc;; + esac +fi # What kind of method was invoked? case "$1" in - start) mysql_validate; - mysql_start;; + start) mysql_start;; stop) mysql_stop;; status) mysql_status;; monitor) mysql_monitor;; - - meta-data) meta_data; exit $OCF_SUCCESS;; - validate-all) mysql_validate; exit $OCF_SUCCESS;; + validate-all) exit $OCF_SUCCESS;; *) usage exit $OCF_ERR_UNIMPLEMENTED;; esac -exit $? - diff --git a/heartbeat/oracle b/heartbeat/oracle index 1b44e674b..404755bf1 100755 --- a/heartbeat/oracle +++ b/heartbeat/oracle @@ -1,681 +1,681 @@ #!/bin/sh # # # oracle # # Description: Manages an Oracle Database as a High-Availability # resource # # # Author: Dejan Muhamedagic # Support: linux-ha@lists.linux-ha.org # License: GNU General Public License (GPL) # Copyright: (C) 2006 International Business Machines, Inc. # # This code inspired by the DB2 resource script # written by Alan Robertson # # An example usage in /etc/ha.d/haresources: # node1 10.0.0.170 oracle::RK1::/oracle/10.2::orark1 # # See usage() function below for more details... # # OCF instance parameters: # OCF_RESKEY_sid # OCF_RESKEY_home (optional; else read it from /etc/oratab) # OCF_RESKEY_user (optional; figure it out by checking file ownership) # OCF_RESKEY_ipcrm (optional; defaults to "instance") # OCF_RESKEY_clear_backupmode (optional; default to "false") # OCF_RESKEY_shutdown_method (optional; default to "checkpoint/abort") # # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs ####################################################################### usage() { methods=`oracle_methods` methods=`echo $methods | tr ' ' '|'` cat <<-! usage: $0 {$methods} $0 manages an Oracle Database instance 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 'dumpinstipc' operation prints IPC resources used by the instance The 'cleanup' operation tries to clean up after Oracle was brutally stopped The 'validate-all' operation reports whether the parameters are valid The 'methods' operation reports on the methods $0 supports ! } meta_data() { cat < 1.0 Resource script for oracle. Manages an Oracle Database instance as an HA resource. oracle resource agent The Oracle SID (aka ORACLE_SID). sid The Oracle home directory (aka ORACLE_HOME). If not specified, then the SID along with its home should be listed in /etc/oratab. home The Oracle owner (aka ORACLE_OWNER). If not specified, then it is set to the owner of file \$ORACLE_HOME/dbs/*\${ORACLE_SID}.ora. If this does not work for you, just set it explicitely. user Sometimes IPC objects (shared memory segments and semaphores) belonging to an Oracle instance might be left behind which prevents the instance from starting. It is not easy to figure out which shared segments belong to which instance, in particular when more instances are running as same user. What we use here is the "oradebug" feature and its "ipc" trace utility. It is not optimal to parse the debugging information, but I am not aware of any other way to find out about the IPC information. In case the format or wording of the trace report changes, parsing might fail. There are some precautions, however, to prevent stepping on other peoples toes. There is also a dumpinstipc option which will make us print the IPC objects which belong to the instance. Use it to see if we parse the trace file correctly. Three settings are possible: - none: don't mess with IPC and hope for the best (beware: you'll probably be out of luck, sooner or later) - instance: try to figure out the IPC stuff which belongs to the instance and remove only those (default; should be safe) - orauser: remove all IPC belonging to the user which runs the instance (don't use this if you run more than one instance as same user or if other apps running as this user use IPC) The default setting "instance" should be safe to use, but in that case we cannot guarantee that the instance will start. In case IPC objects were already left around, because, for instance, someone mercilessly killing Oracle processes, there is no way any more to find out which IPC objects should be removed. In that case, human intervention is necessary, and probably _all_ instances running as same user will have to be stopped. The third setting, "orauser", guarantees IPC objects removal, but it does that based only on IPC objects ownership, so you should use that only if every instance runs as separate user. Please report any problems. Suggestions/fixes welcome. ipcrm The clear of the backup mode of ORACLE. clear_backupmode How to stop Oracle is a matter of taste it seems. The default method ("checkpoint/abort") is: alter system checkpoint; shutdown abort; This should be the fastest safe way bring the instance down. If you find "shutdown abort" distasteful, set this attribute to "immediate" in which case we will shutdown immediate; If you still think that there's even better way to shutdown an Oracle instance we are willing to listen. shutdown_method END } # # methods: What methods/operations do we support? # oracle_methods() { cat <<-! start stop status monitor dumpinstipc cleanup validate-all methods meta-data usage ! } # Gather up information about our oracle instance ora_info() { ORACLE_SID=$1 ORACLE_HOME=$2 ORACLE_OWNER=$3 # get ORACLE_HOME from /etc/oratab if not set [ x = "x$ORACLE_HOME" ] && ORACLE_HOME=`awk -F: "/^$ORACLE_SID:/"'{print $2}' /etc/oratab` # there a better way to find out ORACLE_OWNER? [ x = "x$ORACLE_OWNER" ] && ORACLE_OWNER=`ls -ld $ORACLE_HOME/. 2>/dev/null | awk 'NR==1{print $3}'` sqlplus=$ORACLE_HOME/bin/sqlplus lsnrctl=$ORACLE_HOME/bin/lsnrctl tnsping=$ORACLE_HOME/bin/tnsping } testoraenv() { # Let's make sure a few important things are set... [ x != "x$ORACLE_HOME" -a x != "x$ORACLE_OWNER" ] || return 1 # and some important things are there [ -x "$sqlplus" -a -x "$lsnrctl" -a -x "$tnsping" ] || return 1 return 0 } setoraenv() { LD_LIBRARY_PATH=$ORACLE_HOME/lib LIBPATH=$ORACLE_HOME/lib TNS_ADMIN=$ORACLE_HOME/network/admin PATH=$ORACLE_HOME/bin:$ORACLE_HOME/dbs:$PATH export ORACLE_SID ORACLE_HOME ORACLE_OWNER TNS_ADMIN export LD_LIBRARY_PATH LIBPATH } # # Run commands as the Oracle owner... # execsql() { if [ "$US" = "$ORACLE_OWNER" ]; then $sqlplus -S /nolog else su - $ORACLE_OWNER -c "$sqlplus -S /nolog" fi } # # Run commands in the oracle admin sqlplus... # dbasql() { - typeset func + local func ( echo "connect / as sysdba" echo "set feedback off" echo "set heading off" echo "set pagesize 0" for func; do $func; done ) | execsql | grep -v '^Connected' } # # various interesting sql # dbstat() { echo 'select status from v$instance;' } dbmount() { echo 'alter database mount;' } dbopen() { echo 'alter database open;' } dbstop_immediate() { echo 'shutdown immediate' } dbstop_checkpoint_abort() { echo 'alter system checkpoint;' echo 'shutdown abort' } dbstop() { case "${shutdown_method}" in "immediate") dbstop_immediate ;; "checkpoint/abort") dbstop_checkpoint_abort ;; esac } dbstart() { echo 'startup' } dbstart_mount() { echo 'startup mount' } dbendbackup() { echo 'alter database end backup;' } db_backup_mode() { echo "select 'COUNT'||count(*) from v\$backup where status='ACTIVE';" } is_clear_backupmode_set(){ [ x"${clear_backupmode}" = x"true" ] } is_instance_in_backup_mode() { - typeset count + local count count="`dbasql db_backup_mode | sed 's/COUNT//'`" [ x"$count" != x"0" ] } clear_backup_mode() { - typeset output + local output output="`dbasql dbendbackup`" ocf_log info "Oracle instance $ORACLE_SID alter database end backup: $output" } getdumpdest() { #echo 'select value from v$parameter where name = \'user_dump_dest\';' echo "select value from v\$parameter where name = 'user_dump_dest';" } getipc() { echo "oradebug setmypid" echo "oradebug ipc" } # # IPC stuff: not overly complex, but quite involved :-/ # # Part 1: Oracle dumpinstipc() { - typeset dumpdest=`dbasql getdumpdest` + local dumpdest=`dbasql getdumpdest` [ "x$dumpdest" != x -a -d "$dumpdest" ] || return 1 - typeset -i fcount=`ls -rt $dumpdest | wc -l` + local -i fcount=`ls -rt $dumpdest | wc -l` dbasql getipc >/dev/null 2>&1 - typeset lastf=`ls -rt $dumpdest | grep -v '^\.*$' | tail -1` - typeset -i fcount2=`ls -rt $dumpdest | wc -l` + local lastf=`ls -rt $dumpdest | grep -v '^\.*$' | tail -1` + local -i fcount2=`ls -rt $dumpdest | wc -l` [ $((fcount+1)) -eq $fcount2 ] || return 1 # more than one file created echo $dumpdest/$lastf } parseipc() { - typeset inf=$1 + local inf=$1 test -f "$1" || return 1 awk ' $3 == "Shmid" {n=1;next} n { if( $3~/^[0-9]+$/ ) print $3; n=0 } ' $inf | sort -u | sed 's/^/m:/' awk ' /Semaphore List/ {insems=1;next} insems { for( i=1; i<=NF; i++ ) if( $i~/^[0-9]+$/ ) print $i; } /system semaphore information/ {exit} ' $inf | sort -u | sed 's/^/s:/' } # Part 2: OS (ipcs,ipcrm) filteroraipc() { # this portable? grep -w $ORACLE_OWNER | awk '{print $2}' } ipcdesc() { - typeset what=$1 + local what=$1 case $what in m) echo "shared memory segment";; s) echo "semaphore";; q) echo "message queue";; esac } rmipc() { - typeset what=$1 id=$2 + local what=$1 id=$2 ipcs -$what | filteroraipc | grep -w $id >/dev/null 2>&1 || return ocf_log info "Removing `ipcdesc $what` $id." ipcrm -$what $id } ipcrm_orauser() { - typeset what id + local what id for what in m s q; do for id in `ipcs -$what | filteroraipc`; do rmipc $what $id done done } ipcrm_instance() { - typeset ipcobj + local ipcobj for ipcobj; do rmipc `echo $ipcobj | sed 's/:/ /'` done } # # oracle_status: is the Oracle instance running? # # quick check to see if the instance is up is_oracle_up() { ps -ef | grep -wiqs "[^ ]*[_]pmon_${ORACLE_SID}" } # instance in OPEN state? instance_live() { [ "`dbasql dbstat`" = OPEN ] } ora_cleanup() { #rm -fr /tmp/.oracle #??? rm -f `ls $ORACLE_HOME/dbs/lk* | grep -i $ORACLE_SID` #return case $IPCRM in none) ;; instance) ipcrm_instance $* ;; orauser) ipcrm_orauser $* ;; *) ocf_log warn "bad usage: ipcrm set to $IPCRM" ;; esac } # # oracle_start: Start the Oracle instance # # NOTE: We handle instance in the MOUNTED and STARTED states # efficiently # We *do not* handle instance in the restricted or read-only # mode, i.e. it appears as running, but its availability is # "not for general use" # oracle_start() { - typeset status output + local status output if is_oracle_up; then status="`dbasql dbstat`" case "$status" in "OPEN") : nothing to be done, we can leave right now ocf_log info "Oracle instance $ORACLE_SID already running" return $OCF_SUCCESS ;; "STARTED") output=`dbasql dbmount` ;; "MOUNTED") : we proceed if mounted ;; *) # status unknown output=`dbasql dbstop dbstart_mount` ;; esac else output="`dbasql dbstart_mount`" # try to cleanup in case of # ORA-01081: cannot start already-running ORACLE - shut it down first if echo "$output" | grep ORA-01081 >/dev/null 2>&1; then ora_cleanup output=`dbasql dbstart_mount` fi fi # oracle instance should be mounted. status="`dbasql dbstat`" case "$status" in "MOUNTED") ;; *) : error!! ocf_log error "Oracle $ORACLE_SID can not mount." return $OCF_ERR_GENERIC ;; esac # It is examined whether mode is "online backup mode", # and if it is true, makes clear the mode. # Afterwards, DB is opened. if is_clear_backupmode_set && is_instance_in_backup_mode; then clear_backup_mode fi output=`dbasql dbopen` if is_oracle_up && instance_live; then : cool, we are up and running ocf_log info "Oracle instance $ORACLE_SID started: $output" return $OCF_SUCCESS else ocf_log err "Oracle instance $ORACLE_SID not started: $output" return $OCF_ERR_GENERIC fi } # # oracle_stop: Stop the Oracle instance # oracle_stop() { - typeset status output ipc="" + local status output ipc="" if is_oracle_up; then [ "$IPCRM" = "instance" ] && ipc=$(parseipc `dumpinstipc`) output=`dbasql dbstop` else ocf_log info "Oracle instance $ORACLE_SID already stopped" return $OCF_SUCCESS fi ora_kill # kill any processes left if is_oracle_up; then ocf_log err "Oracle instance $ORACLE_SID not stopped: $output" return $OCF_ERR_GENERIC else ocf_log info "Oracle instance $ORACLE_SID stopped: $output" sleep 1 # give em a chance to cleanup ocf_log info "Cleaning up for $ORACLE_SID" ora_cleanup "$ipc" return $OCF_SUCCESS fi } # kill the database processes (if any left) # give them 30 secs to exit cleanly (6 times 5) killprocs() { - typeset sig=$1 + local sig=$1 shift 1 kill -$sig $* >/dev/null 2>&1 } ora_kill() { killprocs TERM `eval $procs | awk '{print $1}'` for i in 1 2 3 4 5; do killprocs 0 `eval $procs | awk '{print $1}'` || return sleep 5 done killprocs KILL `eval $procs | awk '{print $1}'` } # # oracle_monitor: Can the Oracle instance do anything useful? # oracle_monitor() { if is_oracle_up && instance_live then #ocf_log info "Oracle instance $ORACLE_SID is alive" return $OCF_SUCCESS else ocf_log info "Oracle instance $ORACLE_SID is down" return $OCF_NOT_RUNNING fi } # # 'main' starts here... # if [ $# -ne 1 ] then usage exit $OCF_ERR_ARGS fi # These operations don't require OCF instance parameters to be set case "$1" in meta-data) meta_data exit $OCF_SUCCESS;; usage) usage exit $OCF_SUCCESS;; methods) oracle_methods exit $?;; *);; esac clear_backupmode=${OCF_RESKEY_clear_backupmode:-"false"} shutdown_method=${OCF_RESKEY_shutdown_method:-"checkpoint/abort"} case "${shutdown_method}" in "immediate") ;; "checkpoint/abort") ;; *) ocf_log error "unsupported shutdown_method, please read meta-data" esac if [ x = "x$OCF_RESKEY_sid" ] then ocf_log err "Please set OCF_RESKEY_sid to the Oracle SID !" exit $OCF_ERR_ARGS fi ora_info "$OCF_RESKEY_sid" "$OCF_RESKEY_home" "$OCF_RESKEY_user" LSB_STATUS_STOPPED=3 if ! testoraenv; then ocf_log info "Oracle environment for SID $ORACLE_SID does not exist" case "$1" in stop) exit $OCF_SUCCESS;; monitor) exit $OCF_NOT_RUNNING;; status) exit $LSB_STATUS_STOPPED;; *) ocf_log err "Oracle environment for SID $ORACLE_SID broken" exit $OCF_ERR_ARGS ;; esac fi setoraenv # important: set the environment for the SID procs="ps -e -o pid,args | grep -i \"[o]ra.*$ORACLE_SID\"" US=`id -u -n` if [ $US != root -a $US != $ORACLE_OWNER ] then ocf_log err "$0 must be run as root or $ORACLE_OWNER" exit $OCF_ERR_PERM fi if [ x = "x$OCF_RESKEY_ipcrm" ] then IPCRM="instance" else IPCRM="$OCF_RESKEY_ipcrm" fi # What kind of method was invoked? case "$1" in start) oracle_start exit $?;; stop) oracle_stop exit $?;; status) if is_oracle_up then echo Oracle instance $ORACLE_SID is running exit $OCF_SUCCESS else echo Oracle instance $ORACLE_SID is stopped exit $OCF_NOT_RUNNING fi ;; dumpinstipc) is_oracle_up && parseipc `dumpinstipc` exit $?;; cleanup) if [ "$IPCRM" = "instance" ]; then ora_cleanup $(parseipc `dumpinstipc`) else ora_cleanup fi exit $?;; monitor) oracle_monitor exit $?;; validate-all) # OCF_RESKEY_sid was already checked by testoraenv(), # just exit successfully here. exit $OCF_SUCCESS;; *) oracle_methods exit $OCF_ERR_UNIMPLEMENTED;; esac # # vim:tabstop=4:shiftwidth=4:textwidth=0:wrapmargin=0 diff --git a/heartbeat/oralsnr b/heartbeat/oralsnr index bfdb55867..70c6b393f 100755 --- a/heartbeat/oralsnr +++ b/heartbeat/oralsnr @@ -1,370 +1,370 @@ #!/bin/sh # # # oralsnr # # Description: Manages an Oracle Listener as a High-Availability # resource # # # Author: Dejan Muhamedagic # Support: linux-ha@lists.linux-ha.org # License: GNU General Public License (GPL) # Copyright: (C) 2006 International Business Machines, Inc. # # This code inspired by the DB2 resource script # written by Alan Robertson # # An example usage in /etc/ha.d/haresources: # node1 10.0.0.170 oralsnr::sid::home::user::listener # # See usage() function below for more details... # # OCF instance parameters: # OCF_RESKEY_sid (mandatory; for the monitor op) # OCF_RESKEY_home (optional; else read it from /etc/oratab) # OCF_RESKEY_user (optional; user to run the listener) # OCF_RESKEY_listener (optional; defaults to LISTENER) # # Initialization: . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs ####################################################################### SH=/bin/sh usage() { methods=`oralsnr_methods` methods=`echo $methods | tr ' ' '|'` cat <<-! usage: $0 ($methods) $0 manages an Oracle Database instance 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 'validate-all' operation reports whether the parameters are valid The 'methods' operation reports on the methods $0 supports ! } meta_data() { cat < 1.0 Resource script for Oracle Listener. It manages an Oracle Listener instance as an HA resource. oralsnr resource agent - + The Oracle SID (aka ORACLE_SID). Necessary for the monitor op, i.e. to do tnsping SID. sid The Oracle home directory (aka ORACLE_HOME). If not specified, then the SID should be listed in /etc/oratab. home - + Run the listener as this user. user Listener instance to be started (as defined in listener.ora). Defaults to LISTENER. listener END } # # methods: What methods/operations do we support? # oralsnr_methods() { cat <<-! start stop status monitor validate-all methods meta-data usage ! } # Gather up information about our oralsnr instance ora_info() { ORACLE_SID=$1 ORACLE_HOME=$2 ORACLE_OWNER=$3 # get ORACLE_HOME from /etc/oratab if not set [ x = "x$ORACLE_HOME" ] && ORACLE_HOME=`awk -F: "/^$ORACLE_SID:/"'{print $2}' /etc/oratab` # there a better way to find out ORACLE_OWNER? [ x = "x$ORACLE_OWNER" ] && ORACLE_OWNER=`ls -ld $ORACLE_HOME/. 2>/dev/null | awk 'NR==1{print $3}'` sqlplus=$ORACLE_HOME/bin/sqlplus lsnrctl=$ORACLE_HOME/bin/lsnrctl tnsping=$ORACLE_HOME/bin/tnsping } testoraenv() { # Let's make sure a few important things are set... [ x != "x$ORACLE_HOME" -a x != "x$ORACLE_OWNER" ] || return 1 # and some important things are there [ -x "$sqlplus" -a -x "$lsnrctl" -a -x "$tnsping" ] || return 1 return 0 } setoraenv() { LD_LIBRARY_PATH=$ORACLE_HOME/lib LIBPATH=$ORACLE_HOME/lib TNS_ADMIN=$ORACLE_HOME/network/admin PATH=$ORACLE_HOME/bin:$ORACLE_HOME/dbs:$PATH export ORACLE_SID ORACLE_HOME ORACLE_OWNER TNS_ADMIN export LD_LIBRARY_PATH LIBPATH } # # Run commands as the Oracle owner... # runasdba() { if [ "$US" = "$ORACLE_OWNER" ]; then $SH else su - $ORACLE_OWNER fi } # # oralsnr_start: Start the Oracle listener instance # oralsnr_start() { - typeset output if tnsping; then : nothing to be done, we can leave right now ocf_log info "Listener $listener already running" return $OCF_SUCCESS fi output=`echo $lsnrctl start $listener | runasdba` if tnsping; then : cool, we are up and running ocf_log info "Listener $listener running: $output" return $OCF_SUCCESS else - ocf_log err "Listener $listener stopped: $output" + ocf_log err "Listener $listener appears to have started, but is not running properly: $output" + ocf_log err "Probable Oracle configuration error" + ocf_log err "Oracle $tnsping output: `$tnsping $ORACLE_SID`" return $OCF_ERR_GENERIC fi } # # oralsnr_stop: Stop the Oracle instance # oralsnr_stop() { - typeset status output if is_oralsnr_up; then output=`echo $lsnrctl stop $listener | runasdba` else ocf_log info "Listener $listener already stopped" return $OCF_SUCCESS fi oralsnr_kill # kill the procs if they hanged if is_oralsnr_up; then ocf_log err "Listener $listener not stopped: $output" return $OCF_ERR_GENERIC else ocf_log info "Listener $listener stopped: $output" return $OCF_SUCCESS fi } # kill the listener procs # give them 10 secs to exit cleanly (5 times 2) oralsnr_kill() { killprocs TERM `eval $procs | awk '{print $1}'` for i in 1 2 3 4 5; do killprocs 0 `eval $procs | awk '{print $1}'` || return sleep 2 done killprocs KILL `eval $procs | awk '{print $1}'` } killprocs() { - typeset sig=$1 + sig=$1 shift 1 kill -$sig $* >/dev/null 2>&1 } # # is_oralsnr_up: is listener process running? # oralsnr_status: is the listener running? # is_oralsnr_up() { [ x != "x`eval $procs`" ] } oralsnr_status() { $lsnrctl status $listener | tail -1 | grep -qs 'completed successfully' } # and does it work? tnsping() { $tnsping $ORACLE_SID | tail -1 | grep -qs '^OK' } # # oralsnr_monitor: Can we connect to the listener? # oralsnr_monitor() { if oralsnr_status && tnsping then : good #ocf_log info "Listener $listener running" return $OCF_SUCCESS else ocf_log info "Listener $listener not running" return $OCF_NOT_RUNNING fi } # # 'main' starts here... # if [ $# -ne 1 ] then usage exit $OCF_ERR_ARGS fi # These operations don't require OCF instance parameters to be set case "$1" in meta-data) meta_data exit $OCF_SUCCESS;; usage) usage exit $OCF_SUCCESS;; methods) oralsnr_methods exit $?;; *);; esac if [ x = "x$OCF_RESKEY_sid" ] then ocf_log err "Please set OCF_RESKEY_sid to the Oracle SID !" exit $OCF_ERR_ARGS fi ora_info "$OCF_RESKEY_sid" "$OCF_RESKEY_home" "$OCF_RESKEY_user" LSB_STATUS_STOPPED=3 if ! testoraenv; then ocf_log info "Oracle environment for SID $ORACLE_SID does not exist" case "$1" in stop) exit $OCF_SUCCESS;; monitor) exit $OCF_NOT_RUNNING;; status) exit $LSB_STATUS_STOPPED;; *) ocf_log err "Oracle environment for SID $ORACLE_SID broken" exit $OCF_ERR_ARGS ;; esac fi setoraenv # important: set the environment for the SID # # default listener is "LISTENER" # listener=${OCF_RESKEY_listener:-"LISTENER"} # how to get listener processes procs="ps -e -o pid,args | grep '[t]nslsnr' | grep -w $listener" US=`id -u -n` if [ $US != root -a $US != $ORACLE_OWNER ] then ocf_log err "$0 must be run as root or $ORACLE_OWNER" exit $OCF_ERR_PERM fi # What kind of method was invoked? case "$1" in start) oralsnr_start exit $?;; stop) oralsnr_stop exit $?;; status) if oralsnr_status then echo Listener $listener is running exit $OCF_SUCCESS else echo Listener $listener is stopped exit $OCF_NOT_RUNNING fi ;; monitor) oralsnr_monitor exit $?;; validate-all) # OCF_RESKEY_sid was already checked by ora_info(), # just exit successfully here. exit $OCF_SUCCESS;; *) oralsnr_methods exit $OCF_ERR_UNIMPLEMENTED;; esac # # vim:tabstop=4:shiftwidth=4:textwidth=0:wrapmargin=0 diff --git a/heartbeat/scsi2reservation b/heartbeat/scsi2reservation index b41d8fa76..b8a957798 100644 --- a/heartbeat/scsi2reservation +++ b/heartbeat/scsi2reservation @@ -1,168 +1,168 @@ #!/bin/sh # by hxinwei@gmail.com # License: GNU General Public License 2 (GPL2) if [ -n "$OCF_DEBUG_LIBRARY" ]; then . $OCF_DEBUG_LIBRARY else . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs fi scsi2reserve_meta_data() { cat < 1.0 The scsi-2-reserve resource agent is a place holder for SCSI-2 reservation. A healthy instance of scsi-2-reserve resource, indicates the own of the specified SCSI device. This resource agent depends on the scsi_reserve from scsires package, which is Linux specific. scsi-2 reservation The scsi_reserve is a command from scsires package. It helps to issue SCSI-2 reservation on SCSI devices. scsi_reserve command The shared disk that can be reserved. Shared disk. We are going to try several times before giving up. Start_loop indicates how many times we are going to re-try. Times to re-try before giving up. END return $OCF_SUCCESS } scsi2reserve_usage() { cat </dev/null; then - return $OCF_SUCCESS + if ! have_binary $WGET; then + ocf_log err "Monitoring not supported by $OCF_RESOURCE_INSTANCE" + ocf_log info "Please make sure that wget is available" + return $OCF_ERR_CONFIGURED fi - return $OCF_ERR_GENERIC + $WGET -O /dev/null $RESOURCE_STATUSURL >/dev/null 2>&1 } +############################################################################ +# +isalive_tomcat() +{ + pgrep -f "${SEARCH_STR}" > /dev/null +} +############################################################################ +# Check tomcat process and service availability monitor_tomcat() { - if ! pgrep -f "java -Dname=$TOMCAT_NAME" > /dev/null; then + isalive_tomcat || return $OCF_NOT_RUNNING - fi - isrunning_tomcat - if [ $? != $OCF_SUCCESS ]; then + isrunning_tomcat || return $OCF_NOT_RUNNING + return $OCF_SUCCESS +} + +############################################################################ +# Execute catalina.out log rotation +rotate_catalina_out() +{ + # Look for rotatelogs/rotatelogs2 + if [ -x /usr/sbin/rotatelogs ]; then + ROTATELOGS=/usr/sbin/rotatelogs + elif [ -x /usr/sbin/rotatelogs2 ]; then + ROTATELOGS=/usr/sbin/rotatelogs2 + else + ocf_log warn "rotatelogs command not found." + return 1 fi + + # Clean up and set permissions on required files + rm -rf "$CATALINA_HOME"/temp/* "$CATALINA_HOME/logs/catalina.out" + mkfifo -m700 "$CATALINA_HOME/logs/catalina.out" + chown --dereference "$RESOURCE_TOMCAT_USER" "$CATALINA_HOME/logs/catalina.out" || true + + # -s is required because tomcat5.5's login shell is /bin/false + su - -s /bin/sh $RESOURCE_TOMCAT_USER \ + -c "$ROTATELOGS -l \"$CATALINA_HOME/logs/catalina_%F.log\" $CATALINA_ROTATETIME" \ + < "$CATALINA_HOME/logs/catalina.out" > /dev/null 2>&1 & } +############################################################################ +# Start Tomcat start_tomcat() { cd "$CATALINA_HOME/bin" monitor_tomcat if [ $? = $OCF_SUCCESS ]; then return $OCF_SUCCESS fi + #ocf_log debug "catalina.out rotation FLG = ${CATALINA_ROTATE_LOG}" + if [ ${CATALINA_ROTATE_LOG} = "YES" ]; then + rotate_catalina_out + if [ $? = 0 ]; then + ocf_log debug "Rotate catalina.out succeeded." + else + ocf_log warn "Rotate catalina.out failed. Starting tomcat without catalina.out rotation." + fi + fi + echo "`date "+%Y/%m/%d %T"`: start ===========================" >> "$TOMCAT_CONSOLE" + ocf_log debug "CATALINA_OPTS value = ${CATALINA_OPTS}" if [ "$RESOURCE_TOMCAT_USER" = RUNASIS ]; then - "$CATALINA_HOME/bin/catalina.sh" start \ + "$CATALINA_HOME/bin/catalina.sh" start $TOMCAT_START_OPTS \ >> "$TOMCAT_CONSOLE" 2>&1 & else - su - "$RESOURCE_TOMCAT_USER" \ + su - -s /bin/sh "$RESOURCE_TOMCAT_USER" \ -c "export JAVA_HOME=${OCF_RESKEY_java_home};\n export JAVA_OPTS=-Dname=${TOMCAT_NAME};\n export CATALINA_HOME=${OCF_RESKEY_catalina_home};\n export CATALINA_PID=${OCF_RESKEY_catalina_pid};\n - $CATALINA_HOME/bin/catalina.sh start" \ + export CATALINA_OPTS=\"${OCF_RESKEY_catalina_opts}\";\n + $CATALINA_HOME/bin/catalina.sh start ${OCF_RESKEY_tomcat_start_opts}" \ >> "$TOMCAT_CONSOLE" 2>&1 & fi while true; do monitor_tomcat if [ $? = $OCF_SUCCESS ]; then break fi ocf_log debug "start_tomcat[$TOMCAT_NAME]: retry monitor_tomcat" sleep 3 done return $OCF_SUCCESS } +############################################################################ +# Stop Tomcat stop_tomcat() { cd "$CATALINA_HOME/bin" echo "`date "+%Y/%m/%d %T"`: stop ###########################" >> "$TOMCAT_CONSOLE" if [ "$RESOURCE_TOMCAT_USER" = RUNASIS ]; then "$CATALINA_HOME/bin/catalina.sh" stop \ >> "$TOMCAT_CONSOLE" 2>&1 & eval $tomcat_stop_cmd >> "$TOMCAT_CONSOLE" 2>&1 else - su - "$RESOURCE_TOMCAT_USER" \ + su - -s /bin/sh "$RESOURCE_TOMCAT_USER" \ -c "export JAVA_HOME=${OCF_RESKEY_java_home};\n export JAVA_OPTS=-Dname=${TOMCAT_NAME};\n export CATALINA_HOME=${OCF_RESKEY_catalina_home};\n export CATALINA_PID=${OCF_RESKEY_catalina_pid};\n $CATALINA_HOME/bin/catalina.sh stop" \ >> "$TOMCAT_CONSOLE" 2>&1 fi lapse_sec=0 - while pgrep -f "java -Dname=$TOMCAT_NAME" > /dev/null; do + while isalive_tomcat; do sleep 1 lapse_sec=`expr $lapse_sec + 1` ocf_log debug "stop_tomcat[$TOMCAT_NAME]: stop NORM $lapse_sec/$TOMCAT_STOP_TIMEOUT" if [ $lapse_sec -ge $TOMCAT_STOP_TIMEOUT ]; then break fi done - if pgrep -f "java -Dname=$TOMCAT_NAME" > /dev/null; then + if isalive_tomcat; then lapse_sec=0 while true; do sleep 1 lapse_sec=`expr $lapse_sec + 1` ocf_log debug "stop_tomcat[$TOMCAT_NAME]: suspend tomcat by SIGTERM ($lapse_sec/$TOMCAT_SUSPEND_TRIALCOUNT)" - pkill -TERM -f "java -Dname=$TOMCAT_NAME" - if pgrep -f "java -Dname=$TOMCAT_NAME" > /dev/null; then + pkill -TERM -f "${SEARCH_STR}" + if isalive_tomcat; then ocf_log debug "stop_tomcat[$TOMCAT_NAME]: suspend tomcat by SIGQUIT ($lapse_sec/$TOMCAT_SUSPEND_TRIALCOUNT)" - pkill -QUIT -f "java -Dname=$TOMCAT_NAME" - if pgrep -f "java -Dname=$TOMCAT_NAME" > /dev/null; then + pkill -QUIT -f "${SEARCH_STR}" + if isalive_tomcat; then if [ $lapse_sec -ge $TOMCAT_SUSPEND_TRIALCOUNT ]; then break fi else break fi else break fi done fi lapse_sec=0 - while pgrep -f "java -Dname=$TOMCAT_NAME" > /dev/null; do + while isalive_tomcat; do sleep 1 lapse_sec=`expr $lapse_sec + 1` - ocf_log debug "stop_tomcat[$TOMCAT_NAME]: suspend tomcat by SIGKILL ($lapse_sec/@@@)" - pkill -KILL -f "java -Dname=$TOMCAT_NAME" + ocf_log debug "stop_tomcat[$TOMCAT_NAME]: suspend tomcat by SIGKILL ($lapse_sec)" + pkill -KILL -f "${SEARCH_STR}" done - rm -f "$CATALINA_PID" + if [ ${CATALINA_ROTATE_LOG} = "YES" ]; then + rm -f "$CATALINA_PID" "${CATALINA_HOME}/logs/catalina.out" + else + rm -f "$CATALINA_PID" + fi return $OCF_SUCCESS } status_tomcat() { return $OCF_SUCCESS } metadata_tomcat() { cat < 1.0 Resource script for tomcat. It manages a Tomcat instance as an HA resource. tomcat resource agent The name of the resource The name of the resource A destination of the log of this script A destination of the log of this script Time-out at the time of the stop Time-out at the time of the stop The re-try number of times awaiting a stop The re-try number of times awaiting a stop A user name to start a resource A user name to start a resource URL for state confirmation URL for state confirmation - + Home directory of the Java Home directory of the Java Home directory of Tomcat Home directory of Tomcat A PID file name of Tomcat A PID file name of Tomcat + + +Tomcat start options + +Tomcat start options + + + + + +Catalina options + +Catalina options + + + + + +Rotate catalina.out flag + +Rotate catalina.out flag + + + + + +Time span of the rotate catalina.out + +Time span of the rotate catalina.out + + + END return $OCF_SUCCESS } validate_all_tomcat() { ocf_log info "validate_all_tomcat[$TOMCAT_NAME]" return $OCF_SUCCESS } +# +### tomcat RA environment variables +# + TOMCAT_NAME="${OCF_RESKEY_tomcat_name-tomcat}" TOMCAT_CONSOLE="${OCF_RESKEY_script_log-/var/log/$TOMCAT_NAME.log}" TOMCAT_STOP_TIMEOUT="${OCF_RESKEY_tomcat_stop_timeout-5}" TOMCAT_SUSPEND_TRIALCOUNT="${OCF_RESKEY_tomcat_suspend_trialcount-10}" RESOURCE_TOMCAT_USER="${OCF_RESKEY_tomcat_user-RUNASIS}" RESOURCE_STATUSURL="${OCF_RESKEY_statusurl-http://127.0.0.1:8080}" JAVA_HOME="${OCF_RESKEY_java_home}" JAVA_OPTS="-Dname=$TOMCAT_NAME" +SEARCH_STR="\\""${JAVA_OPTS}" CATALINA_HOME="${OCF_RESKEY_catalina_home}" CATALINA_PID="${OCF_RESKEY_catalina_pid-$CATALINA_HOME/logs/catalina.pid}" -export JAVA_HOME JAVA_OPTS CATALINA_HOME CATALINA_PID +TOMCAT_START_OPTS="${OCF_RESKEY_tomcat_start_opts}" +CATALINA_OPTS="${OCF_RESKEY_catalina_opts}" +CATALINA_ROTATE_LOG="${OCF_RESKEY_catalina_rotate_log-NO}" +CATALINA_ROTATETIME="${OCF_RESKEY_catalina_rotatetime-86400}" + +export JAVA_HOME JAVA_OPTS CATALINA_HOME CATALINA_PID CATALINA_OPTS JAVA=${JAVA_HOME}/bin/java +# +# ------------------ +# the main script +# ------------------ +# + COMMAND=$1 case "$COMMAND" in start) ocf_log debug "[$TOMCAT_NAME] Enter tomcat start" start_tomcat func_status=$? ocf_log debug "[$TOMCAT_NAME] Leave tomcat start $func_status" exit $func_status ;; stop) ocf_log debug "[$TOMCAT_NAME] Enter tomcat stop" stop_tomcat func_status=$? ocf_log debug "[$TOMCAT_NAME] Leave tomcat stop $func_status" exit $func_status ;; status) status_tomcat exit $? ;; monitor) #ocf_log debug "[$TOMCAT_NAME] Enter tomcat monitor" monitor_tomcat func_status=$? #ocf_log debug "[$TOMCAT_NAME] Leave tomcat monitor $func_status" exit $func_status ;; meta-data) metadata_tomcat exit $? ;; validate-all) validate_all_tomcat exit $? ;; + usage|help) + usage + exit $OCF_SUCCESS + ;; *) usage + exit $OCF_ERR_UNIMPLEMENTED ;; esac diff --git a/heartbeat/vmware b/heartbeat/vmware index bc1a83196..52c22b567 100644 --- a/heartbeat/vmware +++ b/heartbeat/vmware @@ -1,350 +1,350 @@ #!/bin/sh # # VMware OCF resource agent # # Copyright (c) 2008 Apra Sistemi s.r.l. # All Rights Reserved. # # Description: Manages a VMware server 2.0 as a High-Availability # resource # # # Author: Cristian Mammoli # License: GNU General Public License (GPL) # Copyright: (C) 2008 Apra Sistemi s.r.l. # # See usage() function below for more details... # # OCF instance parameters: # * OCF_RESKEY_VMXPATH (mandatory, full path to the virtual machine vmx file) # * OCF_RESKEY_VIMSHBIN (mandatory, full path to th vmware-vim-cmd executable) # # Requirements/caveats: # * vmware-server 2.0 RC1 installed and autostarted on all nodes # * vmdk files must be in the same directory of the vmx file # * vmx filenames must be unique, even if stored in different directories # * Default_Action_Timeout stock value (20 sec) isn't enough if you are # dealing with many virtual machines: raise it to something around 300 secs # or use operation attributes with the proposed values # Initialization ################################################################# # Source ocf shell functions . ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs # Basic variables configuration ################################################################# # Path to the virtual machine configuration file VMXPATH="$OCF_RESKEY_vmxpath" # Path to the vmware-vim-cmd executable VIMSHBIN="$OCF_RESKEY_vimshbin" # vmware-vim-cmd functions ################################################################# # Get virtual machine vid vmware_get_vid() { $VIMSHBIN vmsvc/getallvms 2>/dev/null \ | awk '/\/'"$1"'/ {print $1}' } # Is the vm waiting for input after a migration? vmware_uuid_alt() { $VIMSHBIN vmsvc/message $1 2>/dev/null \ | awk /^msg.uuid.altered/ } # Get message id vmware_get_msgid() { $VIMSHBIN vmsvc/message $1 2>/dev/null \ | awk '/^Virtual machine message/ {print $4}' \ | awk -F : '{print $1}' } # Answers message vmware_answer_msg() { - $VIMSHBIN vmsvc/message $1 $2 $3 &> /dev/null + $VIMSHBIN vmsvc/message $1 $2 $3 >/dev/null 2>&1 } # Register a virtual machine vmware_register_vm() { - $VIMSHBIN solo/registervm '"'$1'"' &> /dev/null + $VIMSHBIN solo/registervm '"'$1'"' >/dev/null 2>&1 } # Unregister a virtual machine vmware_unregister_vm() { - $VIMSHBIN vmsvc/unregister $1 &> /dev/null + $VIMSHBIN vmsvc/unregister $1 >/dev/null 2>&1 } # Start a virtual machine vmware_poweron_vm() { - $VIMSHBIN vmsvc/power.on $1 &> /dev/null + $VIMSHBIN vmsvc/power.on $1 >/dev/null 2>&1 } # Suspend a virtual machine vmware_suspend_vm() { - $VIMSHBIN vmsvc/power.suspend $1 &> /dev/null + $VIMSHBIN vmsvc/power.suspend $1 >/dev/null 2>&1 } # Get virtual machine power state vmware_get_status() { $VIMSHBIN vmsvc/power.getstate $1 2>/dev/null \ | awk '/^Powered on/ || /^Powered off/ || /^Suspended/' } # Get vid of missing virtual machines vmware_get_broken() { $VIMSHBIN vmsvc/getallvm 2>&1 \ | awk -F "'" '/^Skipping/ {print $2}' } # Variables depending on the above functions ################################################################# # Directory containing the virtual machine VMXDIR="`dirname "$VMXPATH"`" # Basename of the configuration file RELVMXPATH="`basename "$VMXPATH"`" # Vid of the virtual machine (can be empty if the vm is not registered) VMID=`vmware_get_vid "$RELVMXPATH"` # Power state of the virtual machine (can be empty if the vm is not registered) VMSTATE="`vmware_get_status $VMID`" # Virtual machine name VM="`awk -F '"' '/^displayName/ {print $2}' "$VMXPATH"`" # msg.autoAnswer value in config file VMAUTOMSG="`awk -F '"' '/^msg.autoAnswer/ {print $2}' "$VMXPATH"`" # Main functions ################################################################# # Print usage summary vmware_usage() { cat < 0.1 OCF compliant script to control vmware server 2.0 virtual machines. VMWare server 2.0 resource agent VMX configuration file path VMX file path vmware-vim-cmd executable path vmware-vimsh path END } # See how we were called ################################################################# case $1 in meta-data) meta_data exit $OCF_SUCCESS ;; start) vmware_validate vmware_start ;; stop) vmware_stop ;; status|monitor) vmware_monitor ;; usage|help) vmware_usage exit $OCF_SUCCESS ;; validate-all) vmware_validate ;; *) vmware_usage exit $OCF_ERR_UNIMPLEMENTED ;; esac exit $?