diff --git a/agents/sbd/fence_sbd.py b/agents/sbd/fence_sbd.py index bf95bb72..bebc7fae 100644 --- a/agents/sbd/fence_sbd.py +++ b/agents/sbd/fence_sbd.py @@ -1,435 +1,468 @@ #!@PYTHON@ -tt import sys, stat import logging import os import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import fail_usage, run_commands, fence_action, all_opt from fencing import atexit_handler, check_input, process_input, show_docs from fencing import run_delay import itertools DEVICE_INIT = 1 DEVICE_NOT_INIT = -3 PATH_NOT_EXISTS = -1 PATH_NOT_BLOCK = -2 +SBD_PID_FILE = "@SBDPID_PATH@" def is_block_device(filename): """Checks if a given path is a valid block device Key arguments: filename -- the file to check Return codes: True if it's a valid block device False, otherwise """ try: mode = os.lstat(filename).st_mode except OSError: return False else: return stat.S_ISBLK(mode) def is_link(filename): """Checks if a given path is a link. Key arguments: filename -- the file to check Return codes: True if it's a link False, otherwise """ try: mode = os.lstat(filename).st_mode except OSError: return False else: return stat.S_ISLNK(mode) def check_sbd_device(options, device_path): """checks that a given sbd device exists and is initialized Key arguments: options -- options dictionary device_path -- device path to check Return Codes: 1 / DEVICE_INIT if the device exists and is initialized -1 / PATH_NOT_EXISTS if the path does not exists -2 / PATH_NOT_BLOCK if the path exists but is not a valid block device -3 / DEVICE_NOT_INIT if the sbd device is not initialized """ # First of all we need to check if the device is valid if not os.path.exists(device_path): return PATH_NOT_EXISTS # We need to check if device path is a symbolic link. If so we resolve that # link. if is_link(device_path): link_target = os.readlink(device_path) device_path = os.path.join(os.path.dirname(device_path), link_target) # As second step we make sure it's a valid block device if not is_block_device(device_path): return PATH_NOT_BLOCK cmd = "%s -d %s dump" % (options["--sbd-path"], device_path) (return_code, out, err) = run_commands(options, [ cmd ]) for line in itertools.chain(out.split("\n"), err.split("\n")): if len(line) == 0: continue # If we read "NOT dumped" something went wrong, e.g. the device is not # initialized. if "NOT dumped" in line: return DEVICE_NOT_INIT return DEVICE_INIT def generate_sbd_command(options, command, arguments=None): """Generates a sbd command based on given arguments. Return Value: generated list of sbd commands (strings) depending on command multiple commands with a device each or a single command with multiple devices """ cmds = [] if not command in ["list", "dump"]: cmd = options["--sbd-path"] # add "-d" for each sbd device for device in parse_sbd_devices(options): cmd += " -d %s" % device cmd += " %s %s" % (command, arguments) cmds.append(cmd) else: for device in parse_sbd_devices(options): cmd = options["--sbd-path"] cmd += " -d %s" % device cmd += " %s %s" % (command, arguments) cmds.append(cmd) return cmds def send_sbd_message(conn, options, plug, message): """Sends a message to all sbd devices. Key arguments: conn -- connection structure options -- options dictionary plug -- plug to sent the message to message -- message to send Return Value: (return_code, out, err) Tuple containing the error code, """ del conn arguments = "%s %s" % (plug, message) cmd = generate_sbd_command(options, "message", arguments) (return_code, out, err) = run_commands(options, cmd) return (return_code, out, err) def get_msg_timeout(options): """Reads the configured sbd message timeout from each device. Key arguments: options -- options dictionary Return Value: msg_timeout (integer, seconds) """ # get the defined msg_timeout msg_timeout = -1 # default sbd msg timeout cmd = generate_sbd_command(options, "dump") (return_code, out, err) = run_commands(options, cmd) for line in itertools.chain(out.split("\n"), err.split("\n")): if len(line) == 0: continue if "msgwait" in line: tmp_msg_timeout = int(line.split(':')[1]) if -1 != msg_timeout and tmp_msg_timeout != msg_timeout: logging.warning(\ "sbd message timeouts differ in different devices") # we only save the highest timeout if tmp_msg_timeout > msg_timeout: msg_timeout = tmp_msg_timeout return msg_timeout def set_power_status(conn, options): """send status to sbd device (poison pill) Key arguments: conn -- connection structure options -- options dictionary Return Value: return_code -- action result (bool) """ target_status = options["--action"] plug = options["--plug"] return_code = 99 out = "" err = "" # Map fencing actions to sbd messages if "on" == target_status: (return_code, out, err) = send_sbd_message(conn, options, plug, "clear") elif "off" == target_status: (return_code, out, err) = send_sbd_message(conn, options, plug, "off") elif "reboot" == target_status: (return_code, out, err) = send_sbd_message(conn, options, plug, "reset") if 0 != return_code: logging.error("sending message to sbd device(s) \ failed with return code %d", return_code) logging.error("DETAIL: output on stdout was \"%s\"", out) logging.error("DETAIL: output on stderr was \"%s\"", err) return not bool(return_code) def reboot_cycle(conn, options): """" trigger reboot by sbd messages Key arguments: conn -- connection structure options -- options dictionary Return Value: return_code -- action result (bool) """ plug = options["--plug"] return_code = 99 out = "" err = "" (return_code, out, err) = send_sbd_message(conn, options, plug, "reset") return not bool(return_code) def get_power_status(conn, options): """Returns the status of a specific node. Key arguments: conn -- connection structure options -- option dictionary Return Value: status -- status code (string) """ status = "UNKWNOWN" plug = options["--plug"] nodelist = get_node_list(conn, options) # We need to check if the specified plug / node a already a allocated slot # on the device. if plug not in nodelist: logging.error("node \"%s\" not found in node list", plug) else: status = nodelist[plug][1] return status def translate_status(sbd_status): """Translates the sbd status to fencing status. Key arguments: sbd_status -- status to translate (string) Return Value: status -- fencing status (string) """ status = "UNKNOWN" # Currently we only accept "clear" to be marked as online. Eventually we # should also check against "test" online_status = ["clear"] offline_status = ["reset", "off"] if any(online_status_element in sbd_status \ for online_status_element in online_status): status = "on" if any(offline_status_element in sbd_status \ for offline_status_element in offline_status): status = "off" return status def get_node_list(conn, options): """Returns a list of hostnames, registerd on the sbd device. Key arguments: conn -- connection options options -- options Return Value: nodelist -- dictionary wich contains all node names and there status """ del conn nodelist = {} cmd = generate_sbd_command(options, "list") (return_code, out, err) = run_commands(options, cmd) for line in out.split("\n"): if len(line) == 0: continue # if we read "unreadable" something went wrong if "NOT dumped" in line: return nodelist words = line.split() port = words[1] sbd_status = words[2] nodelist[port] = (port, translate_status(sbd_status)) return nodelist def parse_sbd_devices(options): """Returns an array of all sbd devices. Key arguments: options -- options dictionary Return Value: devices -- array of device paths """ devices = [str.strip(dev) \ for dev in str.split(options["--devices"], ",")] return devices def define_new_opts(): """Defines the all opt list """ all_opt["devices"] = { "getopt" : ":", "longopt" : "devices", "help":"--devices=[device_a,device_b] \ Comma separated list of sbd devices", - "required" : "1", + "required" : "0", "shortdesc" : "SBD Device", "order": 1 } all_opt["sbd_path"] = { "getopt" : ":", "longopt" : "sbd-path", "help" : "--sbd-path=[path] Path to SBD binary", "required" : "0", "default" : "@SBD_PATH@", "order": 200 } + +def sbd_daemon_is_running(): + """Check if the sbd daemon is running + """ + if not os.path.exists(SBD_PID_FILE): + logging.info("SBD PID file %s does not exist", SBD_PID_FILE) + return False + + try: + with open(SBD_PID_FILE, "r") as pid_file: + pid = int(pid_file.read().strip()) + except Exception as e: + logging.error("Failed to read PID file %s: %s", SBD_PID_FILE, e) + return False + + try: + # send signal 0 to check if the process is running + os.kill(pid, 0) + except ProcessLookupError: + logging.info("SBD daemon is not running") + return False + except Exception as e: + logging.error("Failed to send signal 0 to PID %d: %s", pid, e) + return False + + return True + + def main(): """Main function """ # We need to define "no_password" otherwise we will be ask about it if # we don't provide any password. device_opt = ["no_password", "devices", "port", "method", "sbd_path"] # close stdout if we get interrupted atexit.register(atexit_handler) define_new_opts() all_opt["method"]["default"] = "cycle" all_opt["method"]["help"] = "-m, --method=[method] Method to fence (onoff|cycle) (Default: cycle)" all_opt["power_timeout"]["default"] = "30" options = check_input(device_opt, process_input(device_opt)) # fill the needed variables to generate metadata and help text output docs = {} docs["shortdesc"] = "Fence agent for sbd" docs["longdesc"] = "fence_sbd is an I/O Fencing agent \ which can be used in environments where sbd can be used (shared storage)." docs["vendorurl"] = "" show_docs(options, docs) - # We need to check if --devices is given and not empty. + # If not specified then read SBD_DEVICE from environment if "--devices" not in options: - fail_usage("No SBD devices specified. \ - At least one SBD device is required.") + dev_list = os.getenv("SBD_DEVICE") + if dev_list and sbd_daemon_is_running(): + options["--devices"] = ",".join(dev_list.split(";")) + else: + fail_usage("No SBD devices specified. \ + At least one SBD device is required.") run_delay(options) # We need to check if the provided sbd_devices exists. We need to do # that for every given device. # Just for the case we are really rebooting / powering off a device # (pacemaker as well uses the list command to generate a dynamic list) # we leave it to sbd to try and decide if it was successful if not options["--action"] in ["reboot", "off", "list"]: for device_path in parse_sbd_devices(options): logging.debug("check device \"%s\"", device_path) return_code = check_sbd_device(options, device_path) if PATH_NOT_EXISTS == return_code: logging.error("\"%s\" does not exist", device_path) elif PATH_NOT_BLOCK == return_code: logging.error("\"%s\" is not a valid block device", device_path) elif DEVICE_NOT_INIT == return_code: logging.error("\"%s\" is not initialized", device_path) elif DEVICE_INIT != return_code: logging.error("UNKNOWN error while checking \"%s\"", device_path) # If we get any error while checking the device we need to exit at this # point. if DEVICE_INIT != return_code: exit(return_code) # we check against the defined timeouts. If the pacemaker timeout is smaller # then that defined within sbd we should report this. power_timeout = int(options["--power-timeout"]) sbd_msg_timeout = get_msg_timeout(options) if 0 < power_timeout <= sbd_msg_timeout: logging.warning("power timeout needs to be \ greater then sbd message timeout") result = fence_action(\ None, \ options, \ set_power_status, \ get_power_status, \ get_node_list, \ reboot_cycle) sys.exit(result) if __name__ == "__main__": main() diff --git a/configure.ac b/configure.ac index 6b732241..0425a9d2 100644 --- a/configure.ac +++ b/configure.ac @@ -1,601 +1,603 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.63]) # TODO: port .gitarchiver AC_INIT([fence-agents], [m4_esyscmd([make/git-version-gen .tarball-version])], [developers@clusterlabs.org]) AC_CONFIG_AUX_DIR([.]) # Don't let AC_PROC_CC (invoked by AC_USE_SYSTEM_EXTENSIONS) replace # undefined CFLAGS with -g -O2, overriding our special OPT_CFLAGS. : ${CFLAGS=""} AC_USE_SYSTEM_EXTENSIONS AM_INIT_AUTOMAKE([1.13 dist-bzip2 dist-xz color-tests -Wno-portability subdir-objects]) # 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 # It is necessary to have this done before libtool does linker detection. # See also: https://github.com/kronosnet/kronosnet/issues/107 # --as-needed: Modern systems have builtin ceil() making -lm superfluous but # AC_SEARCH_LIBS can't detect this because it tests with a false prototype AX_CHECK_LINK_FLAG([-Wl,--enable-new-dtags], [AM_LDFLAGS=-Wl,--enable-new-dtags], [AC_MSG_ERROR(["Linker support for --enable-new-dtags is required"])]) AX_CHECK_LINK_FLAG([-Wl,--as-needed], [AM_LDFLAGS="$AM_LDFLAGS -Wl,--as-needed"]) LT_PREREQ([2.2.6]) LT_INIT AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([lib/fencing.py.py]) AC_CONFIG_HEADERS([make/config.h]) AC_CANONICAL_HOST AC_PROG_LIBTOOL AC_LANG([C]) # 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_GCC_TRADITIONAL AC_PROG_LN_S AC_PROG_INSTALL AC_PROG_MAKE_SET AC_PROG_AWK AC_PROG_CXX AC_PROG_YACC AC_PROG_LEX PKG_PROG_PKG_CONFIG ## 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="-Werror $@" AC_MSG_CHECKING([whether $CC supports "$@"]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]], [[ ]])], [RC=0; AC_MSG_RESULT([yes])], [RC=1; AC_MSG_RESULT([no])]) return $RC } # Checks for header files. AC_HEADER_DIRENT AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_HEADER_TIME AC_CHECK_HEADERS([arpa/inet.h fcntl.h malloc.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h syslog.h termios.h unistd.h libintl.h limits.h netdb.h stddef.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_INLINE AC_C_CONST AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_TYPE_UINT32_T AC_TYPE_OFF_T AC_TYPE_SIGNAL # Checks for library functions. AC_FUNC_FORK AC_FUNC_MALLOC AC_FUNC_CLOSEDIR_VOID AC_FUNC_MEMCMP AC_FUNC_SELECT_ARGTYPES AC_FUNC_STAT AC_CHECK_FUNCS([alarm atexit bzero dup2 memmove memset select socket strcasecmp strchr strdup strerror strtol gettimeofday]) # 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([agents], [ --with-agents=LIST list of agents to build/ship (default: all). ], [ AGENTS_LIST="$withval" ], [ AGENTS_LIST="all" ]) FENCETMPDIR=${localstatedir}/run/fence-agents AC_ARG_WITH(fencetmpdir, [ --with-fencetmpdir=DIR directory for fence agents state files [${FENCETMPDIR}]], [ FENCETMPDIR="$withval" ]) # Expand $prefix eval FENCETMPDIR="`eval echo ${FENCETMPDIR}`" AC_DEFINE_UNQUOTED(FENCETMPDIR,"$FENCETMPDIR", Where Fence agents keep state files) AC_SUBST(FENCETMPDIR) +SBDPID_PATH=${localstatedir}/run/sbd.pid +AC_SUBST(SBDPID_PATH) if test "x$AGENTS_LIST" = x; then AC_ERROR([No agents selected]) fi # PKG_CHECK_MODULES will fail if systemd is not found by default, so make sure # we set the proper vars and deal with it PKG_CHECK_MODULES([systemd], [systemd], [HAS_SYSTEMD=yes], [HAS_SYSTEMD=no]) if test "x$HAS_SYSTEMD" == "xyes"; then PKG_CHECK_VAR([SYSTEMD_TMPFILES_DIR], [systemd], [tmpfilesdir]) if test "x$SYSTEMD_TMPFILES_DIR" == "x"; then AC_MSG_ERROR([Unable to detect systemd tmpfiles directory automatically]) fi # sanitize systed vars when using non standard prefix if test "$prefix" != "/usr"; then SYSTEMD_TMPFILES_DIR="$prefix/$SYSTEMD_TMPFILES_DIR" AC_SUBST([SYSTEMD_TMPFILES_DIR]) fi fi AM_CONDITIONAL(HAVE_SYSTEMD, [test "x$HAS_SYSTEMD" == xyes ]) FENCE_KDUMP=0 if echo "$AGENTS_LIST" | grep -q -E "all|kdump"; then 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_MPATH=0 if echo "$AGENTS_LIST" | grep -q -E "all|mpath"; then FENCE_MPATH=1 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 "zvm( |$)"; then FENCE_ZVM=1 fi FENCE_VIRT=0 if echo "$AGENTS_LIST" | grep -q -E "all|(^| )virt( |$)"; then AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s/(^| )virt( |$)//") case "$host_os" in *bsd*) ;; *) FENCE_VIRT=1 VIRT_AM_LDFLAGS="$AM_LDFLAGS -fPIC -fPIE -Wl,-z,now" AC_SUBST([VIRT_AM_LDFLAGS]) VIRT_AM_CFLAGS="-fPIC -fPIE -I\$(top_srcdir)/agents/virt/include -D_GNU_SOURCE" AC_SUBST([VIRT_AM_CFLAGS]) VIRT_COMMON_LDFLAGS="-Wl,-wrap,syslog,-wrap,closelog" AC_SUBST([VIRT_COMMON_LDFLAGS]) VIRT_COMMON_LIBS="-Wl,-Bstatic -L\$(top_builddir)/agents/virt/common -lfence_virt -Wl,-Bdynamic" AC_SUBST([VIRT_COMMON_LIBS]) VIRT_CONFIG_LIBS="-L\$(top_builddir)/agents/virt/config -lsimpleconfig" AC_SUBST([VIRT_CONFIG_LIBS]) # Checks for libraries. AX_PTHREAD(,[AC_MSG_ERROR([POSIX threads support is required])]) PKG_CHECK_MODULES([nss], [nss]) PKG_CHECK_MODULES([xml2], [libxml-2.0]) PKG_CHECK_MODULES([uuid], [uuid]) saved_LIBS="$LIBS" LIBS= AC_SEARCH_LIBS([dlopen], [dl dld], , [AC_MSG_ERROR([dlopen not found])]) AC_SUBST([dl_LIBS], [$LIBS]) LIBS="$saved_LIBS" ;; esac fi if test "x$AGENTS_LIST" != xall; then for j in $AGENTS_LIST; do if ! test -f agents/$j/fence_$j*.py; then AC_ERROR([Agent $j does not exist]) 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/agents -mindepth 2 -maxdepth 2 -name 'fence_*.py' -print0 | xargs -0 | sed -E -e 's#[^ ]*/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 ## Program Paths PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin" export PATH AC_PATH_PROGS(XMLLINT, xmllint) AM_CONDITIONAL(BUILD_DOC, test "x$XMLLINT" != "x" ) if test "x$XMLLINT" = "x"; then AC_MSG_WARN([xmllint not installed, unable to (re-)build manual pages]) exit 1 fi AC_SUBST(XMLLINT) AC_PATH_PROGS(XSLTPROC, xsltproc) AM_CONDITIONAL(BUILD_DOC, test "x$XSLTPROC" != "x" ) if test "x$XSLTPROC" = "x"; then AC_MSG_WARN([xsltproc not installed, unable to (re-)build manual pages]) exit 1 fi AC_SUBST(XSLTPROC) AM_PATH_PYTHON([3.6]) if test -z "$PYTHON"; then echo "*** Essential program python not found" 1>&2 exit 1 fi dnl Ensure PYTHON is an absolute path AC_PATH_PROG([PYTHON], [$PYTHON]) AC_PYTHON_MODULE(pexpect, 1) AC_PYTHON_MODULE(pycurl, 1) AC_PYTHON_MODULE(requests, 1) AC_PYTHON_MODULE(boto3) AM_CONDITIONAL(HAVE_BOTO3, [test "x$HAVE_PYMOD_BOTO3" == xyes ]) if echo "$AGENTS_LIST" | grep -q amt_ws; then 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 -E "ovh|vmware_soap"; then 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(google.auth) 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], [/usr/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([LOGDIR]) AC_SUBST([CLUSTERVARRUN]) 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_MPATH, test $FENCE_MPATH -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_FENCE_VIRT, test $FENCE_VIRT -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]) ## fence-virt stuff if test "x$FENCE_VIRT" = "x1"; then sysconf=$(eval echo $sysconfdir) AC_DEFINE_UNQUOTED([SYSCONFDIR], ["$sysconf"], [Default config dir]) fi ### The following options only are used when $modules="yes" ### # libvirt plugin: Enabled by default AC_ARG_ENABLE(libvirt-plugin, [AS_HELP_STRING([--disable-libvirt-plugin], [Disable local-mode libvirt backend plugin])], [ modlibvirt=$enableval ], [ modlibvirt=yes ]) AM_CONDITIONAL([modlibvirt], [test "x$modlibvirt" == "xyes"]) if test "x$modlibvirt" == "xyes" && test "x$FENCE_VIRT" = "x1"; then PKG_CHECK_MODULES([virt], [libvirt]) fi # cpg plugin: Disabled by default AC_ARG_ENABLE(cpg-plugin, [AS_HELP_STRING([--disable-cpg-plugin], [Enable CPG/libvirt backend plugin])], [ modcpg=$enableval ], [ modcpg=yes ]) AM_CONDITIONAL([modcpg], [test "x$modcpg" == "xyes"]) if test "x$modcpg" == "xyes" && test "x$FENCE_VIRT" = "x1"; then PKG_CHECK_MODULES([cpg], [libcpg]) fi # multicast plugin: Enabled by default AC_ARG_ENABLE(multicast-plugin, [AS_HELP_STRING([--disable-multicast-plugin], [Disable multicast listener plugin])], [ modmulticast=$enableval ], [ modmulticast=yes ]) AM_CONDITIONAL([modmulticast], [test "x$modmulticast" == "xyes"]) # tcp plugin: Enabled by default AC_ARG_ENABLE(tcp-plugin, [AS_HELP_STRING([--disable-tcp-plugin], [Disable TCP listener plugin])], [ modtcp=$enableval ], [ modtcp=yes ]) AM_CONDITIONAL([modtcp], [test "x$modtcp" == "xyes"]) # serial/libvirt plugin: Enabled by default AC_ARG_ENABLE(serial-plugin, [AS_HELP_STRING([--disable-serial-plugin], [Disable serial listener plugin])], [ modserial=$enableval ], [ modserial=yes ]) AM_CONDITIONAL([modserial], [test "x$modserial" == "xyes"]) # vsock plugin: Enabled by default AC_ARG_ENABLE(vsock-plugin, [AS_HELP_STRING([--disable-vsock-plugin], [Disable TCP listener plugin])], [ modvsock=$enableval ], [ modvsock=yes ]) AM_CONDITIONAL([modvsock], [test "x$modvsock" == "xyes"]) # # Compatibility symlink: enabled by default # AC_ARG_ENABLE(xvm-compat, [AS_HELP_STRING([--disable-xvm-compat], [Disable fence_xvm symlink compatibility])], [ xvmcompat=$enableval ], [ xvmcompat=yes ]) AM_CONDITIONAL([xvmcompat], [test "x$xvmcompat" == "xyes"]) # Try to detect the appropriate conf dir. Several systems have both /etc/default # and /etc/sysconfig but latter is always primary. AC_ARG_VAR(initconfdir, [directory for initscripts configuration]) if test "x$initconfdir" = x && test "x$FENCE_VIRT" = "x1"; then AC_CHECK_FILE(/etc/conf.d, [initconfdir='$(sysconfdir)/conf.d}'], [# Gentoo/Arch AC_CHECK_FILE(/etc/sysconfig, [initconfdir='$(sysconfdir)/sysconfig'], [# RedHat/Fedora/Slax/Mandriva/S AC_CHECK_FILE(/etc/default, [initconfdir='$(sysconfdir)/default'], [# Debian/Ubuntu AC_MSG_ERROR([could not determine system initscripts config dir; please set initconfdir manually.])])])]) fi ## *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=" error 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 AM_CFLAGS="$ENV_CFLAGS $OPT_CFLAGS $GDB_FLAGS $EXTRA_WARNINGS" AC_SUBST([AM_CFLAGS]) CPPFLAGS="-I\$(top_builddir)/make -I\$(top_srcdir)/make -I. $ENV_CPPFLAGS" LDFLAGS="$ENV_LDFLAGS" AM_EXTRA_RECURSIVE_TARGETS([delay-check xml-check xml-upload test-fencing]) AX_PROG_DATE AS_IF([test "$ax_cv_prog_date_gnu_date:$ax_cv_prog_date_gnu_utc" = yes:yes], [UTC_DATE_AT="date -u -d@"], [AS_IF([test "x$ax_cv_prog_date_bsd_date" = xyes], [UTC_DATE_AT="date -u -r"], [AC_MSG_ERROR([date utility unable to convert epoch to UTC])])]) AC_SUBST([UTC_DATE_AT]) AC_ARG_VAR([SOURCE_EPOCH],[last modification date of the source]) AC_MSG_NOTICE([trying to determine source epoch]) AC_MSG_CHECKING([for source epoch in \$SOURCE_EPOCH]) AS_IF([test -n "$SOURCE_EPOCH"], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_MSG_CHECKING([for source epoch in source_epoch file]) AS_IF([test -e "$srcdir/source_epoch"], [read SOURCE_EPOCH <"$srcdir/source_epoch" AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_MSG_CHECKING([for source epoch baked in by gitattributes export-subst]) SOURCE_EPOCH='$Format:%at$' # template for rewriting by git-archive AS_CASE([$SOURCE_EPOCH], [?Format:*], # was not rewritten [AC_MSG_RESULT([no]) AC_MSG_CHECKING([for source epoch in \$SOURCE_DATE_EPOCH]) AS_IF([test "x$SOURCE_DATE_EPOCH" != x], [SOURCE_EPOCH="$SOURCE_DATE_EPOCH" AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_MSG_CHECKING([whether git log can provide a source epoch]) SOURCE_EPOCH=f${SOURCE_EPOCH#\$F} # convert into git log --pretty format SOURCE_EPOCH=$(cd "$srcdir" && git log -1 --pretty=${SOURCE_EPOCH%$} 2>/dev/null) AS_IF([test -n "$SOURCE_EPOCH"], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no, using current time and breaking reproducibility]) SOURCE_EPOCH=$(date +%s)])])], [AC_MSG_RESULT([yes])] )]) ]) AC_MSG_NOTICE([using source epoch $($UTC_DATE_AT$SOURCE_EPOCH +'%F %T %Z')]) if test "x$VERSION" = "xUNKNOWN"; then AC_MSG_ERROR([m4_text_wrap([ configure was unable to determine the source tree's current version. This generally happens when using git archive (or the github download button) generated tarball/zip file. In order to workaround this issue, either use git clone https://github.com/ClusterLabs/fence-agents.git or use an official release tarball. Alternatively you can add a compatible version in a .tarball-version file at the top of the source tree, wipe your autom4te.cache dir and generated configure, and rerun autogen.sh. ], [ ], [ ], [76])]) fi AC_CONFIG_FILES([Makefile fence-agents.pc agents/Makefile lib/Makefile lib/tests/Makefile doc/Makefile systemd/Makefile systemd/fence-agents.conf agents/virt/Makefile agents/virt/config/Makefile agents/virt/common/Makefile agents/virt/client/Makefile agents/virt/server/Makefile agents/virt/man/Makefile ]) AC_OUTPUT diff --git a/make/fencebuild.mk b/make/fencebuild.mk index 9a3c6d6d..bc925919 100644 --- a/make/fencebuild.mk +++ b/make/fencebuild.mk @@ -1,84 +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#@''FENCETMPDIR@#${FENCETMPDIR}#g' \ + -e 's#@''SBDPID_PATH@#${SBDPID_PATH}#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 2>&1; echo $$?` ]; then \ PYTHONPATH=$(abs_top_srcdir)/lib:$(abs_top_builddir)/lib $(PYTHON) $(top_srcdir)/lib/check_used_options.py $@; \ else true ; fi for x in `PYTHONPATH=$(abs_top_srcdir)/lib:$(abs_top_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): $(abs_top_builddir)/config.status $(call gen_agent_from_py) clean-local: clean-man rm -f $(CLEAN_TARGET:%.8=%) $(CLEAN_TARGET_ADDITIONAL) $(mpathdata_SCRIPTS) $(scsidata_SCRIPTS) */*.pyc *.pyc */*.wiki if [ "$(abs_builddir)" = "$(abs_top_builddir)/lib" ]; then \ rm -rf $(TARGET) __pycache__; \ fi 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_top_srcdir)/lib:$(abs_top_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_top_srcdir)/lib:$(abs_top_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/data/metadata/fence_sbd.xml b/tests/data/metadata/fence_sbd.xml index 82ded25b..c2daf0c5 100644 --- a/tests/data/metadata/fence_sbd.xml +++ b/tests/data/metadata/fence_sbd.xml @@ -1,135 +1,135 @@ fence_sbd is an I/O Fencing agent which can be used in environments where sbd can be used (shared storage). Fencing action - + SBD Device Method to fence Physical plug number on device, UUID or identification of machine Physical plug number on device, UUID or identification of machine Disable logging to stderr. Does not affect --verbose or --debug-file or logging to syslog. Verbose mode. Multiple -v flags can be stacked on the command line (e.g., -vvv) to increase verbosity. Level of debugging detail in output. Defaults to the number of --verbose flags specified on the command line, or to 1 if verbose=1 in a stonith device configuration (i.e., on stdin). Write debug information to given file Write debug information to given file Display version information and exit Display help and exit Separator for plug parameter when specifying more than 1 plug Separator for CSV created by 'list' operation Wait X seconds before fencing is started Disable timeout (true/false) (default: true when run from Pacemaker 2.0+) Wait X seconds for cmd prompt after login Test X seconds for status change after ON/OFF Wait X seconds after issuing ON/OFF Path to SBD binary Wait X seconds for cmd prompt after issuing command Sleep X seconds between status calls during a STONITH action Count of attempts to retry power on