diff --git a/.gitignore b/.gitignore index 8bcae2b1..d028ea19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,89 +1,93 @@ *.swp Makefile.in aclocal.m4 autoconf autoheader autom4te.cache automake autoscan.log compile configure configure.scan config.guess config.log config.sub config.status Makefile depcomp install-sh libtoolize ltmain.sh libtool make/stamp-h1 -m4 + +# ignore "libtoolized" m4 files, but keep our (enumerated) ones +/m4/* +!/m4/ac_python_module.m4 + make/clusterautoconfig.h* missing *.pc .deps .libs *.o *.la *.lo fence/agents/alom/fence_alom fence/agents/apc/fence_apc fence/agents/apc_snmp/fence_apc_snmp fence/agents/baytech/fence_baytech fence/agents/bladecenter/fence_bladecenter fence/agents/brocade/fence_brocade fence/agents/bullpap/fence_bullpap fence/agents/cisco_mds/fence_cisco_mds fence/agents/cisco_ucs/fence_cisco_ucs fence/agents/cpint/fence_cpint fence/agents/docker/fence_docker fence/agents/drac/fence_drac fence/agents/drac5/fence_drac5 fence/agents/eaton_snmp/fence_eaton_snmp fence/agents/egenera/fence_egenera fence/agents/eps/fence_eps fence/agents/ibmblade/fence_ibmblade fence/agents/ifmib/fence_ifmib fence/agents/ilo/fence_ilo fence/agents/ilo_mp/fence_ilo_mp fence/agents/intelmodular/fence_intelmodular fence/agents/ipdu/fence_ipdu fence/agents/ipmilan/fence_ipmilan fence/agents/ldom/fence_ldom fence/agents/lib/fencing.py fence/agents/lib/fencing_snmp.py fence/agents/lib/fencing.pyc fence/agents/lib/fencing_snmp.pyc fence/agents/lpar/fence_lpar fence/agents/manual/fence_ack_manual fence/agents/mcdata/fence_mcdata fence/agents/netio/fence_netio fence/agents/node_assassin/fence_na fence/agents/node_assassin/fence_na.conf fence/agents/node_assassin/fence_na.lib fence/agents/node_assassin/fence_na.pod fence/agents/nss_wrapper/fence_nss_wrapper fence/agents/pve/fence_pve fence/agents/rackswitch/fence_rackswitch fence/agents/rhevm/fence_rhevm fence/agents/raritan/fence_raritan fence/agents/rsa/fence_rsa fence/agents/rsb/fence_rsb fence/agents/sanbox2/fence_sanbox2 fence/agents/scsi/fence_scsi fence/agents/scsi/fence_scsi_test fence/agents/virsh/fence_virsh fence/agents/vixel/fence_vixel fence/agents/vmware/fence_vmware fence/agents/vmware/fence_vmware_helper fence/agents/wti/fence_wti fence/agents/xcat/fence_xcat fence/agents/zvm/fence_zvm fence/agents/zvm/fence_zvmip .fence*.tmp fence-agents* .version tests/devices.d/* diff --git a/Makefile.am b/Makefile.am index 5e2e22dc..e5ee3203 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,46 +1,49 @@ EXTRA_DIST = autogen.sh make/fencebuild.mk scripts/fenceparse \ .version make/release.mk \ make/git-version-gen make/gitlog-to-changelog tests AUTOMAKE_OPTIONS = foreign MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure depcomp \ config.guess config.sub missing install-sh \ autoheader automake autoconf libtool libtoolize \ ltmain.sh compile make/clusterautoconfig.h.in \ make/clusterautoconfig.h.in~ autoscan.log \ configure.scan noinst_HEADERS = make/copyright.cf ACLOCAL_AMFLAGS = -I m4 SUBDIRS = fence/agents/lib fence doc install-exec-local: $(INSTALL) -d $(DESTDIR)/$(LOGDIR) $(INSTALL) -d $(DESTDIR)/$(CLUSTERVARRUN) uninstall-local: rmdir $(DESTDIR)/$(LOGDIR) || :; rmdir $(DESTDIR)/$(CLUSTERVARRUN) || :; BUILT_SOURCES = .version .version: echo $(VERSION) > $@-t && mv $@-t $@ dist-hook: gen-ChangeLog echo $(VERSION) > $(distdir)/.tarball-version gen_start_date = 2000-01-01 .PHONY: gen-ChangeLog gen-ChangeLog: if test -d .git; then \ $(top_srcdir)/make/gitlog-to-changelog \ --since=$(gen_start_date) > $(distdir)/cl-t; \ rm -f $(distdir)/ChangeLog; \ mv $(distdir)/cl-t $(distdir)/ChangeLog; \ fi +# this will get rid of "libtoolized" m4 files maintainer-clean-local: - rm -rf m4 + rm -rf $(filter-out \ + $(top_srcdir)/m4/ac_python_module.m4,$(wildcard \ + $(top_srcdir)/m4/*.m4)) diff --git a/autogen.sh b/autogen.sh index ee22af80..f718cc68 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,4 +1,4 @@ #!/bin/sh # Run this to generate all the initial makefiles, etc. -mkdir -p m4 +# ("-I make" is superfluous, kept only for legacy purposes, if any) autoreconf -i -I make -v && echo Now run ./configure and make diff --git a/configure.ac b/configure.ac index 940b6e66..432d7575 100644 --- a/configure.ac +++ b/configure.ac @@ -1,280 +1,283 @@ # 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 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 '*.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'` 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(suds, 1) AC_PYTHON_MODULE(pexpect, 1) AC_PYTHON_MODULE(pycurl, 1) AC_PYTHON_MODULE(requests, 1) +AC_PYTHON_MODULE(pywsman, 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([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]) ## 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]) +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" 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 4d21e97a..9f0964ca 100644 --- a/fence/agents/Makefile.am +++ b/fence/agents/Makefile.am @@ -1,84 +1,85 @@ MAINTAINERCLEANFILES = Makefile.in TARGET = $(AGENTS_LIST:%.py=%) SRC = $(TARGET:=.py) -CLEAN_TARGET_ADDITIONAL = kdump/fence_kdump_send +CLEAN_TARGET_ADDITIONAL = kdump/fence_kdump_send manual/fence_ack_manual */*.o -EXTRA_DIST = $(SRC) +EXTRA_DIST = $(SRC) manual/fence_ack_manual.in manual/fence_ack_manual.8 zvm/fence_zvm_man_page sbin_SCRIPTS = $(TARGET) manual/fence_ack_manual EXTRA_SCRIPTS = man_MANS = $(sbin_SCRIPTS:=.8) sbin_PROGRAMS = kdump/fence_kdump zvm/fence_zvm libexec_PROGRAMS = kdump/fence_kdump_send -noinst_HEADERS = kdump/list.h kdump/message.h kdump/options.h kdump/version.h +noinst_HEADERS = kdump/list.h kdump/message.h kdump/options.h kdump/version.h zvm/fence_zvm.h 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 -dist_man_MANS = kdump/fence_kdump.8 kdump/fence_kdump_send.8 +dist_man_MANS = kdump/fence_kdump.8 kdump/fence_kdump_send.8 zvm/fence_zvm.8 scsidatadir = $(CLUSTERDATA) scsidata_SCRIPTS = scsi/fence_scsi_check scsi/fence_scsi_check_hardreboot zvm_fence_zvm_SOURCES = zvm/fence_zvm.c -zvm_fence_zvm_CFLAGS = -D_GNU_SOURCE +zvm_fence_zvm_CFLAGS = -D_GNU_SOURCE -Izvm 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 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 ) include $(top_srcdir)/make/fencebuild.mk include $(top_srcdir)/make/fenceman.mk include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/alom/Makefile.am b/fence/agents/alom/Makefile.am deleted file mode 100644 index 10517263..00000000 --- a/fence/agents/alom/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_alom - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a test - -include $(top_srcdir)/make/fencebuild.mk -include $(top_srcdir)/make/fenceman.mk -include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/amt/Makefile.am b/fence/agents/amt/Makefile.am deleted file mode 100644 index 5237f87a..00000000 --- a/fence/agents/amt/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_amt - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -p test -a test - -include $(top_srcdir)/make/fencebuild.mk -include $(top_srcdir)/make/fenceman.mk -include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/amt_ws/Makefile.am b/fence/agents/amt_ws/Makefile.am deleted file mode 100644 index 0876c176..00000000 --- a/fence/agents/amt_ws/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_amt_ws - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -p test -a test - -include $(top_srcdir)/make/fencebuild.mk -include $(top_srcdir)/make/fenceman.mk -include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/apc/Makefile.am b/fence/agents/apc/Makefile.am deleted file mode 100644 index 051ac24e..00000000 --- a/fence/agents/apc/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_apc - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/apc/fence_apc.py b/fence/agents/apc/fence_apc.py index ea89f29c..1e9c3761 100644 --- a/fence/agents/apc/fence_apc.py +++ b/fence/agents/apc/fence_apc.py @@ -1,259 +1,266 @@ #!@PYTHON@ -tt ##### ## ## The Following Agent Has Been Tested On: ## ## Model Firmware ## +---------------------------------------------+ ## AP7951 AOS v2.7.0, PDU APP v2.7.3 ## AP7941 AOS v3.5.7, PDU APP v3.5.6 ## AP9606 AOS v2.5.4, PDU APP v2.7.3 ## ## @note: ssh is very slow on AP79XX devices protocol (1) and ## cipher (des/blowfish) have to be defined ##### -import sys, re +import sys, re, time import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, fail_usage, EC_STATUS #BEGIN_VERSION_GENERATION RELEASE_VERSION="New APC Agent - test release on steroids" REDHAT_COPYRIGHT="" BUILD_DATE="March, 2008" #END_VERSION_GENERATION +# Fix for connection timed out issue in: +# https://bugzilla.redhat.com/show_bug.cgi?id=1342584 +TIMEDOUT_DELAY = 0.5 + def get_power_status(conn, options): exp_result = 0 outlets = {} conn.send_eol("1") conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) version = 0 admin = 0 switch = 0 if None != re.compile('.* MasterSwitch plus.*', re.IGNORECASE | re.S).match(conn.before): switch = 1 if None != re.compile('.* MasterSwitch plus 2', re.IGNORECASE | re.S).match(conn.before): if "--switch" not in options: fail_usage("Failed: You have to enter physical switch number") else: if "--switch" not in options: options["--switch"] = "1" if None == re.compile('.*Outlet Management.*', re.IGNORECASE | re.S).match(conn.before): version = 2 else: version = 3 if None == re.compile('.*Outlet Control/Configuration.*', re.IGNORECASE | re.S).match(conn.before): admin = 0 else: admin = 1 if switch == 0: if version == 2: if admin == 0: conn.send_eol("2") else: conn.send_eol("3") else: conn.send_eol("2") conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) conn.send_eol("1") else: conn.send_eol(options["--switch"]) while True: exp_result = conn.log_expect( ["Press "] + options["--command-prompt"], int(options["--shell-timeout"])) lines = conn.before.split("\n") show_re = re.compile(r'(^|\x0D)\s*(\d+)- (.*?)\s+(ON|OFF)\s*') for line in lines: res = show_re.search(line) if res != None: outlets[res.group(2)] = (res.group(3), res.group(4)) + time.sleep(TIMEDOUT_DELAY) conn.send_eol("") if exp_result != 0: break conn.send(chr(0o3)) conn.log_expect("- Logout", int(options["--shell-timeout"])) conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) if ["list", "monitor"].count(options["--action"]) == 1: return outlets else: try: (_, status) = outlets[options["--plug"]] return status.lower().strip() except KeyError: fail(EC_STATUS) def set_power_status(conn, options): action = { 'on' : "1", 'off': "2" }[options["--action"]] conn.send_eol("1") conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) version = 0 admin2 = 0 admin3 = 0 switch = 0 if None != re.compile('.* MasterSwitch plus.*', re.IGNORECASE | re.S).match(conn.before): switch = 1 ## MasterSwitch has different schema for on/off actions action = { 'on' : "1", 'off': "3" }[options["--action"]] if None != re.compile('.* MasterSwitch plus 2', re.IGNORECASE | re.S).match(conn.before): if "--switch" not in options: fail_usage("Failed: You have to enter physical switch number") else: if "--switch" not in options: options["--switch"] = 1 if None == re.compile('.*Outlet Management.*', re.IGNORECASE | re.S).match(conn.before): version = 2 else: version = 3 if None == re.compile('.*Outlet Control/Configuration.*', re.IGNORECASE | re.S).match(conn.before): admin2 = 0 else: admin2 = 1 if switch == 0: if version == 2: if admin2 == 0: conn.send_eol("2") else: conn.send_eol("3") else: conn.send_eol("2") conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) if None == re.compile('.*2- Outlet Restriction.*', re.IGNORECASE | re.S).match(conn.before): admin3 = 0 else: admin3 = 1 conn.send_eol("1") else: conn.send_eol(options["--switch"]) while 0 == conn.log_expect( ["Press "] + options["--command-prompt"], int(options["--shell-timeout"])): + time.sleep(TIMEDOUT_DELAY) conn.send_eol("") conn.send_eol(options["--plug"]+"") conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) if switch == 0: if admin2 == 1: conn.send_eol("1") conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) if admin3 == 1: conn.send_eol("1") conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) else: conn.send_eol("1") conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) conn.send_eol(action) conn.log_expect("Enter 'YES' to continue or to cancel :", int(options["--shell-timeout"])) conn.send_eol("YES") conn.log_expect("Press to continue...", int(options["--power-timeout"])) + time.sleep(TIMEDOUT_DELAY) conn.send_eol("") conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) conn.send(chr(0o3)) conn.log_expect("- Logout", int(options["--shell-timeout"])) conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) def get_power_status5(conn, options): outlets = {} conn.send_eol("olStatus all") conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) lines = conn.before.split("\n") show_re = re.compile(r'^\s*(\d+): (.*): (On|Off)\s*$', re.IGNORECASE) for line in lines: res = show_re.search(line) if res != None: outlets[res.group(1)] = (res.group(2), res.group(3)) if ["list", "monitor"].count(options["--action"]) == 1: return outlets else: try: (_, status) = outlets[options["--plug"]] return status.lower().strip() except KeyError: fail(EC_STATUS) def set_power_status5(conn, options): action = { 'on' : "olOn", 'off': "olOff" }[options["--action"]] conn.send_eol(action + " " + options["--plug"]) conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) def main(): device_opt = ["ipaddr", "login", "passwd", "cmd_prompt", "secure", \ "port", "switch", "telnet"] atexit.register(atexit_handler) all_opt["cmd_prompt"]["default"] = ["\n>", "\napc>"] all_opt["ssh_options"]["default"] = "-1 -c blowfish" options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for APC over telnet/ssh" docs["longdesc"] = "fence_apc is an I/O Fencing agent \ which can be used with the APC network power switch. It logs into device \ via telnet/ssh and reboots a specified outlet. Lengthy telnet/ssh connections \ should be avoided while a GFS cluster is running because the connection \ will block any necessary fencing actions." docs["vendorurl"] = "http://www.apc.com" show_docs(options, docs) ## Support for --plug [switch]:[plug] notation that was used before if (("--plug" in options) == 1) and (-1 != options["--plug"].find(":")): (switch, plug) = options["--plug"].split(":", 1) options["--switch"] = switch options["--plug"] = plug ## ## Operate the fencing device #### conn = fence_login(options) ## Detect firmware version (ASCII menu vs command-line interface) ## and continue with proper action #### result = -1 firmware_version = re.compile(r'\s*v(\d)*\.').search(conn.before) if (firmware_version != None) and (firmware_version.group(1) in [ "5", "6" ]): result = fence_action(conn, options, set_power_status5, get_power_status5, get_power_status5) else: result = fence_action(conn, options, set_power_status, get_power_status, get_power_status) fence_logout(conn, "4") sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/apc_snmp/Makefile.am b/fence/agents/apc_snmp/Makefile.am deleted file mode 100644 index 258863cd..00000000 --- a/fence/agents/apc_snmp/Makefile.am +++ /dev/null @@ -1,24 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_apc_snmp - -SYMTARGET = fence_tripplite_snmp - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) \ - powernet369.mib \ - README - -sbin_SCRIPTS = $(TARGET) $(SYMTARGET) - -man_MANS = $(TARGET).8 $(SYMTARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a test -n 1 - -include $(top_srcdir)/make/fencebuild.mk -include $(top_srcdir)/make/fenceman.mk -include $(top_srcdir)/make/agentpycheck.mk - -$(SYMTARGET): $(TARGET) - cp $^ $@ diff --git a/fence/agents/autodetect/fencing.py b/fence/agents/autodetect/fencing.py index cc742515..1f387398 100644 --- a/fence/agents/autodetect/fencing.py +++ b/fence/agents/autodetect/fencing.py @@ -1,1389 +1,1389 @@ #!/usr/bin/python -tt import sys, getopt, time, os, uuid, pycurl, stat import pexpect, re, syslog import logging import subprocess import threading import shlex import exceptions import socket import textwrap import __main__ ## do not add code here. #BEGIN_VERSION_GENERATION RELEASE_VERSION="4.0.21.23-eaa13" BUILD_DATE="(built Wed Nov 4 13:28:43 CET 2015)" REDHAT_COPYRIGHT="Copyright (C) Red Hat, Inc. 2004-2010 All rights reserved." #END_VERSION_GENERATION __all__ = ['atexit_handler', 'check_input', 'process_input', 'all_opt', 'show_docs', 'fence_login', 'fence_action', 'fence_logout'] EC_OK = 0 EC_GENERIC_ERROR = 1 EC_BAD_ARGS = 2 EC_LOGIN_DENIED = 3 EC_CONNECTION_LOST = 4 EC_TIMED_OUT = 5 EC_WAITING_ON = 6 EC_WAITING_OFF = 7 EC_STATUS = 8 EC_STATUS_HMC = 9 EC_PASSWORD_MISSING = 10 EC_INVALID_PRIVILEGES = 11 all_opt = { "help" : { "getopt" : "h", "longopt" : "help", "help" : "-h, --help Display this help and exit", "required" : "0", "shortdesc" : "Display help and exit", "order" : 54}, "version" : { "getopt" : "V", "longopt" : "version", "help" : "-V, --version Display version information and exit", "required" : "0", "shortdesc" : "Display version information and exit", "order" : 53}, "verbose" : { "getopt" : "v", "longopt" : "verbose", "help" : "-v, --verbose Verbose mode", "required" : "0", "order" : 51}, "debug" : { "getopt" : "D:", "longopt" : "debug-file", "help" : "-D, --debug-file=[debugfile] Debugging to output file", "required" : "0", "shortdesc" : "Write debug information to given file", "order" : 52}, "delay" : { "getopt" : ":", "longopt" : "delay", "help" : "--delay=[seconds] Wait X seconds before fencing is started", "required" : "0", "default" : "0", "order" : 200}, "agent" : { "getopt" : "", "help" : "", "order" : 1}, "web" : { "getopt" : "", "help" : "", "order" : 1}, "force_on" : { "getopt" : "", "help" : "", "order" : 1}, "action" : { "getopt" : "o:", "longopt" : "action", "help" : "-o, --action=[action] Action: status, reboot (default), off or on", "required" : "1", "shortdesc" : "Fencing action", "default" : "reboot", "order" : 1}, "fabric_fencing" : { "getopt" : "", "help" : "", "order" : 1}, "ipaddr" : { "getopt" : "a:", "longopt" : "ip", "help" : "-a, --ip=[ip] IP address or hostname of fencing device", "required" : "1", "order" : 1}, "ipport" : { "getopt" : "u:", "longopt" : "ipport", "help" : "-u, --ipport=[port] TCP/UDP port to use for connection", "required" : "0", "shortdesc" : "TCP/UDP port to use for connection with device", "order" : 1}, "login" : { "getopt" : "l:", "longopt" : "username", "help" : "-l, --username=[name] Login name", "required" : "?", "order" : 1}, "no_login" : { "getopt" : "", "help" : "", "order" : 1}, "no_password" : { "getopt" : "", "help" : "", "order" : 1}, "no_port" : { "getopt" : "", "help" : "", "order" : 1}, "no_status" : { "getopt" : "", "help" : "", "order" : 1}, "no_on" : { "getopt" : "", "help" : "", "order" : 1}, "no_off" : { "getopt" : "", "help" : "", "order" : 1}, "telnet" : { "getopt" : "", "help" : "", "order" : ""}, "passwd" : { "getopt" : "p:", "longopt" : "password", "help" : "-p, --password=[password] Login password or passphrase", "required" : "0", "order" : 1}, "passwd_script" : { "getopt" : "S:", "longopt" : "password-script", "help" : "-S, --password-script=[script] Script to run to retrieve password", "required" : "0", "order" : 1}, "identity_file" : { "getopt" : "k:", "longopt" : "identity-file", "help" : "-k, --identity-file=[filename] Identity file (private key) for SSH", "required" : "0", "order" : 1}, "cmd_prompt" : { "getopt" : "c:", "longopt" : "command-prompt", "help" : "-c, --command-prompt=[prompt] Force Python regex for command prompt", "required" : "0", "order" : 1}, "secure" : { "getopt" : "x", "longopt" : "ssh", "help" : "-x, --ssh Use SSH connection", "required" : "0", "order" : 1}, "ssh_options" : { "getopt" : ":", "longopt" : "ssh-options", "help" : "--ssh-options=[options] SSH options to use", "required" : "0", "order" : 1}, "ssl" : { "getopt" : "z", "longopt" : "ssl", "help" : "-z, --ssl Use SSL connection with verifying certificate", "required" : "0", "order" : 1}, "ssl_insecure" : { "getopt" : "", "longopt" : "ssl-insecure", "help" : "--ssl-insecure Use SSL connection without verifying certificate", "required" : "0", "order" : 1}, "ssl_secure" : { "getopt" : "", "longopt" : "ssl-secure", "help" : "--ssl-secure Use SSL connection with verifying certificate", "required" : "0", "order" : 1}, "notls" : { "getopt" : "t", "longopt" : "notls", "help" : "-t, --notls " "Disable TLS negotiation and force SSL3.0. " "This should only be used for devices that do not support TLS1.0 and up.", "required" : "0", "order" : 1}, "tls1.0" : { "getopt" : "", "longopt" : "tls1.0", "help" : "--tls1.0 " "Disable TLS negotiation and force TLS1.0. " "This should only be used for devices that do not support TLS1.1 and up.", "required" : "0", "order" : 1}, "port" : { "getopt" : "n:", "longopt" : "plug", "help" : "-n, --plug=[id] " "Physical plug number on device, UUID or identification of machine", "required" : "1", "order" : 1}, "switch" : { "getopt" : "s:", "longopt" : "switch", "help" : "-s, --switch=[id] Physical switch number on device", "required" : "0", "order" : 1}, "exec" : { "getopt" : "e:", "longopt" : "exec", "help" : "-e, --exec=[command] Command to execute", "required" : "0", "order" : 1}, "vmware_type" : { "getopt" : "d:", "longopt" : "vmware_type", "help" : "-d, --vmware_type=[type] Type of VMware to connect", "required" : "0", "order" : 1}, "vmware_datacenter" : { "getopt" : "s:", "longopt" : "vmware-datacenter", "help" : "-s, --vmware-datacenter=[dc] VMWare datacenter filter", "required" : "0", "order" : 2}, "snmp_version" : { "getopt" : "d:", "longopt" : "snmp-version", "help" : "-d, --snmp-version=[version] Specifies SNMP version to use (1|2c|3)", "required" : "0", "shortdesc" : "Specifies SNMP version to use", "choices" : ["1", "2c", "3"], "order" : 1}, "community" : { "getopt" : "c:", "longopt" : "community", "help" : "-c, --community=[community] Set the community string", "required" : "0", "order" : 1}, "snmp_auth_prot" : { "getopt" : "b:", "longopt" : "snmp-auth-prot", "help" : "-b, --snmp-auth-prot=[prot] Set authentication protocol (MD5|SHA)", "required" : "0", "shortdesc" : "Set authentication protocol", "choices" : ["MD5", "SHA"], "order" : 1}, "snmp_sec_level" : { "getopt" : "E:", "longopt" : "snmp-sec-level", "help" : "-E, --snmp-sec-level=[level] " "Set security level (noAuthNoPriv|authNoPriv|authPriv)", "required" : "0", "shortdesc" : "Set security level", "choices" : ["noAuthNoPriv", "authNoPriv", "authPriv"], "order" : 1}, "snmp_priv_prot" : { "getopt" : "B:", "longopt" : "snmp-priv-prot", "help" : "-B, --snmp-priv-prot=[prot] Set privacy protocol (DES|AES)", "required" : "0", "shortdesc" : "Set privacy protocol", "choices" : ["DES", "AES"], "order" : 1}, "snmp_priv_passwd" : { "getopt" : "P:", "longopt" : "snmp-priv-passwd", "help" : "-P, --snmp-priv-passwd=[pass] Set privacy protocol password", "required" : "0", "order" : 1}, "snmp_priv_passwd_script" : { "getopt" : "R:", "longopt" : "snmp-priv-passwd-script", "help" : "-R, --snmp-priv-passwd-script Script to run to retrieve privacy password", "required" : "0", "order" : 1}, "inet4_only" : { "getopt" : "4", "longopt" : "inet4-only", "help" : "-4, --inet4-only Forces agent to use IPv4 addresses only", "required" : "0", "order" : 1}, "inet6_only" : { "getopt" : "6", "longopt" : "inet6-only", "help" : "-6, --inet6-only Forces agent to use IPv6 addresses only", "required" : "0", "order" : 1}, "separator" : { "getopt" : "C:", "longopt" : "separator", "help" : "-C, --separator=[char] Separator for CSV created by 'list' operation", "default" : ",", "required" : "0", "order" : 100}, "login_timeout" : { "getopt" : ":", "longopt" : "login-timeout", "help" : "--login-timeout=[seconds] Wait X seconds for cmd prompt after login", "default" : "5", "required" : "0", "order" : 200}, "shell_timeout" : { "getopt" : ":", "longopt" : "shell-timeout", "help" : "--shell-timeout=[seconds] Wait X seconds for cmd prompt after issuing command", "default" : "3", "required" : "0", "order" : 200}, "power_timeout" : { "getopt" : ":", "longopt" : "power-timeout", "help" : "--power-timeout=[seconds] Test X seconds for status change after ON/OFF", "default" : "20", "required" : "0", "order" : 200}, "power_wait" : { "getopt" : ":", "longopt" : "power-wait", "help" : "--power-wait=[seconds] Wait X seconds after issuing ON/OFF", "default" : "0", "required" : "0", "order" : 200}, "missing_as_off" : { "getopt" : "", "longopt" : "missing-as-off", "help" : "--missing-as-off Missing port returns OFF instead of failure", "required" : "0", "order" : 200}, "retry_on" : { "getopt" : ":", "longopt" : "retry-on", "help" : "--retry-on=[attempts] Count of attempts to retry power on", "default" : "1", "required" : "0", "order" : 201}, "session_url" : { "getopt" : "s:", "longopt" : "session-url", "help" : "-s, --session-url URL to connect to XenServer on", "required" : "1", "order" : 1}, "sudo" : { "getopt" : "", "longopt" : "use-sudo", "help" : "--use-sudo Use sudo (without password) when calling 3rd party software", "required" : "0", "order" : 205}, "method" : { "getopt" : "m:", "longopt" : "method", "help" : "-m, --method=[method] Method to fence (onoff|cycle) (Default: onoff)", "required" : "0", "shortdesc" : "Method to fence", "default" : "onoff", "choices" : ["onoff", "cycle"], "order" : 1}, "telnet_path" : { "getopt" : ":", "longopt" : "telnet-path", "help" : "--telnet-path=[path] Path to telnet binary", "required" : "0", "default" : "/usr/bin/telnet", "order": 300}, "ssh_path" : { "getopt" : ":", "longopt" : "ssh-path", "help" : "--ssh-path=[path] Path to ssh binary", "required" : "0", "default" : "/usr/bin/ssh", "order": 300}, "gnutlscli_path" : { "getopt" : ":", "longopt" : "gnutlscli-path", "help" : "--gnutlscli-path=[path] Path to gnutls-cli binary", "required" : "0", "default" : "/usr/bin/gnutls-cli", "order": 300}, "sudo_path" : { "getopt" : ":", "longopt" : "sudo-path", "help" : "--sudo-path=[path] Path to sudo binary", "required" : "0", "default" : "/usr/bin/sudo", "order": 300}, "snmpwalk_path" : { "getopt" : ":", "longopt" : "snmpwalk-path", "help" : "--snmpwalk-path=[path] Path to snmpwalk binary", "required" : "0", "default" : "/usr/bin/snmpwalk", "order" : 300}, "snmpset_path" : { "getopt" : ":", "longopt" : "snmpset-path", "help" : "--snmpset-path=[path] Path to snmpset binary", "required" : "0", "default" : "/usr/bin/snmpset", "order" : 300}, "snmpget_path" : { "getopt" : ":", "longopt" : "snmpget-path", "help" : "--snmpget-path=[path] Path to snmpget binary", "required" : "0", "default" : "/usr/bin/snmpget", "order" : 300}, "snmp": { "getopt" : "", "help" : "", "order" : 1}, "port_as_ip": { "getopt" : "", "longopt" : "port-as-ip", "help" : "--port-as-ip Make \"port/plug\" to be an alias to IP address", "required" : "0", "order" : 200}, "on_target": { "getopt" : "", "help" : "", "order" : 1} } # options which are added automatically if 'key' is encountered ("default" is always added) DEPENDENCY_OPT = { "default" : ["help", "debug", "verbose", "version", "action", "agent", \ "power_timeout", "shell_timeout", "login_timeout", "power_wait", "retry_on", "delay"], "passwd" : ["passwd_script"], "sudo" : ["sudo_path"], "secure" : ["identity_file", "ssh_options", "ssh_path"], "telnet" : ["telnet_path"], "ipaddr" : ["ipport", "inet4_only", "inet6_only"], "port" : ["separator"], "ssl" : ["ssl_secure", "ssl_insecure", "gnutlscli_path"], "snmp" : ["snmp_auth_prot", "snmp_sec_level", "snmp_priv_prot", \ "snmp_priv_passwd", "snmp_priv_passwd_script", "community", \ "snmpset_path", "snmpget_path", "snmpwalk_path"] } class fspawn(pexpect.spawn): def __init__(self, options, command): logging.info("Running command: %s", command) pexpect.spawn.__init__(self, command) self.opt = options def log_expect(self, pattern, timeout): result = self.expect(pattern, timeout) logging.debug("Received: %s", str(self.before) + str(self.after)) return result def send(self, message): logging.debug("Sent: %s", message) return pexpect.spawn.send(self, message) # send EOL according to what was detected in login process (telnet) def send_eol(self, message): return self.send(message + self.opt["eol"]) def atexit_handler(): try: sys.stdout.close() os.close(1) except IOError: logging.error("%s failed to close standard output\n", sys.argv[0]) sys.exit(EC_GENERIC_ERROR) def _add_dependency_options(options): ## Add also options which are available for every fence agent added_opt = [] for opt in options + ["default"]: if DEPENDENCY_OPT.has_key(opt): added_opt.extend([y for y in DEPENDENCY_OPT[opt] if options.count(y) == 0]) if not "port" in (options + added_opt) and \ not "nodename" in (options + added_opt) and \ "ipaddr" in (options + added_opt): added_opt.append("port_as_ip") all_opt["port"]["help"] = "-n, --plug=[ip] IP address or hostname of fencing device " \ "(together with --port-as-ip)" return added_opt def fail_usage(message="", stop=True): if len(message) > 0: logging.error("%s\n", message) if stop: logging.error("Please use '-h' for usage\n") sys.exit(EC_GENERIC_ERROR) def fail(error_code): message = { EC_LOGIN_DENIED : "Unable to connect/login to fencing device", EC_CONNECTION_LOST : "Connection lost", EC_TIMED_OUT : "Connection timed out", EC_WAITING_ON : "Failed: Timed out waiting to power ON", EC_WAITING_OFF : "Failed: Timed out waiting to power OFF", EC_STATUS : "Failed: Unable to obtain correct plug status or plug is not available", EC_STATUS_HMC : "Failed: Either unable to obtain correct plug status, " "partition is not available or incorrect HMC version used", EC_PASSWORD_MISSING : "Failed: You have to set login password", EC_INVALID_PRIVILEGES : "Failed: The user does not have the correct privileges to do the requested action." }[error_code] + "\n" logging.error("%s\n", message) sys.exit(EC_GENERIC_ERROR) def usage(avail_opt): print "Usage:" print "\t" + os.path.basename(sys.argv[0]) + " [options]" print "Options:" sorted_list = [(key, all_opt[key]) for key in avail_opt] sorted_list.sort(lambda x, y: cmp(x[1]["order"], y[1]["order"])) for key, value in sorted_list: if len(value["help"]) != 0: print " " + _join_wrap([value["help"]], first_indent=3) def metadata(avail_opt, docs): # avail_opt has to be unique, if there are duplicities then they should be removed sorted_list = [(key, all_opt[key]) for key in list(set(avail_opt))] sorted_list.sort(lambda x, y: cmp(x[0], y[0])) sorted_list.sort(lambda x, y: cmp(x[1]["order"], y[1]["order"])) print "" print "" for (symlink, desc) in docs.get("symlink", []): print "" print "" + docs["longdesc"] + "" print "" + docs["vendorurl"] + "" print "" for option, _ in sorted_list: if all_opt[option].has_key("help") and len(all_opt[option]["help"]) > 0: print "\t" default = "" if all_opt[option].has_key("default"): default = "default=\"" + _encode_html_entities(str(all_opt[option]["default"])) + "\" " mixed = all_opt[option]["help"] ## split it between option and help text res = re.compile(r"^(.*?--\S+)\s+", re.IGNORECASE | re.S).search(mixed) if None != res: mixed = res.group(1) mixed = _encode_html_entities(mixed) if not "shortdesc" in all_opt[option]: shortdesc = re.sub("\s\s+", " ", all_opt[option]["help"][31:]) else: shortdesc = all_opt[option]["shortdesc"] print "\t\t" if all_opt[option].has_key("choices"): print "\t\t" for choice in all_opt[option]["choices"]: print "\t\t\t" elif all_opt[option]["getopt"].count(":") > 0: print "\t\t" else: print "\t\t" print "\t\t" + shortdesc + "" print "\t" print "" print "" (available_actions, _) = _get_available_actions(avail_opt) if "on" in available_actions: available_actions.remove("on") on_target = ' on_target="1"' if avail_opt.count("on_target") else '' print "\t" % (on_target, avail_opt.count("fabric_fencing")) for action in available_actions: print "\t" % (action) print "" print "" def process_input(avail_opt): avail_opt.extend(_add_dependency_options(avail_opt)) # @todo: this should be put elsewhere? os.putenv("LANG", "C") os.putenv("LC_ALL", "C") if "port_as_ip" in avail_opt: avail_opt.append("port") if len(sys.argv) > 1: opt = _parse_input_cmdline(avail_opt) else: opt = _parse_input_stdin(avail_opt) if "--port-as-ip" in opt and "--plug" in opt: opt["--ip"] = opt["--plug"] return opt ## ## This function checks input and answers if we want to have same answers ## in each of the fencing agents. It looks for possible errors and run ## password script to set a correct password ###### def check_input(device_opt, opt, other_conditions = False): device_opt.extend(_add_dependency_options(device_opt)) options = dict(opt) options["device_opt"] = device_opt _update_metadata(options) options = _set_default_values(options) options["--action"] = options["--action"].lower() ## In special cases (show help, metadata or version) we don't need to check anything ##### # OCF compatibility if options["--action"] == "meta-data": options["--action"] = "metadata" if options["--action"] == "metadata" or any(options.has_key(k) for k in ("--help", "--version")): return options if options.has_key("--verbose"): logging.getLogger().setLevel(logging.DEBUG) ## add logging to syslog logging.getLogger().addHandler(SyslogLibHandler()) ## add logging to stderr - logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stderr)) + logging.getLogger().addHandler(logging.StreamHandler(sys.stderr)) (acceptable_actions, _) = _get_available_actions(device_opt) if 1 == device_opt.count("fabric_fencing"): acceptable_actions.extend(["enable", "disable"]) if 0 == acceptable_actions.count(options["--action"]): fail_usage("Failed: Unrecognised action '" + options["--action"] + "'") ## Compatibility layer ##### if options["--action"] == "enable": options["--action"] = "on" if options["--action"] == "disable": options["--action"] = "off" if options["--action"] == "validate-all" and not other_conditions: _validate_input(options, False) sys.exit(EC_OK) else: _validate_input(options, True) if options.has_key("--debug-file"): try: debug_file = logging.FileHandler(options["--debug-file"]) debug_file.setLevel(logging.DEBUG) logging.getLogger().addHandler(debug_file) except IOError: logging.error("Unable to create file %s", options["--debug-file"]) fail_usage("Failed: Unable to create file " + options["--debug-file"]) if options.has_key("--snmp-priv-passwd-script"): options["--snmp-priv-passwd"] = os.popen(options["--snmp-priv-passwd-script"]).read().rstrip() if options.has_key("--password-script"): options["--password"] = os.popen(options["--password-script"]).read().rstrip() return options ## Obtain a power status from possibly more than one plug ## "on" is returned if at least one plug is ON ###### def get_multi_power_fn(connection, options, get_power_fn): status = "off" plugs = options["--plugs"] if options.has_key("--plugs") else [""] for plug in plugs: try: options["--uuid"] = str(uuid.UUID(plug)) except ValueError: pass except KeyError: pass options["--plug"] = plug plug_status = get_power_fn(connection, options) if plug_status != "off": status = plug_status return status def set_multi_power_fn(connection, options, set_power_fn, get_power_fn, retry_attempts=1): plugs = options["--plugs"] if options.has_key("--plugs") else [""] for _ in range(retry_attempts): for plug in plugs: try: options["--uuid"] = str(uuid.UUID(plug)) except ValueError: pass except KeyError: pass options["--plug"] = plug set_power_fn(connection, options) time.sleep(int(options["--power-wait"])) for _ in xrange(int(options["--power-timeout"])): if get_multi_power_fn(connection, options, get_power_fn) != options["--action"]: time.sleep(1) else: return True return False def show_docs(options, docs=None): device_opt = options["device_opt"] if docs == None: docs = {} docs["shortdesc"] = "Fence agent" docs["longdesc"] = "" if options.has_key("--help"): usage(device_opt) sys.exit(0) if options.get("--action", "") == "metadata": if "port_as_ip" in device_opt: device_opt.remove("separator") metadata(device_opt, docs) sys.exit(0) if options.has_key("--version"): print __main__.RELEASE_VERSION, __main__.BUILD_DATE print __main__.REDHAT_COPYRIGHT sys.exit(0) def fence_action(connection, options, set_power_fn, get_power_fn, get_outlet_list=None, reboot_cycle_fn=None): result = 0 try: if options.has_key("--plug"): options["--plugs"] = options["--plug"].split(",") ## Process options that manipulate fencing device ##### if (options["--action"] in ["list", "list-status"]) or \ ((options["--action"] == "monitor") and 1 == options["device_opt"].count("port") and \ 0 == options["device_opt"].count("port_as_ip")): if 0 == options["device_opt"].count("port"): print "N/A" elif get_outlet_list == None: ## @todo: exception? ## This is just temporal solution, we will remove default value ## None as soon as all existing agent will support this operation print "NOTICE: List option is not working on this device yet" else: options["--original-action"] = options["--action"] options["--action"] = "list" outlets = get_outlet_list(connection, options) options["--action"] = options["--original-action"] del options["--original-action"] ## keys can be numbers (port numbers) or strings (names of VM, UUID) for outlet_id in outlets.keys(): (alias, status) = outlets[outlet_id] if status is None or (not status.upper() in ["ON", "OFF"]): status = "UNKNOWN" status = status.upper() if options["--action"] == "list": print outlet_id + options["--separator"] + alias elif options["--action"] == "list-status": print outlet_id + options["--separator"] + alias + options["--separator"] + status return if options["--action"] == "monitor" and not "port" in options["device_opt"] and "no_status" in options["device_opt"]: # Unable to do standard monitoring because 'status' action is not available return 0 status = None if not "no_status" in options["device_opt"]: status = get_multi_power_fn(connection, options, get_power_fn) if status != "on" and status != "off": fail(EC_STATUS) if options["--action"] == status: if not (status == "on" and "force_on" in options["device_opt"]): print "Success: Already %s" % (status.upper()) return 0 if options["--action"] == "on": if set_multi_power_fn(connection, options, set_power_fn, get_power_fn, 1 + int(options["--retry-on"])): print "Success: Powered ON" else: fail(EC_WAITING_ON) elif options["--action"] == "off": if set_multi_power_fn(connection, options, set_power_fn, get_power_fn): print "Success: Powered OFF" else: fail(EC_WAITING_OFF) elif options["--action"] == "reboot": power_on = False if options.get("--method", "").lower() == "cycle" and reboot_cycle_fn is not None: for _ in range(1, 1 + int(options["--retry-on"])): if reboot_cycle_fn(connection, options): power_on = True break if not power_on: fail(EC_TIMED_OUT) else: if status != "off": options["--action"] = "off" if not set_multi_power_fn(connection, options, set_power_fn, get_power_fn): fail(EC_WAITING_OFF) options["--action"] = "on" try: power_on = set_multi_power_fn(connection, options, set_power_fn, get_power_fn, int(options["--retry-on"])) except Exception, ex: # an error occured during power ON phase in reboot # fence action was completed succesfully even in that case logging.warning("%s", str(ex)) if power_on == False: # this should not fail as node was fenced succesfully logging.error('Timed out waiting to power ON\n') print "Success: Rebooted" elif options["--action"] == "status": print "Status: " + status.upper() if status.upper() == "OFF": result = 2 elif options["--action"] == "monitor": pass except pexpect.EOF: fail(EC_CONNECTION_LOST) except pexpect.TIMEOUT: fail(EC_TIMED_OUT) except pycurl.error, ex: logging.error("%s\n", str(ex)) fail(EC_TIMED_OUT) except socket.timeout, ex: logging.error("%s\n", str(ex)) fail(EC_TIMED_OUT) return result def fence_login(options, re_login_string=r"(login\s*: )|((?!Last )Login Name: )|(username: )|(User Name :)"): run_delay(options) if not options.has_key("eol"): options["eol"] = "\r\n" if options.has_key("--command-prompt") and type(options["--command-prompt"]) is not list: options["--command-prompt"] = [options["--command-prompt"]] try: if options.has_key("--ssl"): conn = _open_ssl_connection(options) elif options.has_key("--ssh") and not options.has_key("--identity-file"): conn = _login_ssh_with_password(options, re_login_string) elif options.has_key("--ssh") and options.has_key("--identity-file"): conn = _login_ssh_with_identity_file(options) else: conn = _login_telnet(options, re_login_string) except pexpect.EOF, exception: logging.debug("%s", str(exception)) fail(EC_LOGIN_DENIED) except pexpect.TIMEOUT, exception: logging.debug("%s", str(exception)) fail(EC_LOGIN_DENIED) return conn def is_executable(path): if os.path.exists(path): stats = os.stat(path) if stat.S_ISREG(stats.st_mode) and os.access(path, os.X_OK): return True return False def run_command(options, command, timeout=None, env=None): if timeout is None and "--power-timeout" in options: timeout = options["--power-timeout"] if timeout is not None: timeout = float(timeout) # For IPMI password occurs on command line, it should not be part of debug info log_command = command if "ipmitool" in log_command: log_command = re.sub("-P (.+?) -p", "-P [set] -p", log_command) logging.info("Executing: %s\n", log_command) try: process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) except OSError: fail_usage("Unable to run %s\n" % command) thread = threading.Thread(target=process.wait) thread.start() thread.join(timeout) if thread.is_alive(): process.kill() fail(EC_TIMED_OUT) status = process.wait() (pipe_stdout, pipe_stderr) = process.communicate() process.stdout.close() process.stderr.close() logging.debug("%s %s %s\n", str(status), str(pipe_stdout), str(pipe_stderr)) return (status, pipe_stdout, pipe_stderr) def run_delay(options): ## Delay is important for two-node clusters fencing but we do not need to delay 'status' operations if options["--action"] in ["off", "reboot"]: logging.info("Delay %s second(s) before logging in to the fence device", options["--delay"]) time.sleep(int(options["--delay"])) def fence_logout(conn, logout_string, sleep=0): # Logout is not required part of fencing but we should attempt to do it properly # In some cases our 'exit' command is faster and we can not close connection as it # was already closed by fencing device try: conn.send_eol(logout_string) time.sleep(sleep) conn.close() except exceptions.OSError: pass except pexpect.ExceptionPexpect: pass # Convert array of format [[key1, value1], [key2, value2], ... [keyN, valueN]] to dict, where key is # in format a.b.c.d...z and returned dict has key only z def array_to_dict(array): return dict([[x[0].split(".")[-1], x[1]] for x in array]) ## Own logger handler that uses old-style syslog handler as otherwise everything is sourced ## from /dev/syslog class SyslogLibHandler(logging.StreamHandler): """ A handler class that correctly push messages into syslog """ def emit(self, record): syslog_level = { logging.CRITICAL:syslog.LOG_CRIT, logging.ERROR:syslog.LOG_ERR, logging.WARNING:syslog.LOG_WARNING, logging.INFO:syslog.LOG_INFO, logging.DEBUG:syslog.LOG_DEBUG, logging.NOTSET:syslog.LOG_DEBUG, }[record.levelno] msg = self.format(record) # syslos.syslog can not have 0x00 character inside or exception is thrown syslog.syslog(syslog_level, msg.replace("\x00", "\n")) return def _open_ssl_connection(options): gnutls_opts = "" ssl_opts = "" if options.has_key("--notls"): gnutls_opts = "--priority \"NORMAL:-VERS-TLS1.2:-VERS-TLS1.1:-VERS-TLS1.0:+VERS-SSL3.0\"" elif options.has_key("--tls1.0"): gnutls_opts = "--priority \"NORMAL:-VERS-TLS1.2:-VERS-TLS1.1:+VERS-TLS1.0:%LATEST_RECORD_VERSION\"" # --ssl is same as the --ssl-secure; it means we want to verify certificate in these cases if options.has_key("--ssl-insecure"): ssl_opts = "--insecure" command = '%s %s %s --crlf -p %s %s' % \ (options["--gnutlscli-path"], gnutls_opts, ssl_opts, options["--ipport"], options["--ip"]) try: conn = fspawn(options, command) except pexpect.ExceptionPexpect, ex: logging.error("%s\n", str(ex)) sys.exit(EC_GENERIC_ERROR) return conn def _login_ssh_with_identity_file(options): if options.has_key("--inet6-only"): force_ipvx = "-6 " elif options.has_key("--inet4-only"): force_ipvx = "-4 " else: force_ipvx = "" command = '%s %s %s@%s -i %s -p %s' % \ (options["--ssh-path"], force_ipvx, options["--username"], options["--ip"], \ options["--identity-file"], options["--ipport"]) if options.has_key("--ssh-options"): command += ' ' + options["--ssh-options"] conn = fspawn(options, command) result = conn.log_expect(["Enter passphrase for key '" + options["--identity-file"] + "':", \ "Are you sure you want to continue connecting (yes/no)?"] + \ options["--command-prompt"], int(options["--login-timeout"])) if result == 1: conn.sendline("yes") result = conn.log_expect( ["Enter passphrase for key '" + options["--identity-file"]+"':"] + \ options["--command-prompt"], int(options["--login-timeout"])) if result == 0: if options.has_key("--password"): conn.sendline(options["--password"]) conn.log_expect(options["--command-prompt"], int(options["--login-timeout"])) else: fail_usage("Failed: You have to enter passphrase (-p) for identity file") return conn def _login_telnet(options, re_login_string): re_login = re.compile(re_login_string, re.IGNORECASE) re_pass = re.compile("(password)|(pass phrase)", re.IGNORECASE) conn = fspawn(options, options["--telnet-path"]) conn.send("set binary\n") conn.send("open %s -%s\n"%(options["--ip"], options["--ipport"])) conn.log_expect(re_login, int(options["--login-timeout"])) conn.send_eol(options["--username"]) ## automatically change end of line separator screen = conn.read_nonblocking(size=100, timeout=int(options["--shell-timeout"])) if re_login.search(screen) != None: options["eol"] = "\n" conn.send_eol(options["--username"]) conn.log_expect(re_pass, int(options["--login-timeout"])) elif re_pass.search(screen) == None: conn.log_expect(re_pass, int(options["--shell-timeout"])) try: conn.send_eol(options["--password"]) valid_password = conn.log_expect([re_login] + \ options["--command-prompt"], int(options["--shell-timeout"])) if valid_password == 0: ## password is invalid or we have to change EOL separator options["eol"] = "\r" conn.send_eol("") screen = conn.read_nonblocking(size=100, timeout=int(options["--shell-timeout"])) ## after sending EOL the fence device can either show 'Login' or 'Password' if re_login.search(conn.after + screen) != None: conn.send_eol("") conn.send_eol(options["--username"]) conn.log_expect(re_pass, int(options["--login-timeout"])) conn.send_eol(options["--password"]) conn.log_expect(options["--command-prompt"], int(options["--login-timeout"])) except KeyError: fail(EC_PASSWORD_MISSING) return conn def _login_ssh_with_password(options, re_login_string): re_login = re.compile(re_login_string, re.IGNORECASE) re_pass = re.compile("(password)|(pass phrase)", re.IGNORECASE) if options.has_key("--inet6-only"): force_ipvx = "-6 " elif options.has_key("--inet4-only"): force_ipvx = "-4 " else: force_ipvx = "" command = '%s %s %s@%s -p %s -o PubkeyAuthentication=no' % \ (options["--ssh-path"], force_ipvx, options["--username"], options["--ip"], options["--ipport"]) if options.has_key("--ssh-options"): command += ' ' + options["--ssh-options"] conn = fspawn(options, command) if options.has_key("telnet_over_ssh"): # This is for stupid ssh servers (like ALOM) which behave more like telnet # (ignore name and display login prompt) result = conn.log_expect( \ [re_login, "Are you sure you want to continue connecting (yes/no)?"], int(options["--login-timeout"])) if result == 1: conn.sendline("yes") # Host identity confirm conn.log_expect(re_login, int(options["--login-timeout"])) conn.sendline(options["--username"]) conn.log_expect(re_pass, int(options["--login-timeout"])) else: result = conn.log_expect( \ ["ssword:", "Are you sure you want to continue connecting (yes/no)?"], int(options["--login-timeout"])) if result == 1: conn.sendline("yes") conn.log_expect("ssword:", int(options["--login-timeout"])) conn.sendline(options["--password"]) conn.log_expect(options["--command-prompt"], int(options["--login-timeout"])) return conn # # To update metadata, we change values in all_opt def _update_metadata(options): device_opt = options["device_opt"] if device_opt.count("login") and device_opt.count("no_login") == 0: all_opt["login"]["required"] = "1" else: all_opt["login"]["required"] = "0" if device_opt.count("port_as_ip"): all_opt["ipaddr"]["required"] = "0" all_opt["port"]["required"] = "0" (available_actions, default_value) = _get_available_actions(device_opt) all_opt["action"]["default"] = default_value actions_with_default = \ [x if not x == all_opt["action"]["default"] else x + " (default)" for x in available_actions] all_opt["action"]["help"] = \ "-o, --action=[action] Action: %s" % (_join_wrap(actions_with_default, last_separator=" or ")) if device_opt.count("ipport"): default_value = None default_string = None if all_opt["ipport"].has_key("default"): default_value = all_opt["ipport"]["default"] elif device_opt.count("web") and device_opt.count("ssl"): default_value = "80" default_string = "(default 80, 443 if --ssl option is used)" elif device_opt.count("telnet") and device_opt.count("secure"): default_value = "23" default_string = "(default 23, 22 if --ssh option is used)" else: tcp_ports = {"community" : "161", "secure" : "22", "telnet" : "23", "web" : "80", "ssl" : "443"} # all cases where next command returns multiple results are covered by previous blocks protocol = [x for x in ["community", "secure", "ssl", "web", "telnet"] if device_opt.count(x)][0] default_value = tcp_ports[protocol] if default_string is None: all_opt["ipport"]["help"] = "-u, --ipport=[port] TCP/UDP port to use (default %s)" % \ (default_value) else: all_opt["ipport"]["help"] = "-u, --ipport=[port] TCP/UDP port to use\n" + " "*40 + default_string def _set_default_values(options): if "ipport" in options["device_opt"]: if not "--ipport" in options: if "default" in all_opt["ipport"]: options["--ipport"] = all_opt["ipport"]["default"] elif "community" in options["device_opt"]: options["--ipport"] = "161" elif "--ssh" in options or all_opt["secure"].get("default", "0") == "1": options["--ipport"] = "22" elif "--ssl" in options or all_opt["ssl"].get("default", "0") == "1": options["--ipport"] = "443" elif "--ssl-secure" in options or all_opt["ssl_secure"].get("default", "0") == "1": options["--ipport"] = "443" elif "--ssl-insecure" in options or all_opt["ssl_insecure"].get("default", "0") == "1": options["--ipport"] = "443" elif "web" in options["device_opt"]: options["--ipport"] = "80" elif "telnet" in options["device_opt"]: options["--ipport"] = "23" if "--ipport" in options: all_opt["ipport"]["default"] = options["--ipport"] for opt in options["device_opt"]: if all_opt[opt].has_key("default") and not opt == "ipport": getopt_long = "--" + all_opt[opt]["longopt"] if not options.has_key(getopt_long): options[getopt_long] = all_opt[opt]["default"] return options # stop = True/False : exit fence agent when problem is encountered def _validate_input(options, stop = True): device_opt = options["device_opt"] valid_input = True if not options.has_key("--username") and \ device_opt.count("login") and (device_opt.count("no_login") == 0): valid_input = False fail_usage("Failed: You have to set login name", stop) if device_opt.count("ipaddr") and not options.has_key("--ip") and not options.has_key("--managed"): valid_input = False fail_usage("Failed: You have to enter fence address", stop) if device_opt.count("no_password") == 0: if 0 == device_opt.count("identity_file"): if not (options.has_key("--password") or options.has_key("--password-script")): valid_input = False fail_usage("Failed: You have to enter password or password script", stop) else: if not (options.has_key("--password") or \ options.has_key("--password-script") or options.has_key("--identity-file")): valid_input = False fail_usage("Failed: You have to enter password, password script or identity file", stop) if not options.has_key("--ssh") and options.has_key("--identity-file"): valid_input = False fail_usage("Failed: You have to use identity file together with ssh connection (-x)", stop) if options.has_key("--identity-file") and not os.path.isfile(options["--identity-file"]): valid_input = False fail_usage("Failed: Identity file " + options["--identity-file"] + " does not exist", stop) if (0 == ["list", "list-status", "monitor"].count(options["--action"])) and \ not options.has_key("--plug") and device_opt.count("port") and \ device_opt.count("no_port") == 0 and not device_opt.count("port_as_ip"): valid_input = False fail_usage("Failed: You have to enter plug number or machine identification", stop) if options.has_key("--plug") and len(options["--plug"].split(",")) > 1 and \ options.has_key("--method") and options["--method"] == "cycle": valid_input = False fail_usage("Failed: Cannot use --method cycle for more than 1 plug", stop) for failed_opt in _get_opts_with_invalid_choices(options): valid_input = False fail_usage("Failed: You have to enter a valid choice for %s from the valid values: %s" % \ ("--" + all_opt[failed_opt]["longopt"], str(all_opt[failed_opt]["choices"])), stop) return valid_input def _encode_html_entities(text): return text.replace("&", "&").replace('"', """).replace('<', "<"). \ replace('>', ">").replace("'", "'") def _prepare_getopt_args(options): getopt_string = "" longopt_list = [] for k in options: if all_opt.has_key(k) and all_opt[k]["getopt"] != ":": # getopt == ":" means that opt is without short getopt, but has value getopt_string += all_opt[k]["getopt"] elif not all_opt.has_key(k): fail_usage("Parse error: unknown option '"+k+"'") if all_opt.has_key(k) and all_opt[k].has_key("longopt"): if all_opt[k]["getopt"].endswith(":"): longopt_list.append(all_opt[k]["longopt"] + "=") else: longopt_list.append(all_opt[k]["longopt"]) return (getopt_string, longopt_list) def _parse_input_stdin(avail_opt): opt = {} name = "" for line in sys.stdin.readlines(): line = line.strip() if (line.startswith("#")) or (len(line) == 0): continue (name, value) = (line + "=").split("=", 1) value = value[:-1] if avail_opt.count(name) == 0 and name in ["nodename"]: continue elif avail_opt.count(name) == 0: logging.warning("Parse error: Ignoring unknown option '%s'\n", line) continue if all_opt[name]["getopt"].endswith(":"): opt["--"+all_opt[name]["longopt"].rstrip(":")] = value elif value.lower() in ["1", "yes", "on", "true"]: opt["--"+all_opt[name]["longopt"]] = "1" else: logging.warning("Parse error: Ignoring option '%s' because it does not have value\n", name) return opt def _parse_input_cmdline(avail_opt): filtered_opts = {} _verify_unique_getopt(avail_opt) (getopt_string, longopt_list) = _prepare_getopt_args(avail_opt) try: (entered_opt, left_arg) = getopt.gnu_getopt(sys.argv[1:], getopt_string, longopt_list) if len(left_arg) > 0: logging.warning("Unused arguments on command line: %s" % (str(left_arg))) except getopt.GetoptError, error: fail_usage("Parse error: " + error.msg) for opt in avail_opt: filtered_opts.update({opt : all_opt[opt]}) # Short and long getopt names are changed to consistent "--" + long name (e.g. --username) long_opts = {} for arg_name in dict(entered_opt).keys(): all_key = [key for (key, value) in filtered_opts.items() \ if "--" + value.get("longopt", "") == arg_name or "-" + value.get("getopt", "").rstrip(":") == arg_name][0] long_opts["--" + filtered_opts[all_key]["longopt"]] = dict(entered_opt)[arg_name] # This test is specific because it does not apply to input on stdin if "port_as_ip" in avail_opt and not "--port-as-ip" in long_opts and "--plug" in long_opts: fail_usage("Parser error: option -n/--plug is not recognized") return long_opts # for ["John", "Mary", "Eli"] returns "John, Mary and Eli" def _join2(words, normal_separator=", ", last_separator=" and "): if len(words) <= 1: return "".join(words) else: return last_separator.join([normal_separator.join(words[:-1]), words[-1]]) def _join_wrap(words, normal_separator=", ", last_separator=" and ", first_indent=42): x = _join2(words, normal_separator, last_separator) wrapper = textwrap.TextWrapper() wrapper.initial_indent = " "*first_indent wrapper.subsequent_indent = " "*40 wrapper.width = 85 wrapper.break_on_hyphens = False wrapper.break_long_words = False wrapped_text = "" for line in wrapper.wrap(x): wrapped_text += line + "\n" return wrapped_text.lstrip().rstrip("\n") def _get_opts_with_invalid_choices(options): options_failed = [] device_opt = options["device_opt"] for opt in device_opt: if all_opt[opt].has_key("choices"): longopt = "--" + all_opt[opt]["longopt"] possible_values_upper = [y.upper() for y in all_opt[opt]["choices"]] if options.has_key(longopt): options[longopt] = options[longopt].upper() if not options["--" + all_opt[opt]["longopt"]] in possible_values_upper: options_failed.append(opt) return options_failed def _verify_unique_getopt(avail_opt): used_getopt = set() for opt in avail_opt: getopt_value = all_opt[opt].get("getopt", "").rstrip(":") if getopt_value and getopt_value in used_getopt: fail_usage("Short getopt for %s (-%s) is not unique" % (opt, getopt_value)) else: used_getopt.add(getopt_value) def _get_available_actions(device_opt): available_actions = ["on", "off", "reboot", "status", "list", "list-status", \ "monitor", "metadata", "validate-all"] default_value = "reboot" if device_opt.count("fabric_fencing"): available_actions.remove("reboot") default_value = "off" if device_opt.count("no_status"): available_actions.remove("status") if device_opt.count("no_on"): available_actions.remove("on") if device_opt.count("no_off"): available_actions.remove("off") if not device_opt.count("separator"): available_actions.remove("list") available_actions.remove("list-status") return (available_actions, default_value) diff --git a/fence/agents/azure_arm/fence_azure_arm.py b/fence/agents/azure_arm/fence_azure_arm.py new file mode 100644 index 00000000..16d29aee --- /dev/null +++ b/fence/agents/azure_arm/fence_azure_arm.py @@ -0,0 +1,131 @@ +#!@PYTHON@ -tt + +import sys, re, pexpect +import logging +import atexit +sys.path.append("/usr/share/fence") +from fencing import * +from fencing import fail, fail_usage, EC_TIMED_OUT, run_delay + +#BEGIN_VERSION_GENERATION +RELEASE_VERSION="4.0.24.6-7e576" +BUILD_DATE="(built Thu Nov 3 15:43:03 STD 2016)" +REDHAT_COPYRIGHT="Copyright (C) Red Hat, Inc. 2004-2010 All rights reserved." +#END_VERSION_GENERATION + +def get_nodes_list(compute_client, options): + result = {} + if compute_client: + rgName = options["--resourceGroup"] + vms = compute_client.virtual_machines.list(rgName) + for vm in vms: + result[vm.name] = ("", None) + + return result + +def get_power_status(compute_client, options): + logging.info("getting power status for VM " + options["--plug"]) + + if compute_client: + rgName = options["--resourceGroup"] + vmName = options["--plug"] + + powerState = "unknown" + vmStatus = compute_client.virtual_machines.get(rgName, vmName, "instanceView") + for status in vmStatus.instance_view.statuses: + if status.code.startswith("PowerState"): + powerState = status.code + break + + logging.info("Found power state of VM: " + powerState) + if powerState == "PowerState/running": + return "on" + + return "off" + +def set_power_status(compute_client, options): + logging.info("setting power status for VM " + options["--plug"] + " to " + options["--action"]) + + if compute_client: + rgName = options["--resourceGroup"] + vmName = options["--plug"] + + if (options["--action"]=="off"): + logging.info("Deallocating " + vmName + "in resource group " + rgName) + compute_client.virtual_machines.deallocate(rgName, vmName) + elif (options["--action"]=="on"): + logging.info("Starting " + vmName + "in resource group " + rgName) + compute_client.virtual_machines.start(rgName, vmName) + + +def define_new_opts(): + all_opt["resourceGroup"] = { + "getopt" : "rg:", + "longopt" : "resourceGroup", + "help" : "-rg, --resourceGroup=[name] Name of the resource group", + "shortdesc" : "Name of resource group.", + "required" : "1", + "order" : 2 + } + all_opt["tenantId"] = { + "getopt" : "tid:", + "longopt" : "tenantId", + "help" : "-tid, --tenantId=[name] Id of the Azure Active Directory tenant", + "shortdesc" : "Id of Azure Active Directory tenant.", + "required" : "1", + "order" : 3 + } + all_opt["subscriptionId"] = { + "getopt" : "sid:", + "longopt" : "subscriptionId", + "help" : "-sid, --subscriptionId=[name] Id of the Azure subscription", + "shortdesc" : "Id of the Azure subscription.", + "required" : "1", + "order" : 4 + } + +# Main agent method +def main(): + compute_client = None + + device_opt = ["resourceGroup", "login", "passwd", "tenantId", "subscriptionId","port"] + + atexit.register(atexit_handler) + + define_new_opts() + options = check_input(device_opt, process_input(device_opt)) + + docs = {} + docs["shortdesc"] = "Fence agent for Azure Resource Manager" + docs["longdesc"] = "Used to deallocate virtual machines and to report power state of virtual machines running in Azure" + docs["vendorurl"] = "http://www.microsoft.com" + show_docs(options, docs) + + run_delay(options) + + try: + from azure.common.credentials import ServicePrincipalCredentials + from azure.mgmt.compute import ComputeManagementClient + + tenantid = options["--tenantId"] + servicePrincipal = options["--username"] + spPassword = options["--password"] + subscriptionId = options["--subscriptionId"] + credentials = ServicePrincipalCredentials( + client_id = servicePrincipal, + secret = spPassword, + tenant = tenantid + ) + compute_client = ComputeManagementClient( + credentials, + subscriptionId + ) + except ImportError: + fail_usage("Azure Resource Manager Pyhton SDK not found or not accessible") + + # Operate the fencing device + result = fence_action(compute_client, options, set_power_status, get_power_status, get_nodes_list) + sys.exit(result) + +if __name__ == "__main__": + main() diff --git a/fence/agents/bladecenter/Makefile.am b/fence/agents/bladecenter/Makefile.am deleted file mode 100644 index 09358b52..00000000 --- a/fence/agents/bladecenter/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_bladecenter - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/brocade/Makefile.am b/fence/agents/brocade/Makefile.am deleted file mode 100644 index 6ff704da..00000000 --- a/fence/agents/brocade/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_brocade - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/cisco_mds/Makefile.am b/fence/agents/cisco_mds/Makefile.am deleted file mode 100644 index c6a19de4..00000000 --- a/fence/agents/cisco_mds/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_cisco_mds - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a test -n fc1/1 - -include $(top_srcdir)/make/fencebuild.mk -include $(top_srcdir)/make/fenceman.mk -include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/cisco_ucs/Makefile.am b/fence/agents/cisco_ucs/Makefile.am deleted file mode 100644 index cc81ee6e..00000000 --- a/fence/agents/cisco_ucs/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_cisco_ucs - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/cisco_ucs/fence_cisco_ucs.py b/fence/agents/cisco_ucs/fence_cisco_ucs.py index 648e45fd..0d9609db 100644 --- a/fence/agents/cisco_ucs/fence_cisco_ucs.py +++ b/fence/agents/cisco_ucs/fence_cisco_ucs.py @@ -1,192 +1,202 @@ #!@PYTHON@ -tt import sys, re import pycurl, io import logging import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, EC_STATUS, EC_LOGIN_DENIED, run_delay #BEGIN_VERSION_GENERATION RELEASE_VERSION="New Cisco UCS Agent - test release on steroids" REDHAT_COPYRIGHT="" BUILD_DATE="March, 2008" #END_VERSION_GENERATION RE_COOKIE = re.compile("", int(options["--shell-timeout"])) result = RE_GET_PNDN.search(res) if result == None: pndn = "" else: pndn = result.group(1) if pndn.strip() == "": if "--missing-as-off" in options: return "off" else: fail(EC_STATUS) res = send_command(options, "", int(options["--shell-timeout"])) result = RE_GET_PRESENCE.search(res) if result == None: fail(EC_STATUS) else: - status = result.group(1) + presence_status = result.group(1) - if status in ["missing", "mismatch"]: + if presence_status in ["missing", "mismatch"]: return "off" else: - return "on" + result = RE_GET_OPERPOWER.search(res) + if result == None: + fail(EC_STATUS) + else: + power_status = result.group(1) + + if power_status == "on": + return "on" + else: + return "off" def set_power_status(conn, options): del conn action = { 'on' : "up", 'off' : "down" }[options["--action"]] send_command(options, "" + "" + "" + "", int(options["--shell-timeout"])) return def get_list(conn, options): del conn outlets = {} try: res = send_command(options, "", int(options["--shell-timeout"])) lines = res.split("", int(options_global["--shell-timeout"])) except Exception: pass def main(): global options_global device_opt = ["ipaddr", "login", "passwd", "ssl", "notls", "port", "web", "suborg", "missing_as_off"] atexit.register(atexit_handler) atexit.register(logout) define_new_opts() options_global = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for Cisco UCS" docs["longdesc"] = "fence_cisco_ucs is an I/O Fencing agent which can be \ used with Cisco UCS to fence machines." docs["vendorurl"] = "http://www.cisco.com" show_docs(options_global, docs) run_delay(options_global) ### Login try: res = send_command(options_global, "", int(options_global["--login-timeout"])) result = RE_COOKIE.search(res) if result == None: ## Cookie is absenting in response fail(EC_LOGIN_DENIED) except Exception: fail(EC_LOGIN_DENIED) options_global["cookie"] = result.group(1) ## ## Modify suborg to format /suborg if options_global["--suborg"] != "": options_global["--suborg"] = "/" + options_global["--suborg"].lstrip("/").rstrip("/") ## ## Fence operations #### result = fence_action(None, options_global, set_power_status, get_power_status, get_list) ## Logout is done every time at atexit phase sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/compute/Makefile.am b/fence/agents/compute/Makefile.am deleted file mode 100644 index ab212728..00000000 --- a/fence/agents/compute/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -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 index a16ee74e..1845cc49 100644 --- a/fence/agents/compute/fence_compute.py +++ b/fence/agents/compute/fence_compute.py @@ -1,470 +1,474 @@ #!@PYTHON@ -tt import sys import time import atexit import logging import requests.exceptions sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage, is_executable, run_command, run_delay #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 EVACUABLE_TAG = "evacuable" TRUE_TAGS = ['true'] 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"]) for service in services: logging.debug("Status of %s is %s" % (service.binary, service.state)) if service.binary == "nova-compute": if service.state == "up": status = "on" elif service.state == "down": status = "off" else: logging.debug("Unknown status detected from nova: " + service.state) break - except ConnectionError as err: + except requests.exception.ConnectionError as err: logging.warning("Nova connection failed: " + str(err)) 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 = False error_message = "" try: logging.debug("Resurrecting instance: %s" % server) (response, dictionary) = nova.servers.evacuate(server=server, on_shared_storage=on_shared_storage) if response == None: error_message = "No response while evacuating instance" elif response.status_code == 200: success = True error_message = response.reason else: error_message = response.reason except Exception as e: error_message = "Error while evacuating instance: %s" % e return { "uuid": server, "accepted": success, "reason": error_message, } def _is_server_evacuable(server, evac_flavors, evac_images): if server.flavor.get('id') in evac_flavors: return True if server.image.get('id') in evac_images: return True logging.debug("Instance %s is not evacuable" % server.image.get('id')) return False def _get_evacuable_flavors(): result = [] flavors = nova.flavors.list() # Since the detailed view for all flavors doesn't provide the extra specs, # we need to call each of the flavor to get them. for flavor in flavors: tag = flavor.get_keys().get(EVACUABLE_TAG) if tag and tag.strip().lower() in TRUE_TAGS: result.append(flavor.id) return result def _get_evacuable_images(): result = [] images = nova.images.list(detailed=True) for image in images: if hasattr(image, 'metadata'): tag = image.metadata.get(EVACUABLE_TAG) if tag and tag.strip().lower() in TRUE_TAGS: result.append(image.id) return result def _host_evacuate(options): result = True images = _get_evacuable_images() flavors = _get_evacuable_flavors() servers = nova.servers.list(search_opts={'host': options["--plug"], 'all_tenants': 1 }) if options["--instance-filtering"] == "False": logging.debug("Not evacuating anything") evacuables = [] elif len(flavors) or len(images): logging.debug("Filtering images and flavors: %s %s" % (repr(flavors), repr(images))) # Identify all evacuable servers logging.debug("Checking %s" % repr(servers)) evacuables = [server for server in servers if _is_server_evacuable(server, flavors, images)] logging.debug("Evacuating %s" % repr(evacuables)) else: logging.debug("Evacuating all images and flavors") evacuables = servers if options["--no-shared-storage"] != "False": on_shared_storage = False else: on_shared_storage = True for server in evacuables: logging.debug("Processing %s" % server) if hasattr(server, 'id'): response = _server_evacuate(server.id, on_shared_storage) if response["accepted"]: logging.debug("Evacuated %s from %s: %s" % (response["uuid"], options["--plug"], response["reason"])) else: logging.error("Evacuation of %s on %s failed: %s" % (response["uuid"], options["--plug"], response["reason"])) result = False else: logging.error("Could not evacuate instance: %s" % server.to_dict()) # Should a malformed instance result in a failed evacuation? # result = False return result def set_attrd_status(host, status, options): logging.debug("Setting fencing status for %s to %s" % (host, status)) run_command(options, "attrd_updater -p -n evacuate -Q -N %s -U %s" % (host, status)) 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": # Forcing the service back up in case it was disabled nova.services.enable(options["--plug"], 'nova-compute') try: # Forcing the host back up nova.services.force_down( options["--plug"], "nova-compute", force_down=False) except Exception as e: # In theory, if force_down=False fails, that's for the exact # same possible reasons that below with force_down=True # eg. either an incompatible version or an old client. # Since it's about forcing back to a default value, there is # no real worries to just consider it's still okay even if the # command failed logging.info("Exception from attempt to force " "host back up via nova API: " "%s: %s" % (e.__class__.__name__, e)) else: # Pretend we're 'on' so that the fencing library doesn't loop forever waiting for the node to boot override_status = "on" return try: nova.services.force_down( options["--plug"], "nova-compute", force_down=True) except Exception as e: # Something went wrong when we tried to force the host down. # That could come from either an incompatible API version # eg. UnsupportedVersion or VersionNotFoundForAPIMethod # or because novaclient is old and doesn't include force_down yet # eg. AttributeError # In that case, fallbacking to wait for Nova to catch the right state. logging.error("Exception from attempt to force host down via nova API: " "%s: %s" % (e.__class__.__name__, e)) # 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 its internal state for %s" % options["--plug"]) time.sleep(1) if not _host_evacuate(options): sys.exit(1) return def fix_domain(options): domains = {} last_domain = None if nova: # Find it in nova hypervisors = nova.hypervisors.list() for hypervisor in hypervisors: shorthost = hypervisor.hypervisor_hostname.split('.')[0] if shorthost == hypervisor.hypervisor_hostname: # Nova is not using FQDN calculated = "" else: # Compute nodes are named as FQDN, strip off the hostname calculated = hypervisor.hypervisor_hostname.replace(shorthost+".", "") domains[calculated] = shorthost if calculated == last_domain: # Avoid complaining for each compute node with the same name # One hopes they don't appear interleaved as A.com B.com A.com B.com logging.debug("Calculated the same domain from: %s" % hypervisor.hypervisor_hostname) elif "--domain" in options and options["--domain"] == calculated: # Supplied domain name is valid return elif "--domain" in options: # Warn in case nova isn't available at some point logging.warning("Supplied domain '%s' does not match the one calculated from: %s" % (options["--domain"], hypervisor.hypervisor_hostname)) last_domain = calculated if len(domains) == 0 and "--domain" not in options: logging.error("Could not calculate the domain names used by compute nodes in nova") elif len(domains) == 1 and "--domain" not in options: options["--domain"] = last_domain + return options["--domain"] elif len(domains) == 1: logging.error("Overriding supplied domain '%s' does not match the one calculated from: %s" % (options["--domain"], hypervisor.hypervisor_hostname)) options["--domain"] = last_domain + return options["--domain"] elif len(domains) > 1: logging.error("The supplied domain '%s' did not match any used inside nova: %s" % (options["--domain"], repr(domains))) sys.exit(1) + return None + def fix_plug_name(options): if options["--action"] == "list": return if "--plug" not in options: return - fix_domain(options) + calculated = fix_domain(options) short_plug = options["--plug"].split('.')[0] logging.debug("Checking target '%s' against calculated domain '%s'"% (options["--plug"], options["--domain"])) if "--domain" not in options: # Nothing supplied and nova not available... what to do... nothing return elif options["--domain"] == "": # Ensure any domain is stripped off since nova isn't using FQDN options["--plug"] = short_plug elif options["--domain"] in options["--plug"]: # Plug already contains the domain, don't re-add return else: # Add the domain to the plug options["--plug"] = short_plug + "." + options["--domain"] def get_plugs_list(_, options): result = {} if nova: hypervisors = nova.hypervisors.list() for hypervisor in hypervisors: longhost = hypervisor.hypervisor_hostname shorthost = longhost.split('.')[0] result[longhost] = ("", None) result[shorthost] = ("", None) return result def create_nova_connection(options): global nova try: from novaclient import client from novaclient.exceptions import NotAcceptable except ImportError: fail_usage("Nova not found or not accessible") versions = [ "2.11", "2" ] for version in versions: nova = client.Client(version, options["--username"], options["--password"], options["--tenant-name"], options["--auth-url"], insecure=options["--insecure"], region_name=options["--region-name"], endpoint_type=options["--endpoint-type"], http_log_debug=options.has_key("--verbose")) try: nova.hypervisors.list() return except NotAcceptable as e: logging.warning(e) except Exception as e: logging.warning("Nova connection failed. %s: %s" % (e.__class__.__name__, e)) fail_usage("Couldn't obtain a supported connection to nova, tried: %s" % repr(versions)) def define_new_opts(): all_opt["endpoint-type"] = { "getopt" : "e:", "longopt" : "endpoint-type", "help" : "-e, --endpoint-type=[endpoint] Nova Endpoint type (publicURL, internalURL, adminURL)", "required" : "0", "shortdesc" : "Nova Endpoint type", "default" : "internalURL", "order": 1, } 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=[url] Keystone Admin Auth URL", "required" : "0", "shortdesc" : "Keystone Admin Auth URL", "default" : "", "order": 1, } all_opt["region-name"] = { "getopt" : "", "longopt" : "region-name", "help" : "--region-name=[region] Region Name", "required" : "0", "shortdesc" : "Region Name", "default" : "", "order": 1, } all_opt["insecure"] = { "getopt" : "", "longopt" : "insecure", "help" : "--insecure Explicitly allow agent to perform \"insecure\" TLS (https) requests", "required" : "0", "shortdesc" : "Allow Insecure TLS Requests", "default" : "False", "order": 2, } 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", "order": 5, } all_opt["record-only"] = { "getopt" : "r:", "longopt" : "record-only", "help" : "--record-only Record the target as needing evacuation but as yet do not intiate it", "required" : "0", "shortdesc" : "Only record the target as needing evacuation", "default" : "False", "order": 5, } all_opt["instance-filtering"] = { "getopt" : "", "longopt" : "instance-filtering", "help" : "--instance-filtering Allow instances created from images and flavors with evacuable=true to be evacuated (or all if no images/flavors have been tagged)", "required" : "0", "shortdesc" : "Allow instances to be evacuated", "default" : "True", "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 atexit.register(atexit_handler) device_opt = ["login", "passwd", "tenant-name", "auth-url", "fabric_fencing", "on_target", "no_login", "no_password", "port", "domain", "no-shared-storage", "endpoint-type", "record-only", "instance-filtering", "insecure", "region-name"] define_new_opts() all_opt["shell_timeout"]["default"] = "180" options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for the automatic resurrection of OpenStack compute instances" docs["longdesc"] = "Used to tell Nova that compute nodes are down and to reschedule flagged instances" docs["vendorurl"] = "" show_docs(options, docs) if options["--record-only"] in [ "2", "Disabled", "disabled" ]: sys.exit(0) run_delay(options) create_nova_connection(options) fix_plug_name(options) if options["--record-only"] in [ "1", "True", "true", "Yes", "yes"]: if options["--action"] == "on": set_attrd_status(options["--plug"], "no", options) sys.exit(0) elif options["--action"] in ["off", "reboot"]: set_attrd_status(options["--plug"], "yes", options) sys.exit(0) elif options["--action"] in ["monitor", "status"]: sys.exit(0) 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" 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/fence/agents/docker/Makefile.am b/fence/agents/docker/Makefile.am deleted file mode 100644 index 5e623f7c..00000000 --- a/fence/agents/docker/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_docker - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -a 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/drac/Makefile.am b/fence/agents/drac/Makefile.am deleted file mode 100644 index 178da390..00000000 --- a/fence/agents/drac/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_drac - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a test - -include $(top_srcdir)/make/fencebuild.mk -include $(top_srcdir)/make/fenceman.mk -include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/drac5/Makefile.am b/fence/agents/drac5/Makefile.am deleted file mode 100644 index 83247649..00000000 --- a/fence/agents/drac5/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_drac5 - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/dummy/Makefile.am b/fence/agents/dummy/Makefile.am deleted file mode 100644 index 0c160a54..00000000 --- a/fence/agents/dummy/Makefile.am +++ /dev/null @@ -1,15 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_dummy - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -include $(top_srcdir)/make/fencebuild.mk -include $(top_srcdir)/make/fenceman.mk -include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/eaton_snmp/Makefile.am b/fence/agents/eaton_snmp/Makefile.am deleted file mode 100644 index 415c2b63..00000000 --- a/fence/agents/eaton_snmp/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_eaton_snmp - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) \ - README - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/emerson/Makefile.am b/fence/agents/emerson/Makefile.am deleted file mode 100644 index f7e54973..00000000 --- a/fence/agents/emerson/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_emerson - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/eps/Makefile.am b/fence/agents/eps/Makefile.am deleted file mode 100644 index c3d67627..00000000 --- a/fence/agents/eps/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_eps - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/eps/fence_eps.py b/fence/agents/eps/fence_eps.py index dcdacb86..65de6a0b 100644 --- a/fence/agents/eps/fence_eps.py +++ b/fence/agents/eps/fence_eps.py @@ -1,134 +1,134 @@ #!@PYTHON@ -tt # The Following Agent Has Been Tested On: # ePowerSwitch 8M+ version 1.0.0.4 import sys, re import base64, string, socket import logging import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, fail_usage, EC_LOGIN_DENIED, EC_TIMED_OUT, run_delay -if sys.version_info.major > 2: +if sys.version_info[0] > 2: import http.client as httplib else: import httplib #BEGIN_VERSION_GENERATION RELEASE_VERSION="ePowerSwitch 8M+ (eps)" REDHAT_COPYRIGHT="" BUILD_DATE="" #END_VERSION_GENERATION # Run command on EPS device. # @param options Device options # @param params HTTP GET parameters (without ?) def eps_run_command(options, params): try: # New http connection conn = httplib.HTTPConnection(options["--ip"]) request_str = "/"+options["--page"] if params != "": request_str += "?"+params logging.debug("GET %s\n", request_str) conn.putrequest('GET', request_str) if "--username" in options: if "--password" not in options: options["--password"] = "" # Default is empty password # String for Authorization header auth_str = 'Basic ' + string.strip(base64.encodestring(options["--username"]+':'+options["--password"])) logging.debug("Authorization: %s\n", auth_str) conn.putheader('Authorization', auth_str) conn.endheaders() response = conn.getresponse() logging.debug("%d %s\n", response.status, response.reason) #Response != OK -> couldn't login if response.status != 200: fail(EC_LOGIN_DENIED) result = response.read() logging.debug("%s \n", result) conn.close() except socket.timeout: fail(EC_TIMED_OUT) except socket.error: fail(EC_LOGIN_DENIED) return result def get_power_status(conn, options): del conn ret_val = eps_run_command(options, "") result = {} status = re.findall(r"p(\d{2})=(0|1)\s*\", ret_val.lower()) for out_num, out_stat in status: result[out_num] = ("", (out_stat == "1" and "on" or "off")) if not options["--action"] in ['monitor', 'list']: if not options["--plug"] in result: fail_usage("Failed: You have to enter existing physical plug!") else: return result[options["--plug"]][1] else: return result def set_power_status(conn, options): del conn eps_run_command(options, "P%s=%s"%(options["--plug"], (options["--action"] == "on" and "1" or "0"))) # Define new option def eps_define_new_opts(): all_opt["hidden_page"] = { "getopt" : "c:", "longopt" : "page", "help":"-c, --page=[page] Name of hidden page (default: hidden.htm)", "required" : "0", "shortdesc" : "Name of hidden page", "default" : "hidden.htm", "order": 1 } # Starting point of fence agent def main(): device_opt = ["ipaddr", "login", "passwd", "no_login", "no_password", \ "port", "hidden_page", "web"] atexit.register(atexit_handler) eps_define_new_opts() options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for ePowerSwitch" docs["longdesc"] = "fence_eps is an I/O Fencing agent \ which can be used with the ePowerSwitch 8M+ power switch to fence \ connected machines. Fence agent works ONLY on 8M+ device, because \ this is only one, which has support for hidden page feature. \ \n.TP\n\ Agent basically works by connecting to hidden page and pass \ appropriate arguments to GET request. This means, that hidden \ page feature must be enabled and properly configured." docs["vendorurl"] = "http://www.epowerswitch.com" show_docs(options, docs) run_delay(options) #Run fence action. Conn is None, beacause we always need open new http connection result = fence_action(None, options, set_power_status, get_power_status, get_power_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/hds_cb/Makefile.am b/fence/agents/hds_cb/Makefile.am deleted file mode 100644 index f36fca15..00000000 --- a/fence/agents/hds_cb/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_hds_cb - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/hpblade/Makefile.am b/fence/agents/hpblade/Makefile.am deleted file mode 100644 index a5fc2b11..00000000 --- a/fence/agents/hpblade/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_hpblade - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/ibmblade/Makefile.am b/fence/agents/ibmblade/Makefile.am deleted file mode 100644 index 47b83d77..00000000 --- a/fence/agents/ibmblade/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_ibmblade - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/ifmib/Makefile.am b/fence/agents/ifmib/Makefile.am deleted file mode 100644 index 74ba9508..00000000 --- a/fence/agents/ifmib/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_ifmib - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) \ - README - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/ilo/Makefile.am b/fence/agents/ilo/Makefile.am deleted file mode 100644 index c8bdf638..00000000 --- a/fence/agents/ilo/Makefile.am +++ /dev/null @@ -1,22 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_ilo - -SYMTARGET = fence_ilo2 - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) $(SYMTARGET) - -man_MANS = $(TARGET).8 $(SYMTARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a test - -$(SYMTARGET): $(TARGET) - cp $^ $@ - -include $(top_srcdir)/make/fencebuild.mk -include $(top_srcdir)/make/fenceman.mk -include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/ilo_moonshot/Makefile.am b/fence/agents/ilo_moonshot/Makefile.am deleted file mode 100644 index d6e12d76..00000000 --- a/fence/agents/ilo_moonshot/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_ilo_moonshot - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/ilo_mp/Makefile.am b/fence/agents/ilo_mp/Makefile.am deleted file mode 100644 index fdf265c2..00000000 --- a/fence/agents/ilo_mp/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_ilo_mp - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a test - -include $(top_srcdir)/make/fencebuild.mk -include $(top_srcdir)/make/fenceman.mk -include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/ilo_ssh/Makefile.am b/fence/agents/ilo_ssh/Makefile.am deleted file mode 100644 index 883c3066..00000000 --- a/fence/agents/ilo_ssh/Makefile.am +++ /dev/null @@ -1,22 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_ilo_ssh - -SYMTARGET = fence_ilo3_ssh fence_ilo4_ssh - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) $(SYMTARGET) - -man_MANS = $(TARGET).8 $(SYMTARGET:%=%.8) - -FENCE_TEST_ARGS = -p test -a test -l test - -$(SYMTARGET) : $(TARGET) - cp $^ $@ - -include $(top_srcdir)/make/fencebuild.mk -include $(top_srcdir)/make/fenceman.mk -include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/intelmodular/Makefile.am b/fence/agents/intelmodular/Makefile.am deleted file mode 100644 index 837fceea..00000000 --- a/fence/agents/intelmodular/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_intelmodular - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/ipdu/Makefile.am b/fence/agents/ipdu/Makefile.am deleted file mode 100644 index 8b162843..00000000 --- a/fence/agents/ipdu/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_ipdu - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/ipmilan/Makefile.am b/fence/agents/ipmilan/Makefile.am deleted file mode 100644 index fba2198a..00000000 --- a/fence/agents/ipmilan/Makefile.am +++ /dev/null @@ -1,22 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_ipmilan - -SYMTARGET = fence_ilo3 fence_ilo4 fence_imm fence_idrac - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) $(SYMTARGET) - -man_MANS = $(TARGET).8 $(SYMTARGET:%=%.8) - -FENCE_TEST_ARGS = -l test -p test -a test - -$(SYMTARGET): $(TARGET) - cp $^ $@ - -include $(top_srcdir)/make/fencebuild.mk -include $(top_srcdir)/make/fenceman.mk -include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/kdump/Makefile.am b/fence/agents/kdump/Makefile.am deleted file mode 100644 index 2195f646..00000000 --- a/fence/agents/kdump/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -sbin_PROGRAMS = fence_kdump -libexec_PROGRAMS = fence_kdump_send - -noinst_HEADERS = list.h message.h options.h version.h - -fence_kdump_SOURCES = fence_kdump.c -fence_kdump_CFLAGS = -D_GNU_SOURCE - -fence_kdump_send_SOURCES = fence_kdump_send.c -fence_kdump_send_CFLAGS = -D_GNU_SOURCE - -dist_man_MANS = fence_kdump.8 fence_kdump_send.8 - -FENCE_TEST_ARGS = -n test - -include $(top_srcdir)/make/agentccheck.mk - -check: xml-check.fence_kdump diff --git a/fence/agents/ldom/Makefile.am b/fence/agents/ldom/Makefile.am deleted file mode 100644 index 8a58536a..00000000 --- a/fence/agents/ldom/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_ldom - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/lib/XenAPI.py.py b/fence/agents/lib/XenAPI.py.py index 3f5456d5..6fe11ef6 100644 --- a/fence/agents/lib/XenAPI.py.py +++ b/fence/agents/lib/XenAPI.py.py @@ -1,212 +1,212 @@ #============================================================================ # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #============================================================================ # Copyright (C) 2006 XenSource Inc. #============================================================================ # # Parts of this file are based upon xmlrpclib.py, the XML-RPC client # interface included in the Python distribution. # # Copyright (c) 1999-2002 by Secret Labs AB # Copyright (c) 1999-2002 by Fredrik Lundh # # By obtaining, using, and/or copying this software and/or its # associated documentation, you agree that you have read, understood, # and will comply with the following terms and conditions: # # Permission to use, copy, modify, and distribute this software and # its associated documentation for any purpose and without fee is # hereby granted, provided that the above copyright notice appears in # all copies, and that both that copyright notice and this permission # notice appear in supporting documentation, and that the name of # Secret Labs AB or the author not be used in advertising or publicity # pertaining to distribution of the software without specific, written # prior permission. # # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- # ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE # OF THIS SOFTWARE. # -------------------------------------------------------------------- import sys import gettext import socket import logging -if sys.version_info.major > 2: +if sys.version_info[0] > 2: import xmlrpc.client as xmlrpclib import http.client as httplib else: import xmlrpclib import httplib translation = gettext.translation('xen-xm', fallback=True) class Failure(Exception): def __init__(self, details): try: # If this failure is MESSAGE_PARAMETER_COUNT_MISMATCH, then we # correct the return values here, to account for the fact that we # transparently add the session handle as the first argument. if details[0] == 'MESSAGE_PARAMETER_COUNT_MISMATCH': details[2] = str(int(details[2]) - 1) details[3] = str(int(details[3]) - 1) self.details = details except Exception as exn: self.details = ['INTERNAL_ERROR', 'Client-side: ' + str(exn)] def __str__(self): try: return translation.ugettext(self.details[0]) % self._details_map() except TypeError as exn: return "Message database broken: %s.\nXen-API failure: %s" % \ (exn, str(self.details)) except Exception as exn: logging.error("%s\n", str(exn)) return "Xen-API failure: %s" % str(self.details) def _details_map(self): return dict([(str(i), self.details[i]) for i in range(len(self.details))]) _RECONNECT_AND_RETRY = (lambda _: ()) class UDSHTTPConnection(httplib.HTTPConnection): """ Stupid hacked up HTTPConnection subclass to allow HTTP over Unix domain sockets. """ def connect(self): path = self.host.replace("_", "/") self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.sock.connect(path) class UDSTransport(xmlrpclib.Transport): def make_connection(self, host): return httplib.HTTPConnection(host) class Session(xmlrpclib.ServerProxy): """A server proxy and session manager for communicating with Xend using the Xen-API. Example: session = Session('http://localhost:9363/') session.login_with_password('me', 'mypassword') session.xenapi.VM.start(vm_uuid) session.xenapi.session.logout() For now, this class also supports the legacy XML-RPC API, using session.xend.domain('Domain-0') and similar. This support will disappear once there is a working Xen-API replacement for every call in the legacy API. """ def __init__(self, uri, transport=None, encoding=None, verbose=0, allow_none=1): xmlrpclib.ServerProxy.__init__(self, uri, transport, encoding, verbose, allow_none) self._session = None self.last_login_method = None self.last_login_params = None def xenapi_request(self, methodname, params): if methodname.startswith('login'): self._login(methodname, params) return None else: retry_count = 0 while retry_count < 3: full_params = (self._session,) + params result = _parse_result(getattr(self, methodname)(*full_params)) if result == _RECONNECT_AND_RETRY: retry_count += 1 if self.last_login_method: self._login(self.last_login_method, self.last_login_params) else: raise xmlrpclib.Fault(401, 'You must log in') else: return result raise xmlrpclib.Fault( 500, 'Tried 3 times to get a valid session, but failed') def _login(self, method, params): result = _parse_result(getattr(self, 'session.%s' % method)(*params)) if result == _RECONNECT_AND_RETRY: raise xmlrpclib.Fault( 500, 'Received SESSION_INVALID when logging in') self._session = result self.last_login_method = method self.last_login_params = params def __getattr__(self, name): if name == 'xenapi': return _Dispatcher(self.xenapi_request, None) elif name.startswith('login'): return lambda *params: self._login(name, params) else: return xmlrpclib.ServerProxy.__getattr__(self, name) def xapi_local(): return Session("http://_var_xapi_xapi/", transport=UDSTransport()) def _parse_result(result): if type(result) != dict or 'Status' not in result: raise xmlrpclib.Fault(500, 'Missing Status in response from server' + result) if result['Status'] == 'Success': if 'Value' in result: return result['Value'] else: raise xmlrpclib.Fault(500, 'Missing Value in response from server') else: if 'ErrorDescription' in result: if result['ErrorDescription'][0] == 'SESSION_INVALID': return _RECONNECT_AND_RETRY else: raise Failure(result['ErrorDescription']) else: raise xmlrpclib.Fault( 500, 'Missing ErrorDescription in response from server') # Based upon _Method from xmlrpclib. class _Dispatcher: def __init__(self, send, name): self.__send = send self.__name = name def __repr__(self): if self.__name: return '' % self.__name else: return '' def __getattr__(self, name): if self.__name is None: return _Dispatcher(self.__send, name) else: return _Dispatcher(self.__send, "%s.%s" % (self.__name, name)) def __call__(self, *args): return self.__send(self.__name, args) diff --git a/fence/agents/lib/fencing.py.py b/fence/agents/lib/fencing.py.py index 4e0b30af..91b11aae 100644 --- a/fence/agents/lib/fencing.py.py +++ b/fence/agents/lib/fencing.py.py @@ -1,1393 +1,1425 @@ #!@PYTHON@ -tt import sys, getopt, time, os, uuid, pycurl, stat import pexpect, re, syslog import logging import subprocess import threading import shlex import socket import textwrap import __main__ ## do not add code here. #BEGIN_VERSION_GENERATION RELEASE_VERSION = "New fence lib agent - test release on steroids" REDHAT_COPYRIGHT = "" BUILD_DATE = "March, 2008" #END_VERSION_GENERATION __all__ = ['atexit_handler', 'check_input', 'process_input', 'all_opt', 'show_docs', 'fence_login', 'fence_action', 'fence_logout'] EC_OK = 0 EC_GENERIC_ERROR = 1 EC_BAD_ARGS = 2 EC_LOGIN_DENIED = 3 EC_CONNECTION_LOST = 4 EC_TIMED_OUT = 5 EC_WAITING_ON = 6 EC_WAITING_OFF = 7 EC_STATUS = 8 EC_STATUS_HMC = 9 EC_PASSWORD_MISSING = 10 EC_INVALID_PRIVILEGES = 11 all_opt = { "help" : { "getopt" : "h", "longopt" : "help", "help" : "-h, --help Display this help and exit", "required" : "0", "shortdesc" : "Display help and exit", "order" : 54}, "version" : { "getopt" : "V", "longopt" : "version", "help" : "-V, --version Display version information and exit", "required" : "0", "shortdesc" : "Display version information and exit", "order" : 53}, "verbose" : { "getopt" : "v", "longopt" : "verbose", "help" : "-v, --verbose Verbose mode", "required" : "0", "order" : 51}, "debug" : { "getopt" : "D:", "longopt" : "debug-file", "help" : "-D, --debug-file=[debugfile] Debugging to output file", "required" : "0", "shortdesc" : "Write debug information to given file", "order" : 52}, "delay" : { "getopt" : ":", "longopt" : "delay", + "type" : "second", "help" : "--delay=[seconds] Wait X seconds before fencing is started", "required" : "0", "default" : "0", "order" : 200}, "agent" : { "getopt" : "", "help" : "", "order" : 1}, "web" : { "getopt" : "", "help" : "", "order" : 1}, "force_on" : { "getopt" : "", "help" : "", "order" : 1}, "action" : { "getopt" : "o:", "longopt" : "action", "help" : "-o, --action=[action] Action: status, reboot (default), off or on", "required" : "1", "shortdesc" : "Fencing action", "default" : "reboot", "order" : 1}, "fabric_fencing" : { "getopt" : "", "help" : "", "order" : 1}, "ipaddr" : { "getopt" : "a:", "longopt" : "ip", "help" : "-a, --ip=[ip] IP address or hostname of fencing device", "required" : "1", "order" : 1}, "ipport" : { "getopt" : "u:", "longopt" : "ipport", + "type" : "integer", "help" : "-u, --ipport=[port] TCP/UDP port to use for connection", "required" : "0", "shortdesc" : "TCP/UDP port to use for connection with device", "order" : 1}, "login" : { "getopt" : "l:", "longopt" : "username", "help" : "-l, --username=[name] Login name", "required" : "?", "order" : 1}, "no_login" : { "getopt" : "", "help" : "", "order" : 1}, "no_password" : { "getopt" : "", "help" : "", "order" : 1}, "no_port" : { "getopt" : "", "help" : "", "order" : 1}, "no_status" : { "getopt" : "", "help" : "", "order" : 1}, "no_on" : { "getopt" : "", "help" : "", "order" : 1}, "no_off" : { "getopt" : "", "help" : "", "order" : 1}, "telnet" : { "getopt" : "", "help" : "", "order" : 1}, "diag" : { "getopt" : "", "help" : "", "order" : 1}, "passwd" : { "getopt" : "p:", "longopt" : "password", "help" : "-p, --password=[password] Login password or passphrase", "required" : "0", "order" : 1}, "passwd_script" : { "getopt" : "S:", "longopt" : "password-script", "help" : "-S, --password-script=[script] Script to run to retrieve password", "required" : "0", "order" : 1}, "identity_file" : { "getopt" : "k:", "longopt" : "identity-file", "help" : "-k, --identity-file=[filename] Identity file (private key) for SSH", "required" : "0", "order" : 1}, "cmd_prompt" : { "getopt" : "c:", "longopt" : "command-prompt", "help" : "-c, --command-prompt=[prompt] Force Python regex for command prompt", "required" : "0", "order" : 1}, "secure" : { "getopt" : "x", "longopt" : "ssh", "help" : "-x, --ssh Use SSH connection", "required" : "0", "order" : 1}, "ssh_options" : { "getopt" : ":", "longopt" : "ssh-options", "help" : "--ssh-options=[options] SSH options to use", "required" : "0", "order" : 1}, "ssl" : { "getopt" : "z", "longopt" : "ssl", "help" : "-z, --ssl Use SSL connection with verifying certificate", "required" : "0", "order" : 1}, "ssl_insecure" : { "getopt" : "", "longopt" : "ssl-insecure", "help" : "--ssl-insecure Use SSL connection without verifying certificate", "required" : "0", "order" : 1}, "ssl_secure" : { "getopt" : "", "longopt" : "ssl-secure", "help" : "--ssl-secure Use SSL connection with verifying certificate", "required" : "0", "order" : 1}, "notls" : { "getopt" : "t", "longopt" : "notls", "help" : "-t, --notls " "Disable TLS negotiation and force SSL3.0. " "This should only be used for devices that do not support TLS1.0 and up.", "required" : "0", "order" : 1}, "tls1.0" : { "getopt" : "", "longopt" : "tls1.0", "help" : "--tls1.0 " "Disable TLS negotiation and force TLS1.0. " "This should only be used for devices that do not support TLS1.1 and up.", "required" : "0", "order" : 1}, "port" : { "getopt" : "n:", "longopt" : "plug", "help" : "-n, --plug=[id] " "Physical plug number on device, UUID or identification of machine", "required" : "1", "order" : 1}, "switch" : { "getopt" : "s:", "longopt" : "switch", "help" : "-s, --switch=[id] Physical switch number on device", "required" : "0", "order" : 1}, "exec" : { "getopt" : "e:", "longopt" : "exec", "help" : "-e, --exec=[command] Command to execute", "required" : "0", "order" : 1}, "vmware_type" : { "getopt" : "d:", "longopt" : "vmware_type", "help" : "-d, --vmware_type=[type] Type of VMware to connect", "required" : "0", "order" : 1}, "vmware_datacenter" : { "getopt" : "s:", "longopt" : "vmware-datacenter", "help" : "-s, --vmware-datacenter=[dc] VMWare datacenter filter", "required" : "0", "order" : 2}, "snmp_version" : { "getopt" : "d:", "longopt" : "snmp-version", "help" : "-d, --snmp-version=[version] Specifies SNMP version to use (1|2c|3)", "required" : "0", "shortdesc" : "Specifies SNMP version to use", "choices" : ["1", "2c", "3"], "order" : 1}, "community" : { "getopt" : "c:", "longopt" : "community", "help" : "-c, --community=[community] Set the community string", "required" : "0", "order" : 1}, "snmp_auth_prot" : { "getopt" : "b:", "longopt" : "snmp-auth-prot", "help" : "-b, --snmp-auth-prot=[prot] Set authentication protocol (MD5|SHA)", "required" : "0", "shortdesc" : "Set authentication protocol", "choices" : ["MD5", "SHA"], "order" : 1}, "snmp_sec_level" : { "getopt" : "E:", "longopt" : "snmp-sec-level", "help" : "-E, --snmp-sec-level=[level] " "Set security level (noAuthNoPriv|authNoPriv|authPriv)", "required" : "0", "shortdesc" : "Set security level", "choices" : ["noAuthNoPriv", "authNoPriv", "authPriv"], "order" : 1}, "snmp_priv_prot" : { "getopt" : "B:", "longopt" : "snmp-priv-prot", "help" : "-B, --snmp-priv-prot=[prot] Set privacy protocol (DES|AES)", "required" : "0", "shortdesc" : "Set privacy protocol", "choices" : ["DES", "AES"], "order" : 1}, "snmp_priv_passwd" : { "getopt" : "P:", "longopt" : "snmp-priv-passwd", "help" : "-P, --snmp-priv-passwd=[pass] Set privacy protocol password", "required" : "0", "order" : 1}, "snmp_priv_passwd_script" : { "getopt" : "R:", "longopt" : "snmp-priv-passwd-script", "help" : "-R, --snmp-priv-passwd-script Script to run to retrieve privacy password", "required" : "0", "order" : 1}, "inet4_only" : { "getopt" : "4", "longopt" : "inet4-only", "help" : "-4, --inet4-only Forces agent to use IPv4 addresses only", "required" : "0", "order" : 1}, "inet6_only" : { "getopt" : "6", "longopt" : "inet6-only", "help" : "-6, --inet6-only Forces agent to use IPv6 addresses only", "required" : "0", "order" : 1}, "separator" : { "getopt" : "C:", "longopt" : "separator", "help" : "-C, --separator=[char] Separator for CSV created by 'list' operation", "default" : ",", "required" : "0", "order" : 100}, "login_timeout" : { "getopt" : ":", "longopt" : "login-timeout", + "type" : "second", "help" : "--login-timeout=[seconds] Wait X seconds for cmd prompt after login", "default" : "5", "required" : "0", "order" : 200}, "shell_timeout" : { "getopt" : ":", "longopt" : "shell-timeout", + "type" : "second", "help" : "--shell-timeout=[seconds] Wait X seconds for cmd prompt after issuing command", "default" : "3", "required" : "0", "order" : 200}, "power_timeout" : { "getopt" : ":", "longopt" : "power-timeout", + "type" : "second", "help" : "--power-timeout=[seconds] Test X seconds for status change after ON/OFF", "default" : "20", "required" : "0", "order" : 200}, "power_wait" : { "getopt" : ":", "longopt" : "power-wait", + "type" : "second", "help" : "--power-wait=[seconds] Wait X seconds after issuing ON/OFF", "default" : "0", "required" : "0", "order" : 200}, "missing_as_off" : { "getopt" : "", "longopt" : "missing-as-off", "help" : "--missing-as-off Missing port returns OFF instead of failure", "required" : "0", "order" : 200}, "retry_on" : { "getopt" : ":", "longopt" : "retry-on", + "type" : "integer", "help" : "--retry-on=[attempts] Count of attempts to retry power on", "default" : "1", "required" : "0", "order" : 201}, "session_url" : { "getopt" : "s:", "longopt" : "session-url", "help" : "-s, --session-url URL to connect to XenServer on", "required" : "1", "order" : 1}, "sudo" : { "getopt" : "", "longopt" : "use-sudo", "help" : "--use-sudo Use sudo (without password) when calling 3rd party software", "required" : "0", "order" : 205}, "method" : { "getopt" : "m:", "longopt" : "method", "help" : "-m, --method=[method] Method to fence (onoff|cycle) (Default: onoff)", "required" : "0", "shortdesc" : "Method to fence", "default" : "onoff", "choices" : ["onoff", "cycle"], "order" : 1}, "telnet_path" : { "getopt" : ":", "longopt" : "telnet-path", "help" : "--telnet-path=[path] Path to telnet binary", "required" : "0", "default" : "@TELNET_PATH@", "order": 300}, "ssh_path" : { "getopt" : ":", "longopt" : "ssh-path", "help" : "--ssh-path=[path] Path to ssh binary", "required" : "0", "default" : "@SSH_PATH@", "order": 300}, "gnutlscli_path" : { "getopt" : ":", "longopt" : "gnutlscli-path", "help" : "--gnutlscli-path=[path] Path to gnutls-cli binary", "required" : "0", "default" : "@GNUTLSCLI_PATH@", "order": 300}, "sudo_path" : { "getopt" : ":", "longopt" : "sudo-path", "help" : "--sudo-path=[path] Path to sudo binary", "required" : "0", "default" : "@SUDO_PATH@", "order": 300}, "snmpwalk_path" : { "getopt" : ":", "longopt" : "snmpwalk-path", "help" : "--snmpwalk-path=[path] Path to snmpwalk binary", "required" : "0", "default" : "@SNMPWALK_PATH@", "order" : 300}, "snmpset_path" : { "getopt" : ":", "longopt" : "snmpset-path", "help" : "--snmpset-path=[path] Path to snmpset binary", "required" : "0", "default" : "@SNMPSET_PATH@", "order" : 300}, "snmpget_path" : { "getopt" : ":", "longopt" : "snmpget-path", "help" : "--snmpget-path=[path] Path to snmpget binary", "required" : "0", "default" : "@SNMPGET_PATH@", "order" : 300}, "snmp": { "getopt" : "", "help" : "", "order" : 1}, "port_as_ip": { "getopt" : "", "longopt" : "port-as-ip", "help" : "--port-as-ip Make \"port/plug\" to be an alias to IP address", "required" : "0", "order" : 200}, "on_target": { "getopt" : "", "help" : "", "order" : 1} } # options which are added automatically if 'key' is encountered ("default" is always added) DEPENDENCY_OPT = { "default" : ["help", "debug", "verbose", "version", "action", "agent", \ "power_timeout", "shell_timeout", "login_timeout", "power_wait", "retry_on", "delay"], "passwd" : ["passwd_script"], "sudo" : ["sudo_path"], "secure" : ["identity_file", "ssh_options", "ssh_path"], "telnet" : ["telnet_path"], "ipaddr" : ["ipport", "inet4_only", "inet6_only"], "port" : ["separator"], "ssl" : ["ssl_secure", "ssl_insecure", "gnutlscli_path"], "snmp" : ["snmp_auth_prot", "snmp_sec_level", "snmp_priv_prot", \ "snmp_priv_passwd", "snmp_priv_passwd_script", "community", \ "snmpset_path", "snmpget_path", "snmpwalk_path"] } class fspawn(pexpect.spawn): def __init__(self, options, command): logging.info("Running command: %s", command) pexpect.spawn.__init__(self, command) self.opt = options def log_expect(self, pattern, timeout): result = self.expect(pattern, timeout) logging.debug("Received: %s", self.before + self.after) return result def send(self, message): logging.debug("Sent: %s", message) return pexpect.spawn.send(self, message) # send EOL according to what was detected in login process (telnet) def send_eol(self, message): return self.send(message + self.opt["eol"]) def atexit_handler(): try: sys.stdout.close() os.close(1) except IOError: logging.error("%s failed to close standard output\n", sys.argv[0]) sys.exit(EC_GENERIC_ERROR) def _add_dependency_options(options): ## Add also options which are available for every fence agent added_opt = [] for opt in options + ["default"]: if opt in DEPENDENCY_OPT: added_opt.extend([y for y in DEPENDENCY_OPT[opt] if options.count(y) == 0]) if not "port" in (options + added_opt) and \ not "nodename" in (options + added_opt) and \ "ipaddr" in (options + added_opt): added_opt.append("port_as_ip") all_opt["port"]["help"] = "-n, --plug=[ip] IP address or hostname of fencing device " \ "(together with --port-as-ip)" return added_opt def fail_usage(message="", stop=True): if len(message) > 0: logging.error("%s\n", message) if stop: logging.error("Please use '-h' for usage\n") sys.exit(EC_GENERIC_ERROR) def fail(error_code): message = { EC_LOGIN_DENIED : "Unable to connect/login to fencing device", EC_CONNECTION_LOST : "Connection lost", EC_TIMED_OUT : "Connection timed out", EC_WAITING_ON : "Failed: Timed out waiting to power ON", EC_WAITING_OFF : "Failed: Timed out waiting to power OFF", EC_STATUS : "Failed: Unable to obtain correct plug status or plug is not available", EC_STATUS_HMC : "Failed: Either unable to obtain correct plug status, " "partition is not available or incorrect HMC version used", EC_PASSWORD_MISSING : "Failed: You have to set login password", EC_INVALID_PRIVILEGES : "Failed: The user does not have the correct privileges to do the requested action." }[error_code] + "\n" logging.error("%s\n", message) sys.exit(EC_GENERIC_ERROR) def usage(avail_opt): print("Usage:") print("\t" + os.path.basename(sys.argv[0]) + " [options]") print("Options:") sorted_list = [(key, all_opt[key]) for key in avail_opt] sorted_list.sort(key=lambda x: x[1]["order"]) for key, value in sorted_list: if len(value["help"]) != 0: print(" " + _join_wrap([value["help"]], first_indent=3)) def metadata(avail_opt, docs): # avail_opt has to be unique, if there are duplicities then they should be removed sorted_list = [(key, all_opt[key]) for key in list(set(avail_opt))] sorted_list.sort(key=lambda x: (x[1]["order"], x[0])) print("") print("") for (symlink, desc) in docs.get("symlink", []): print("") print("" + docs["longdesc"] + "") print("" + docs["vendorurl"] + "") print("") for option, _ in sorted_list: if "help" in all_opt[option] and len(all_opt[option]["help"]) > 0: print("\t") default = "" if "default" in all_opt[option]: default = "default=\"" + _encode_html_entities(str(all_opt[option]["default"])) + "\" " mixed = all_opt[option]["help"] ## split it between option and help text res = re.compile(r"^(.*?--\S+)\s+", re.IGNORECASE | re.S).search(mixed) if None != res: mixed = res.group(1) mixed = _encode_html_entities(mixed) if not "shortdesc" in all_opt[option]: shortdesc = re.sub("\s\s+", " ", all_opt[option]["help"][31:]) else: shortdesc = all_opt[option]["shortdesc"] print("\t\t") if "choices" in all_opt[option]: print("\t\t") for choice in all_opt[option]["choices"]: print("\t\t\t") elif all_opt[option]["getopt"].count(":") > 0: - print("\t\t") + t = all_opt[option].get("type", "string") + print("\t\t") else: print("\t\t") print("\t\t" + shortdesc + "") print("\t") print("") print("") (available_actions, _) = _get_available_actions(avail_opt) if "on" in available_actions: available_actions.remove("on") on_target = ' on_target="1"' if avail_opt.count("on_target") else '' print("\t" % (on_target, avail_opt.count("fabric_fencing"))) for action in available_actions: print("\t" % (action)) print("") print("") def process_input(avail_opt): avail_opt.extend(_add_dependency_options(avail_opt)) # @todo: this should be put elsewhere? os.putenv("LANG", "C") os.putenv("LC_ALL", "C") if "port_as_ip" in avail_opt: avail_opt.append("port") if len(sys.argv) > 1: opt = _parse_input_cmdline(avail_opt) else: opt = _parse_input_stdin(avail_opt) if "--port-as-ip" in opt and "--plug" in opt: opt["--ip"] = opt["--plug"] return opt ## ## This function checks input and answers if we want to have same answers ## in each of the fencing agents. It looks for possible errors and run ## password script to set a correct password ###### def check_input(device_opt, opt, other_conditions = False): device_opt.extend(_add_dependency_options(device_opt)) options = dict(opt) options["device_opt"] = device_opt _update_metadata(options) options = _set_default_values(options) options["--action"] = options["--action"].lower() ## In special cases (show help, metadata or version) we don't need to check anything ##### # OCF compatibility if options["--action"] == "meta-data": options["--action"] = "metadata" if options["--action"] == "metadata" or any(k in options for k in ("--help", "--version")): return options if "--verbose" in options: logging.getLogger().setLevel(logging.DEBUG) ## add logging to syslog logging.getLogger().addHandler(SyslogLibHandler()) ## add logging to stderr - logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stderr)) + logging.getLogger().addHandler(logging.StreamHandler(sys.stderr)) (acceptable_actions, _) = _get_available_actions(device_opt) if 1 == device_opt.count("fabric_fencing"): acceptable_actions.extend(["enable", "disable"]) if 0 == acceptable_actions.count(options["--action"]): fail_usage("Failed: Unrecognised action '" + options["--action"] + "'") ## Compatibility layer ##### if options["--action"] == "enable": options["--action"] = "on" if options["--action"] == "disable": options["--action"] = "off" if options["--action"] == "validate-all" and not other_conditions: _validate_input(options, False) sys.exit(EC_OK) else: _validate_input(options, True) if "--debug-file" in options: try: debug_file = logging.FileHandler(options["--debug-file"]) debug_file.setLevel(logging.DEBUG) logging.getLogger().addHandler(debug_file) except IOError: logging.error("Unable to create file %s", options["--debug-file"]) fail_usage("Failed: Unable to create file " + options["--debug-file"]) if "--snmp-priv-passwd-script" in options: options["--snmp-priv-passwd"] = os.popen(options["--snmp-priv-passwd-script"]).read().rstrip() if "--password-script" in options: options["--password"] = os.popen(options["--password-script"]).read().rstrip() return options ## Obtain a power status from possibly more than one plug ## "on" is returned if at least one plug is ON ###### def get_multi_power_fn(connection, options, get_power_fn): status = "off" plugs = options["--plugs"] if "--plugs" in options else [""] for plug in plugs: try: options["--uuid"] = str(uuid.UUID(plug)) except ValueError: pass except KeyError: pass options["--plug"] = plug plug_status = get_power_fn(connection, options) if plug_status != "off": status = plug_status return status def set_multi_power_fn(connection, options, set_power_fn, get_power_fn, retry_attempts=1): plugs = options["--plugs"] if "--plugs" in options else [""] for _ in range(retry_attempts): for plug in plugs: try: options["--uuid"] = str(uuid.UUID(plug)) except ValueError: pass except KeyError: pass options["--plug"] = plug set_power_fn(connection, options) time.sleep(int(options["--power-wait"])) for _ in range(int(options["--power-timeout"])): if get_multi_power_fn(connection, options, get_power_fn) != options["--action"]: time.sleep(1) else: return True return False def show_docs(options, docs=None): device_opt = options["device_opt"] if docs == None: docs = {} docs["shortdesc"] = "Fence agent" docs["longdesc"] = "" if "--help" in options: usage(device_opt) sys.exit(0) if options.get("--action", "") == "metadata": if "port_as_ip" in device_opt: device_opt.remove("separator") metadata(device_opt, docs) sys.exit(0) if "--version" in options: print(__main__.RELEASE_VERSION, __main__.BUILD_DATE) print(__main__.REDHAT_COPYRIGHT) sys.exit(0) def fence_action(connection, options, set_power_fn, get_power_fn, get_outlet_list=None, reboot_cycle_fn=None): result = 0 try: if "--plug" in options: options["--plugs"] = options["--plug"].split(",") ## Process options that manipulate fencing device ##### if (options["--action"] in ["list", "list-status"]) or \ ((options["--action"] == "monitor") and 1 == options["device_opt"].count("port") and \ 0 == options["device_opt"].count("port_as_ip")): if 0 == options["device_opt"].count("port"): print("N/A") elif get_outlet_list == None: ## @todo: exception? ## This is just temporal solution, we will remove default value ## None as soon as all existing agent will support this operation print("NOTICE: List option is not working on this device yet") else: options["--original-action"] = options["--action"] options["--action"] = "list" outlets = get_outlet_list(connection, options) options["--action"] = options["--original-action"] del options["--original-action"] ## keys can be numbers (port numbers) or strings (names of VM, UUID) for outlet_id in list(outlets.keys()): (alias, status) = outlets[outlet_id] if status is None or (not status.upper() in ["ON", "OFF"]): status = "UNKNOWN" status = status.upper() if options["--action"] == "list": print(outlet_id + options["--separator"] + alias) elif options["--action"] == "list-status": print(outlet_id + options["--separator"] + alias + options["--separator"] + status) return if options["--action"] == "monitor" and not "port" in options["device_opt"] and "no_status" in options["device_opt"]: # Unable to do standard monitoring because 'status' action is not available return 0 status = None if not "no_status" in options["device_opt"]: status = get_multi_power_fn(connection, options, get_power_fn) if status != "on" and status != "off": fail(EC_STATUS) if options["--action"] == status: if not (status == "on" and "force_on" in options["device_opt"]): print("Success: Already %s" % (status.upper())) return 0 if options["--action"] == "on": if set_multi_power_fn(connection, options, set_power_fn, get_power_fn, 1 + int(options["--retry-on"])): print("Success: Powered ON") else: fail(EC_WAITING_ON) elif options["--action"] == "off": if set_multi_power_fn(connection, options, set_power_fn, get_power_fn): print("Success: Powered OFF") else: fail(EC_WAITING_OFF) elif options["--action"] == "reboot": power_on = False if options.get("--method", "").lower() == "cycle" and reboot_cycle_fn is not None: for _ in range(1, 1 + int(options["--retry-on"])): if reboot_cycle_fn(connection, options): power_on = True break if not power_on: fail(EC_TIMED_OUT) else: if status != "off": options["--action"] = "off" if not set_multi_power_fn(connection, options, set_power_fn, get_power_fn): fail(EC_WAITING_OFF) options["--action"] = "on" try: power_on = set_multi_power_fn(connection, options, set_power_fn, get_power_fn, int(options["--retry-on"])) except Exception as ex: # an error occured during power ON phase in reboot # fence action was completed succesfully even in that case logging.warning("%s", str(ex)) if power_on == False: # this should not fail as node was fenced succesfully logging.error('Timed out waiting to power ON\n') print("Success: Rebooted") elif options["--action"] == "status": print("Status: " + status.upper()) if status.upper() == "OFF": result = 2 elif options["--action"] == "monitor": pass except pexpect.EOF: fail(EC_CONNECTION_LOST) except pexpect.TIMEOUT: fail(EC_TIMED_OUT) except pycurl.error as ex: logging.error("%s\n", str(ex)) fail(EC_TIMED_OUT) except socket.timeout as ex: logging.error("%s\n", str(ex)) fail(EC_TIMED_OUT) return result def fence_login(options, re_login_string=r"(login\s*: )|((?!Last )Login Name: )|(username: )|(User Name :)"): run_delay(options) if "eol" not in options: options["eol"] = "\r\n" if "--command-prompt" in options and type(options["--command-prompt"]) is not list: options["--command-prompt"] = [options["--command-prompt"]] try: if "--ssl" in options: conn = _open_ssl_connection(options) elif "--ssh" in options and "--identity-file" not in options: conn = _login_ssh_with_password(options, re_login_string) elif "--ssh" in options and "--identity-file" in options: conn = _login_ssh_with_identity_file(options) else: conn = _login_telnet(options, re_login_string) except pexpect.EOF as exception: logging.debug("%s", str(exception)) fail(EC_LOGIN_DENIED) except pexpect.TIMEOUT as exception: logging.debug("%s", str(exception)) fail(EC_LOGIN_DENIED) return conn def is_executable(path): if os.path.exists(path): stats = os.stat(path) if stat.S_ISREG(stats.st_mode) and os.access(path, os.X_OK): return True return False def run_command(options, command, timeout=None, env=None): if timeout is None and "--power-timeout" in options: timeout = options["--power-timeout"] if timeout is not None: timeout = float(timeout) # For IPMI password occurs on command line, it should not be part of debug info log_command = command if "ipmitool" in log_command: log_command = re.sub("-P (.+?) -p", "-P [set] -p", log_command) logging.info("Executing: %s\n", log_command) try: process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) except OSError: fail_usage("Unable to run %s\n" % command) thread = threading.Thread(target=process.wait) thread.start() thread.join(timeout) if thread.is_alive(): process.kill() fail(EC_TIMED_OUT) status = process.wait() (pipe_stdout, pipe_stderr) = process.communicate() process.stdout.close() process.stderr.close() logging.debug("%s %s %s\n", str(status), str(pipe_stdout), str(pipe_stderr)) return (status, pipe_stdout, pipe_stderr) def run_delay(options): ## Delay is important for two-node clusters fencing but we do not need to delay 'status' operations if options["--action"] in ["off", "reboot"]: logging.info("Delay %s second(s) before logging in to the fence device", options["--delay"]) time.sleep(int(options["--delay"])) def fence_logout(conn, logout_string, sleep=0): # Logout is not required part of fencing but we should attempt to do it properly # In some cases our 'exit' command is faster and we can not close connection as it # was already closed by fencing device try: conn.send_eol(logout_string) time.sleep(sleep) conn.close() except OSError: pass except pexpect.ExceptionPexpect: pass # Convert array of format [[key1, value1], [key2, value2], ... [keyN, valueN]] to dict, where key is # in format a.b.c.d...z and returned dict has key only z def array_to_dict(array): return dict([[x[0].split(".")[-1], x[1]] for x in array]) ## Own logger handler that uses old-style syslog handler as otherwise everything is sourced ## from /dev/syslog class SyslogLibHandler(logging.StreamHandler): """ A handler class that correctly push messages into syslog """ def emit(self, record): syslog_level = { logging.CRITICAL:syslog.LOG_CRIT, logging.ERROR:syslog.LOG_ERR, logging.WARNING:syslog.LOG_WARNING, logging.INFO:syslog.LOG_INFO, logging.DEBUG:syslog.LOG_DEBUG, logging.NOTSET:syslog.LOG_DEBUG, }[record.levelno] msg = self.format(record) # syslos.syslog can not have 0x00 character inside or exception is thrown syslog.syslog(syslog_level, msg.replace("\x00", "\n")) return def _open_ssl_connection(options): gnutls_opts = "" ssl_opts = "" if "--notls" in options: gnutls_opts = "--priority \"NORMAL:-VERS-TLS1.2:-VERS-TLS1.1:-VERS-TLS1.0:+VERS-SSL3.0\"" elif "--tls1.0" in options: gnutls_opts = "--priority \"NORMAL:-VERS-TLS1.2:-VERS-TLS1.1:+VERS-TLS1.0:%LATEST_RECORD_VERSION\"" # --ssl is same as the --ssl-secure; it means we want to verify certificate in these cases if "--ssl-insecure" in options: ssl_opts = "--insecure" command = '%s %s %s --crlf -p %s %s' % \ (options["--gnutlscli-path"], gnutls_opts, ssl_opts, options["--ipport"], options["--ip"]) try: conn = fspawn(options, command) except pexpect.ExceptionPexpect as ex: logging.error("%s\n", str(ex)) sys.exit(EC_GENERIC_ERROR) return conn def _login_ssh_with_identity_file(options): if "--inet6-only" in options: force_ipvx = "-6 " elif "--inet4-only" in options: force_ipvx = "-4 " else: force_ipvx = "" command = '%s %s %s@%s -i %s -p %s' % \ (options["--ssh-path"], force_ipvx, options["--username"], options["--ip"], \ options["--identity-file"], options["--ipport"]) if "--ssh-options" in options: command += ' ' + options["--ssh-options"] conn = fspawn(options, command) result = conn.log_expect(["Enter passphrase for key '" + options["--identity-file"] + "':", \ "Are you sure you want to continue connecting (yes/no)?"] + \ options["--command-prompt"], int(options["--login-timeout"])) if result == 1: conn.sendline("yes") result = conn.log_expect( ["Enter passphrase for key '" + options["--identity-file"]+"':"] + \ options["--command-prompt"], int(options["--login-timeout"])) if result == 0: if "--password" in options: conn.sendline(options["--password"]) conn.log_expect(options["--command-prompt"], int(options["--login-timeout"])) else: fail_usage("Failed: You have to enter passphrase (-p) for identity file") return conn def _login_telnet(options, re_login_string): re_login = re.compile(re_login_string, re.IGNORECASE) re_pass = re.compile("(password)|(pass phrase)", re.IGNORECASE) conn = fspawn(options, options["--telnet-path"]) conn.send("set binary\n") conn.send("open %s -%s\n"%(options["--ip"], options["--ipport"])) conn.log_expect(re_login, int(options["--login-timeout"])) conn.send_eol(options["--username"]) ## automatically change end of line separator screen = conn.read_nonblocking(size=100, timeout=int(options["--shell-timeout"])) if re_login.search(screen) != None: options["eol"] = "\n" conn.send_eol(options["--username"]) conn.log_expect(re_pass, int(options["--login-timeout"])) elif re_pass.search(screen) == None: conn.log_expect(re_pass, int(options["--shell-timeout"])) try: conn.send_eol(options["--password"]) valid_password = conn.log_expect([re_login] + \ options["--command-prompt"], int(options["--shell-timeout"])) if valid_password == 0: ## password is invalid or we have to change EOL separator options["eol"] = "\r" conn.send_eol("") screen = conn.read_nonblocking(size=100, timeout=int(options["--shell-timeout"])) ## after sending EOL the fence device can either show 'Login' or 'Password' if re_login.search(conn.after + screen) != None: conn.send_eol("") conn.send_eol(options["--username"]) conn.log_expect(re_pass, int(options["--login-timeout"])) conn.send_eol(options["--password"]) conn.log_expect(options["--command-prompt"], int(options["--login-timeout"])) except KeyError: fail(EC_PASSWORD_MISSING) return conn def _login_ssh_with_password(options, re_login_string): re_login = re.compile(re_login_string, re.IGNORECASE) re_pass = re.compile("(password)|(pass phrase)", re.IGNORECASE) if "--inet6-only" in options: force_ipvx = "-6 " elif "--inet4-only" in options: force_ipvx = "-4 " else: force_ipvx = "" command = '%s %s %s@%s -p %s -o PubkeyAuthentication=no' % \ (options["--ssh-path"], force_ipvx, options["--username"], options["--ip"], options["--ipport"]) if "--ssh-options" in options: command += ' ' + options["--ssh-options"] conn = fspawn(options, command) if "telnet_over_ssh" in options: # This is for stupid ssh servers (like ALOM) which behave more like telnet # (ignore name and display login prompt) result = conn.log_expect( \ [re_login, "Are you sure you want to continue connecting (yes/no)?"], int(options["--login-timeout"])) if result == 1: conn.sendline("yes") # Host identity confirm conn.log_expect(re_login, int(options["--login-timeout"])) conn.sendline(options["--username"]) conn.log_expect(re_pass, int(options["--login-timeout"])) else: result = conn.log_expect( \ ["ssword:", "Are you sure you want to continue connecting (yes/no)?"], int(options["--login-timeout"])) if result == 1: conn.sendline("yes") conn.log_expect("ssword:", int(options["--login-timeout"])) conn.sendline(options["--password"]) conn.log_expect(options["--command-prompt"], int(options["--login-timeout"])) return conn # # To update metadata, we change values in all_opt def _update_metadata(options): device_opt = options["device_opt"] if device_opt.count("login") and device_opt.count("no_login") == 0: all_opt["login"]["required"] = "1" else: all_opt["login"]["required"] = "0" if device_opt.count("port_as_ip"): all_opt["ipaddr"]["required"] = "0" all_opt["port"]["required"] = "0" (available_actions, default_value) = _get_available_actions(device_opt) all_opt["action"]["default"] = default_value actions_with_default = \ [x if not x == all_opt["action"]["default"] else x + " (default)" for x in available_actions] all_opt["action"]["help"] = \ "-o, --action=[action] Action: %s" % (_join_wrap(actions_with_default, last_separator=" or ")) if device_opt.count("ipport"): default_value = None default_string = None if "default" in all_opt["ipport"]: default_value = all_opt["ipport"]["default"] elif device_opt.count("web") and device_opt.count("ssl"): default_value = "80" default_string = "(default 80, 443 if --ssl option is used)" elif device_opt.count("telnet") and device_opt.count("secure"): default_value = "23" default_string = "(default 23, 22 if --ssh option is used)" else: tcp_ports = {"community" : "161", "secure" : "22", "telnet" : "23", "web" : "80", "ssl" : "443"} # all cases where next command returns multiple results are covered by previous blocks protocol = [x for x in ["community", "secure", "ssl", "web", "telnet"] if device_opt.count(x)][0] default_value = tcp_ports[protocol] if default_string is None: all_opt["ipport"]["help"] = "-u, --ipport=[port] TCP/UDP port to use (default %s)" % \ (default_value) else: all_opt["ipport"]["help"] = "-u, --ipport=[port] TCP/UDP port to use\n" + " "*40 + default_string def _set_default_values(options): if "ipport" in options["device_opt"]: if not "--ipport" in options: if "default" in all_opt["ipport"]: options["--ipport"] = all_opt["ipport"]["default"] elif "community" in options["device_opt"]: options["--ipport"] = "161" elif "--ssh" in options or all_opt["secure"].get("default", "0") == "1": options["--ipport"] = "22" elif "--ssl" in options or all_opt["ssl"].get("default", "0") == "1": options["--ipport"] = "443" elif "--ssl-secure" in options or all_opt["ssl_secure"].get("default", "0") == "1": options["--ipport"] = "443" elif "--ssl-insecure" in options or all_opt["ssl_insecure"].get("default", "0") == "1": options["--ipport"] = "443" elif "web" in options["device_opt"]: options["--ipport"] = "80" elif "telnet" in options["device_opt"]: options["--ipport"] = "23" if "--ipport" in options: all_opt["ipport"]["default"] = options["--ipport"] for opt in options["device_opt"]: if "default" in all_opt[opt] and not opt == "ipport": getopt_long = "--" + all_opt[opt]["longopt"] if getopt_long not in options: options[getopt_long] = all_opt[opt]["default"] return options # stop = True/False : exit fence agent when problem is encountered def _validate_input(options, stop = True): device_opt = options["device_opt"] valid_input = True if "--username" not in options and \ device_opt.count("login") and (device_opt.count("no_login") == 0): valid_input = False fail_usage("Failed: You have to set login name", stop) if device_opt.count("ipaddr") and "--ip" not in options and "--managed" not in options: valid_input = False fail_usage("Failed: You have to enter fence address", stop) if device_opt.count("no_password") == 0: if 0 == device_opt.count("identity_file"): if not ("--password" in options or "--password-script" in options): valid_input = False fail_usage("Failed: You have to enter password or password script", stop) else: if not ("--password" in options or \ "--password-script" in options or "--identity-file" in options): valid_input = False fail_usage("Failed: You have to enter password, password script or identity file", stop) if "--ssh" not in options and "--identity-file" in options: valid_input = False fail_usage("Failed: You have to use identity file together with ssh connection (-x)", stop) if "--identity-file" in options and not os.path.isfile(options["--identity-file"]): valid_input = False fail_usage("Failed: Identity file " + options["--identity-file"] + " does not exist", stop) if (0 == ["list", "list-status", "monitor"].count(options["--action"])) and \ "--plug" not in options and device_opt.count("port") and \ device_opt.count("no_port") == 0 and not device_opt.count("port_as_ip"): valid_input = False fail_usage("Failed: You have to enter plug number or machine identification", stop) if "--plug" in options and len(options["--plug"].split(",")) > 1 and \ "--method" in options and options["--method"] == "cycle": valid_input = False fail_usage("Failed: Cannot use --method cycle for more than 1 plug", stop) for failed_opt in _get_opts_with_invalid_choices(options): valid_input = False fail_usage("Failed: You have to enter a valid choice for %s from the valid values: %s" % \ ("--" + all_opt[failed_opt]["longopt"], str(all_opt[failed_opt]["choices"])), stop) + for failed_opt in _get_opts_with_invalid_types(options): + valid_input = False + if all_opt[failed_opt]["type"] == "second": + fail_usage("Failed: The value you have entered for %s is not a valid time in seconds" % \ + ("--" + all_opt[failed_opt]["longopt"]), stop) + else: + fail_usage("Failed: The value you have entered for %s is not a valid %s" % \ + ("--" + all_opt[failed_opt]["longopt"], all_opt[failed_opt]["type"]), stop) + return valid_input def _encode_html_entities(text): return text.replace("&", "&").replace('"', """).replace('<', "<"). \ replace('>', ">").replace("'", "'") def _prepare_getopt_args(options): getopt_string = "" longopt_list = [] for k in options: if k in all_opt and all_opt[k]["getopt"] != ":": # getopt == ":" means that opt is without short getopt, but has value getopt_string += all_opt[k]["getopt"] elif k not in all_opt: fail_usage("Parse error: unknown option '"+k+"'") if k in all_opt and "longopt" in all_opt[k]: if all_opt[k]["getopt"].endswith(":"): longopt_list.append(all_opt[k]["longopt"] + "=") else: longopt_list.append(all_opt[k]["longopt"]) return (getopt_string, longopt_list) def _parse_input_stdin(avail_opt): opt = {} name = "" for line in sys.stdin.readlines(): line = line.strip() if (line.startswith("#")) or (len(line) == 0): continue (name, value) = (line + "=").split("=", 1) value = value[:-1] if avail_opt.count(name) == 0 and name in ["nodename"]: continue elif avail_opt.count(name) == 0: logging.warning("Parse error: Ignoring unknown option '%s'\n", line) continue if all_opt[name]["getopt"].endswith(":"): opt["--"+all_opt[name]["longopt"].rstrip(":")] = value elif value.lower() in ["1", "yes", "on", "true"]: opt["--"+all_opt[name]["longopt"]] = "1" else: logging.warning("Parse error: Ignoring option '%s' because it does not have value\n", name) return opt def _parse_input_cmdline(avail_opt): filtered_opts = {} _verify_unique_getopt(avail_opt) (getopt_string, longopt_list) = _prepare_getopt_args(avail_opt) try: (entered_opt, left_arg) = getopt.gnu_getopt(sys.argv[1:], getopt_string, longopt_list) if len(left_arg) > 0: logging.warning("Unused arguments on command line: %s" % (str(left_arg))) except getopt.GetoptError as error: fail_usage("Parse error: " + error.msg) for opt in avail_opt: filtered_opts.update({opt : all_opt[opt]}) # Short and long getopt names are changed to consistent "--" + long name (e.g. --username) long_opts = {} for arg_name in list(dict(entered_opt).keys()): all_key = [key for (key, value) in list(filtered_opts.items()) \ if "--" + value.get("longopt", "") == arg_name or "-" + value.get("getopt", "").rstrip(":") == arg_name][0] long_opts["--" + filtered_opts[all_key]["longopt"]] = dict(entered_opt)[arg_name] # This test is specific because it does not apply to input on stdin if "port_as_ip" in avail_opt and not "--port-as-ip" in long_opts and "--plug" in long_opts: fail_usage("Parser error: option -n/--plug is not recognized") return long_opts # for ["John", "Mary", "Eli"] returns "John, Mary and Eli" def _join2(words, normal_separator=", ", last_separator=" and "): if len(words) <= 1: return "".join(words) else: return last_separator.join([normal_separator.join(words[:-1]), words[-1]]) def _join_wrap(words, normal_separator=", ", last_separator=" and ", first_indent=42): x = _join2(words, normal_separator, last_separator) wrapper = textwrap.TextWrapper() wrapper.initial_indent = " "*first_indent wrapper.subsequent_indent = " "*40 wrapper.width = 85 wrapper.break_on_hyphens = False wrapper.break_long_words = False wrapped_text = "" for line in wrapper.wrap(x): wrapped_text += line + "\n" return wrapped_text.lstrip().rstrip("\n") def _get_opts_with_invalid_choices(options): options_failed = [] device_opt = options["device_opt"] for opt in device_opt: if "choices" in all_opt[opt]: longopt = "--" + all_opt[opt]["longopt"] possible_values_upper = [y.upper() for y in all_opt[opt]["choices"]] if longopt in options: options[longopt] = options[longopt].upper() if not options["--" + all_opt[opt]["longopt"]] in possible_values_upper: options_failed.append(opt) return options_failed +def _get_opts_with_invalid_types(options): + options_failed = [] + device_opt = options["device_opt"] + + for opt in device_opt: + if "type" in all_opt[opt]: + longopt = "--" + all_opt[opt]["longopt"] + if longopt in options: + if all_opt[opt]["type"] in ["integer", "second"]: + try: + number = int(options["--" + all_opt[opt]["longopt"]]) + except ValueError: + options_failed.append(opt) + return options_failed + def _verify_unique_getopt(avail_opt): used_getopt = set() for opt in avail_opt: getopt_value = all_opt[opt].get("getopt", "").rstrip(":") if getopt_value and getopt_value in used_getopt: fail_usage("Short getopt for %s (-%s) is not unique" % (opt, getopt_value)) else: used_getopt.add(getopt_value) def _get_available_actions(device_opt): available_actions = ["on", "off", "reboot", "status", "list", "list-status", \ "monitor", "metadata", "validate-all"] default_value = "reboot" if device_opt.count("fabric_fencing"): available_actions.remove("reboot") default_value = "off" if device_opt.count("no_status"): available_actions.remove("status") if device_opt.count("no_on"): available_actions.remove("on") if device_opt.count("no_off"): available_actions.remove("off") if not device_opt.count("separator"): available_actions.remove("list") available_actions.remove("list-status") if device_opt.count("diag"): available_actions.append("diag") return (available_actions, default_value) diff --git a/fence/agents/lib/metadata.rng b/fence/agents/lib/metadata.rng index c7758e69..2185cbf8 100644 --- a/fence/agents/lib/metadata.rng +++ b/fence/agents/lib/metadata.rng @@ -1,77 +1,79 @@ boolean string + second + integer select 0 1 diff --git a/fence/agents/lpar/Makefile.am b/fence/agents/lpar/Makefile.am deleted file mode 100644 index ae4fb5e1..00000000 --- a/fence/agents/lpar/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_lpar - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a test -n 1 --managed 1 - -include $(top_srcdir)/make/fencebuild.mk -include $(top_srcdir)/make/fenceman.mk -include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/lpar/fence_lpar.py b/fence/agents/lpar/fence_lpar.py index 26111ce9..5487b84c 100644 --- a/fence/agents/lpar/fence_lpar.py +++ b/fence/agents/lpar/fence_lpar.py @@ -1,159 +1,181 @@ #!@PYTHON@ -tt ##### ## ## The Following Agent Has Been Tested On: ## ## Version ## +---------------------------------------------+ ## Tested on HMC ## ##### import sys, re import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, fail_usage, EC_STATUS_HMC #BEGIN_VERSION_GENERATION RELEASE_VERSION="" REDHAT_COPYRIGHT="" BUILD_DATE="" #END_VERSION_GENERATION def get_power_status(conn, options): if options["--hmc-version"] == "3": conn.send("lssyscfg -r lpar -m " + options["--managed"] + " -n " + options["--plug"] + " -F name,state\n") conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) try: status = re.compile("^" + options["--plug"] + ",(.*?),.*$", re.IGNORECASE | re.MULTILINE).search(conn.before).group(1) except AttributeError: fail(EC_STATUS_HMC) - elif options["--hmc-version"] == "4": + elif options["--hmc-version"] in ["4", "IVM"]: conn.send("lssyscfg -r lpar -m "+ options["--managed"] + " --filter 'lpar_names=" + options["--plug"] + "'\n") conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) try: status = re.compile(",state=(.*?),", re.IGNORECASE).search(conn.before).group(1) except AttributeError: fail(EC_STATUS_HMC) ## ## Transformation to standard ON/OFF status if possible if status in ["Running", "Open Firmware", "Shutting Down", "Starting"]: status = "on" else: status = "off" return status def set_power_status(conn, options): if options["--hmc-version"] == "3": conn.send("chsysstate -o " + options["--action"] + " -r lpar -m " + options["--managed"] + " -n " + options["--plug"] + "\n") conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) - elif options["--hmc-version"] == "4": + elif options["--hmc-version"] in ["4", "IVM"]: if options["--action"] == "on": conn.send("chsysstate -o on -r lpar -m " + options["--managed"] + " -n " + options["--plug"] + " -f `lssyscfg -r lpar -F curr_profile " + " -m " + options["--managed"] + " --filter \"lpar_names=" + options["--plug"] + "\"`\n") else: conn.send("chsysstate -o shutdown -r lpar --immed" + " -m " + options["--managed"] + " -n " + options["--plug"] + "\n") conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) def get_lpar_list(conn, options): outlets = {} if options["--hmc-version"] == "3": conn.send("query_partition_names -m " + options["--managed"] + "\n") conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) ## We have to remove first 3 lines (command + header) and last line (part of new prompt) #### res = re.search("^.+?\n(.+?\n){2}(.*)\n.*$", conn.before, re.S) if res == None: fail_usage("Unable to parse output of list command") lines = res.group(2).split("\n") for outlet_line in lines: outlets[outlet_line.rstrip()] = ("", "") elif options["--hmc-version"] == "4": conn.send("lssyscfg -r lpar -m " + options["--managed"] + " -F name:state\n") conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) ## We have to remove first line (command) and last line (part of new prompt) #### res = re.search("^.+?\n(.*)\n.*$", conn.before, re.S) if res == None: fail_usage("Unable to parse output of list command") lines = res.group(1).split("\n") for outlet_line in lines: - (port, status) = outlet_line.split(":") + try: + (port, status) = outlet_line.split(":") + except ValueError: + fail_usage('Output does not match expected HMC version, try different one'); + outlets[port] = ("", status) + elif options["--hmc-version"] == "IVM": + conn.send("lssyscfg -r lpar -m " + options["--managed"] + + " -F name,state\n") + conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) + + ## We have to remove first line (command) and last line (part of new prompt) + #### + res = re.search("^.+?\n(.*)\n.*$", conn.before, re.S) + + if res == None: + fail_usage("Unable to parse output of list command") + + lines = res.group(1).split("\n") + for outlet_line in lines: + try: + (port, status) = outlet_line.split(",") + except ValueError: + fail_usage('Output does not match expected HMC version, try different one'); outlets[port] = ("", status) return outlets def define_new_opts(): all_opt["managed"] = { "getopt" : "s:", "longopt" : "managed", "help" : "-s, --managed=[id] Name of the managed system", "required" : "0", "shortdesc" : "Managed system name", "order" : 1} all_opt["hmc_version"] = { "getopt" : "H:", "longopt" : "hmc-version", - "help" : "-H, --hmc-version=[version] Force HMC version to use: (3|4) (default: 4)", + "help" : "-H, --hmc-version=[version] Force HMC version to use: (3|4|ivm) (default: 4)", "required" : "0", "shortdesc" : "Force HMC version to use", "default" : "4", - "choices" : ["3", "4"], + "choices" : ["3", "4", "ivm"], "order" : 1} def main(): device_opt = ["ipaddr", "login", "passwd", "secure", "cmd_prompt", \ "port", "managed", "hmc_version"] atexit.register(atexit_handler) define_new_opts() all_opt["login_timeout"]["default"] = "15" all_opt["secure"]["default"] = "1" all_opt["cmd_prompt"]["default"] = [r":~>", r"]\$", r"\$ "] options = check_input(device_opt, process_input(device_opt), other_conditions = True) docs = {} docs["shortdesc"] = "Fence agent for IBM LPAR" docs["longdesc"] = "" docs["vendorurl"] = "http://www.ibm.com" show_docs(options, docs) if "--managed" not in options: fail_usage("Failed: You have to enter name of managed system") if options["--action"] == "validate-all": sys.exit(0) ## ## Operate the fencing device #### conn = fence_login(options) result = fence_action(conn, options, set_power_status, get_power_status, get_lpar_list) fence_logout(conn, "quit\r\n") sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/manual/Makefile.am b/fence/agents/manual/Makefile.am deleted file mode 100644 index 1eb29717..00000000 --- a/fence/agents/manual/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_ack_manual - -sbin_SCRIPTS = $(TARGET) - -EXTRA_DIST = $(TARGET).in - -dist_man_MANS = $(TARGET).8 - -$(TARGET): $(TARGET).in - cat $^ | sed \ - -e 's#@clustervarrun@#${CLUSTERVARRUN}#g' \ - > $@ - -clean-local: - rm -f $(TARGET) diff --git a/fence/agents/mpath/Makefile.am b/fence/agents/mpath/Makefile.am deleted file mode 100644 index fd3d8d2a..00000000 --- a/fence/agents/mpath/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_mpath - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -k 1 - -include $(top_srcdir)/make/fencebuild.mk -include $(top_srcdir)/make/fenceman.mk -include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/mpath/fence_mpath.py b/fence/agents/mpath/fence_mpath.py index 349225ff..5426a18d 100644 --- a/fence/agents/mpath/fence_mpath.py +++ b/fence/agents/mpath/fence_mpath.py @@ -1,251 +1,251 @@ #!@PYTHON@ -tt import sys import stat import re import os import logging import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import fail_usage, run_command, atexit_handler, check_input, process_input, show_docs from fencing import fence_action, all_opt, run_delay #BEGIN_VERSION_GENERATION RELEASE_VERSION="" REDHAT_COPYRIGHT="" BUILD_DATE="" #END_VERSION_GENERATION def get_status(conn, options): del conn status = "off" for dev in options["devices"]: is_block_device(dev) if options["--key"] in get_registration_keys(options, dev): status = "on" else: logging.debug("No registration for key "\ + options["--key"] + " on device " + dev + "\n") if options["--action"] == "monitor": dev_read(options) return status def set_status(conn, options): del conn count = 0 if options["--action"] == "on": for dev in options["devices"]: is_block_device(dev) register_dev(options, dev) if options["--key"] not in get_registration_keys(options, dev): count += 1 logging.debug("Failed to register key "\ + options["--key"] + "on device " + dev + "\n") continue dev_write(options, dev) if get_reservation_key(options, dev) is None \ and not reserve_dev(options, dev) \ and get_reservation_key(options, dev) is None: count += 1 logging.debug("Failed to create reservation (key="\ + options["--key"] + ", device=" + dev + ")\n") else: dev_keys = dev_read(options) for dev in options["devices"]: is_block_device(dev) if options["--key"] in get_registration_keys(options, dev): preempt_abort(options, dev_keys[dev], dev) for dev in options["devices"]: if options["--key"] in get_registration_keys(options, dev): count += 1 logging.debug("Failed to remove key "\ + options["--key"] + " on device " + dev + "\n") continue if not get_reservation_key(options, dev): count += 1 logging.debug("No reservation exists on device " + dev + "\n") if count: logging.error("Failed to verify " + str(count) + " device(s)") sys.exit(1) #run command, returns dict, ret["err"] = exit code; ret["out"] = output def run_cmd(options, cmd): ret = {} if "--use-sudo" in options: prefix = options["--sudo-path"] + " " else: prefix = "" (ret["err"], ret["out"], _) = run_command(options, prefix + cmd) ret["out"] = "".join([i for i in ret["out"] if i is not None]) return ret # check if device exist and is block device def is_block_device(dev): if not os.path.exists(dev): fail_usage("Failed: device \"" + dev + "\" does not exist") if not stat.S_ISBLK(os.stat(dev).st_mode): fail_usage("Failed: device \"" + dev + "\" is not a block device") # cancel registration def preempt_abort(options, host, dev): cmd = options["--mpathpersist-path"] + " -o --preempt-abort --prout-type=5 --param-rk=" + host +" --param-sark=" + options["--key"] +"-d " + dev return not bool(run_cmd(options, cmd)["err"]) def register_dev(options, dev): cmd = options["--mpathpersist-path"] + " -o --register --param-sark=" + options["--key"] + " -d " + dev #cmd return code != 0 but registration can be successful return not bool(run_cmd(options, cmd)["err"]) def reserve_dev(options, dev): cmd = options["--mpathpersist-path"] + " -o --reserv --prout-type=5 --param-rk=" + options["--key"] + " -d " + dev return not bool(run_cmd(options, cmd)["err"]) def get_reservation_key(options, dev): cmd = options["--mpathpersist-path"] + " -i -r -d " + dev out = run_cmd(options, cmd) if out["err"]: fail_usage("Cannot get reservation key") match = re.search(r"\s+key\s*=\s*0x(\S+)\s+", out["out"], re.IGNORECASE) return match.group(1) if match else None def get_registration_keys(options, dev): keys = [] cmd = options["--mpathpersist-path"] + " -i -k -d " + dev out = run_cmd(options, cmd) if out["err"]: fail_usage("Cannot get registration keys") for line in out["out"].split("\n"): match = re.search(r"\s+0x(\S+)\s*", line) if match: keys.append(match.group(1)) return keys def dev_write(options, dev): file_path = options["--store-path"] + "/mpath.devices" if not os.path.isdir(options["--store-path"]): os.makedirs(options["--store-path"]) try: store_fh = open(file_path, "a+") except IOError: fail_usage("Failed: Cannot open file \""+ file_path + "\"") out = store_fh.read() if not re.search(r"^" + dev + r"\s+", out): store_fh.write(dev + "\t" + options["--key"] + "\n") store_fh.close() def dev_read(options): dev_key = {} file_path = options["--store-path"] + "/mpath.devices" try: store_fh = open(file_path, "r") except IOError: fail_usage("Failed: Cannot open file \"" + file_path + "\"") # get not empty lines from file for (device, key) in [line.strip().split() for line in store_fh if line.strip()]: dev_key[device] = key store_fh.close() return dev_key def define_new_opts(): all_opt["devices"] = { "getopt" : "d:", "longopt" : "devices", "help" : "-d, --devices=[devices] List of devices to use for current operation", "required" : "1", "shortdesc" : "List of devices to use for current operation. Devices can \ be comma-separated list of device-mapper multipath devices (eg. /dev/mapper/3600508b400105df70000e00000ac0000 or /dev/mapper/mpath1). \ Each device must support SCSI-3 persistent reservations.", "order": 1 } all_opt["key"] = { "getopt" : "k:", "longopt" : "key", "help" : "-k, --key=[key] Key to use for the current operation", "required" : "1", "shortdesc" : "Key to use for the current operation. This key should be \ unique to a node and have to be written in /etc/multipath.conf. For the \"on\" action, the key specifies the key use to \ register the local node. For the \"off\" action, this key specifies the key to \ be removed from the device(s).", "order": 1 } all_opt["mpathpersist_path"] = { "getopt" : ":", "longopt" : "mpathpersist-path", "help" : "--mpathpersist-path=[path] Path to mpathpersist binary", "required" : "0", "shortdesc" : "Path to mpathpersist binary", "default" : "@MPATH_PATH@", "order": 200 } all_opt["store_path"] = { "getopt" : ":", "longopt" : "store-path", "help" : "--store-path=[path] Path to directory containing cached keys", "required" : "0", "shortdesc" : "Path to directory where fence agent can store information", "default" : "@STORE_PATH@", "order": 200 } def main(): atexit.register(atexit_handler) device_opt = ["no_login", "no_password", "devices", "key", "sudo", \ "fabric_fencing", "on_target", "store_path", "mpathpersist_path", "force_on"] define_new_opts() options = check_input(device_opt, process_input(device_opt), other_conditions=True) docs = {} docs["shortdesc"] = "Fence agent for multipath persistent reservation" docs["longdesc"] = "fence_mpath is an I/O fencing agent that uses SCSI-3 \ persistent reservations to control access multipath devices. Underlying \ devices must support SCSI-3 persistent reservations (SPC-3 or greater) as \ well as the \"preempt-and-abort\" subcommand.\nThe fence_mpath agent works by \ -having an unique key for each pair of node and device that has to be set also \ -in /etc/multipath.conf. Once registered, a single node will become the reservation holder \ +having a unique key for each node that has to be set in /etc/multipath.conf. \ +Once registered, a single node will become the reservation holder \ by creating a \"write exclusive, registrants only\" reservation on the \ device(s). The result is that only registered nodes may write to the \ device(s). When a node failure occurs, the fence_mpath agent will remove the \ key belonging to the failed node from the device(s). The failed node will no \ longer be able to write to the device(s). A manual reboot is required." docs["vendorurl"] = "https://www.sourceware.org/dm/" show_docs(options, docs) run_delay(options) # Input control BEGIN if not "--key" in options: fail_usage("Failed: key is required") if options["--action"] == "validate-all": sys.exit(0) options["devices"] = options["--devices"].split(",") if not options["devices"]: fail_usage("Failed: No devices found") # Input control END result = fence_action(None, options, set_status, get_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/netio/Makefile.am b/fence/agents/netio/Makefile.am deleted file mode 100644 index 8820b8e2..00000000 --- a/fence/agents/netio/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_netio - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -a 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/ovh/Makefile.am b/fence/agents/ovh/Makefile.am deleted file mode 100644 index 3793562f..00000000 --- a/fence/agents/ovh/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -TARGET = fence_ovh - -SRC = $(TARGET).py - -EXTRA_DIST = $(SRC) - -sbin_SCRIPTS = $(TARGET) - -man_MANS = $(TARGET).8 - -FENCE_TEST_ARGS = -l test -p test -n 1 --email test@test.te - -include $(top_srcdir)/make/fencebuild.mk -include $(top_srcdir)/make/fenceman.mk -include $(top_srcdir)/make/agentpycheck.mk diff --git a/fence/agents/powerman/fence_powerman.py b/fence/agents/powerman/fence_powerman.py new file mode 100755 index 00000000..cd499132 --- /dev/null +++ b/fence/agents/powerman/fence_powerman.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python +import os +import time +from datetime import datetime +import sys +import subprocess +import re +import atexit +sys.path.append("@FENCEAGENTSLIBDIR@") +from fencing import * +from fencing import is_executable, fail_usage, run_delay +import logging + + +#BEGIN_VERSION_GENERATION +RELEASE_VERSION="Powerman Fencing Agent" +REDHAT_COPYRIGHT="" +BUILD_DATE="" +#END_VERSION_GENERATION + + +#### important!!! ####### +class PowerMan: + """Python wrapper for calling powerman commands + + This class makes calls to a powerman deamon for a cluster of computers. + The make-up of such a call looks something like: + $ pm -h elssd1:10101