diff --git a/configure.ac b/configure.ac index 4b9e742d..570746eb 100644 --- a/configure.ac +++ b/configure.ac @@ -1,346 +1,368 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.63]) AC_INIT([fence-agents], m4_esyscmd([make/git-version-gen .tarball-version]), [linux-cluster@redhat.com]) AM_INIT_AUTOMAKE([-Wno-portability dist-bzip2 dist-xz subdir-objects]) LT_PREREQ([2.2.6]) LT_INIT AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([fence/agents/lib/fencing.py.py]) AC_CONFIG_HEADERS([make/clusterautoconfig.h]) AC_CANONICAL_HOST AC_PROG_LIBTOOL AC_LANG([C]) # Sanitize path if test "$prefix" = "NONE"; then prefix="/usr" if test "$localstatedir" = "\${prefix}/var"; then localstatedir="/var" fi if test "$sysconfdir" = "\${prefix}/etc"; then sysconfdir="/etc" fi if test "$libdir" = "\${exec_prefix}/lib"; then if test -e /usr/lib64; then libdir="/usr/lib64" else libdir="/usr/lib" fi fi fi case $exec_prefix in NONE) exec_prefix=$prefix;; prefix) exec_prefix=$prefix;; esac # Checks for programs. # check stolen from gnulib/m4/gnu-make.m4 if ! ${MAKE-make} --version /cannot/make/this >/dev/null 2>&1; then AC_MSG_ERROR([you don't seem to have GNU make; it is required]) fi AC_PROG_CC AM_PROG_CC_C_O AC_PROG_LN_S AC_PROG_INSTALL AC_PROG_MAKE_SET AC_PROG_AWK AC_PROG_CXX AC_PROG_RANLIB ## local helper functions # this function checks if CC support options passed as # args. Global CFLAGS are ignored during this test. cc_supports_flag() { local CFLAGS="$@" AC_MSG_CHECKING([whether $CC supports "$@"]) AC_COMPILE_IFELSE([int main(){return 0;}] , [RC=0; AC_MSG_RESULT([yes])], [RC=1; AC_MSG_RESULT([no])]) return $RC } # this function tests if a library has a certain function # by using AC_CHECK_LIB but restores the original LIBS global # envvar. This is required to avoid libtool to link everything # with everything. check_lib_no_libs() { AC_CHECK_LIB([$1], [$2],, [AC_MSG_ERROR([Unable to find $1 library])]) LIBS=$ac_check_lib_save_LIBS } # Checks for header files. AC_CHECK_HEADERS([arpa/inet.h fcntl.h libintl.h limits.h netdb.h stddef.h sys/socket.h sys/time.h syslog.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_INLINE AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_TYPE_UINT32_T # Checks for library functions. AC_FUNC_FORK AC_FUNC_MALLOC AC_CHECK_FUNCS([alarm atexit bzero dup2 memmove memset select socket strcasecmp strchr strdup strerror strtol]) # local options AC_ARG_ENABLE([debug], [ --enable-debug enable debug build. ], [ default="no" ]) AC_ARG_WITH([fenceagentslibdir], [ --with-fenceagentslibdir=PATH installation path for fence library. ], [ FENCEAGENTSLIBDIR="$withval" ], [ FENCEAGENTSLIBDIR="${datadir}/fence" ]) AC_ARG_WITH([default-config-dir], [ --with-default-config-dir=DIR cluster config directory. ], [ DEFAULT_CONFIG_DIR="$withval" ], [ DEFAULT_CONFIG_DIR="$sysconfdir/cluster" ]) AC_ARG_WITH([default-config-file], [ --with-default-config-file=FILE cluster config file. ], [ DEFAULT_CONFIG_FILE="$withval" ], [ DEFAULT_CONFIG_FILE="cluster.conf" ]) AC_ARG_WITH([agents], [ --with-agents=LIST list of agents to build/ship (default: all). ], [ AGENTS_LIST="$withval" ], [ AGENTS_LIST="all" ]) if test "x$AGENTS_LIST" = x; then AC_ERROR([No agents selected]) fi FENCE_KDUMP=0 if echo "$AGENTS_LIST" | grep -q -E "all|kdump"; then - FENCE_KDUMP=1 + case "$host_os" in + *bsd*) + ;; + *) + FENCE_KDUMP=1 + ;; + esac AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s/kdump( |$)//") fi FENCE_MANUAL=0 if echo "$AGENTS_LIST" | grep -q -E "all|manual"; then FENCE_MANUAL=1 AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s/manual( |$)//") fi FENCE_SCSI=0 if echo "$AGENTS_LIST" | grep -q -E "all|scsi"; then FENCE_SCSI=1 fi FENCE_ZVM=0 if echo "$AGENTS_LIST" | grep -q -E "all|zvm( |$)"; then FENCE_ZVM=1 fi if test "x$AGENTS_LIST" != xall; then for j in $AGENTS_LIST; do if ! test -f fence/agents/$j/fence_$j*.py; then AC_ERROR([Agent $j does not exists]) fi AGENTS_LIST=`echo "$AGENTS_LIST" | sed -E -e "s#$j([^_/]|$)#$j/fence_$j\1#g" -e "s#zvm/fence_zvm( |$)#zvm/fence_zvmip\1#g"` done fi if test "x$AGENTS_LIST" = xall; then - AGENTS_LIST=`find $srcdir/fence/agents -mindepth 2 -maxdepth 2 -name '*.py' -printf '%P ' | sed -e 's#lib/[A-Za-z_.]* ##g' -e 's#nss_wrapper/[A-Za-z_.]* ##g' -e 's#autodetect/[A-Za-z_.]* ##g'` + AGENTS_LIST=`find $srcdir/fence/agents -mindepth 2 -maxdepth 2 -name 'fence_*.py' -print0 | xargs -0 | sed -e 's#[^ ]*/fence/agents/##g' -e 's#lib/[A-Za-z_.]* ##g' -e 's#nss_wrapper/[A-Za-z_.]* ##g' -e 's#autodetect/[A-Za-z_.]* ##g'` fi XENAPILIB=0 if echo "$AGENTS_LIST" | grep -q xenapi; then XENAPILIB=1 fi ## random vars LOGDIR=${localstatedir}/log/cluster CLUSTERVARRUN=${localstatedir}/run/cluster CLUSTERDATA=${datadir}/cluster AM_PATH_PYTHON if test -z "$PYTHON"; then echo "*** Essential program python not found" 1>&2 exit 1 fi AC_PYTHON_MODULE(pexpect, 1) AC_PYTHON_MODULE(pycurl, 1) AC_PYTHON_MODULE(requests, 1) if echo "$AGENTS_LIST" | grep -q amt_ws; then - AC_PYTHON_MODULE(pywsman, 1) + AC_PYTHON_MODULE(pywsman) + if test "x${HAVE_PYMOD_pywsman}" != xyes; then + AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s#amt_ws/fence_amt_ws.py( |$)##") + AC_MSG_WARN("Not building fence_amt_ws") + fi fi if echo "$AGENTS_LIST" | grep -q aws; then - AC_PYTHON_MODULE(boto3, 1) + AC_PYTHON_MODULE(boto3) + if test "x${HAVE_PYMOD_boto3}" != xyes; then + AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s#aws/fence_aws.py( |$)##") + AC_MSG_WARN("Not building fence_aws") + fi fi if echo "$AGENTS_LIST" | grep -q -E "ovh|vmware_soap"; then - AC_PYTHON_MODULE(suds, 1) + AC_PYTHON_MODULE(suds) + if test "x${HAVE_PYMOD_suds}" != xyes; then + AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s#(ovh/fence_ovh|vmware_soap/fence_vmware_soap).py( |$)##g") + AC_MSG_WARN("Not building fence_ovh and fence_vmware_soap") + fi fi if echo "$AGENTS_LIST" | grep -q gce; then - AC_PYTHON_MODULE(googleapiclient, 1) + AC_PYTHON_MODULE(googleapiclient) + if test "x${HAVE_PYMOD_googleapiclient}" != xyes; then + AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s#gce/fence_gce.py( |$)##") + AC_MSG_WARN("Not building fence_ovh and fence_gce") + fi fi ## path to 3rd-party binaries AC_PATH_PROG([IPMITOOL_PATH], [ipmitool], [/usr/bin/ipmitool]) AC_PATH_PROG([OPENSTACK_PATH], [openstack], [/usr/bin/openstack]) AC_PATH_PROG([AMTTOOL_PATH], [amttool], [/usr/bin/amttool]) AC_PATH_PROG([GNUTLSCLI_PATH], [gnutlscli], [/usr/bin/gnutls-cli]) AC_PATH_PROG([COROSYNC_CMAPCTL_PATH], [corosync-cmapctl], [/usr/sbin/corosync-cmapctl]) AC_PATH_PROG([SG_PERSIST_PATH], [sg_persist], [/usr/bin/sg_persist]) AC_PATH_PROG([SG_TURS_PATH], [sg_turs], [/usr/bin/sg_turs]) AC_PATH_PROG([VGS_PATH], [vgs], [/usr/sbin/vgs]) AC_PATH_PROG([SUDO_PATH], [sudo], [/usr/bin/sudo]) AC_PATH_PROG([SSH_PATH], [ssh], [/usr/bin/ssh]) AC_PATH_PROG([TELNET_PATH], [telnet], [/usr/bin/telnet]) AC_PATH_PROG([MPATH_PATH], [mpathpersist], [/usr/sbin/mpathpersist]) AC_PATH_PROG([SBD_PATH], [sbd], [/sbin/sbd]) AC_PATH_PROG([SUDO_PATH], [sudo], [/usr/bin/sudo]) AC_PATH_PROG([SNMPWALK_PATH], [snmpwalk], [/usr/bin/snmpwalk]) AC_PATH_PROG([SNMPSET_PATH], [snmpset], [/usr/bin/snmpset]) AC_PATH_PROG([SNMPGET_PATH], [snmpget], [/usr/bin/snmpget]) AC_PATH_PROG([NOVA_PATH], [nova], [/usr/bin/nova]) AC_PATH_PROG([POWERMAN_PATH], [powerman], [/usr/bin/powerman]) AC_PATH_PROG([PING_CMD], [ping]) AC_PATH_PROG([PING6_CMD], [ping6]) AC_PATH_PROG([PING4_CMD], [ping4]) if test "x${ac_cv_path_PING_CMD}" = x; then # assume multicall-ping just not available in build-environment PING_CMD="/bin/ping" PING4_CMD="/bin/ping -4" PING6_CMD="/bin/ping -6" elif test "x${ac_cv_path_PING6_CMD}" = x; then # just IPv4 PING4_CMD="${ac_cv_path_PING_CMD}" elif test -L ${ac_cv_path_PING6_CMD}; then # assume multicall-ping PING4_CMD="${ac_cv_path_PING_CMD} -4" else # ping is just IPv4 PING4_CMD="${ac_cv_path_PING_CMD}" fi ## do subst AC_SUBST([DEFAULT_CONFIG_DIR]) AC_DEFINE_UNQUOTED([DEFAULT_CONFIG_DIR], "$(eval echo ${DEFAULT_CONFIG_DIR})", [Default config directory]) AC_SUBST([DEFAULT_CONFIG_FILE]) AC_DEFINE_UNQUOTED([DEFAULT_CONFIG_FILE], "$(eval echo ${DEFAULT_CONFIG_FILE})", [Default config file]) AC_SUBST([LOGDIR]) AC_DEFINE_UNQUOTED([LOGDIR], "$(eval echo ${LOGDIR})", [Default logging directory]) AC_SUBST([CLUSTERVARRUN]) AC_DEFINE_UNQUOTED([CLUSTERVARRUN], "$(eval echo ${CLUSTERVARRUN})", [Default cluster var/run directory]) AC_SUBST([CLUSTERDATA]) AC_SUBST([FENCEAGENTSLIBDIR]) AC_SUBST([SNMPBIN]) AC_SUBST([AGENTS_LIST]) AM_CONDITIONAL(BUILD_FENCE_KDUMP, test $FENCE_KDUMP -eq 1) AM_CONDITIONAL(BUILD_FENCE_MANUAL, test $FENCE_MANUAL -eq 1) AM_CONDITIONAL(BUILD_FENCE_SCSI, test $FENCE_SCSI -eq 1) AM_CONDITIONAL(BUILD_FENCE_ZVM, test $FENCE_ZVM -eq 1) AM_CONDITIONAL(BUILD_XENAPILIB, test $XENAPILIB -eq 1) AC_SUBST([IPMITOOL_PATH]) AC_SUBST([OPENSTACK_PATH]) AC_SUBST([AMTTOOL_PATH]) AC_SUBST([COROSYNC_CMAPCTL_PATH]) AC_SUBST([SG_PERSIST_PATH]) AC_SUBST([SG_TURS_PATH]) AC_SUBST([VGS_PATH]) AC_SUBST([POWERMAN_PATH]) ## *FLAGS handling ENV_CFLAGS="$CFLAGS" ENV_CPPFLAGS="$CPPFLAGS" ENV_LDFLAGS="$LDFLAGS" # debug build stuff if test "x${enable_debug}" = xyes; then AC_DEFINE_UNQUOTED([DEBUG], [1], [Compiling Debugging code]) OPT_CFLAGS="-O0" else OPT_CFLAGS="-O2" fi # gdb flags if test "x${GCC}" = xyes; then GDB_FLAGS="-ggdb3" else GDB_FLAGS="-g" fi # extra warnings EXTRA_WARNINGS="" WARNLIST=" all shadow missing-prototypes missing-declarations strict-prototypes declaration-after-statement pointer-arith write-strings cast-align bad-function-cast missing-format-attribute format=2 format-security format-nonliteral no-long-long unsigned-char gnu89-inline no-strict-aliasing " for j in $WARNLIST; do if cc_supports_flag -W$j; then EXTRA_WARNINGS="$EXTRA_WARNINGS -W$j"; fi done CFLAGS="$ENV_CFLAGS $OPT_CFLAGS $GDB_FLAGS \ $EXTRA_WARNINGS $WERROR_CFLAGS" CPPFLAGS="-I\$(top_builddir)/make -I\$(top_srcdir)/make -I. $ENV_CPPFLAGS" LDFLAGS="$ENV_LDFLAGS" AM_EXTRA_RECURSIVE_TARGETS([xml-upload]) AC_CONFIG_FILES([Makefile fence/Makefile fence/agents/Makefile fence/agents/lib/Makefile doc/Makefile]) AC_OUTPUT diff --git a/fence/agents/Makefile.am b/fence/agents/Makefile.am index 023eea7d..8feff038 100644 --- a/fence/agents/Makefile.am +++ b/fence/agents/Makefile.am @@ -1,108 +1,108 @@ MAINTAINERCLEANFILES = Makefile.in TARGET = $(AGENTS_LIST:%.py=%) SRC = $(TARGET:=.py) CLEAN_TARGET_ADDITIONAL = kdump/fence_kdump_send manual/fence_ack_manual */*.o EXTRA_DIST = $(SRC) sbin_PROGRAMS = sbin_SCRIPTS = $(TARGET) libexec_PROGRAMS = dist_man_MANS = noinst_HEADERS = kdump/list.h kdump/message.h kdump/options.h kdump/version.h zvm/fence_zvm.h EXTRA_SCRIPTS = man_MANS = $(sbin_SCRIPTS:=.8) if BUILD_FENCE_KDUMP sbin_PROGRAMS += kdump/fence_kdump libexec_PROGRAMS += kdump/fence_kdump_send dist_man_MANS += kdump/fence_kdump.8 kdump/fence_kdump_send.8 kdump_fence_kdump_SOURCES = kdump/fence_kdump.c kdump_fence_kdump_CFLAGS = -D_GNU_SOURCE -Ikdump kdump_fence_kdump_send_SOURCES = kdump/fence_kdump_send.c kdump_fence_kdump_send_CFLAGS = -D_GNU_SOURCE -Ikdump endif if BUILD_FENCE_MANUAL EXTRA_DIST += manual/fence_ack_manual.in manual/fence_ack_manual.8 sbin_SCRIPTS += manual/fence_ack_manual endif if BUILD_FENCE_ZVM EXTRA_DIST += zvm/fence_zvm_man_page sbin_PROGRAMS += zvm/fence_zvm dist_man_MANS += zvm/fence_zvm.8 zvm_fence_zvm_SOURCES = zvm/fence_zvm.c zvm_fence_zvm_CFLAGS = -D_GNU_SOURCE -Izvm endif if BUILD_FENCE_SCSI scsidatadir = $(CLUSTERDATA) scsidata_SCRIPTS = scsi/fence_scsi_check scsi/fence_scsi_check_hardreboot endif FENCE_TEST_ARGS = \ login=test\n\ passwd=test\n\ ipaddr=test\n\ port=1\n\ managed=1\n\ devices=test\n\ session_url=http://test\n\ email=test@test.te\n\ ping_targets=localhost manual/fence_ack_manual: manual/fence_ack_manual.in mkdir -p $(@D) cat $^ | sed \ -e 's#@clustervarrun@#${CLUSTERVARRUN}#g' \ > $@ scsi/fence_scsi_check: scsi/fence_scsi cp $^ $@ scsi/fence_scsi_check_hardreboot: scsi/fence_scsi cp $^ $@ kdump/fence_kdump.8: kdump/fence_kdump $(top_srcdir)/fence/agents/lib/fence2man.xsl set -e && \ ./$(@:%.8=%) -o metadata > $(@D)/.$(@F).tmp && \ xmllint --noout --relaxng $(top_srcdir)/fence/agents/lib/metadata.rng $(@D)/.$(@F).tmp && \ xsltproc $(top_srcdir)/fence/agents/lib/fence2man.xsl $(@D)/.$(@F).tmp > $@ xsltproc $(top_srcdir)/fence/agents/lib/fence2wiki.xsl $(@D)/.$(@F).tmp | grep -v ' $(@D)/$(@F:%.8=%.wiki) kdump/fence_kdump_send.8: true manual/fence_ack_manual.8: true zvm/fence_zvm.8: zvm/fence_zvm cp $(top_srcdir)/fence/agents/zvm/fence_zvm_man_page $(@D)/fence_zvm.8 cisco_mds/fence_cisco_mds.delay-check: cisco_mds/fence_cisco_mds $(eval INPUT=$(subst .delay-check,,$@)) - FENCE_TEST_ARGS_CISCO_MDS=$$(/bin/echo -e '$(FENCE_TEST_ARGS)' | sed 's#port=1#port=fc1/1#'); \ - test `PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib /usr/bin/time -f "%e" \ - sh -c "/bin/echo -e 'delay=10\n $$FENCE_TEST_ARGS_CISCO_MDS' | $(PYTHON) ./$(INPUT)" 2>&1 |\ - sed 's/\.//' | tail -n 1` -ge 1000 || ( \ - PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib /usr/bin/time -f "%e" \ - sh -c "/bin/echo -e "delay=0\n $$FENCE_TEST_ARGS_CISCO_MDS" | $(PYTHON) ./$(INPUT)"; false ) + FENCE_TEST_ARGS_CISCO_MDS=$$(printf '$(FENCE_TEST_ARGS)' | sed 's#port=1#port=fc1/1#'); \ + test `PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib /usr/bin/time -p \ + sh -c "printf 'delay=10\n $$FENCE_TEST_ARGS_CISCO_MDS' | $(PYTHON) ./$(INPUT)" 2>&1 |\ + awk -F"[. ]" -vOFS= '/real/ {print $$2,$$3}'` -ge 1000 || ( \ + PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib /usr/bin/time -p \ + sh -c "printf "delay=0\n $$FENCE_TEST_ARGS_CISCO_MDS" | $(PYTHON) ./$(INPUT)"; false ) include $(top_srcdir)/make/fencebuild.mk include $(top_srcdir)/make/fenceman.mk include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/heuristics_ping/fence_heuristics_ping.py b/fence/agents/heuristics_ping/fence_heuristics_ping.py index 619d39bb..5b2c61d8 100644 --- a/fence/agents/heuristics_ping/fence_heuristics_ping.py +++ b/fence/agents/heuristics_ping/fence_heuristics_ping.py @@ -1,198 +1,198 @@ #!@PYTHON@ -tt # The Following Agent Has Been Tested On: # # RHEL 7.4 # import io import re import subprocess import shlex import sys, stat import logging import os import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import fail_usage, run_command, fence_action, all_opt from fencing import atexit_handler, check_input, process_input, show_docs from fencing import run_delay def ping_test(con, options): # Send pings to the targets if options["--action"] == "on": # we want unfencing to always succeed return True if not "--ping-targets" in options or options["--ping-targets"] == "": # "off" was requested so fake "on" to provoke failure logging.error("ping target required") return False timeout = int(options["--ping-timeout"]) count = int(options["--ping-count"]) interval = int(options["--ping-interval"]) good_required = int(options["--ping-good-count"]) maxfail = int(options["--ping-maxfail"]) targets = options["--ping-targets"].split(",") exitcode = True p = {} failcount = 0 # search string for parsing the results of the ping-executable - packet_count = re.compile(r".*transmitted, ([0-9]*) received.*") + packet_count = re.compile(r".*transmitted, ([0-9]*)( packets)? received.*") # start a ping-process per target for target in targets: ping_path = '@PING_CMD@' target_mangled = target if target.startswith('inet6:'): if '@PING6_CMD@' == '': p[target] = None continue ping_path = '@PING6_CMD@' target_mangled = target.split(':',2)[1] elif target.startswith('inet:'): ping_path = '@PING4_CMD@' target_mangled = target.split(':',2)[1] ping_cmd = "%s -n -q -W %d -c %d -i %d %s" % ( ping_path, timeout, count, interval, target_mangled) logging.info("Running command: %s", ping_cmd) try: p[target] = subprocess.Popen(shlex.split(ping_cmd), stdout=subprocess.PIPE); except OSError: p[target] = None # collect the results of the ping-processes for target in targets: good = 0 if p[target] != None: p[target].wait() if p[target].returncode == 0: for line in p[target].stdout: searchres = packet_count.search(line.decode()) if searchres: good = int(searchres.group(1)) break if good >= good_required: logging.info("ping target %s received %d of %d" \ % (target, good, count)) continue failcount += 1 logging.info("ping target %s received %d of %d and thus failed" % (target, good, count)) else: failcount += 1 logging.error("ping target %s failed on OS level" % target) if failcount > maxfail: exitcode = False return exitcode def define_new_opts(): all_opt["ping_count"] = { "getopt" : ":", "longopt" : "ping-count", "required" : "0", "help" : "--ping-count=[number] Number of ping-probes to send", "shortdesc" : "The number of ping-probes that is being sent per target", "default" : "10", "order" : 1 } all_opt["ping_good_count"] = { "getopt" : ":", "longopt" : "ping-good-count", "required" : "0", "help" : "--ping-good-count=[number] Number of positive ping-probes required", "shortdesc" : "The number of positive ping-probes required to account a target as available", "default" : "8", "order" : 1 } all_opt["ping_interval"] = { "getopt" : ":", "longopt" : "ping-interval", "required" : "0", "help" : "--ping-interval=[seconds] Seconds between ping-probes", "shortdesc" : "The interval in seconds between ping-probes", "default" : "1", "order" : 1 } all_opt["ping_timeout"] = { "getopt" : ":", "longopt" : "ping-timeout", "required" : "0", "help" : "--ping-timeout=[seconds] Timeout for individual ping-probes", "shortdesc" : "The timeout in seconds till an individual ping-probe is accounted as lost", "default" : "2", "order" : 1 } all_opt["ping_maxfail"] = { "getopt" : ":", "longopt" : "ping-maxfail", "required" : "0", "help" : "--ping-maxfail=[number] Number of failed ping-targets allowed", "shortdesc" : "The number of failed ping-targets to still account as overall success", "default" : "0", "order" : 1 } all_opt["ping_targets"] = { "getopt" : ":", "longopt" : "ping-targets", "required" : "1", "help" : "--ping-targets=tgt1,[inet6:]tgt2 Comma separated list of ping-targets", "shortdesc" : "A comma separated list of ping-targets (optionally prepended by 'inet:' or 'inet6:') to be probed", "default" : "", "order" : 1 } def main(): device_opt = ["no_status", "no_password", "ping_count", "ping_good_count", "ping_interval", "ping_timeout", "ping_maxfail", "ping_targets", "method"] define_new_opts() atexit.register(atexit_handler) all_opt["method"]["default"] = "cycle" all_opt["method"]["help"] = "-m, --method=[method] Method to fence (cycle|onoff) (Default: cycle)" options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for ping-heuristic based fencing" docs["longdesc"] = "fence_heuristics_ping uses ping-heuristics to control execution of another fence agent on the same fencing level.\ \n.P\n\ This is not a fence agent by itself! \ Its only purpose is to enable/disable another fence agent that lives on the same fencing level but after fence_heuristics_ping." docs["vendorurl"] = "" show_docs(options, docs) # move ping-test to the end of the time-window set via --delay # as to give the network time to settle after the incident that has # caused fencing and have the results as current as possible max_pingcheck = (int(options["--ping-count"]) - 1) * \ int(options["--ping-interval"]) + int(options["--ping-timeout"]) run_delay(options, reserve=max_pingcheck) result = fence_action(\ None, \ options, \ None, \ None, \ reboot_cycle_fn = ping_test, sync_set_power_fn = ping_test) # execute the remaining delay run_delay(options, result=result) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/kdump/fence_kdump.c b/fence/agents/kdump/fence_kdump.c index c3bcf084..e2f3cd80 100644 --- a/fence/agents/kdump/fence_kdump.c +++ b/fence/agents/kdump/fence_kdump.c @@ -1,565 +1,566 @@ /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- * * Copyright (c) Ryan O'Hara (rohara@redhat.com) * Copyright (c) Red Hat, Inc. * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include "options.h" #include "message.h" #include "version.h" static int verbose = 0; #define log_debug(lvl, fmt, args...) \ do { \ if (lvl <= verbose) { \ fprintf (stdout, "[debug]: " fmt, ##args); \ syslog (LOG_INFO, fmt, ##args); \ } \ } while (0); #define log_error(lvl, fmt, args...) \ do { \ if (lvl <= verbose) { \ fprintf (stderr, "[error]: " fmt, ##args); \ syslog (LOG_ERR, fmt, ##args); \ } \ } while (0); static int trim (char *str) { char *p; int len; if (!str) return (0); len = strlen (str); while (len--) { if (isspace (str[len])) { str[len] = 0; } else { break; } } for (p = str; *p && isspace (*p); p++); memmove (str, p, strlen (p) + 1); return (strlen (str)); } static int read_message (const fence_kdump_node_t *node, void *msg, int len) { int error; char addr[NI_MAXHOST]; char port[NI_MAXSERV]; struct sockaddr_storage ss; socklen_t size = sizeof (ss); error = recvfrom (node->socket, msg, len, 0, (struct sockaddr *) &ss, &size); if (error < 0) { log_error (2, "recvfrom (%s)\n", strerror (errno)); goto out; } error = getnameinfo ((struct sockaddr *) &ss, size, addr, sizeof (addr), port, sizeof (port), NI_NUMERICHOST | NI_NUMERICSERV); if (error != 0) { log_error (2, "getnameinfo (%s)\n", gai_strerror (error)); goto out; } error = strcasecmp (node->addr, addr); if (error != 0) { log_debug (1, "discard message from '%s'\n", addr); } out: return (error); } static int do_action_monitor (void) { const char cmdline_path[] = "/proc/cmdline"; FILE *procFile; size_t sz = 0; char *lines = NULL; int result = 1; procFile = fopen(cmdline_path, "r"); if (procFile == NULL) { log_error (0, "Unable to open file %s (%s)\n", cmdline_path, strerror (errno)); return 1; } while (!feof (procFile)) { ssize_t rv = getline (&lines, &sz, procFile); if ((rv != -1) && (strstr(lines, "crashkernel=") != NULL)) { result = 0; } } free (lines); fclose (procFile); return result; } static int do_action_off (const fence_kdump_opts_t *opts) { int error; fd_set rfds; fence_kdump_msg_t msg; fence_kdump_node_t *node; struct timeval timeout; if (list_empty (&opts->nodes)) { return (1); } else { node = list_first_entry (&opts->nodes, fence_kdump_node_t, list); } timeout.tv_sec = opts->timeout; timeout.tv_usec = 0; FD_ZERO (&rfds); FD_SET (node->socket, &rfds); log_debug (0, "waiting for message from '%s'\n", node->addr); for (;;) { error = select (node->socket + 1, &rfds, NULL, NULL, &timeout); if (error < 0) { log_error (2, "select (%s)\n", strerror (errno)); break; } if (error == 0) { log_debug (0, "timeout after %d seconds\n", opts->timeout); break; } if (read_message (node, &msg, sizeof (msg)) != 0) { continue; } if (msg.magic != FENCE_KDUMP_MAGIC) { log_debug (1, "invalid magic number '0x%X'\n", msg.magic); continue; } switch (msg.version) { case FENCE_KDUMP_MSGV1: log_debug (0, "received valid message from '%s'\n", node->addr); return (0); default: log_debug (1, "invalid message version '0x%X'\n", msg.version); continue; } } return (1); } static int do_action_metadata (const char *self) { fprintf (stdout, "\n"); fprintf (stdout, "\n"); fprintf (stdout, ""); fprintf (stdout, "\\fIfence_kdump\\fP is an I/O fencing agent to be used with the kdump\n" "crash recovery service. When the \\fIfence_kdump\\fP agent is invoked,\n" "it will listen for a message from the failed node that acknowledges\n" "that the failed node it executing the kdump crash kernel.\n" "Note that \\fIfence_kdump\\fP is not a replacement for traditional\n" "fencing methods. The \\fIfence_kdump\\fP agent can only detect that a\n" "node has entered the kdump crash recovery service. This allows the\n" "kdump crash recovery service complete without being preempted by\n" "traditional power fencing methods.\n\n" "Note: the \"off\" action listen for message from failed node that\n" "acknowledges node has entered kdump crash recovery service. If a valid\n" "message is received from the failed node, the node is considered to be\n" "fenced and the agent returns success. Failure to receive a valid\n" "message from the failed node in the given timeout period results in\n" "fencing failure."); fprintf (stdout, "\n"); fprintf (stdout, "http://www.kernel.org/pub/linux/utils/kernel/kexec/\n"); fprintf (stdout, "\n"); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t%s\n", "Name or IP address of node to be fenced. This option is required for\n" "the \"off\" action."); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t%s\n", "IP port number that the \\fIfence_kdump\\fP agent will use to listen for\n" "messages."); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t%s\n", "IP network family. Force the \\fIfence_kdump\\fP agent to use a specific\n" "family. The value for \\fIFAMILY\\fP can be \"auto\", \"ipv4\", or\n" "\"ipv6\"."); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t%s\n", "Fencing action to perform. The value for \\fIACTION\\fP can be either\n" "\"off\" or \"metadata\"."); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t%s\n", "Number of seconds to wait for message from failed node. If no message\n" "is received within \\fITIMEOUT\\fP seconds, the \\fIfence_kdump\\fP agent\n" "returns failure."); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t%s\n", "Print verbose output"); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t%s\n", "Print version"); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t\n"); fprintf (stdout, "\t\t%s\n", "Print usage"); fprintf (stdout, "\t\n"); fprintf (stdout, "\n"); fprintf (stdout, "\n"); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\n"); fprintf (stdout, "\t\n"); fprintf (stdout, "\n"); fprintf (stdout, "\n"); return (0); } static void print_usage (const char *self) { fprintf (stdout, "Usage: %s [options]\n", basename (self)); fprintf (stdout, "\n"); fprintf (stdout, "Options:\n"); fprintf (stdout, "\n"); fprintf (stdout, "%s\n", " -n, --nodename=NODE Name or IP address of node to be fenced"); fprintf (stdout, "%s\n", " -p, --ipport=PORT IP port number (default: 7410)"); fprintf (stdout, "%s\n", " -f, --family=FAMILY Network family: ([auto], ipv4, ipv6)"); fprintf (stdout, "%s\n", " -o, --action=ACTION Fencing action: ([off], monitor, metadata)"); fprintf (stdout, "%s\n", " -t, --timeout=TIMEOUT Timeout in seconds (default: 60)"); fprintf (stdout, "%s\n", " -v, --verbose Print verbose output"); fprintf (stdout, "%s\n", " -V, --version Print version"); fprintf (stdout, "%s\n", " -h, --help Print usage"); fprintf (stdout, "\n"); return; } static int get_options_node (fence_kdump_opts_t *opts) { int error; struct addrinfo hints; fence_kdump_node_t *node; node = malloc (sizeof (fence_kdump_node_t)); if (!node) { log_error (2, "malloc (%s)\n", strerror (errno)); return (1); } memset (node, 0, sizeof (fence_kdump_node_t)); memset (&hints, 0, sizeof (hints)); hints.ai_family = opts->family; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = AI_NUMERICSERV; strncpy (node->name, opts->nodename, sizeof (node->name)); snprintf (node->port, sizeof (node->port), "%d", opts->ipport); node->info = NULL; error = getaddrinfo (node->name, node->port, &hints, &node->info); if (error != 0) { log_error (2, "getaddrinfo (%s)\n", gai_strerror (error)); free_node (node); return (1); } error = getnameinfo (node->info->ai_addr, node->info->ai_addrlen, node->addr, sizeof (node->addr), node->port, sizeof (node->port), NI_NUMERICHOST | NI_NUMERICSERV); if (error != 0) { log_error (2, "getnameinfo (%s)\n", gai_strerror (error)); free_node (node); return (1); } hints.ai_family = node->info->ai_family; hints.ai_flags |= AI_PASSIVE; freeaddrinfo (node->info); node->info = NULL; error = getaddrinfo (NULL, node->port, &hints, &node->info); if (error != 0) { log_error (2, "getaddrinfo (%s)\n", gai_strerror (error)); free_node (node); return (1); } node->socket = socket (node->info->ai_family, node->info->ai_socktype, node->info->ai_protocol); if (node->socket < 0) { log_error (2, "socket (%s)\n", strerror (errno)); free_node (node); return (1); } error = bind (node->socket, node->info->ai_addr, node->info->ai_addrlen); if (error != 0) { log_error (2, "bind (%s)\n", strerror (errno)); free_node (node); return (1); } list_add_tail (&node->list, &opts->nodes); return (0); } static void get_options (int argc, char **argv, fence_kdump_opts_t *opts) { int opt; struct option options[] = { { "nodename", required_argument, NULL, 'n' }, { "ipport", required_argument, NULL, 'p' }, { "family", required_argument, NULL, 'f' }, { "action", required_argument, NULL, 'o' }, { "timeout", required_argument, NULL, 't' }, { "verbose", optional_argument, NULL, 'v' }, { "version", no_argument, NULL, 'V' }, { "help", no_argument, NULL, 'h' }, { 0, 0, 0, 0 } }; while ((opt = getopt_long (argc, argv, "n:p:f:o:t:v::Vh", options, NULL)) != EOF) { switch (opt) { case 'n': set_option_nodename (opts, optarg); break; case 'p': set_option_ipport (opts, optarg); break; case 'f': set_option_family (opts, optarg); break; case 'o': set_option_action (opts, optarg); break; case 't': set_option_timeout (opts, optarg); break; case 'v': set_option_verbose (opts, optarg); break; case 'V': print_version (argv[0]); exit (0); case 'h': print_usage (argv[0]); exit (0); default: print_usage (argv[0]); exit (1); } } verbose = opts->verbose; return; } static void get_options_stdin (fence_kdump_opts_t *opts) { char buf[1024]; char *opt; char *arg; while (fgets (buf, sizeof (buf), stdin) != 0) { if (trim (buf) == 0) { continue; } if (buf[0] == '#') { continue; } opt = buf; if ((arg = strchr (opt, '=')) != 0) { *arg = 0; arg += 1; } else { continue; } if (!strcasecmp (opt, "nodename")) { set_option_nodename (opts, arg); continue; } if (!strcasecmp (opt, "ipport")) { set_option_ipport (opts, arg); continue; } if (!strcasecmp (opt, "family")) { set_option_family (opts, arg); continue; } if (!strcasecmp (opt, "action")) { set_option_action (opts, arg); continue; } if (!strcasecmp (opt, "timeout")) { set_option_timeout (opts, arg); continue; } if (!strcasecmp (opt, "verbose")) { set_option_verbose (opts, arg); continue; } } verbose = opts->verbose; return; } int main (int argc, char **argv) { int error = 1; fence_kdump_opts_t opts; init_options (&opts); if (argc > 1) { get_options (argc, argv, &opts); } else { get_options_stdin (&opts); } openlog ("fence_kdump", LOG_CONS|LOG_PID, LOG_DAEMON); if (opts.action == FENCE_KDUMP_ACTION_OFF) { if (opts.nodename == NULL) { log_error (0, "action 'off' requires nodename\n"); exit (1); } if (get_options_node (&opts) != 0) { log_error (0, "failed to get node '%s'\n", opts.nodename); exit (1); } } if (verbose != 0) { print_options (&opts); } switch (opts.action) { case FENCE_KDUMP_ACTION_OFF: error = do_action_off (&opts); break; case FENCE_KDUMP_ACTION_METADATA: error = do_action_metadata (argv[0]); break; case FENCE_KDUMP_ACTION_MONITOR: error = do_action_monitor (); break; default: break; } free_options (&opts); return (error); } diff --git a/fence/agents/kdump/fence_kdump_send.c b/fence/agents/kdump/fence_kdump_send.c index fab3d364..d668bed3 100644 --- a/fence/agents/kdump/fence_kdump_send.c +++ b/fence/agents/kdump/fence_kdump_send.c @@ -1,254 +1,255 @@ /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- * * Copyright (c) Ryan O'Hara (rohara@redhat.com) * Copyright (c) Red Hat, Inc. * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include "options.h" #include "message.h" #include "version.h" static int verbose = 0; #define log_debug(lvl, fmt, args...) \ do { \ if (lvl <= verbose) \ fprintf (stdout, "[debug]: " fmt, ##args); \ } while (0); #define log_error(lvl, fmt, args...) \ do { \ if (lvl <= verbose) \ fprintf (stderr, "[error]: " fmt, ##args); \ } while (0); static int send_message (const fence_kdump_node_t *node, void *msg, int len) { int error; error = sendto (node->socket, msg, len, 0, node->info->ai_addr, node->info->ai_addrlen); if (error < 0) { log_error (2, "sendto (%s)\n", strerror (errno)); goto out; } log_debug (1, "message sent to node '%s'\n", node->addr); out: return (error); } static void print_usage (const char *self) { fprintf (stdout, "Usage: %s [options] [nodes]\n", basename (self)); fprintf (stdout, "\n"); fprintf (stdout, "Options:\n"); fprintf (stdout, "\n"); fprintf (stdout, "%s\n", " -p, --ipport=PORT Port number (default: 7410)"); fprintf (stdout, "%s\n", " -f, --family=FAMILY Network family ([auto], ipv4, ipv6)"); fprintf (stdout, "%s\n", " -c, --count=COUNT Number of messages to send (default: 0)"); fprintf (stdout, "%s\n", " -i, --interval=INTERVAL Interval in seconds (default: 10)"); fprintf (stdout, "%s\n", " -v, --verbose Print verbose output"); fprintf (stdout, "%s\n", " -V, --version Print version"); fprintf (stdout, "%s\n", " -h, --help Print usage"); fprintf (stdout, "\n"); return; } static int get_options_node (fence_kdump_opts_t *opts) { int error; struct addrinfo hints; fence_kdump_node_t *node; node = malloc (sizeof (fence_kdump_node_t)); if (!node) { log_error (2, "malloc (%s)\n", strerror (errno)); return (1); } memset (node, 0, sizeof (fence_kdump_node_t)); memset (&hints, 0, sizeof (hints)); hints.ai_family = opts->family; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = AI_NUMERICSERV; strncpy (node->name, opts->nodename, sizeof (node->name)); snprintf (node->port, sizeof (node->port), "%d", opts->ipport); node->info = NULL; error = getaddrinfo (node->name, node->port, &hints, &node->info); if (error != 0) { log_error (2, "getaddrinfo (%s)\n", gai_strerror (error)); free_node (node); return (1); } error = getnameinfo (node->info->ai_addr, node->info->ai_addrlen, node->addr, sizeof (node->addr), node->port, sizeof (node->port), NI_NUMERICHOST | NI_NUMERICSERV); if (error != 0) { log_error (2, "getnameinfo (%s)\n", gai_strerror (error)); free_node (node); return (1); } node->socket = socket (node->info->ai_family, node->info->ai_socktype, node->info->ai_protocol); if (node->socket < 0) { log_error (2, "socket (%s)\n", strerror (errno)); free_node (node); return (1); } list_add_tail (&node->list, &opts->nodes); return (0); } static void get_options (int argc, char **argv, fence_kdump_opts_t *opts) { int opt; struct option options[] = { { "ipport", required_argument, NULL, 'p' }, { "family", required_argument, NULL, 'f' }, { "count", required_argument, NULL, 'c' }, { "interval", required_argument, NULL, 'i' }, { "verbose", optional_argument, NULL, 'v' }, { "version", no_argument, NULL, 'V' }, { "help", no_argument, NULL, 'h' }, { 0, 0, 0, 0 } }; while ((opt = getopt_long (argc, argv, "p:f:c:i:v::Vh", options, NULL)) != EOF) { switch (opt) { case 'p': set_option_ipport (opts, optarg); break; case 'f': set_option_family (opts, optarg); break; case 'c': set_option_count (opts, optarg); break; case 'i': set_option_interval (opts, optarg); break; case 'v': set_option_verbose (opts, optarg); break; case 'V': print_version (argv[0]); exit (0); case 'h': print_usage (argv[0]); exit (0); default: print_usage (argv[0]); exit (1); } } verbose = opts->verbose; return; } int main (int argc, char **argv) { int count = 1; fence_kdump_msg_t msg; fence_kdump_opts_t opts; fence_kdump_node_t *node; init_options (&opts); if (argc > 1) { get_options (argc, argv, &opts); } else { print_usage (argv[0]); exit (1); } for (; optind < argc; optind++) { opts.nodename = argv[optind]; if (get_options_node (&opts) != 0) { log_error (1, "failed to get node '%s'\n", opts.nodename); } opts.nodename = NULL; } if (list_empty (&opts.nodes)) { print_usage (argv[0]); exit (1); } if (verbose != 0) { print_options (&opts); } init_message (&msg); for (;;) { list_for_each_entry (node, &opts.nodes, list) { send_message (node, &msg, sizeof (msg)); } if ((opts.count != 0) && (++count > opts.count)) { break; } sleep (opts.interval); } free_options (&opts); return (0); } diff --git a/make/agentccheck.mk b/make/agentccheck.mk index c4f47538..e9f08311 100644 --- a/make/agentccheck.mk +++ b/make/agentccheck.mk @@ -1,23 +1,23 @@ DATADIR:=$(abs_top_srcdir)/tests/data/metadata check: $(TARGET:%=%.xml-check) $(SYMTARGET:%=%.xml-check) xml-upload: $(TEST_TARGET:%=%.xml-upload) $(SYMTARGET:%=%.xml-upload) xml-check.%: % $(eval INPUT=$(subst xml-check.,,$@)) $(eval TEMPFILE = $(shell mktemp)) ./$(INPUT) -o metadata > $(TEMPFILE) diff $(TEMPFILE) $(DATADIR)/$(INPUT).xml rm $(TEMPFILE) xml-upload.%: % $(eval INPUT=$(subst xml-upload.,,$@)) ./$(INPUT) -o metadata > $(DATADIR)/$(INPUT).xml # If test will fail, rerun fence agents to show problems delay-check.%: % $(eval INPUT=$(subst delay-check.,,$@)) - test `/usr/bin/time -f "%e" ./$(INPUT) --delay 10 $(FENCE_TEST_ARGS) -- 2>&1 |\ - sed 's/\.//' | tail -n 1` -ge 1000 || \ - /usr/bin/time -f "%e" ./$(INPUT) --delay 0 $(FENCE_TEST_ARGS) -- + test `/usr/bin/time -p ./$(INPUT) --delay 10 $(FENCE_TEST_ARGS) -- 2>&1 |\ + awk -F"[. ]" -vOFS= '/real/ {print $$2,$$3}' | tail -n 1` -ge 1000 || \ + /usr/bin/time -p ./$(INPUT) --delay 0 $(FENCE_TEST_ARGS) -- diff --git a/make/agentpycheck.mk b/make/agentpycheck.mk index c16a7ac0..8d05a758 100644 --- a/make/agentpycheck.mk +++ b/make/agentpycheck.mk @@ -1,42 +1,42 @@ DATADIR:=$(abs_top_srcdir)/tests/data/metadata AWK_VAL='BEGIN {store=-1} /name=\".*_path\"/ {store=2} {if (store!=0) {print}; store--}' TEST_TARGET=$(filter-out $(TEST_TARGET_SKIP),$(TARGET)) check: $(TEST_TARGET:%=%.xml-check) $(SYMTARGET:%=%.xml-check) $(TEST_TARGET:%=%.delay-check) $(TEST_TARGET:%=%.rng-check) xml-upload: $(TEST_TARGET:%=%.xml-upload) $(SYMTARGET:%=%.xml-upload) %.xml-check: % $(eval INPUT=$(subst .xml-check,,$(@F))) for x in $(INPUT) `PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib $(PYTHON) $(@D)/$(INPUT) -o metadata | grep symlink | sed -e "s/.*\(fence.*\)\" .*/\1/g"`; do \ TEMPFILE=$$(mktemp); \ PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib $(PYTHON) $(@D)/$$x -o metadata | $(AWK) $(AWK_VAL) > $$TEMPFILE && \ diff $$TEMPFILE $(DATADIR)/$$x.xml && \ rm $$TEMPFILE; \ done %.xml-upload: % $(eval INPUT=$(subst .xml-upload,,$(@F))) for x in $(INPUT) `PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib $(PYTHON) $(@D)/$(INPUT) -o metadata | grep symlink | sed -e "s/.*\(fence.*\)\" .*/\1/g"`; do \ PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib $(PYTHON) $(@D)/$$x -o metadata | $(AWK) $(AWK_VAL) > $(DATADIR)/$$x.xml; \ done # If test will fail, rerun fence agents to show problems %.delay-check: % $(eval INPUT=$(subst .delay-check,,$(@F))) for x in $(INPUT) `PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib $(PYTHON) $(@D)/$(INPUT) -o metadata | grep symlink | sed -e "s/.*\(fence.*\)\" .*/\1/g"`; do \ - test `PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib /usr/bin/time -f "%e" \ - sh -c "/bin/echo -e 'delay=10\n $(FENCE_TEST_ARGS)' | $(PYTHON) $(@D)/$$x" 2>&1 |\ - sed 's/\.//' | tail -n 1` -ge 1000 || ( \ - PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib /usr/bin/time -f "%e" \ - sh -c "/bin/echo -e 'delay=0\n $(FENCE_TEST_ARGS)' | $(PYTHON) $(@D)/$$x"; false ); \ + test `PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib /usr/bin/time -p \ + sh -c "printf 'delay=10\n $(FENCE_TEST_ARGS)' | $(PYTHON) $(@D)/$$x" 2>&1 |\ + awk -F"[. ]" -vOFS= '/real/ {print $$2,$$3}'` -ge 1000 || ( \ + PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib /usr/bin/time -p \ + sh -c "printf 'delay=0\n $(FENCE_TEST_ARGS)' | $(PYTHON) $(@D)/$$x"; false ); \ done %.rng-check: % $(eval INPUT=$(subst .rng-check,,$(@F))) for x in $(INPUT) `PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib $(PYTHON) $(@D)/$(INPUT) -o metadata | grep symlink | sed -e "s/.*\(fence.*\)\" .*/\1/g"`; do \ PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib $(PYTHON) $(@D)/$$x -o metadata | \ - /usr/bin/xsltproc ${abs_top_srcdir}/fence/agents/lib/fence2rng.xsl - | \ + xsltproc ${abs_top_srcdir}/fence/agents/lib/fence2rng.xsl - | \ sed -e 's/ rha:description=/ description=/g' -e 's/ rha:name=/ name=/g' | \ xmllint --nsclean --noout -; \ done diff --git a/make/fencebuild.mk b/make/fencebuild.mk index e6a4acd5..139cedf9 100644 --- a/make/fencebuild.mk +++ b/make/fencebuild.mk @@ -1,85 +1,85 @@ define gen_agent_from_py mkdir -p `dirname $@` cat $(abs_srcdir)/$@.py | \ sed \ -e 's#@''PYTHON@#${PYTHON}#g' \ -e 's#@''RELEASE_VERSION@#${VERSION}#g' \ -e 's#@''FENCEAGENTSLIBDIR@#${FENCEAGENTSLIBDIR}#g' \ -e 's#@''LOGDIR@#${LOGDIR}#g' \ -e 's#@''SBINDIR@#${sbindir}#g' \ -e 's#@''LIBEXECDIR@#${libexecdir}#g' \ -e 's#@''IPMITOOL_PATH@#${IPMITOOL_PATH}#g' \ -e 's#@''OPENSTACK_PATH@#${OPENSTACK_PATH}#g' \ -e 's#@''AMTTOOL_PATH@#${AMTTOOL_PATH}#g' \ -e 's#@''GNUTLSCLI_PATH@#${GNUTLSCLI_PATH}#g' \ -e 's#@''COROSYNC_CMAPCTL_PATH@#${COROSYNC_CMAPCTL_PATH}#g' \ -e 's#@''SG_PERSIST_PATH@#${SG_PERSIST_PATH}#g' \ -e 's#@''SG_TURS_PATH@#${SG_TURS_PATH}#g' \ -e 's#@''VGS_PATH@#${VGS_PATH}#g' \ -e 's#@''SUDO_PATH@#${SUDO_PATH}#g' \ -e 's#@''SSH_PATH@#${SSH_PATH}#g' \ -e 's#@''TELNET_PATH@#${TELNET_PATH}#g' \ -e 's#@''MPATH_PATH@#${MPATH_PATH}#g' \ -e 's#@''SBD_PATH@#${SBD_PATH}#g' \ -e 's#@''STORE_PATH@#${CLUSTERVARRUN}#g' \ -e 's#@''SUDO_PATH@#${SUDO_PATH}#g' \ -e 's#@''SNMPWALK_PATH@#${SNMPWALK_PATH}#g' \ -e 's#@''SNMPSET_PATH@#${SNMPSET_PATH}#g' \ -e 's#@''SNMPGET_PATH@#${SNMPGET_PATH}#g' \ -e 's#@''NOVA_PATH@#${NOVA_PATH}#g' \ -e 's#@''POWERMAN_PATH@#${POWERMAN_PATH}#g' \ -e 's#@''PING_CMD@#${PING_CMD}#g' \ -e 's#@''PING6_CMD@#${PING6_CMD}#g' \ -e 's#@''PING4_CMD@#${PING4_CMD}#g' \ > $@ - if [ 0 -eq `echo "$(@)" | grep fence_ &> /dev/null; echo $$?` ]; then \ + if [ 0 -eq `echo "$(@)" | grep fence_ 2>&1 /dev/null; echo $$?` ]; then \ PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib $(PYTHON) $(top_srcdir)/fence/agents/lib/check_used_options.py $@; \ else true ; fi for x in `PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib $(PYTHON) $(@) -o metadata | grep symlink | sed -e "s/.*\(fence.*\)\" .*/\1/g"`; do \ cp -f $(@) $(@D)/$$x; \ $(MAKE) $(@D)/$$x.8; \ done endef # dependency, one on one $(foreach t,$(TARGET),$(eval $(t) : $(t:=.py))) # rule $(TARGET): $(call gen_agent_from_py) clean: clean-man rm -f $(CLEAN_TARGET:%.8=%) $(CLEAN_TARGET_ADDITIONAL) $(scsidata_SCRIPTS) */*.pyc */*.wiki if [ "$(abs_builddir)" = "$(abs_top_builddir)/fence/agents/lib" ]; then \ rm -f $(TARGET); \ fi clean-local: clean install-exec-hook: $(TARGET) if [ -n "$(man8dir)" ]; then \ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \ fi for p in $(TARGET); do \ dir=`dirname $$p`; \ for x in `PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib $(PYTHON) $$p -o metadata | grep symlink | sed -e "s/.*\(fence.*\)\" .*/\1/g"`; do \ echo " $(INSTALL_SCRIPT) $$dir/$$x '$(DESTDIR)$(sbindir)'"; \ $(INSTALL_SCRIPT) $$dir/$$x "$(DESTDIR)$(sbindir)" || exit $$?; \ echo " $(INSTALL_DATA) '$$dir/$$x.8' '$(DESTDIR)$(man8dir)'"; \ $(INSTALL_DATA) "$$dir/$$x.8" "$(DESTDIR)$(man8dir)" || exit $$?; \ done; \ done uninstall-hook: $(TARGET) files=`for p in $(TARGET); do \ for x in \`PYTHONPATH=$(abs_srcdir)/lib:$(abs_builddir)/lib $(PYTHON) $$p -o metadata | grep symlink | sed -e "s/.*\(fence.*\)\" .*/\1/g"\`; do \ echo " rm -f '$(DESTDIR)$(sbindir)/$$x'"; \ rm -f "$(DESTDIR)$(sbindir)/$$x"; \ echo " rm -f '$(DESTDIR)$(man8dir)/$$x.8'"; \ rm -f "$(DESTDIR)$(man8dir)/$$x.8"; \ done; \ done` diff --git a/tests/fence_testing.py b/tests/fence_testing.py index 524a56a6..14cfe0cc 100755 --- a/tests/fence_testing.py +++ b/tests/fence_testing.py @@ -1,126 +1,126 @@ """ Library for fence agents testing via predefined scenarios """ from configobj import ConfigObj import re, sys, os EC_CONFIG_FAIL = 1 def _prepare_command(agent_file, method): """ Parse configuration of fence device and prepare (command + STDIN values) to execute. Fence device configuration is used to generate a command which can be executed. Because fence agents supports several options how to enter data, we can select from three different methods ("stdin", "getopt" - short options, "longopt"). When method "stdin" is used then this function will generate also text which should be entered on STDIN instead of command itself. Example of agent definition: name = "Dummy fence device configuration" agent = "/bin/true" [options] login = [ "foo", "--username", "-l" ] passwd = [ "bar", "--password", "-p" ] ipaddr = [ "fence.example.com", "--ip", "-a" ] port = [ "1", "--plug" ] """ assert (method in ["stdin", "getopt", "longopt"]), "Invalid method entered" config = ConfigObj(agent_file, unrepr = True) assert ("agent" in config), "Fence agent has to be defined" final_command = config["agent"] stdin_values = None for opt in list(config["options"].keys()): assert isinstance(config["options"][opt], list), "Option %s have to have at least value and longopt"% (opt) assert len(config["options"][opt]) >= 2, "Option %s have to have at least value and longopt"% (opt) value = config["options"][opt][0] if opt == "action": ## ignore action as it is not part of fence device definition continue if method == "stdin": option = opt if stdin_values == None: stdin_values = "" stdin_values += option + "=" + value + "\n" elif method == "longopt": option = config["options"][opt][1] final_command += " " + option + " " + value elif method == "getopt": if len(config["options"][opt]) == (2 + 1): option = config["options"][opt][2] else: option = config["options"][opt][1] final_command += " " + option + " " + value return (final_command, stdin_values) def test_action(agent, action_file, method, verbose = False): """ Run defined sequence of actions on a given fence agent. This function will run one set of test on a fence agent. Test itself consists of sequence of action and expected return codes. User can select from actions supported by fence agent (on, off, reboot, list, status, monitor) and sleep(X) command where X is in seconds and determine the length of pause between commands. Each action has to have defined regular expression which define acceptable return codes from fence agent or sleep. Example of action configuration file: name = "Simple Status" actions = [ { "command" : "status", "return_code" : "^[02]$" }, { "command" : "sleep(1)", "return_code" : "^0$" } ] """ re_sleep_command = re.compile('sleep\(([0-9]+)\)', re.IGNORECASE) config = ConfigObj(action_file, unrepr = True) (command, stdin_options) = _prepare_command(agent, method) for action in config["actions"]: assert "command" in action, "Action %s need to have defined 'command'"% (action_file) assert "return_code" in action, "Command %s (in %s) need to have 'return_code' defined"% (action_file, action["command"]) sleep_wait = None current_command = None current_stdin_options = None if not (action["command"] in [ "status", "reboot", "on", "off", "list", "monitor" ]): is_sleep = re.search(re_sleep_command, action["command"]) if is_sleep != None: sleep_wait = is_sleep.group(1) else: sys.stderr.write("ERROR: %s contains unsupported action \"%s\"\n"% (action_file, action["command"])) sys.exit(1) if sleep_wait != None: current_command = "/bin/sleep " + sleep_wait current_stdin_options = None else: current_command = command current_stdin_options = stdin_options if method == "stdin": if current_stdin_options == None: current_stdin_options = "" current_stdin_options += "action=%s"% (action["command"]) elif method == "longopt": current_command += " --action=%s"% (action["command"]) elif method == "getopt": current_command += " -o %s"% (action["command"]) # @note: Broken pipe can occur here and I'm not sure why - non-deterministic if method == "stdin" and sleep_wait == None: - current_command = "/bin/echo -e \"" + current_stdin_options + "\" | " + current_command + current_command = "printf \"" + current_stdin_options + "\" | " + current_command if verbose == False: result = os.system(current_command + " &> /dev/null") else: print(current_command) result = os.system(current_command) exitcode = (result >> 8) & 0xFF is_valid_result_code = re.search(action["return_code"], str(exitcode), re.IGNORECASE) if is_valid_result_code == None: print(("TEST FAILED: %s failed on %s when using (%s)\n"% (agent, action_file, method))) print(("TEST INFO: %s returns %s\n"% (action["command"], str(exitcode)))) return print(("TEST PASSED: %s worked on %s (%s)\n"% (agent, action_file, method)))