diff --git a/configure.ac b/configure.ac index ce2ca466..42e8ffcf 100644 --- a/configure.ac +++ b/configure.ac @@ -1,318 +1,320 @@ # 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]) 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 if test "x$AGENTS_LIST" != xall; then for j in $AGENTS_LIST; do if ! test -d fence/agents/$j; then AC_ERROR([Agent $j does not exists]) fi done fi if test "x$AGENTS_LIST" = xall; then AGENTS_LIST=`find $srcdir/fence/agents -mindepth 2 -maxdepth 2 -name Makefile.am -printf '%h ' | sed -e 's#'$srcdir'/fence/agents/##g' -e 's#lib ##g' -e 's#nss_wrapper ##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 AC_PYTHON_MODULE(suds, 1) AC_PYTHON_MODULE(pexpect, 1) AC_PYTHON_MODULE(pycurl, 1) AC_PYTHON_MODULE(requests, 1) ## path to 3rd-party binaries AC_PATH_PROG([IPMITOOL_PATH], [ipmitool], [/usr/bin/ipmitool]) 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([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]) ## 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_XENAPILIB, test $XENAPILIB -eq 1) AC_SUBST([IPMITOOL_PATH]) AC_SUBST([AMTTOOL_PATH]) AC_SUBST([COROSYNC_CMAPCTL_PATH]) AC_SUBST([SG_PERSIST_PATH]) AC_SUBST([SG_TURS_PATH]) AC_SUBST([VGS_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" AC_CONFIG_FILES([Makefile fence/Makefile fence/agents/Makefile fence/agents/alom/Makefile fence/agents/apc/Makefile fence/agents/apc_snmp/Makefile fence/agents/amt/Makefile fence/agents/bladecenter/Makefile fence/agents/brocade/Makefile fence/agents/cisco_mds/Makefile fence/agents/cisco_ucs/Makefile + fence/agents/compute/Makefile fence/agents/docker/Makefile fence/agents/drac/Makefile fence/agents/drac5/Makefile fence/agents/dummy/Makefile fence/agents/eaton_snmp/Makefile fence/agents/emerson/Makefile fence/agents/eps/Makefile fence/agents/hpblade/Makefile fence/agents/ibmblade/Makefile fence/agents/ipdu/Makefile fence/agents/ifmib/Makefile fence/agents/ilo/Makefile fence/agents/ilo_moonshot/Makefile fence/agents/ilo_mp/Makefile fence/agents/ilo_ssh/Makefile fence/agents/intelmodular/Makefile fence/agents/ipmilan/Makefile fence/agents/kdump/Makefile fence/agents/ldom/Makefile fence/agents/lib/Makefile fence/agents/lpar/Makefile fence/agents/manual/Makefile fence/agents/mpath/Makefile fence/agents/netio/Makefile fence/agents/ovh/Makefile fence/agents/pve/Makefile fence/agents/raritan/Makefile fence/agents/rhevm/Makefile fence/agents/rsa/Makefile fence/agents/rsb/Makefile fence/agents/sanbox2/Makefile fence/agents/scsi/Makefile fence/agents/virsh/Makefile fence/agents/vmware/Makefile fence/agents/vmware_soap/Makefile fence/agents/wti/Makefile fence/agents/xenapi/Makefile fence/agents/hds_cb/Makefile fence/agents/zvm/Makefile doc/Makefile]) AC_OUTPUT diff --git a/fence/agents/compute/Makefile.am b/fence/agents/compute/Makefile.am new file mode 100644 index 00000000..ab212728 --- /dev/null +++ b/fence/agents/compute/Makefile.am @@ -0,0 +1,17 @@ +MAINTAINERCLEANFILES = Makefile.in + +TARGET = fence_compute + +SRC = $(TARGET).py + +EXTRA_DIST = $(SRC) + +sbin_SCRIPTS = $(TARGET) + +man_MANS = $(TARGET).8 + +FENCE_TEST_ARGS = -l test -p test -n 1 + +include $(top_srcdir)/make/fencebuild.mk +include $(top_srcdir)/make/fenceman.mk +include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/compute/fence_compute.py b/fence/agents/compute/fence_compute.py new file mode 100644 index 00000000..2b37de7c --- /dev/null +++ b/fence/agents/compute/fence_compute.py @@ -0,0 +1,218 @@ +#!/usr/bin/python -tt + +import sys +import time +import atexit +import logging + +sys.path.append("@FENCEAGENTSLIBDIR@") +from fencing import * +from fencing import fail_usage, is_executable, run_command, run_delay +from novaclient import client as nova_client + +#BEGIN_VERSION_GENERATION +RELEASE_VERSION="4.0.11" +BUILD_DATE="(built Wed Nov 12 06:33:38 EST 2014)" +REDHAT_COPYRIGHT="Copyright (C) Red Hat, Inc. 2004-2010 All rights reserved." +#END_VERSION_GENERATION + +override_status = "" +nova = None + +def get_power_status(_, options): + global override_status + + status = "unknown" + logging.debug("get action: " + options["--action"]) + + if len(override_status): + logging.debug("Pretending we're " + override_status) + return override_status + + if nova: + try: + services = nova.services.list(host=options["--plug"]) + except Exception, e: + fail_usage(str(e)) + + for service in services: + if service.binary == "nova-compute": + if service.state == "up": + status = "on" + elif service.state == "down": + status = "down" + else: + logging.debug("Unknown status detected from nova: " + service.state) + break + return status + +# NOTE(sbauza); We mimic the host-evacuate module since it's only a contrib +# module which is not stable +def _server_evacuate(server, on_shared_storage): + success = True + error_message = "" + try: + nova.servers.evacuate(server=server['uuid'], on_shared_storage=on_shared_storage) + except Exception as e: + success = False + error_message = "Error while evacuating instance: %s" % e + + return { + "server_uuid": server['uuid'], + "evacuate_accepted": success, + "error_message": error_message, + } + +def _host_evacuate(host, on_shared_storage): + hypervisors = nova.hypervisors.search(host, servers=True) + response = [] + for hyper in hypervisors: + if hasattr(hyper, 'servers'): + for server in hyper.servers: + response.append(_server_evacuate(server, on_shared_storage)) + +def set_power_status(_, options): + global override_status + + override_status = "" + logging.debug("set action: " + options["--action"]) + + if not nova: + return + + if options["--action"] == "on": + if get_power_status(_, options) == "on": + nova.services.enable(options["--plug"], 'nova-compute') + else: + # Pretend we're 'on' so that the fencing library doesn't loop forever waiting for the node to boot + override_status = "on" + return + + # need to wait for nova to update its internal status or we + # cannot call host-evacuate + while get_power_status(_, options) != "off": + # Loop forever if need be. + # + # Some callers (such as Pacemaker) will have a timer + # running and kill us if necessary + logging.debug("Waiting for nova to update it's internal state") + time.sleep(1) + + if "--no-shared-storage" not in options: + # If the admin sets this when they DO have shared + # storage in use, then they get what they asked for + on_shared_storage = True + else: + on_shared_storage = False + + _host_evacuate(options["--plug"], on_shared_storage) + return + +def get_plugs_list(_, options): + result = {} + + if nova: + hypervisors = nova.hypervisors.list() + for hypervisor in hypervisors: + longhost = hypervisor.hypervisor_hostname + if options["--action"] == "list" and options["--domain"] != "": + shorthost = longhost.replace("." + options["--domain"], + "") + result[shorthost] = ("", None) + else: + result[longhost] = ("", None) + return result + + +def define_new_opts(): + all_opt["tenant-name"] = { + "getopt" : "t:", + "longopt" : "tenant-name", + "help" : "-t, --tenant-name=[tenant] Keystone Admin Tenant", + "required" : "0", + "shortdesc" : "Keystone Admin Tenant", + "default" : "", + "order": 1, + } + all_opt["auth-url"] = { + "getopt" : "k:", + "longopt" : "auth-url", + "help" : "-k, --auth-url=[tenant] Keystone Admin Auth URL", + "required" : "0", + "shortdesc" : "Keystone Admin Auth URL", + "default" : "", + "order": 1, + } + all_opt["novatool-path"] = { + "getopt" : "i:", + "longopt" : "novatool-path", + "help" : "-i, --novatool-path=[path] Path to nova binary", + "required" : "0", + "shortdesc" : "Path to nova binary", + "default" : "@NOVA_PATH@", + "order": 6, + } + all_opt["domain"] = { + "getopt" : "d:", + "longopt" : "domain", + "help" : "-d, --domain=[string] DNS domain in which hosts live, useful when the cluster uses short names and nova uses FQDN", + "required" : "0", + "shortdesc" : "DNS domain in which hosts live", + "default" : "", + "order": 5, + } + all_opt["no-shared-storage"] = { + "getopt" : "", + "longopt" : "no-shared-storage", + "help" : "--no-shared-storage Disable functionality for shared storage", + "required" : "0", + "shortdesc" : "Disable functionality for dealing with shared storage", + "default" : "False", + "order": 5, + } + +def main(): + global override_status + global nova + atexit.register(atexit_handler) + + device_opt = ["login", "passwd", "tenant-name", "auth-url", + "novatool-path", "no_login", "no_password", "port", "domain", "no-shared-storage"] + define_new_opts() + all_opt["shell_timeout"]["default"] = "180" + + options = check_input(device_opt, process_input(device_opt)) + + docs = {} + docs["shortdesc"] = "Fence agent for nova compute nodes" + docs["longdesc"] = "fence_nova_host is a Nova fencing notification agent" + docs["vendorurl"] = "" + + show_docs(options, docs) + + run_delay(options) + + # The first argument is the Nova client version + nova = nova_client.Client('2', + options["--username"], + options["--password"], + options["--tenant-name"], + options["--auth-url"]) + + if options["--action"] in ["off", "reboot"]: + # Pretend we're 'on' so that the fencing library will always call set_power_status(off) + override_status = "on" + + if options["--action"] == "on": + # Pretend we're 'off' so that the fencing library will always call set_power_status(on) + override_status = "off" + + # Potentially we should make this a pacemaker feature + if options["--action"] != "list" and options["--domain"] != "" and options.has_key("--plug"): + options["--plug"] = options["--plug"]+"."+options["--domain"] + + result = fence_action(None, options, set_power_status, get_power_status, get_plugs_list, None) + sys.exit(result) + +if __name__ == "__main__": + main() diff --git a/make/fencebuild.mk b/make/fencebuild.mk index 3a035e9a..9b394844 100644 --- a/make/fencebuild.mk +++ b/make/fencebuild.mk @@ -1,36 +1,37 @@ $(TARGET): $(SRC) bash $(top_srcdir)/scripts/fenceparse \ $(top_srcdir)/make/copyright.cf REDHAT_COPYRIGHT \ $(VERSION) \ $(abs_srcdir) $@ | \ sed \ -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#@''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#@''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' \ > $@ if [ 0 -eq `echo "$(SRC)" | grep fence_ &> /dev/null; echo $$?` ]; then \ PYTHONPATH=$(abs_srcdir)/../lib:$(abs_builddir)/../lib $(top_srcdir)/fence/agents/lib/check_used_options.py $@; \ else true ; fi clean: clean-man rm -f $(TARGET) $(SYMTARGET) *.pyc *.wiki clean-local: clean \ No newline at end of file diff --git a/tests/data/metadata/fence_compute.xml b/tests/data/metadata/fence_compute.xml new file mode 100644 index 00000000..e9caa084 --- /dev/null +++ b/tests/data/metadata/fence_compute.xml @@ -0,0 +1,123 @@ + + +fence_nova_host is a Nova fencing notification agent + + + + + + Fencing action + + + + + Keystone Admin Auth URL + + + + + Login name + + + + + Login password or passphrase + + + + + Script to run to retrieve password + + + + + Physical plug number on device, UUID or identification of machine + + + + + Keystone Admin Tenant + + + + + DNS domain in which hosts live + + + + + Disable functionality for dealing with shared storage + + + + + Path to nova binary + + + + + Verbose mode + + + + + Write debug information to given file + + + + + Display version information and exit + + + + + Display help and exit + + + + + Separator for CSV created by 'list' operation + + + + + Wait X seconds before fencing is started + + + + + Wait X seconds for cmd prompt after login + + + + + Test X seconds for status change after ON/OFF + + + + + Wait X seconds after issuing ON/OFF + + + + + Wait X seconds for cmd prompt after issuing command + + + + + Count of attempts to retry power on + + + + + + + + + + + + + +