diff --git a/.gitignore b/.gitignore
index d028ea19..205f4379 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,93 +1,97 @@
*.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
# ignore "libtoolized" m4 files, but keep our (enumerated) ones
/m4/*
!/m4/ac_python_module.m4
+# make/release.mk related litter
+/.tarball-version
+/tag-*
+
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/.travis.yml b/.travis.yml
index f11a2083..36f3ed0f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,43 +1,41 @@
language: python
sudo: required
dist: trusty
python:
- "2.7"
addons:
apt:
packages:
- python-pexpect
- xsltproc
- time
- libpam0g-dev
- libxml2-utils
- libcimcclient0-dev
- swig
before_install:
- pip install suds
- pip install pycurl
- pip install requests
- pip install pexpect
before_script:
- - wget https://github.com/Openwsman/openwsman/archive/v2.6.2.tar.gz -O /tmp/openwsman-2.6.2.tar.gz
- - tar zxvf /tmp/openwsman-2.6.2.tar.gz
- - cd openwsman-2.6.2
+ - wget https://github.com/Openwsman/openwsman/archive/v2.6.3.tar.gz
+ - tar zxvf v2.6.3.tar.gz
+ - cd openwsman-2.6.3
- perl -p -i -e "s/(\\$\{CURL_LIBRARIES\})/\1 ssl crypto/g" src/lib/CMakeLists.txt
- mkdir build && cd build
- - cmake .. -DPYTHON_EXECUTABLE:FILEPATH=~/virtualenv/python2.7.10/bin/python -DLIB=/lib/x86_64-linux-gnu -DCMAKE_LIBRARY_ARCHITECTURE=x86_64-linux-gnu -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_VERBOSE_MAKEFILE=TRUE
+ - cmake .. -DPYTHON_EXECUTABLE:FILEPATH=~/virtualenv/python2.7/bin/python -DLIB=/lib/x86_64-linux-gnu -DCMAKE_LIBRARY_ARCHITECTURE=x86_64-linux-gnu -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_VERBOSE_MAKEFILE=TRUE
- make
- sudo make install
- cd ../..
- - sudo rm -rf ~/virtualenv/python2.7.10/lib/python2.7/site-packages/{_,}pywsman*
- - pip install pywsman
script:
- ./autogen.sh
- ./configure
- make -j4
- make -j4 check
- PYTHONPATH=fence/agents/lib python fence/agents/lib/tests/test_fencing.py
diff --git a/README.md b/README.md
index 9582214a..d03589cf 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
Fence agents
============
Fence agents were developed as device "drivers" which are able to prevent computers from destroying data on shared storage. Their aim is to isolate a corrupted computer, using one of three methods:
* Power - A computer that is switched off cannot corrupt data, but it is important to not do a "soft-reboot" as we won't know if this is possible. This also works for virtual machines when the fence device is a hypervisor.
* Network - Switches can prevent routing to a given computer, so even if a computer is powered on it won't be able to harm the data.
* Configuration - Fibre-channel switches or SCSI devices allow us to limit who can write to managed disks.
Fence agents do not use configuration files, as configuration management is outside of their scope. All of the configuration has to be specified either as command-line arguments or lines of standard input (see the complete list for more info).
Because many fence agents are quite similar to each other, a fencing library (in Python) was developed. Please use it for further development. Creating or modifying a new fence agent should be quite simple using this library.
-Detailed user and developer documentation can be found here: [https://fedorahosted.org/cluster/wiki/fence-agents](https://fedorahosted.org/cluster/wiki/fence-agents)
+Detailed user and developer documentation can be found here: [https://docs.pagure.org/ClusterLabs.fence-agents/FenceAgentAPI.md](https://docs.pagure.org/ClusterLabs.fence-agents/FenceAgentAPI.md)
diff --git a/configure.ac b/configure.ac
index 432d7575..790341f6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,283 +1,313 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.63])
AC_INIT([fence-agents],
m4_esyscmd([make/git-version-gen .tarball-version]),
[linux-cluster@redhat.com])
AM_INIT_AUTOMAKE([-Wno-portability dist-bzip2 dist-xz subdir-objects])
LT_PREREQ([2.2.6])
LT_INIT
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([fence/agents/lib/fencing.py.py])
AC_CONFIG_HEADERS([make/clusterautoconfig.h])
AC_CANONICAL_HOST
AC_PROG_LIBTOOL
AC_LANG([C])
# Sanitize path
if test "$prefix" = "NONE"; then
prefix="/usr"
if test "$localstatedir" = "\${prefix}/var"; then
localstatedir="/var"
fi
if test "$sysconfdir" = "\${prefix}/etc"; then
sysconfdir="/etc"
fi
if test "$libdir" = "\${exec_prefix}/lib"; then
if test -e /usr/lib64; then
libdir="/usr/lib64"
else
libdir="/usr/lib"
fi
fi
fi
case $exec_prefix in
NONE) exec_prefix=$prefix;;
prefix) exec_prefix=$prefix;;
esac
# Checks for programs.
# check stolen from gnulib/m4/gnu-make.m4
if ! ${MAKE-make} --version /cannot/make/this >/dev/null 2>&1; then
AC_MSG_ERROR([you don't seem to have GNU make; it is required])
fi
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_LN_S
AC_PROG_INSTALL
AC_PROG_MAKE_SET
AC_PROG_AWK
AC_PROG_CXX
AC_PROG_RANLIB
## local helper functions
# this function checks if CC support options passed as
# args. Global CFLAGS are ignored during this test.
cc_supports_flag() {
local CFLAGS="$@"
AC_MSG_CHECKING([whether $CC supports "$@"])
AC_COMPILE_IFELSE([int main(){return 0;}] ,
[RC=0; AC_MSG_RESULT([yes])],
[RC=1; AC_MSG_RESULT([no])])
return $RC
}
# this function tests if a library has a certain function
# by using AC_CHECK_LIB but restores the original LIBS global
# envvar. This is required to avoid libtool to link everything
# with everything.
check_lib_no_libs() {
AC_CHECK_LIB([$1], [$2],,
[AC_MSG_ERROR([Unable to find $1 library])])
LIBS=$ac_check_lib_save_LIBS
}
# Checks for header files.
AC_CHECK_HEADERS([arpa/inet.h fcntl.h libintl.h limits.h netdb.h stddef.h sys/socket.h sys/time.h syslog.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_C_INLINE
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
AC_TYPE_UINT32_T
# Checks for library functions.
AC_FUNC_FORK
AC_FUNC_MALLOC
AC_CHECK_FUNCS([alarm atexit bzero dup2 memmove memset select socket strcasecmp strchr strdup strerror strtol])
# local options
AC_ARG_ENABLE([debug],
[ --enable-debug enable debug build. ],
[ default="no" ])
AC_ARG_WITH([fenceagentslibdir],
[ --with-fenceagentslibdir=PATH
installation path for fence library. ],
[ FENCEAGENTSLIBDIR="$withval" ],
[ FENCEAGENTSLIBDIR="${datadir}/fence" ])
AC_ARG_WITH([default-config-dir],
[ --with-default-config-dir=DIR
cluster config directory. ],
[ DEFAULT_CONFIG_DIR="$withval" ],
[ DEFAULT_CONFIG_DIR="$sysconfdir/cluster" ])
AC_ARG_WITH([default-config-file],
[ --with-default-config-file=FILE
cluster config file. ],
[ DEFAULT_CONFIG_FILE="$withval" ],
[ DEFAULT_CONFIG_FILE="cluster.conf" ])
AC_ARG_WITH([agents],
[ --with-agents=LIST
list of agents to build/ship (default: all). ],
[ AGENTS_LIST="$withval" ],
[ AGENTS_LIST="all" ])
if test "x$AGENTS_LIST" = x; then
AC_ERROR([No agents selected])
fi
+FENCE_KDUMP=0
+if echo "$AGENTS_LIST" | grep -q -E "all|kdump"; then
+ FENCE_KDUMP=1
+ AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s/kdump( |$)//")
+fi
+
+FENCE_MANUAL=0
+if echo "$AGENTS_LIST" | grep -q -E "all|manual"; then
+ FENCE_MANUAL=1
+ AGENTS_LIST=$(echo "$AGENTS_LIST" | sed -E "s/manual( |$)//")
+fi
+
+FENCE_SCSI=0
+if echo "$AGENTS_LIST" | grep -q -E "all|scsi"; then
+ FENCE_SCSI=1
+fi
+
+FENCE_ZVM=0
+if echo "$AGENTS_LIST" | grep -q -E "all|zvm( |$)"; then
+ FENCE_ZVM=1
+fi
+
if test "x$AGENTS_LIST" != xall; then
for j in $AGENTS_LIST; do
- if ! test -d fence/agents/$j; then
+ if ! test -f fence/agents/$j/fence_$j*.py; then
AC_ERROR([Agent $j does not exists])
fi
+ AGENTS_LIST=`echo "$AGENTS_LIST" | sed -E -e "s#$j([^_/]|$)#$j/fence_$j\1#g" -e "s#zvm/fence_zvm( |$)#zvm/fence_zvmip\1#g"`
done
fi
if test "x$AGENTS_LIST" = xall; then
AGENTS_LIST=`find $srcdir/fence/agents -mindepth 2 -maxdepth 2 -name '*.py' -printf '%P ' | sed -e 's#lib/[A-Za-z_.]* ##g' -e 's#nss_wrapper/[A-Za-z_.]* ##g' -e 's#autodetect/[A-Za-z_.]* ##g'`
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([OPENSTACK_PATH], [openstack], [/usr/bin/openstack])
AC_PATH_PROG([AMTTOOL_PATH], [amttool], [/usr/bin/amttool])
AC_PATH_PROG([GNUTLSCLI_PATH], [gnutlscli], [/usr/bin/gnutls-cli])
AC_PATH_PROG([COROSYNC_CMAPCTL_PATH], [corosync-cmapctl], [/usr/sbin/corosync-cmapctl])
AC_PATH_PROG([SG_PERSIST_PATH], [sg_persist], [/usr/bin/sg_persist])
AC_PATH_PROG([SG_TURS_PATH], [sg_turs], [/usr/bin/sg_turs])
AC_PATH_PROG([VGS_PATH], [vgs], [/usr/sbin/vgs])
AC_PATH_PROG([SUDO_PATH], [sudo], [/usr/bin/sudo])
AC_PATH_PROG([SSH_PATH], [ssh], [/usr/bin/ssh])
AC_PATH_PROG([TELNET_PATH], [telnet], [/usr/bin/telnet])
AC_PATH_PROG([MPATH_PATH], [mpathpersist], [/usr/sbin/mpathpersist])
AC_PATH_PROG([SBD_PATH], [sbd], [/sbin/sbd])
AC_PATH_PROG([SUDO_PATH], [sudo], [/usr/bin/sudo])
AC_PATH_PROG([SNMPWALK_PATH], [snmpwalk], [/usr/bin/snmpwalk])
AC_PATH_PROG([SNMPSET_PATH], [snmpset], [/usr/bin/snmpset])
AC_PATH_PROG([SNMPGET_PATH], [snmpget], [/usr/bin/snmpget])
AC_PATH_PROG([NOVA_PATH], [nova], [/usr/bin/nova])
AC_PATH_PROG([POWERMAN_PATH], [powerman], [/usr/bin/powerman])
## do subst
AC_SUBST([DEFAULT_CONFIG_DIR])
AC_DEFINE_UNQUOTED([DEFAULT_CONFIG_DIR], "$(eval echo ${DEFAULT_CONFIG_DIR})",
[Default config directory])
AC_SUBST([DEFAULT_CONFIG_FILE])
AC_DEFINE_UNQUOTED([DEFAULT_CONFIG_FILE], "$(eval echo ${DEFAULT_CONFIG_FILE})",
[Default config file])
AC_SUBST([LOGDIR])
AC_DEFINE_UNQUOTED([LOGDIR], "$(eval echo ${LOGDIR})",
[Default logging directory])
AC_SUBST([CLUSTERVARRUN])
AC_DEFINE_UNQUOTED([CLUSTERVARRUN], "$(eval echo ${CLUSTERVARRUN})",
[Default cluster var/run directory])
AC_SUBST([CLUSTERDATA])
AC_SUBST([FENCEAGENTSLIBDIR])
AC_SUBST([SNMPBIN])
AC_SUBST([AGENTS_LIST])
+AM_CONDITIONAL(BUILD_FENCE_KDUMP, test $FENCE_KDUMP -eq 1)
+AM_CONDITIONAL(BUILD_FENCE_MANUAL, test $FENCE_MANUAL -eq 1)
+AM_CONDITIONAL(BUILD_FENCE_SCSI, test $FENCE_SCSI -eq 1)
+AM_CONDITIONAL(BUILD_FENCE_ZVM, test $FENCE_ZVM -eq 1)
AM_CONDITIONAL(BUILD_XENAPILIB, test $XENAPILIB -eq 1)
AC_SUBST([IPMITOOL_PATH])
+AC_SUBST([OPENSTACK_PATH])
AC_SUBST([AMTTOOL_PATH])
AC_SUBST([COROSYNC_CMAPCTL_PATH])
AC_SUBST([SG_PERSIST_PATH])
AC_SUBST([SG_TURS_PATH])
AC_SUBST([VGS_PATH])
AC_SUBST([POWERMAN_PATH])
## *FLAGS handling
ENV_CFLAGS="$CFLAGS"
ENV_CPPFLAGS="$CPPFLAGS"
ENV_LDFLAGS="$LDFLAGS"
# debug build stuff
if test "x${enable_debug}" = xyes; then
AC_DEFINE_UNQUOTED([DEBUG], [1], [Compiling Debugging code])
OPT_CFLAGS="-O0"
else
OPT_CFLAGS="-O2"
fi
# gdb flags
if test "x${GCC}" = xyes; then
GDB_FLAGS="-ggdb3"
else
GDB_FLAGS="-g"
fi
# extra warnings
EXTRA_WARNINGS=""
WARNLIST="
all
shadow
missing-prototypes
missing-declarations
strict-prototypes
declaration-after-statement
pointer-arith
write-strings
cast-align
bad-function-cast
missing-format-attribute
format=2
format-security
format-nonliteral
no-long-long
unsigned-char
gnu89-inline
no-strict-aliasing
"
for j in $WARNLIST; do
if cc_supports_flag -W$j; then
EXTRA_WARNINGS="$EXTRA_WARNINGS -W$j";
fi
done
CFLAGS="$ENV_CFLAGS $OPT_CFLAGS $GDB_FLAGS \
$EXTRA_WARNINGS $WERROR_CFLAGS"
CPPFLAGS="-I\$(top_builddir)/make -I\$(top_srcdir)/make -I. $ENV_CPPFLAGS"
LDFLAGS="$ENV_LDFLAGS"
+AM_EXTRA_RECURSIVE_TARGETS([xml-upload])
AC_CONFIG_FILES([Makefile
fence/Makefile
fence/agents/Makefile
fence/agents/lib/Makefile
doc/Makefile])
AC_OUTPUT
diff --git a/fence/agents/Makefile.am b/fence/agents/Makefile.am
index 9f0964ca..c5e06bd3 100644
--- a/fence/agents/Makefile.am
+++ b/fence/agents/Makefile.am
@@ -1,85 +1,107 @@
MAINTAINERCLEANFILES = Makefile.in
TARGET = $(AGENTS_LIST:%.py=%)
SRC = $(TARGET:=.py)
CLEAN_TARGET_ADDITIONAL = kdump/fence_kdump_send manual/fence_ack_manual */*.o
-EXTRA_DIST = $(SRC) manual/fence_ack_manual.in manual/fence_ack_manual.8 zvm/fence_zvm_man_page
+EXTRA_DIST = $(SRC)
-sbin_SCRIPTS = $(TARGET) manual/fence_ack_manual
+sbin_PROGRAMS =
+sbin_SCRIPTS = $(TARGET)
+libexec_PROGRAMS =
+
+dist_man_MANS =
+
+noinst_HEADERS = kdump/list.h kdump/message.h kdump/options.h kdump/version.h zvm/fence_zvm.h
EXTRA_SCRIPTS =
man_MANS = $(sbin_SCRIPTS:=.8)
-sbin_PROGRAMS = kdump/fence_kdump zvm/fence_zvm
-libexec_PROGRAMS = kdump/fence_kdump_send
+if BUILD_FENCE_KDUMP
+sbin_PROGRAMS += kdump/fence_kdump
+libexec_PROGRAMS += kdump/fence_kdump_send
-noinst_HEADERS = kdump/list.h kdump/message.h kdump/options.h kdump/version.h zvm/fence_zvm.h
+dist_man_MANS += kdump/fence_kdump.8 kdump/fence_kdump_send.8
kdump_fence_kdump_SOURCES = kdump/fence_kdump.c
kdump_fence_kdump_CFLAGS = -D_GNU_SOURCE -Ikdump
kdump_fence_kdump_send_SOURCES = kdump/fence_kdump_send.c
kdump_fence_kdump_send_CFLAGS = -D_GNU_SOURCE -Ikdump
+endif
-dist_man_MANS = kdump/fence_kdump.8 kdump/fence_kdump_send.8 zvm/fence_zvm.8
+if BUILD_FENCE_MANUAL
+EXTRA_DIST += manual/fence_ack_manual.in manual/fence_ack_manual.8
-scsidatadir = $(CLUSTERDATA)
-scsidata_SCRIPTS = scsi/fence_scsi_check scsi/fence_scsi_check_hardreboot
+sbin_SCRIPTS += manual/fence_ack_manual
+endif
+
+if BUILD_FENCE_ZVM
+EXTRA_DIST += zvm/fence_zvm_man_page
+
+sbin_PROGRAMS += zvm/fence_zvm
+
+dist_man_MANS += zvm/fence_zvm.8
zvm_fence_zvm_SOURCES = zvm/fence_zvm.c
zvm_fence_zvm_CFLAGS = -D_GNU_SOURCE -Izvm
+endif
+
+if BUILD_FENCE_SCSI
+scsidatadir = $(CLUSTERDATA)
+scsidata_SCRIPTS = scsi/fence_scsi_check scsi/fence_scsi_check_hardreboot
+endif
FENCE_TEST_ARGS = \
login=test\n\
passwd=test\n\
ipaddr=test\n\
port=1\n\
managed=1\n\
devices=test\n\
session_url=http://test\n\
email=test@test.te
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/autodetect/fencing.py b/fence/agents/autodetect/fencing.py
index 1f387398..3976298c 100644
--- a/fence/agents/autodetect/fencing.py
+++ b/fence/agents/autodetect/fencing.py
@@ -1,1389 +1,1393 @@
#!/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}
+ "order" : 1},
+ "quiet": {
+ "getopt" : "q",
+ "longopt": "quiet",
+ "help" : "-q, --quiet Disable logging to stderr. Does not affect --verbose or --debug logging to syslog.",
+ "required" : "0",
+ "order" : 50}
}
# 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"],
+ "power_timeout", "shell_timeout", "login_timeout", "power_wait", "retry_on", \
+ "delay", "quiet"],
"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" % (choice)
print "\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(sys.stderr))
+ if not options.has_key("--quiet"):
+ ## add logging to 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):
+def run_command(options, command, timeout=None, env=None, log_command=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)
+ logging.info("Executing: %s\n", log_command or 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
index 16d29aee..72caddfc 100644
--- a/fence/agents/azure_arm/fence_azure_arm.py
+++ b/fence/agents/azure_arm/fence_azure_arm.py
@@ -1,131 +1,131 @@
#!@PYTHON@ -tt
import sys, re, pexpect
import logging
import atexit
-sys.path.append("/usr/share/fence")
+sys.path.append("@FENCEAGENTSLIBDIR@")
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)"
+RELEASE_VERSION=""
+BUILD_DATE=""
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:",
+ "getopt" : ":",
"longopt" : "resourceGroup",
- "help" : "-rg, --resourceGroup=[name] Name of the resource group",
+ "help" : "--resourceGroup=[name] Name of the resource group",
"shortdesc" : "Name of resource group.",
"required" : "1",
"order" : 2
}
all_opt["tenantId"] = {
- "getopt" : "tid:",
+ "getopt" : ":",
"longopt" : "tenantId",
- "help" : "-tid, --tenantId=[name] Id of the Azure Active Directory tenant",
+ "help" : "--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:",
+ "getopt" : ":",
"longopt" : "subscriptionId",
- "help" : "-sid, --subscriptionId=[name] Id of the Azure subscription",
+ "help" : "--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/cisco_ucs/fence_cisco_ucs.py b/fence/agents/cisco_ucs/fence_cisco_ucs.py
index 0d9609db..7102c445 100644
--- a/fence/agents/cisco_ucs/fence_cisco_ucs.py
+++ b/fence/agents/cisco_ucs/fence_cisco_ucs.py
@@ -1,202 +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:
presence_status = result.group(1)
if presence_status in ["missing", "mismatch"]:
return "off"
else:
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"
+ 'on' : "admin-up",
+ 'off' : "admin-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/fence_compute.py b/fence/agents/compute/fence_compute.py
index 0a238b60..907b9c1c 100644
--- a/fence/agents/compute/fence_compute.py
+++ b/fence/agents/compute/fence_compute.py
@@ -1,467 +1,467 @@
#!@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"], binary="nova-compute")
for service in services:
logging.debug("Status of %s is %s" % (service.binary, service.state))
if service.state == "up":
status = "on"
elif service.state == "down":
status = "off"
else:
logging.debug("Unknown status detected from nova: " + service.state)
break
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
services = nova.services.list(binary="nova-compute")
for service in services:
shorthost = service.host.split('.')[0]
if shorthost == service.host:
# Nova is not using FQDN
calculated = ""
else:
# Compute nodes are named as FQDN, strip off the hostname
calculated = service.host.replace(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" % service.host)
continue
domains[calculated] = service.host
last_domain = calculated
if "--domain" in options and options["--domain"] != calculated:
# 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"], service.host))
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
elif len(domains) == 1 and options["--domain"] != last_domain:
logging.error("Overriding supplied domain '%s' as it does not match the one calculated from: %s"
% (options["--domain"], domains[last_domain]))
options["--domain"] = last_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 last_domain
def fix_plug_name(options):
if options["--action"] == "list":
return
if "--plug" not in options:
return
calculated = fix_domain(options)
if calculated is None or "--domain" not in options:
# Nothing supplied and nova not available... what to do... nothing
return
short_plug = options["--plug"].split('.')[0]
logging.debug("Checking target '%s' against calculated domain '%s'"% (options["--plug"], calculated))
if options["--domain"] == "":
# Ensure any domain is stripped off since nova isn't using FQDN
options["--plug"] = short_plug
elif options["--plug"].endswith(options["--domain"]):
# Plug already uses 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:
services = nova.services.list(binary="nova-compute")
for service in services:
longhost = service.host
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))
+
+ logging.warning("Couldn't obtain a supported connection to nova, tried: %s\n" % 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/fence_docker.py b/fence/agents/docker/fence_docker.py
index 453a987f..2bfa5eae 100644
--- a/fence/agents/docker/fence_docker.py
+++ b/fence/agents/docker/fence_docker.py
@@ -1,164 +1,164 @@
#!@PYTHON@ -tt
import atexit
import sys
import io
import logging
import pycurl
import json
sys.path.append("@FENCEAGENTSLIBDIR@")
from fencing import fail_usage, all_opt, fence_action, atexit_handler, check_input, process_input, show_docs, run_delay
#BEGIN_VERSION_GENERATION
RELEASE_VERSION = ""
REDHAT_COPYRIGHT = ""
BUILD_DATE = ""
#END_VERSION_GENERATION
def get_power_status(conn, options):
del conn
status = send_cmd(options, "containers/%s/json" % options["--plug"])
if status is None:
return None
return "on" if status["State"]["Running"] else "off"
def set_power_status(conn, options):
del conn
if options["--action"] == "on":
send_cmd(options, "containers/%s/start" % options["--plug"], True)
else:
send_cmd(options, "containers/%s/kill" % options["--plug"], True)
return
def reboot_cycle(conn, options):
del conn
send_cmd(options, "containers/%s/restart" % options["--plug"], True)
return get_power_status(conn, options)
def get_list(conn, options):
del conn
output = send_cmd(options, "containers/json?all=1")
containers = {}
for container in output:
containers[container["Id"]] = (container["Names"][0], {True:"off", False: "on"}[container["Status"][:4].lower() == "exit"])
return containers
def send_cmd(options, cmd, post = False):
url = "http%s://%s:%s/v%s/%s" % ("s" if "--ssl" in options else "", options["--ip"], options["--ipport"], options["--api-version"], cmd)
conn = pycurl.Curl()
- output_buffer = io.StringIO()
+ output_buffer = io.BytesIO()
if logging.getLogger().getEffectiveLevel() < logging.WARNING:
conn.setopt(pycurl.VERBOSE, True)
conn.setopt(pycurl.HTTPGET, 1)
- conn.setopt(pycurl.URL, str(url))
+ conn.setopt(pycurl.URL, url.encode("ascii"))
if post:
conn.setopt(pycurl.POST, 1)
conn.setopt(pycurl.POSTFIELDSIZE, 0)
conn.setopt(pycurl.WRITEFUNCTION, output_buffer.write)
conn.setopt(pycurl.TIMEOUT, int(options["--shell-timeout"]))
if "--ssl" in options:
if not (set(("--tlscert", "--tlskey", "--tlscacert")) <= set(options)):
fail_usage("Failed. If --ssl option is used, You have to also \
specify: --tlscert, --tlskey and --tlscacert")
conn.setopt(pycurl.SSL_VERIFYPEER, 1)
conn.setopt(pycurl.SSLCERT, options["--tlscert"])
conn.setopt(pycurl.SSLKEY, options["--tlskey"])
conn.setopt(pycurl.CAINFO, options["--tlscacert"])
else:
conn.setopt(pycurl.SSL_VERIFYPEER, 0)
conn.setopt(pycurl.SSL_VERIFYHOST, 0)
logging.debug("URL: " + url)
try:
conn.perform()
- result = output_buffer.getvalue()
+ result = output_buffer.getvalue().decode()
return_code = conn.getinfo(pycurl.RESPONSE_CODE)
logging.debug("RESULT [" + str(return_code) + \
"]: " + result)
conn.close()
if return_code == 200:
return json.loads(result)
except pycurl.error:
logging.error("Connection failed")
except:
if result is not None:
logging.error(result)
logging.error("Cannot parse json")
return None
def main():
atexit.register(atexit_handler)
all_opt["tlscert"] = {
"getopt" : ":",
"longopt" : "tlscert",
"help" : "--tlscert "
"Path to client certificate for TLS authentication",
"required" : "0",
"shortdesc" : "Path to client certificate (PEM format) \
for TLS authentication. Required if --ssl option is used.",
"order": 2
}
all_opt["tlskey"] = {
"getopt" : ":",
"longopt" : "tlskey",
"help" : "--tlskey "
"Path to client key for TLS authentication",
"required" : "0",
"shortdesc" : "Path to client key (PEM format) for TLS \
authentication. Required if --ssl option is used.",
"order": 2
}
all_opt["tlscacert"] = {
"getopt" : ":",
"longopt" : "tlscacert",
"help" : "--tlscacert "
"Path to CA certificate for TLS authentication",
"required" : "0",
"shortdesc" : "Path to CA certificate (PEM format) for \
TLS authentication. Required if --ssl option is used.",
"order": 2
}
all_opt["api_version"] = {
"getopt" : ":",
"longopt" : "api-version",
"help" : "--api-version "
"Version of Docker Remote API (default: 1.11)",
"required" : "0",
"order" : 2,
"default" : "1.11",
}
device_opt = ["ipaddr", "no_password", "no_login", "port", "method", "web", "tlscert", "tlskey", "tlscacert", "ssl", "api_version"]
options = check_input(device_opt, process_input(device_opt))
docs = { }
docs["shortdesc"] = "Fence agent for Docker"
docs["longdesc"] = "fence_docker is I/O fencing agent which \
can be used with the Docker Engine containers. You can use this \
fence-agent without any authentication, or you can use TLS authentication \
(use --ssl option, more info about TLS authentication in docker: \
http://docs.docker.com/examples/https/)."
docs["vendorurl"] = "www.docker.io"
show_docs(options, docs)
run_delay(options)
result = fence_action(None, options, set_power_status, get_power_status, get_list, reboot_cycle)
sys.exit(result)
if __name__ == "__main__":
main()
diff --git a/fence/agents/ipmilan/fence_ipmilan.py b/fence/agents/ipmilan/fence_ipmilan.py
index 2b1831b0..6c43b185 100644
--- a/fence/agents/ipmilan/fence_ipmilan.py
+++ b/fence/agents/ipmilan/fence_ipmilan.py
@@ -1,180 +1,219 @@
#!@PYTHON@ -tt
import sys, re, os
import atexit
from pipes import quote
sys.path.append("@FENCEAGENTSLIBDIR@")
from fencing import *
from fencing import fail_usage, is_executable, run_command, run_delay
#BEGIN_VERSION_GENERATION
RELEASE_VERSION=""
REDHAT_COPYRIGHT=""
BUILD_DATE=""
#END_VERSION_GENERATION
def get_power_status(_, options):
- output = run_command(options, create_command(options, "status"))
+ output = _run_command(options, "status")
match = re.search('[Cc]hassis [Pp]ower is [\\s]*([a-zA-Z]{2,3})', str(output))
status = match.group(1) if match else None
return status
def set_power_status(_, options):
- run_command(options, create_command(options, options["--action"]))
+ _run_command(options, options["--action"])
return
def reboot_cycle(_, options):
- output = run_command(options, create_command(options, "cycle"))
+ output = _run_command(options, "cycle")
return bool(re.search('chassis power control: cycle', str(output).lower()))
def reboot_diag(_, options):
- output = run_command(options, create_command(options, "diag"))
+ output = _run_command(options, "diag")
return bool(re.search('chassis power control: diag', str(output).lower()))
+def _run_command(options, action):
+ cmd, log_cmd = create_command(options, action)
+ return run_command(options, cmd, log_command=log_cmd)
+
def create_command(options, action):
- cmd = options["--ipmitool-path"]
+ class Cmd:
+ cmd = ""
+ log = ""
+
+ @classmethod
+ def append(cls, cmd, log=None):
+ cls.cmd += cmd
+ cls.log += (cmd if log is None else log)
+
+ # --use-sudo / -d
+ if "--use-sudo" in options:
+ Cmd.append(options["--sudo-path"] + " ")
+
+ Cmd.append(options["--ipmitool-path"])
# --lanplus / -L
if "--lanplus" in options and options["--lanplus"] in ["", "1"]:
- cmd += " -I lanplus"
+ Cmd.append(" -I lanplus")
else:
- cmd += " -I lan"
+ Cmd.append(" -I lan")
+
# --ip / -a
- cmd += " -H " + options["--ip"]
+ Cmd.append(" -H " + options["--ip"])
+
+ # --port / -n
+ if "--ipport" in options:
+ Cmd.append(" -p " + options["--ipport"])
+
+ # --target
+ if "--target" in options:
+ Cmd.append(" -t " + options["--target"])
# --username / -l
if "--username" in options and len(options["--username"]) != 0:
- cmd += " -U " + quote(options["--username"])
+ Cmd.append(" -U " + quote(options["--username"]))
# --auth / -A
if "--auth" in options:
- cmd += " -A " + options["--auth"]
+ Cmd.append(" -A " + options["--auth"])
# --password / -p
if "--password" in options:
- cmd += " -P " + quote(options["--password"])
+ Cmd.append(" -P " + quote(options["--password"]), " -P [set]")
else:
- cmd += " -P ''"
+ Cmd.append(" -P ''", " -P [set]")
# --cipher / -C
if "--cipher" in options:
- cmd += " -C " + options["--cipher"]
-
- # --port / -n
- if "--ipport" in options:
- cmd += " -p " + options["--ipport"]
+ Cmd.append(" -C " + options["--cipher"])
if "--privlvl" in options:
- cmd += " -L " + options["--privlvl"]
+ Cmd.append(" -L " + options["--privlvl"])
- # --action / -o
- cmd += " chassis power " + action
+ if "--hexadecimal-kg" in options:
+ Cmd.append(" -y " + options["--hexadecimal-kg"])
- # --use-sudo / -d
- if "--use-sudo" in options:
- cmd = options["--sudo-path"] + " " + cmd
+ # --action / -o
+ Cmd.append(" chassis power " + action)
- return cmd
+ return (Cmd.cmd, Cmd.log)
def define_new_opts():
all_opt["lanplus"] = {
"getopt" : "P",
"longopt" : "lanplus",
"help" : "-P, --lanplus Use Lanplus to improve security of connection",
"required" : "0",
"default" : "0",
"shortdesc" : "Use Lanplus to improve security of connection",
"order": 1
}
all_opt["auth"] = {
"getopt" : "A:",
"longopt" : "auth",
"help" : "-A, --auth=[auth] IPMI Lan Auth type (md5|password|none)",
"required" : "0",
"shortdesc" : "IPMI Lan Auth type.",
"choices" : ["md5", "password", "none"],
"order": 1
}
all_opt["cipher"] = {
"getopt" : "C:",
"longopt" : "cipher",
"help" : "-C, --cipher=[cipher] Ciphersuite to use (same as ipmitool -C parameter)",
"required" : "0",
"shortdesc" : "Ciphersuite to use (same as ipmitool -C parameter)",
"order": 1
}
all_opt["privlvl"] = {
"getopt" : "L:",
"longopt" : "privlvl",
"help" : "-L, --privlvl=[level] "
"Privilege level on IPMI device (callback|user|operator|administrator)",
"required" : "0",
"shortdesc" : "Privilege level on IPMI device",
"default" : "administrator",
"choices" : ["callback", "user", "operator", "administrator"],
"order": 1
}
all_opt["ipmitool_path"] = {
"getopt" : ":",
"longopt" : "ipmitool-path",
"help" : "--ipmitool-path=[path] Path to ipmitool binary",
"required" : "0",
"shortdesc" : "Path to ipmitool binary",
"default" : "@IPMITOOL_PATH@",
"order": 200
}
+ all_opt["target"] = {
+ "getopt" : ":",
+ "longopt" : "target",
+ "help" : "--target=[targetaddress] Bridge IPMI requests to the remote target address",
+ "required" : "0",
+ "shortdesc" : "Bridge IPMI requests to the remote target address",
+ "order": 1
+ }
+ all_opt["hexadecimal_kg"] = {
+ "getopt" : ":",
+ "longopt" : "hexadecimal-kg",
+ "help" : "--hexadecimal-kg=[key] Hexadecimal-encoded Kg key for IPMIv2 authentication",
+ "required" : "0",
+ "shortdesc" : "Hexadecimal-encoded Kg key for IPMIv2 authentication",
+ "order": 1
+ }
def main():
atexit.register(atexit_handler)
- device_opt = ["ipaddr", "login", "no_login", "no_password", "passwd", "diag",
- "lanplus", "auth", "cipher", "privlvl", "sudo", "ipmitool_path", "method"]
+ device_opt = ["ipaddr", "login", "no_login", "no_password", "passwd",
+ "diag", "lanplus", "auth", "cipher", "privlvl", "sudo",
+ "ipmitool_path", "method", "target", "hexadecimal_kg"]
define_new_opts()
all_opt["power_wait"]["default"] = 2
if os.path.basename(sys.argv[0]) == "fence_ilo3":
all_opt["power_wait"]["default"] = "4"
all_opt["method"]["default"] = "cycle"
all_opt["lanplus"]["default"] = "1"
elif os.path.basename(sys.argv[0]) == "fence_ilo4":
all_opt["lanplus"]["default"] = "1"
all_opt["ipport"]["default"] = "623"
- all_opt["method"]["help"] = "-m, --method=[method] Method to fence (onoff|cycle) (Default: cycle)\n" \
+ if all_opt["method"]["default"] == "cycle":
+ all_opt["method"]["help"] = "-m, --method=[method] Method to fence (onoff|cycle) (Default: cycle)\n" \
"WARNING! This fence agent might report success before the node is powered off. " \
"You should use -m/method onoff if your fence device works correctly with that option."
options = check_input(device_opt, process_input(device_opt))
docs = {}
docs["shortdesc"] = "Fence agent for IPMI"
docs["longdesc"] = "fence_ipmilan is an I/O Fencing agent\
which can be used with machines controlled by IPMI.\
This agent calls support software ipmitool (http://ipmitool.sf.net/). \
WARNING! This fence agent might report success before the node is powered off. \
You should use -m/method onoff if your fence device works correctly with that option."
docs["vendorurl"] = ""
docs["symlink"] = [("fence_ilo3", "Fence agent for HP iLO3"),
("fence_ilo4", "Fence agent for HP iLO4"),
("fence_imm", "Fence agent for IBM Integrated Management Module"),
("fence_idrac", "Fence agent for Dell iDRAC")]
show_docs(options, docs)
run_delay(options)
if not is_executable(options["--ipmitool-path"]):
fail_usage("Ipmitool not found or not accessible")
reboot_fn = reboot_cycle
if options["--action"] == "diag":
# Diag is a special action that can't be verified so we will reuse reboot functionality
# to minimize impact on generic library
options["--action"] = "reboot"
options["--method"] = "cycle"
reboot_fn = reboot_diag
result = fence_action(None, options, set_power_status, get_power_status, None, reboot_fn)
sys.exit(result)
if __name__ == "__main__":
main()
diff --git a/fence/agents/ironic/fence_ironic.py b/fence/agents/ironic/fence_ironic.py
new file mode 100644
index 00000000..8e5d5b0f
--- /dev/null
+++ b/fence/agents/ironic/fence_ironic.py
@@ -0,0 +1,136 @@
+#!@PYTHON@ -tt
+
+import atexit
+import logging
+import os
+import re
+import sys
+from pipes import quote
+sys.path.append("@FENCEAGENTSLIBDIR@")
+from fencing import *
+from fencing import fail_usage, is_executable, run_command, run_delay
+
+#BEGIN_VERSION_GENERATION
+RELEASE_VERSION=""
+REDHAT_COPYRIGHT=""
+BUILD_DATE=""
+#END_VERSION_GENERATION
+
+def get_name_or_uuid(options):
+ return options["--uuid"] if "--uuid" in options else options["--plug"]
+
+def get_power_status(_, options):
+ output = ironic_run_command(options, "status")
+ stdout = output[1]
+ match = re.search('power[\\s]*([a-zA-Z]{2,3})', str(stdout))
+ status = match.group(1) if match else None
+ return status
+
+def set_power_status(_, options):
+ ironic_run_command(options, options["--action"])
+ return
+
+def get_devices_list(_, options):
+ nodes = {}
+ output = ironic_run_command(options, "list")
+ stdout = output[1]
+ for line in stdout.splitlines():
+ uuid = "UUID"
+ try:
+ (uuid, name, state) = line.split(',')
+ except ValueError:
+ pass
+ if "UUID" in uuid:
+ continue # skip line header
+ match = re.search('power[\\s]*([a-zA-Z]{2,3})', state)
+ status = match.group(1) if match else None
+ nodes[uuid] = (name, status)
+
+ return nodes
+
+def ironic_run_command(options, action, timeout=None):
+ cmd = options["--openstack-path"] + " baremetal"
+ env = os.environ.copy()
+ # --username / -l
+ if "--username" in options and len(options["--username"]) != 0:
+ env["OS_USERNAME"] = options["--username"]
+
+ # --password / -p
+ if "--password" in options:
+ env["OS_PASSWORD"] = options["--password"]
+
+ # --tenant-name -t
+ if "--tenant-name" in options:
+ env["OS_TENANT_NAME"] = options["--tenant-name"]
+
+ # --auth-url
+ if "--auth-url" in options:
+ env["OS_AUTH_URL"] = options["--auth-url"]
+
+ # --action / -o
+ if action == "status":
+ cmd += " show %s -c power_state --format value" % (get_name_or_uuid(options))
+ elif action in ["on", "off"]:
+ cmd += " power %s %s" % (action, get_name_or_uuid(options))
+ elif action == "list":
+ cmd += " list -c 'Instance UUID' -c Name -c 'Power State' --format csv --quote minimal"
+
+
+ logging.debug("cmd -> %s" % cmd)
+ return run_command(options, cmd, timeout, env)
+
+def define_new_opts():
+ all_opt["auth-url"] = {
+ "getopt" : ":",
+ "longopt" : "auth-url",
+ "help" : "--auth-url=[authurl] Auth URL",
+ "required" : "1",
+ "shortdesc" : "Keystone Admin Auth URL",
+ "order": 1
+ }
+ all_opt["tenant-name"] = {
+ "getopt" : "t:",
+ "longopt" : "tenant-name",
+ "help" : "-t, --tenant-name=[tenant] Tenantname",
+ "required" : "0",
+ "shortdesc" : "Keystone Admin Tenant",
+ "default": "admin",
+ "order": 1
+ }
+ all_opt["openstack-path"] = {
+ "getopt" : ":",
+ "longopt" : "openstack-path",
+ "help" : "--openstack-path=[path] Path to openstack binary",
+ "required" : "0",
+ "shortdesc" : "Path to the OpenStack binary",
+ "default" : "@OPENSTACK_PATH@",
+ "order": 200
+ }
+
+def main():
+ atexit.register(atexit_handler)
+
+ device_opt = ["login", "passwd", "port", "auth-url", "tenant-name", "openstack-path"]
+ define_new_opts()
+
+ options = check_input(device_opt, process_input(device_opt))
+
+ docs = {}
+ docs["shortdesc"] = "Fence agent for OpenStack's Ironic (Bare Metal as a service) service"
+ docs["longdesc"] = "fence_ironic is a Fencing agent \
+which can be used with machines controlled by the Ironic service. \
+This agent calls the openstack CLI. \
+WARNING! This fence agent is not intended for production use. Relying on a functional ironic service for fencing is not a good design choice."
+ docs["vendorurl"] = "https://wiki.openstack.org/wiki/Ironic"
+ show_docs(options, docs)
+
+ run_delay(options)
+
+ if not is_executable(options["--openstack-path"]):
+ fail_usage("openstack tool not found or not accessible")
+
+ result = fence_action(None, options, set_power_status, get_power_status, get_devices_list)
+ sys.exit(result)
+
+if __name__ == "__main__":
+ main()
diff --git a/fence/agents/lib/check_used_options.py b/fence/agents/lib/check_used_options.py
old mode 100755
new mode 100644
index b90bc056..6ed3827e
--- a/fence/agents/lib/check_used_options.py
+++ b/fence/agents/lib/check_used_options.py
@@ -1,65 +1,63 @@
-#!/usr/bin/python -tt
-
## Check if fence agent uses only options["--??"] which are defined in fencing library or
## fence agent itself
##
## Usage: ./check_used_options.py fence-agent (e.g. lpar/fence_lpar.py)
##
import sys, re
sys.path.append("@FENCEAGENTSLIBDIR@")
from fencing import all_opt
def main():
agent = sys.argv[1]
available = {}
## all_opt from fencing library are imported
for k in list(all_opt.keys()):
if "longopt" in all_opt[k]:
available["--" + all_opt[k]["longopt"]] = True
## add UUID which is derived automatically from --plug if possible
available["--uuid"] = True
## all_opt defined in fence agent are found
agent_file = open(agent)
opt_re = re.compile(r"\s*all_opt\[\"([^\"]*)\"\] = {")
opt_longopt_re = re.compile(r"\s*\"longopt\" : \"([^\"]*)\"")
in_opt = False
for line in agent_file:
if opt_re.search(line) != None:
in_opt = True
if in_opt and opt_longopt_re.search(line) != None:
available["--" + opt_longopt_re.search(line).group(1)] = True
in_opt = False
## check if all options are defined
agent_file = open(agent)
option_use_re = re.compile(r"options\[\"(-[^\"]*)\"\]")
option_has_re = re.compile(r"options.has_key\(\"(-[^\"]*)\"\)")
counter = 0
without_errors = True
for line in agent_file:
counter += 1
for option in option_use_re.findall(line):
if option not in available:
print("ERROR on line %d in %s: option %s is not defined" % (counter, agent, option_use_re.search(line).group(1)))
without_errors = False
for option in option_has_re.findall(line):
if option not in available:
print("ERROR on line %d in %s: option %s is not defined" % (counter, agent, option_has_re.search(line).group(1)))
without_errors = False
if without_errors:
sys.exit(0)
else:
sys.exit(1)
if __name__ == "__main__":
main()
diff --git a/fence/agents/lib/fence2man.xsl b/fence/agents/lib/fence2man.xsl
index 073de958..3dc0b39c 100644
--- a/fence/agents/lib/fence2man.xsl
+++ b/fence/agents/lib/fence2man.xsl
@@ -1,72 +1,78 @@
+
.TP
.B
.B
.
(
|
)
This parameter is always required.
(Default Value: )
+
+ Obsoletes:
+
+
+
.TP
\fB \fP
Power on machine.
Power off machine.
Enable fabric access.
Disable fabric access.
Reboot machine.
Pulse a diagnostic interrupt to the processor(s).
Check the health of fence device
Display the XML metadata describing this resource.
List available plugs with aliases/virtual machines if there is support for more then one device. Returns N/A otherwise.
List available plugs with aliases/virtual machines and their power state if it can be obtained without additional commands.
This returns the status of the plug/virtual machine.
Validate if all required parameters are entered.
The operational behavior of this is not known.
.TH FENCE_AGENT 8 2009-10-20 " (Fence Agent)"
.SH NAME
-
.P
- (symlink)
.SH DESCRIPTION
.P
.P
accepts options on the command line as well
as from stdin. Fenced sends parameters through stdin when it execs the
agent. can be run by itself with command
line options. This is useful for testing and for turning outlets on or off
from scripts.
Vendor URL:
.SH PARAMETERS
getopt
.SH ACTIONS
.SH STDIN PARAMETERS
stdin
diff --git a/fence/agents/lib/fencing.py.py b/fence/agents/lib/fencing.py.py
index 91b11aae..4001787d 100644
--- a/fence/agents/lib/fencing.py.py
+++ b/fence/agents/lib/fencing.py.py
@@ -1,1425 +1,1451 @@
#!@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}
+ "order" : 1},
+ "quiet": {
+ "getopt" : "q",
+ "longopt": "quiet",
+ "help" : "-q, --quiet Disable logging to stderr. Does not affect --verbose or --debug logging to syslog.",
+ "required" : "0",
+ "order" : 50}
}
# 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"],
+ "power_timeout", "shell_timeout", "login_timeout", "power_wait", "retry_on", \
+ "delay", "quiet"],
"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 = [(key, all_opt[key]) for key in list(set(avail_opt)) if "longopt" in all_opt[key]]
+ # Find keys that are going to replace inconsistent names
+ mapping = dict([(opt["longopt"].replace("-", "_"), key) for (key, opt) in sorted_list if (key != opt["longopt"].replace("-", "_"))])
+ new_options = [(key, all_opt[mapping[key]]) for key in mapping]
+ sorted_list.extend(new_options)
+
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")
+ for (key, opt) in sorted_list:
+ info = ""
+ if key in all_opt:
+ if key != all_opt[key].get('longopt', key).replace("-", "_"):
+ info = "deprecated=\"1\""
+ else:
+ info = "obsoletes=\"%s\"" % (mapping.get(key))
+
+ if "help" in opt and len(opt["help"]) > 0:
+ if info != "":
+ info = " " + info
+ print("\t")
default = ""
- if "default" in all_opt[option]:
- default = "default=\"" + _encode_html_entities(str(all_opt[option]["default"])) + "\" "
+ if "default" in opt:
+ default = "default=\"" + _encode_html_entities(str(opt["default"])) + "\" "
- mixed = all_opt[option]["help"]
+ mixed = opt["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:])
+ if not "shortdesc" in opt:
+ shortdesc = re.sub("\s\s+", " ", opt["help"][31:])
else:
- shortdesc = all_opt[option]["shortdesc"]
+ shortdesc = opt["shortdesc"]
print("\t\t")
- if "choices" in all_opt[option]:
+ if "choices" in opt:
print("\t\t")
- for choice in all_opt[option]["choices"]:
+ for choice in opt["choices"]:
print("\t\t\t" % (choice))
print("\t\t")
- elif all_opt[option]["getopt"].count(":") > 0:
- t = all_opt[option].get("type", "string")
+ elif opt["getopt"].count(":") > 0:
+ t = opt.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(sys.stderr))
+ if "--quiet" not in options:
+ ## add logging to 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)
+ if not _validate_input(options, False):
+ fail_usage("validate-all failed")
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):
+def run_command(options, command, timeout=None, env=None, log_command=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)
+ logging.info("Executing: %s\n", log_command or 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:
+ if device_opt.count("ipaddr") and "--ip" not in options and "--managed" not in options and "--target" 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 = ""
+
+ mapping_longopt_names = dict([(all_opt[o].get("longopt"), o) for o in avail_opt])
+
for line in sys.stdin.readlines():
line = line.strip()
if (line.startswith("#")) or (len(line) == 0):
continue
(name, value) = (line + "=").split("=", 1)
+ name = name.replace("-", "_");
value = value[:-1]
+ if name in mapping_longopt_names:
+ name = mapping_longopt_names[name]
+
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 2185cbf8..e0cd441e 100644
--- a/fence/agents/lib/metadata.rng
+++ b/fence/agents/lib/metadata.rng
@@ -1,79 +1,80 @@
-
+
+
boolean
string
second
integer
select
0
1
diff --git a/fence/agents/powerman/fence_powerman.py b/fence/agents/powerman/fence_powerman.py
index cd499132..d9ab8495 100755
--- a/fence/agents/powerman/fence_powerman.py
+++ b/fence/agents/powerman/fence_powerman.py
@@ -1,265 +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